Completed
Pull Request — master (#9222)
by Joas
25:00 queued 07:26
created
lib/public/Comments/ICommentsManager.php 1 patch
Indentation   +244 added lines, -244 removed lines patch added patch discarded remove patch
@@ -38,267 +38,267 @@
 block discarded – undo
38 38
  */
39 39
 interface ICommentsManager {
40 40
 
41
-	/**
42
-	 * @const DELETED_USER type and id for a user that has been deleted
43
-	 * @see deleteReferencesOfActor
44
-	 * @since 9.0.0
45
-	 *
46
-	 * To be used as replacement for user type actors in deleteReferencesOfActor().
47
-	 *
48
-	 * User interfaces shall show "Deleted user" as display name, if needed.
49
-	 */
50
-	const DELETED_USER = 'deleted_users';
41
+    /**
42
+     * @const DELETED_USER type and id for a user that has been deleted
43
+     * @see deleteReferencesOfActor
44
+     * @since 9.0.0
45
+     *
46
+     * To be used as replacement for user type actors in deleteReferencesOfActor().
47
+     *
48
+     * User interfaces shall show "Deleted user" as display name, if needed.
49
+     */
50
+    const DELETED_USER = 'deleted_users';
51 51
 
52
-	/**
53
-	 * returns a comment instance
54
-	 *
55
-	 * @param string $id the ID of the comment
56
-	 * @return IComment
57
-	 * @throws NotFoundException
58
-	 * @since 9.0.0
59
-	 */
60
-	public function get($id);
52
+    /**
53
+     * returns a comment instance
54
+     *
55
+     * @param string $id the ID of the comment
56
+     * @return IComment
57
+     * @throws NotFoundException
58
+     * @since 9.0.0
59
+     */
60
+    public function get($id);
61 61
 
62
-	/**
63
-	 * returns the comment specified by the id and all it's child comments
64
-	 *
65
-	 * @param string $id
66
-	 * @param int $limit max number of entries to return, 0 returns all
67
-	 * @param int $offset the start entry
68
-	 * @return array
69
-	 * @since 9.0.0
70
-	 *
71
-	 * The return array looks like this
72
-	 * [
73
-	 * 	 'comment' => IComment, // root comment
74
-	 *   'replies' =>
75
-	 *   [
76
-	 *     0 =>
77
-	 *     [
78
-	 *       'comment' => IComment,
79
-	 *       'replies' =>
80
-	 *       [
81
-	 *         0 =>
82
-	 *         [
83
-	 *           'comment' => IComment,
84
-	 *           'replies' => [ … ]
85
-	 *         ],
86
-	 *         …
87
-	 *       ]
88
-	 *     ]
89
-	 *     1 =>
90
-	 *     [
91
-	 *       'comment' => IComment,
92
-	 *       'replies'=> [ … ]
93
-	 *     ],
94
-	 *     …
95
-	 *   ]
96
-	 * ]
97
-	 */
98
-	public function getTree($id, $limit = 0, $offset = 0);
62
+    /**
63
+     * returns the comment specified by the id and all it's child comments
64
+     *
65
+     * @param string $id
66
+     * @param int $limit max number of entries to return, 0 returns all
67
+     * @param int $offset the start entry
68
+     * @return array
69
+     * @since 9.0.0
70
+     *
71
+     * The return array looks like this
72
+     * [
73
+     * 	 'comment' => IComment, // root comment
74
+     *   'replies' =>
75
+     *   [
76
+     *     0 =>
77
+     *     [
78
+     *       'comment' => IComment,
79
+     *       'replies' =>
80
+     *       [
81
+     *         0 =>
82
+     *         [
83
+     *           'comment' => IComment,
84
+     *           'replies' => [ … ]
85
+     *         ],
86
+     *         …
87
+     *       ]
88
+     *     ]
89
+     *     1 =>
90
+     *     [
91
+     *       'comment' => IComment,
92
+     *       'replies'=> [ … ]
93
+     *     ],
94
+     *     …
95
+     *   ]
96
+     * ]
97
+     */
98
+    public function getTree($id, $limit = 0, $offset = 0);
99 99
 
100
-	/**
101
-	 * returns comments for a specific object (e.g. a file).
102
-	 *
103
-	 * The sort order is always newest to oldest.
104
-	 *
105
-	 * @param string $objectType the object type, e.g. 'files'
106
-	 * @param string $objectId the id of the object
107
-	 * @param int $limit optional, number of maximum comments to be returned. if
108
-	 * not specified, all comments are returned.
109
-	 * @param int $offset optional, starting point
110
-	 * @param \DateTime|null $notOlderThan optional, timestamp of the oldest comments
111
-	 * that may be returned
112
-	 * @return IComment[]
113
-	 * @since 9.0.0
114
-	 */
115
-	public function getForObject(
116
-			$objectType,
117
-			$objectId,
118
-			$limit = 0,
119
-			$offset = 0,
120
-			\DateTime $notOlderThan = null
121
-	);
100
+    /**
101
+     * returns comments for a specific object (e.g. a file).
102
+     *
103
+     * The sort order is always newest to oldest.
104
+     *
105
+     * @param string $objectType the object type, e.g. 'files'
106
+     * @param string $objectId the id of the object
107
+     * @param int $limit optional, number of maximum comments to be returned. if
108
+     * not specified, all comments are returned.
109
+     * @param int $offset optional, starting point
110
+     * @param \DateTime|null $notOlderThan optional, timestamp of the oldest comments
111
+     * that may be returned
112
+     * @return IComment[]
113
+     * @since 9.0.0
114
+     */
115
+    public function getForObject(
116
+            $objectType,
117
+            $objectId,
118
+            $limit = 0,
119
+            $offset = 0,
120
+            \DateTime $notOlderThan = null
121
+    );
122 122
 
123
-	/**
124
-	 * Search for comments with a given content
125
-	 *
126
-	 * @param string $search content to search for
127
-	 * @param string $objectType Limit the search by object type
128
-	 * @param string $objectId Limit the search by object id
129
-	 * @param string $verb Limit the verb of the comment
130
-	 * @return IComment[]
131
-	 * @since 14.0.0
132
-	 */
133
-	public function search(string $search, string $objectType, string $objectId, string $verb): array;
123
+    /**
124
+     * Search for comments with a given content
125
+     *
126
+     * @param string $search content to search for
127
+     * @param string $objectType Limit the search by object type
128
+     * @param string $objectId Limit the search by object id
129
+     * @param string $verb Limit the verb of the comment
130
+     * @return IComment[]
131
+     * @since 14.0.0
132
+     */
133
+    public function search(string $search, string $objectType, string $objectId, string $verb): array;
134 134
 
135
-	/**
136
-	 * @param $objectType string the object type, e.g. 'files'
137
-	 * @param $objectId string the id of the object
138
-	 * @param \DateTime|null $notOlderThan optional, timestamp of the oldest comments
139
-	 * that may be returned
140
-	 * @return Int
141
-	 * @since 9.0.0
142
-	 */
143
-	public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null);
135
+    /**
136
+     * @param $objectType string the object type, e.g. 'files'
137
+     * @param $objectId string the id of the object
138
+     * @param \DateTime|null $notOlderThan optional, timestamp of the oldest comments
139
+     * that may be returned
140
+     * @return Int
141
+     * @since 9.0.0
142
+     */
143
+    public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null);
144 144
 
145
-	/**
146
-	 * Get the number of unread comments for all files in a folder
147
-	 *
148
-	 * @param int $folderId
149
-	 * @param IUser $user
150
-	 * @return array [$fileId => $unreadCount]
151
-	 * @since 12.0.0
152
-	 */
153
-	public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user);
145
+    /**
146
+     * Get the number of unread comments for all files in a folder
147
+     *
148
+     * @param int $folderId
149
+     * @param IUser $user
150
+     * @return array [$fileId => $unreadCount]
151
+     * @since 12.0.0
152
+     */
153
+    public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user);
154 154
 
155
-	/**
156
-	 * creates a new comment and returns it. At this point of time, it is not
157
-	 * saved in the used data storage. Use save() after setting other fields
158
-	 * of the comment (e.g. message or verb).
159
-	 *
160
-	 * @param string $actorType the actor type (e.g. 'users')
161
-	 * @param string $actorId a user id
162
-	 * @param string $objectType the object type the comment is attached to
163
-	 * @param string $objectId the object id the comment is attached to
164
-	 * @return IComment
165
-	 * @since 9.0.0
166
-	 */
167
-	public function create($actorType, $actorId, $objectType, $objectId);
155
+    /**
156
+     * creates a new comment and returns it. At this point of time, it is not
157
+     * saved in the used data storage. Use save() after setting other fields
158
+     * of the comment (e.g. message or verb).
159
+     *
160
+     * @param string $actorType the actor type (e.g. 'users')
161
+     * @param string $actorId a user id
162
+     * @param string $objectType the object type the comment is attached to
163
+     * @param string $objectId the object id the comment is attached to
164
+     * @return IComment
165
+     * @since 9.0.0
166
+     */
167
+    public function create($actorType, $actorId, $objectType, $objectId);
168 168
 
169
-	/**
170
-	 * permanently deletes the comment specified by the ID
171
-	 *
172
-	 * When the comment has child comments, their parent ID will be changed to
173
-	 * the parent ID of the item that is to be deleted.
174
-	 *
175
-	 * @param string $id
176
-	 * @return bool
177
-	 * @since 9.0.0
178
-	 */
179
-	public function delete($id);
169
+    /**
170
+     * permanently deletes the comment specified by the ID
171
+     *
172
+     * When the comment has child comments, their parent ID will be changed to
173
+     * the parent ID of the item that is to be deleted.
174
+     *
175
+     * @param string $id
176
+     * @return bool
177
+     * @since 9.0.0
178
+     */
179
+    public function delete($id);
180 180
 
181
-	/**
182
-	 * saves the comment permanently
183
-	 *
184
-	 * if the supplied comment has an empty ID, a new entry comment will be
185
-	 * saved and the instance updated with the new ID.
186
-	 *
187
-	 * Otherwise, an existing comment will be updated.
188
-	 *
189
-	 * Throws NotFoundException when a comment that is to be updated does not
190
-	 * exist anymore at this point of time.
191
-	 *
192
-	 * @param IComment $comment
193
-	 * @return bool
194
-	 * @throws NotFoundException
195
-	 * @since 9.0.0
196
-	 */
197
-	public function save(IComment $comment);
181
+    /**
182
+     * saves the comment permanently
183
+     *
184
+     * if the supplied comment has an empty ID, a new entry comment will be
185
+     * saved and the instance updated with the new ID.
186
+     *
187
+     * Otherwise, an existing comment will be updated.
188
+     *
189
+     * Throws NotFoundException when a comment that is to be updated does not
190
+     * exist anymore at this point of time.
191
+     *
192
+     * @param IComment $comment
193
+     * @return bool
194
+     * @throws NotFoundException
195
+     * @since 9.0.0
196
+     */
197
+    public function save(IComment $comment);
198 198
 
199
-	/**
200
-	 * removes references to specific actor (e.g. on user delete) of a comment.
201
-	 * The comment itself must not get lost/deleted.
202
-	 *
203
-	 * A 'users' type actor (type and id) should get replaced by the
204
-	 * value of the DELETED_USER constant of this interface.
205
-	 *
206
-	 * @param string $actorType the actor type (e.g. 'users')
207
-	 * @param string $actorId a user id
208
-	 * @return boolean
209
-	 * @since 9.0.0
210
-	 */
211
-	public function deleteReferencesOfActor($actorType, $actorId);
199
+    /**
200
+     * removes references to specific actor (e.g. on user delete) of a comment.
201
+     * The comment itself must not get lost/deleted.
202
+     *
203
+     * A 'users' type actor (type and id) should get replaced by the
204
+     * value of the DELETED_USER constant of this interface.
205
+     *
206
+     * @param string $actorType the actor type (e.g. 'users')
207
+     * @param string $actorId a user id
208
+     * @return boolean
209
+     * @since 9.0.0
210
+     */
211
+    public function deleteReferencesOfActor($actorType, $actorId);
212 212
 
213
-	/**
214
-	 * deletes all comments made of a specific object (e.g. on file delete)
215
-	 *
216
-	 * @param string $objectType the object type (e.g. 'files')
217
-	 * @param string $objectId e.g. the file id
218
-	 * @return boolean
219
-	 * @since 9.0.0
220
-	 */
221
-	public function deleteCommentsAtObject($objectType, $objectId);
213
+    /**
214
+     * deletes all comments made of a specific object (e.g. on file delete)
215
+     *
216
+     * @param string $objectType the object type (e.g. 'files')
217
+     * @param string $objectId e.g. the file id
218
+     * @return boolean
219
+     * @since 9.0.0
220
+     */
221
+    public function deleteCommentsAtObject($objectType, $objectId);
222 222
 
223
-	/**
224
-	 * sets the read marker for a given file to the specified date for the
225
-	 * provided user
226
-	 *
227
-	 * @param string $objectType
228
-	 * @param string $objectId
229
-	 * @param \DateTime $dateTime
230
-	 * @param \OCP\IUser $user
231
-	 * @since 9.0.0
232
-	 */
233
-	public function setReadMark($objectType, $objectId, \DateTime $dateTime, \OCP\IUser $user);
223
+    /**
224
+     * sets the read marker for a given file to the specified date for the
225
+     * provided user
226
+     *
227
+     * @param string $objectType
228
+     * @param string $objectId
229
+     * @param \DateTime $dateTime
230
+     * @param \OCP\IUser $user
231
+     * @since 9.0.0
232
+     */
233
+    public function setReadMark($objectType, $objectId, \DateTime $dateTime, \OCP\IUser $user);
234 234
 
235
-	/**
236
-	 * returns the read marker for a given file to the specified date for the
237
-	 * provided user. It returns null, when the marker is not present, i.e.
238
-	 * no comments were marked as read.
239
-	 *
240
-	 * @param string $objectType
241
-	 * @param string $objectId
242
-	 * @param \OCP\IUser $user
243
-	 * @return \DateTime|null
244
-	 * @since 9.0.0
245
-	 */
246
-	public function getReadMark($objectType, $objectId, \OCP\IUser $user);
235
+    /**
236
+     * returns the read marker for a given file to the specified date for the
237
+     * provided user. It returns null, when the marker is not present, i.e.
238
+     * no comments were marked as read.
239
+     *
240
+     * @param string $objectType
241
+     * @param string $objectId
242
+     * @param \OCP\IUser $user
243
+     * @return \DateTime|null
244
+     * @since 9.0.0
245
+     */
246
+    public function getReadMark($objectType, $objectId, \OCP\IUser $user);
247 247
 
248
-	/**
249
-	 * deletes the read markers for the specified user
250
-	 *
251
-	 * @param \OCP\IUser $user
252
-	 * @return bool
253
-	 * @since 9.0.0
254
-	 */
255
-	public function deleteReadMarksFromUser(\OCP\IUser $user);
248
+    /**
249
+     * deletes the read markers for the specified user
250
+     *
251
+     * @param \OCP\IUser $user
252
+     * @return bool
253
+     * @since 9.0.0
254
+     */
255
+    public function deleteReadMarksFromUser(\OCP\IUser $user);
256 256
 
257
-	/**
258
-	 * deletes the read markers on the specified object
259
-	 *
260
-	 * @param string $objectType
261
-	 * @param string $objectId
262
-	 * @return bool
263
-	 * @since 9.0.0
264
-	 */
265
-	public function deleteReadMarksOnObject($objectType, $objectId);
257
+    /**
258
+     * deletes the read markers on the specified object
259
+     *
260
+     * @param string $objectType
261
+     * @param string $objectId
262
+     * @return bool
263
+     * @since 9.0.0
264
+     */
265
+    public function deleteReadMarksOnObject($objectType, $objectId);
266 266
 
267
-	/**
268
-	 * registers an Entity to the manager, so event notifications can be send
269
-	 * to consumers of the comments infrastructure
270
-	 *
271
-	 * @param \Closure $closure
272
-	 * @since 11.0.0
273
-	 */
274
-	public function registerEventHandler(\Closure $closure);
267
+    /**
268
+     * registers an Entity to the manager, so event notifications can be send
269
+     * to consumers of the comments infrastructure
270
+     *
271
+     * @param \Closure $closure
272
+     * @since 11.0.0
273
+     */
274
+    public function registerEventHandler(\Closure $closure);
275 275
 
276
-	/**
277
-	 * registers a method that resolves an ID to a display name for a given type
278
-	 *
279
-	 * @param string $type
280
-	 * @param \Closure $closure
281
-	 * @throws \OutOfBoundsException
282
-	 * @since 11.0.0
283
-	 *
284
-	 * Only one resolver shall be registered per type. Otherwise a
285
-	 * \OutOfBoundsException has to thrown.
286
-	 */
287
-	public function registerDisplayNameResolver($type, \Closure $closure);
276
+    /**
277
+     * registers a method that resolves an ID to a display name for a given type
278
+     *
279
+     * @param string $type
280
+     * @param \Closure $closure
281
+     * @throws \OutOfBoundsException
282
+     * @since 11.0.0
283
+     *
284
+     * Only one resolver shall be registered per type. Otherwise a
285
+     * \OutOfBoundsException has to thrown.
286
+     */
287
+    public function registerDisplayNameResolver($type, \Closure $closure);
288 288
 
289
-	/**
290
-	 * resolves a given ID of a given Type to a display name.
291
-	 *
292
-	 * @param string $type
293
-	 * @param string $id
294
-	 * @return string
295
-	 * @throws \OutOfBoundsException
296
-	 * @since 11.0.0
297
-	 *
298
-	 * If a provided type was not registered, an \OutOfBoundsException shall
299
-	 * be thrown. It is upon the resolver discretion what to return of the
300
-	 * provided ID is unknown. It must be ensured that a string is returned.
301
-	 */
302
-	public function resolveDisplayName($type, $id);
289
+    /**
290
+     * resolves a given ID of a given Type to a display name.
291
+     *
292
+     * @param string $type
293
+     * @param string $id
294
+     * @return string
295
+     * @throws \OutOfBoundsException
296
+     * @since 11.0.0
297
+     *
298
+     * If a provided type was not registered, an \OutOfBoundsException shall
299
+     * be thrown. It is upon the resolver discretion what to return of the
300
+     * provided ID is unknown. It must be ensured that a string is returned.
301
+     */
302
+    public function resolveDisplayName($type, $id);
303 303
 
304 304
 }
Please login to merge, or discard this patch.
lib/public/Search/Provider.php 1 patch
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -32,60 +32,60 @@
 block discarded – undo
32 32
  */
33 33
 abstract class Provider {
34 34
 
35
-	/**
36
-	 * @since 8.0.0
37
-	 */
38
-	const OPTION_APPS = 'apps';
35
+    /**
36
+     * @since 8.0.0
37
+     */
38
+    const OPTION_APPS = 'apps';
39 39
 
40
-	/**
41
-	 * List of options
42
-	 * @var array
43
-	 * @since 7.0.0
44
-	 */
45
-	protected $options;
40
+    /**
41
+     * List of options
42
+     * @var array
43
+     * @since 7.0.0
44
+     */
45
+    protected $options;
46 46
 
47
-	/**
48
-	 * Constructor
49
-	 * @param array $options as key => value
50
-	 * @since 7.0.0 - default value for $options was added in 8.0.0
51
-	 */
52
-	public function __construct($options = array()) {
53
-		$this->options = $options;
54
-	}
47
+    /**
48
+     * Constructor
49
+     * @param array $options as key => value
50
+     * @since 7.0.0 - default value for $options was added in 8.0.0
51
+     */
52
+    public function __construct($options = array()) {
53
+        $this->options = $options;
54
+    }
55 55
 
56
-	/**
57
-	 * get a value from the options array or null
58
-	 * @param string $key
59
-	 * @return mixed
60
-	 * @since 8.0.0
61
-	 */
62
-	public function getOption($key) {
63
-		if (is_array($this->options) && isset($this->options[$key])) {
64
-			return $this->options[$key];
65
-		} else {
66
-			return null;
67
-		}
68
-	}
56
+    /**
57
+     * get a value from the options array or null
58
+     * @param string $key
59
+     * @return mixed
60
+     * @since 8.0.0
61
+     */
62
+    public function getOption($key) {
63
+        if (is_array($this->options) && isset($this->options[$key])) {
64
+            return $this->options[$key];
65
+        } else {
66
+            return null;
67
+        }
68
+    }
69 69
 
70
-	/**
71
-	 * checks if the given apps and the apps this provider has results for intersect
72
-	 * returns true if the given array is empty (all apps)
73
-	 * or if this provider does not have a list of apps it provides results for (legacy search providers)
74
-	 * or if the two above arrays have elements in common (intersect)
75
-	 * @param string[] $apps
76
-	 * @return bool
77
-	 * @since 8.0.0
78
-	 */
79
-	public function providesResultsFor(array $apps = array()) {
80
-		$forApps = $this->getOption(self::OPTION_APPS);
81
-		return empty($apps) || empty($forApps) || array_intersect($forApps, $apps);
82
-	}
70
+    /**
71
+     * checks if the given apps and the apps this provider has results for intersect
72
+     * returns true if the given array is empty (all apps)
73
+     * or if this provider does not have a list of apps it provides results for (legacy search providers)
74
+     * or if the two above arrays have elements in common (intersect)
75
+     * @param string[] $apps
76
+     * @return bool
77
+     * @since 8.0.0
78
+     */
79
+    public function providesResultsFor(array $apps = array()) {
80
+        $forApps = $this->getOption(self::OPTION_APPS);
81
+        return empty($apps) || empty($forApps) || array_intersect($forApps, $apps);
82
+    }
83 83
 
84
-	/**
85
-	 * Search for $query
86
-	 * @param string $query
87
-	 * @return Result[] An array of OCP\Search\Result's
88
-	 * @since 7.0.0
89
-	 */
90
-	abstract public function search($query);
84
+    /**
85
+     * Search for $query
86
+     * @param string $query
87
+     * @return Result[] An array of OCP\Search\Result's
88
+     * @since 7.0.0
89
+     */
90
+    abstract public function search($query);
91 91
 }
Please login to merge, or discard this patch.
lib/private/Comments/Manager.php 1 patch
Indentation   +888 added lines, -888 removed lines patch added patch discarded remove patch
@@ -41,892 +41,892 @@
 block discarded – undo
41 41
 
42 42
 class Manager implements ICommentsManager {
43 43
 
44
-	/** @var  IDBConnection */
45
-	protected $dbConn;
46
-
47
-	/** @var  ILogger */
48
-	protected $logger;
49
-
50
-	/** @var IConfig */
51
-	protected $config;
52
-
53
-	/** @var IComment[] */
54
-	protected $commentsCache = [];
55
-
56
-	/** @var  \Closure[] */
57
-	protected $eventHandlerClosures = [];
58
-
59
-	/** @var  ICommentsEventHandler[] */
60
-	protected $eventHandlers = [];
61
-
62
-	/** @var \Closure[] */
63
-	protected $displayNameResolvers = [];
64
-
65
-	/**
66
-	 * Manager constructor.
67
-	 *
68
-	 * @param IDBConnection $dbConn
69
-	 * @param ILogger $logger
70
-	 * @param IConfig $config
71
-	 */
72
-	public function __construct(
73
-		IDBConnection $dbConn,
74
-		ILogger $logger,
75
-		IConfig $config
76
-	) {
77
-		$this->dbConn = $dbConn;
78
-		$this->logger = $logger;
79
-		$this->config = $config;
80
-	}
81
-
82
-	/**
83
-	 * converts data base data into PHP native, proper types as defined by
84
-	 * IComment interface.
85
-	 *
86
-	 * @param array $data
87
-	 * @return array
88
-	 */
89
-	protected function normalizeDatabaseData(array $data) {
90
-		$data['id'] = (string)$data['id'];
91
-		$data['parent_id'] = (string)$data['parent_id'];
92
-		$data['topmost_parent_id'] = (string)$data['topmost_parent_id'];
93
-		$data['creation_timestamp'] = new \DateTime($data['creation_timestamp']);
94
-		if (!is_null($data['latest_child_timestamp'])) {
95
-			$data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']);
96
-		}
97
-		$data['children_count'] = (int)$data['children_count'];
98
-		return $data;
99
-	}
100
-
101
-	/**
102
-	 * prepares a comment for an insert or update operation after making sure
103
-	 * all necessary fields have a value assigned.
104
-	 *
105
-	 * @param IComment $comment
106
-	 * @return IComment returns the same updated IComment instance as provided
107
-	 *                  by parameter for convenience
108
-	 * @throws \UnexpectedValueException
109
-	 */
110
-	protected function prepareCommentForDatabaseWrite(IComment $comment) {
111
-		if (!$comment->getActorType()
112
-			|| !$comment->getActorId()
113
-			|| !$comment->getObjectType()
114
-			|| !$comment->getObjectId()
115
-			|| !$comment->getVerb()
116
-		) {
117
-			throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving');
118
-		}
119
-
120
-		if ($comment->getId() === '') {
121
-			$comment->setChildrenCount(0);
122
-			$comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC')));
123
-			$comment->setLatestChildDateTime(null);
124
-		}
125
-
126
-		if (is_null($comment->getCreationDateTime())) {
127
-			$comment->setCreationDateTime(new \DateTime());
128
-		}
129
-
130
-		if ($comment->getParentId() !== '0') {
131
-			$comment->setTopmostParentId($this->determineTopmostParentId($comment->getParentId()));
132
-		} else {
133
-			$comment->setTopmostParentId('0');
134
-		}
135
-
136
-		$this->cache($comment);
137
-
138
-		return $comment;
139
-	}
140
-
141
-	/**
142
-	 * returns the topmost parent id of a given comment identified by ID
143
-	 *
144
-	 * @param string $id
145
-	 * @return string
146
-	 * @throws NotFoundException
147
-	 */
148
-	protected function determineTopmostParentId($id) {
149
-		$comment = $this->get($id);
150
-		if ($comment->getParentId() === '0') {
151
-			return $comment->getId();
152
-		} else {
153
-			return $this->determineTopmostParentId($comment->getId());
154
-		}
155
-	}
156
-
157
-	/**
158
-	 * updates child information of a comment
159
-	 *
160
-	 * @param string $id
161
-	 * @param \DateTime $cDateTime the date time of the most recent child
162
-	 * @throws NotFoundException
163
-	 */
164
-	protected function updateChildrenInformation($id, \DateTime $cDateTime) {
165
-		$qb = $this->dbConn->getQueryBuilder();
166
-		$query = $qb->select($qb->createFunction('COUNT(`id`)'))
167
-			->from('comments')
168
-			->where($qb->expr()->eq('parent_id', $qb->createParameter('id')))
169
-			->setParameter('id', $id);
170
-
171
-		$resultStatement = $query->execute();
172
-		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
173
-		$resultStatement->closeCursor();
174
-		$children = (int)$data[0];
175
-
176
-		$comment = $this->get($id);
177
-		$comment->setChildrenCount($children);
178
-		$comment->setLatestChildDateTime($cDateTime);
179
-		$this->save($comment);
180
-	}
181
-
182
-	/**
183
-	 * Tests whether actor or object type and id parameters are acceptable.
184
-	 * Throws exception if not.
185
-	 *
186
-	 * @param string $role
187
-	 * @param string $type
188
-	 * @param string $id
189
-	 * @throws \InvalidArgumentException
190
-	 */
191
-	protected function checkRoleParameters($role, $type, $id) {
192
-		if (
193
-			!is_string($type) || empty($type)
194
-			|| !is_string($id) || empty($id)
195
-		) {
196
-			throw new \InvalidArgumentException($role . ' parameters must be string and not empty');
197
-		}
198
-	}
199
-
200
-	/**
201
-	 * run-time caches a comment
202
-	 *
203
-	 * @param IComment $comment
204
-	 */
205
-	protected function cache(IComment $comment) {
206
-		$id = $comment->getId();
207
-		if (empty($id)) {
208
-			return;
209
-		}
210
-		$this->commentsCache[(string)$id] = $comment;
211
-	}
212
-
213
-	/**
214
-	 * removes an entry from the comments run time cache
215
-	 *
216
-	 * @param mixed $id the comment's id
217
-	 */
218
-	protected function uncache($id) {
219
-		$id = (string)$id;
220
-		if (isset($this->commentsCache[$id])) {
221
-			unset($this->commentsCache[$id]);
222
-		}
223
-	}
224
-
225
-	/**
226
-	 * returns a comment instance
227
-	 *
228
-	 * @param string $id the ID of the comment
229
-	 * @return IComment
230
-	 * @throws NotFoundException
231
-	 * @throws \InvalidArgumentException
232
-	 * @since 9.0.0
233
-	 */
234
-	public function get($id) {
235
-		if ((int)$id === 0) {
236
-			throw new \InvalidArgumentException('IDs must be translatable to a number in this implementation.');
237
-		}
238
-
239
-		if (isset($this->commentsCache[$id])) {
240
-			return $this->commentsCache[$id];
241
-		}
242
-
243
-		$qb = $this->dbConn->getQueryBuilder();
244
-		$resultStatement = $qb->select('*')
245
-			->from('comments')
246
-			->where($qb->expr()->eq('id', $qb->createParameter('id')))
247
-			->setParameter('id', $id, IQueryBuilder::PARAM_INT)
248
-			->execute();
249
-
250
-		$data = $resultStatement->fetch();
251
-		$resultStatement->closeCursor();
252
-		if (!$data) {
253
-			throw new NotFoundException();
254
-		}
255
-
256
-		$comment = new Comment($this->normalizeDatabaseData($data));
257
-		$this->cache($comment);
258
-		return $comment;
259
-	}
260
-
261
-	/**
262
-	 * returns the comment specified by the id and all it's child comments.
263
-	 * At this point of time, we do only support one level depth.
264
-	 *
265
-	 * @param string $id
266
-	 * @param int $limit max number of entries to return, 0 returns all
267
-	 * @param int $offset the start entry
268
-	 * @return array
269
-	 * @since 9.0.0
270
-	 *
271
-	 * The return array looks like this
272
-	 * [
273
-	 *   'comment' => IComment, // root comment
274
-	 *   'replies' =>
275
-	 *   [
276
-	 *     0 =>
277
-	 *     [
278
-	 *       'comment' => IComment,
279
-	 *       'replies' => []
280
-	 *     ]
281
-	 *     1 =>
282
-	 *     [
283
-	 *       'comment' => IComment,
284
-	 *       'replies'=> []
285
-	 *     ],
286
-	 *     …
287
-	 *   ]
288
-	 * ]
289
-	 */
290
-	public function getTree($id, $limit = 0, $offset = 0) {
291
-		$tree = [];
292
-		$tree['comment'] = $this->get($id);
293
-		$tree['replies'] = [];
294
-
295
-		$qb = $this->dbConn->getQueryBuilder();
296
-		$query = $qb->select('*')
297
-			->from('comments')
298
-			->where($qb->expr()->eq('topmost_parent_id', $qb->createParameter('id')))
299
-			->orderBy('creation_timestamp', 'DESC')
300
-			->setParameter('id', $id);
301
-
302
-		if ($limit > 0) {
303
-			$query->setMaxResults($limit);
304
-		}
305
-		if ($offset > 0) {
306
-			$query->setFirstResult($offset);
307
-		}
308
-
309
-		$resultStatement = $query->execute();
310
-		while ($data = $resultStatement->fetch()) {
311
-			$comment = new Comment($this->normalizeDatabaseData($data));
312
-			$this->cache($comment);
313
-			$tree['replies'][] = [
314
-				'comment' => $comment,
315
-				'replies' => []
316
-			];
317
-		}
318
-		$resultStatement->closeCursor();
319
-
320
-		return $tree;
321
-	}
322
-
323
-	/**
324
-	 * returns comments for a specific object (e.g. a file).
325
-	 *
326
-	 * The sort order is always newest to oldest.
327
-	 *
328
-	 * @param string $objectType the object type, e.g. 'files'
329
-	 * @param string $objectId the id of the object
330
-	 * @param int $limit optional, number of maximum comments to be returned. if
331
-	 * not specified, all comments are returned.
332
-	 * @param int $offset optional, starting point
333
-	 * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
334
-	 * that may be returned
335
-	 * @return IComment[]
336
-	 * @since 9.0.0
337
-	 */
338
-	public function getForObject(
339
-		$objectType,
340
-		$objectId,
341
-		$limit = 0,
342
-		$offset = 0,
343
-		\DateTime $notOlderThan = null
344
-	) {
345
-		$comments = [];
346
-
347
-		$qb = $this->dbConn->getQueryBuilder();
348
-		$query = $qb->select('*')
349
-			->from('comments')
350
-			->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
351
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
352
-			->orderBy('creation_timestamp', 'DESC')
353
-			->setParameter('type', $objectType)
354
-			->setParameter('id', $objectId);
355
-
356
-		if ($limit > 0) {
357
-			$query->setMaxResults($limit);
358
-		}
359
-		if ($offset > 0) {
360
-			$query->setFirstResult($offset);
361
-		}
362
-		if (!is_null($notOlderThan)) {
363
-			$query
364
-				->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
365
-				->setParameter('notOlderThan', $notOlderThan, 'datetime');
366
-		}
367
-
368
-		$resultStatement = $query->execute();
369
-		while ($data = $resultStatement->fetch()) {
370
-			$comment = new Comment($this->normalizeDatabaseData($data));
371
-			$this->cache($comment);
372
-			$comments[] = $comment;
373
-		}
374
-		$resultStatement->closeCursor();
375
-
376
-		return $comments;
377
-	}
378
-
379
-	/**
380
-	 * Search for comments with a given content
381
-	 *
382
-	 * @param string $search content to search for
383
-	 * @param string $objectType Limit the search by object type
384
-	 * @param string $objectId Limit the search by object id
385
-	 * @param string $verb Limit the verb of the comment
386
-	 * @return IComment[]
387
-	 */
388
-	public function search(string $search, string $objectType, string $objectId, string $verb): array {
389
-		$query = $this->dbConn->getQueryBuilder();
390
-
391
-		$query->select('*')
392
-			->from('comments')
393
-			->where($query->expr()->iLike('message', $query->createNamedParameter(
394
-				'%' . $this->dbConn->escapeLikeParameter($search). '%'
395
-			)))
396
-			->orderBy('creation_timestamp', 'DESC')
397
-			->addOrderBy('id', 'DESC');
398
-
399
-		if ($objectType !== '') {
400
-			$query->andWhere($query->expr()->eq('object_type', $query->createNamedParameter($objectType)));
401
-		}
402
-		if ($objectId !== '') {
403
-			$query->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
404
-		}
405
-		if ($verb !== '') {
406
-			$query->andWhere($query->expr()->eq('verb', $query->createNamedParameter($verb)));
407
-		}
408
-
409
-		$comments = [];
410
-		$result = $query->execute();
411
-		while ($data = $result->fetch()) {
412
-			$comment = new Comment($this->normalizeDatabaseData($data));
413
-			$this->cache($comment);
414
-			$comments[] = $comment;
415
-		}
416
-		$result->closeCursor();
417
-
418
-		return $comments;
419
-	}
420
-
421
-	/**
422
-	 * @param $objectType string the object type, e.g. 'files'
423
-	 * @param $objectId string the id of the object
424
-	 * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
425
-	 * that may be returned
426
-	 * @return Int
427
-	 * @since 9.0.0
428
-	 */
429
-	public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null) {
430
-		$qb = $this->dbConn->getQueryBuilder();
431
-		$query = $qb->select($qb->createFunction('COUNT(`id`)'))
432
-			->from('comments')
433
-			->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
434
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
435
-			->setParameter('type', $objectType)
436
-			->setParameter('id', $objectId);
437
-
438
-		if (!is_null($notOlderThan)) {
439
-			$query
440
-				->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
441
-				->setParameter('notOlderThan', $notOlderThan, 'datetime');
442
-		}
443
-
444
-		$resultStatement = $query->execute();
445
-		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
446
-		$resultStatement->closeCursor();
447
-		return (int)$data[0];
448
-	}
449
-
450
-	/**
451
-	 * Get the number of unread comments for all files in a folder
452
-	 *
453
-	 * @param int $folderId
454
-	 * @param IUser $user
455
-	 * @return array [$fileId => $unreadCount]
456
-	 */
457
-	public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user) {
458
-		$qb = $this->dbConn->getQueryBuilder();
459
-		$query = $qb->select('f.fileid')
460
-			->selectAlias(
461
-				$qb->createFunction('COUNT(' . $qb->getColumnName('c.id') . ')'),
462
-				'num_ids'
463
-			)
464
-			->from('comments', 'c')
465
-			->innerJoin('c', 'filecache', 'f', $qb->expr()->andX(
466
-				$qb->expr()->eq('c.object_type', $qb->createNamedParameter('files')),
467
-				$qb->expr()->eq('f.fileid', $qb->expr()->castColumn('c.object_id', IQueryBuilder::PARAM_INT))
468
-			))
469
-			->leftJoin('c', 'comments_read_markers', 'm', $qb->expr()->andX(
470
-				$qb->expr()->eq('m.object_type', $qb->createNamedParameter('files')),
471
-				$qb->expr()->eq('m.object_id', 'c.object_id'),
472
-				$qb->expr()->eq('m.user_id', $qb->createNamedParameter($user->getUID()))
473
-			))
474
-			->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($folderId)))
475
-			->andWhere($qb->expr()->orX(
476
-				$qb->expr()->gt('c.creation_timestamp', 'marker_datetime'),
477
-				$qb->expr()->isNull('marker_datetime')
478
-			))
479
-			->groupBy('f.fileid');
480
-
481
-		$resultStatement = $query->execute();
482
-
483
-		$results = [];
484
-		while ($row = $resultStatement->fetch()) {
485
-			$results[$row['fileid']] = (int) $row['num_ids'];
486
-		}
487
-		$resultStatement->closeCursor();
488
-		return $results;
489
-	}
490
-
491
-	/**
492
-	 * creates a new comment and returns it. At this point of time, it is not
493
-	 * saved in the used data storage. Use save() after setting other fields
494
-	 * of the comment (e.g. message or verb).
495
-	 *
496
-	 * @param string $actorType the actor type (e.g. 'users')
497
-	 * @param string $actorId a user id
498
-	 * @param string $objectType the object type the comment is attached to
499
-	 * @param string $objectId the object id the comment is attached to
500
-	 * @return IComment
501
-	 * @since 9.0.0
502
-	 */
503
-	public function create($actorType, $actorId, $objectType, $objectId) {
504
-		$comment = new Comment();
505
-		$comment
506
-			->setActor($actorType, $actorId)
507
-			->setObject($objectType, $objectId);
508
-		return $comment;
509
-	}
510
-
511
-	/**
512
-	 * permanently deletes the comment specified by the ID
513
-	 *
514
-	 * When the comment has child comments, their parent ID will be changed to
515
-	 * the parent ID of the item that is to be deleted.
516
-	 *
517
-	 * @param string $id
518
-	 * @return bool
519
-	 * @throws \InvalidArgumentException
520
-	 * @since 9.0.0
521
-	 */
522
-	public function delete($id) {
523
-		if (!is_string($id)) {
524
-			throw new \InvalidArgumentException('Parameter must be string');
525
-		}
526
-
527
-		try {
528
-			$comment = $this->get($id);
529
-		} catch (\Exception $e) {
530
-			// Ignore exceptions, we just don't fire a hook then
531
-			$comment = null;
532
-		}
533
-
534
-		$qb = $this->dbConn->getQueryBuilder();
535
-		$query = $qb->delete('comments')
536
-			->where($qb->expr()->eq('id', $qb->createParameter('id')))
537
-			->setParameter('id', $id);
538
-
539
-		try {
540
-			$affectedRows = $query->execute();
541
-			$this->uncache($id);
542
-		} catch (DriverException $e) {
543
-			$this->logger->logException($e, ['app' => 'core_comments']);
544
-			return false;
545
-		}
546
-
547
-		if ($affectedRows > 0 && $comment instanceof IComment) {
548
-			$this->sendEvent(CommentsEvent::EVENT_DELETE, $comment);
549
-		}
550
-
551
-		return ($affectedRows > 0);
552
-	}
553
-
554
-	/**
555
-	 * saves the comment permanently
556
-	 *
557
-	 * if the supplied comment has an empty ID, a new entry comment will be
558
-	 * saved and the instance updated with the new ID.
559
-	 *
560
-	 * Otherwise, an existing comment will be updated.
561
-	 *
562
-	 * Throws NotFoundException when a comment that is to be updated does not
563
-	 * exist anymore at this point of time.
564
-	 *
565
-	 * @param IComment $comment
566
-	 * @return bool
567
-	 * @throws NotFoundException
568
-	 * @since 9.0.0
569
-	 */
570
-	public function save(IComment $comment) {
571
-		if ($this->prepareCommentForDatabaseWrite($comment)->getId() === '') {
572
-			$result = $this->insert($comment);
573
-		} else {
574
-			$result = $this->update($comment);
575
-		}
576
-
577
-		if ($result && !!$comment->getParentId()) {
578
-			$this->updateChildrenInformation(
579
-				$comment->getParentId(),
580
-				$comment->getCreationDateTime()
581
-			);
582
-			$this->cache($comment);
583
-		}
584
-
585
-		return $result;
586
-	}
587
-
588
-	/**
589
-	 * inserts the provided comment in the database
590
-	 *
591
-	 * @param IComment $comment
592
-	 * @return bool
593
-	 */
594
-	protected function insert(IComment &$comment) {
595
-		$qb = $this->dbConn->getQueryBuilder();
596
-		$affectedRows = $qb
597
-			->insert('comments')
598
-			->values([
599
-				'parent_id' => $qb->createNamedParameter($comment->getParentId()),
600
-				'topmost_parent_id' => $qb->createNamedParameter($comment->getTopmostParentId()),
601
-				'children_count' => $qb->createNamedParameter($comment->getChildrenCount()),
602
-				'actor_type' => $qb->createNamedParameter($comment->getActorType()),
603
-				'actor_id' => $qb->createNamedParameter($comment->getActorId()),
604
-				'message' => $qb->createNamedParameter($comment->getMessage()),
605
-				'verb' => $qb->createNamedParameter($comment->getVerb()),
606
-				'creation_timestamp' => $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'),
607
-				'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
608
-				'object_type' => $qb->createNamedParameter($comment->getObjectType()),
609
-				'object_id' => $qb->createNamedParameter($comment->getObjectId()),
610
-			])
611
-			->execute();
612
-
613
-		if ($affectedRows > 0) {
614
-			$comment->setId((string)$qb->getLastInsertId());
615
-			$this->sendEvent(CommentsEvent::EVENT_ADD, $comment);
616
-		}
617
-
618
-		return $affectedRows > 0;
619
-	}
620
-
621
-	/**
622
-	 * updates a Comment data row
623
-	 *
624
-	 * @param IComment $comment
625
-	 * @return bool
626
-	 * @throws NotFoundException
627
-	 */
628
-	protected function update(IComment $comment) {
629
-		// for properly working preUpdate Events we need the old comments as is
630
-		// in the DB and overcome caching. Also avoid that outdated information stays.
631
-		$this->uncache($comment->getId());
632
-		$this->sendEvent(CommentsEvent::EVENT_PRE_UPDATE, $this->get($comment->getId()));
633
-		$this->uncache($comment->getId());
634
-
635
-		$qb = $this->dbConn->getQueryBuilder();
636
-		$affectedRows = $qb
637
-			->update('comments')
638
-			->set('parent_id', $qb->createNamedParameter($comment->getParentId()))
639
-			->set('topmost_parent_id', $qb->createNamedParameter($comment->getTopmostParentId()))
640
-			->set('children_count', $qb->createNamedParameter($comment->getChildrenCount()))
641
-			->set('actor_type', $qb->createNamedParameter($comment->getActorType()))
642
-			->set('actor_id', $qb->createNamedParameter($comment->getActorId()))
643
-			->set('message', $qb->createNamedParameter($comment->getMessage()))
644
-			->set('verb', $qb->createNamedParameter($comment->getVerb()))
645
-			->set('creation_timestamp', $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'))
646
-			->set('latest_child_timestamp', $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'))
647
-			->set('object_type', $qb->createNamedParameter($comment->getObjectType()))
648
-			->set('object_id', $qb->createNamedParameter($comment->getObjectId()))
649
-			->where($qb->expr()->eq('id', $qb->createParameter('id')))
650
-			->setParameter('id', $comment->getId())
651
-			->execute();
652
-
653
-		if ($affectedRows === 0) {
654
-			throw new NotFoundException('Comment to update does ceased to exist');
655
-		}
656
-
657
-		$this->sendEvent(CommentsEvent::EVENT_UPDATE, $comment);
658
-
659
-		return $affectedRows > 0;
660
-	}
661
-
662
-	/**
663
-	 * removes references to specific actor (e.g. on user delete) of a comment.
664
-	 * The comment itself must not get lost/deleted.
665
-	 *
666
-	 * @param string $actorType the actor type (e.g. 'users')
667
-	 * @param string $actorId a user id
668
-	 * @return boolean
669
-	 * @since 9.0.0
670
-	 */
671
-	public function deleteReferencesOfActor($actorType, $actorId) {
672
-		$this->checkRoleParameters('Actor', $actorType, $actorId);
673
-
674
-		$qb = $this->dbConn->getQueryBuilder();
675
-		$affectedRows = $qb
676
-			->update('comments')
677
-			->set('actor_type', $qb->createNamedParameter(ICommentsManager::DELETED_USER))
678
-			->set('actor_id', $qb->createNamedParameter(ICommentsManager::DELETED_USER))
679
-			->where($qb->expr()->eq('actor_type', $qb->createParameter('type')))
680
-			->andWhere($qb->expr()->eq('actor_id', $qb->createParameter('id')))
681
-			->setParameter('type', $actorType)
682
-			->setParameter('id', $actorId)
683
-			->execute();
684
-
685
-		$this->commentsCache = [];
686
-
687
-		return is_int($affectedRows);
688
-	}
689
-
690
-	/**
691
-	 * deletes all comments made of a specific object (e.g. on file delete)
692
-	 *
693
-	 * @param string $objectType the object type (e.g. 'files')
694
-	 * @param string $objectId e.g. the file id
695
-	 * @return boolean
696
-	 * @since 9.0.0
697
-	 */
698
-	public function deleteCommentsAtObject($objectType, $objectId) {
699
-		$this->checkRoleParameters('Object', $objectType, $objectId);
700
-
701
-		$qb = $this->dbConn->getQueryBuilder();
702
-		$affectedRows = $qb
703
-			->delete('comments')
704
-			->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
705
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
706
-			->setParameter('type', $objectType)
707
-			->setParameter('id', $objectId)
708
-			->execute();
709
-
710
-		$this->commentsCache = [];
711
-
712
-		return is_int($affectedRows);
713
-	}
714
-
715
-	/**
716
-	 * deletes the read markers for the specified user
717
-	 *
718
-	 * @param \OCP\IUser $user
719
-	 * @return bool
720
-	 * @since 9.0.0
721
-	 */
722
-	public function deleteReadMarksFromUser(IUser $user) {
723
-		$qb = $this->dbConn->getQueryBuilder();
724
-		$query = $qb->delete('comments_read_markers')
725
-			->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
726
-			->setParameter('user_id', $user->getUID());
727
-
728
-		try {
729
-			$affectedRows = $query->execute();
730
-		} catch (DriverException $e) {
731
-			$this->logger->logException($e, ['app' => 'core_comments']);
732
-			return false;
733
-		}
734
-		return ($affectedRows > 0);
735
-	}
736
-
737
-	/**
738
-	 * sets the read marker for a given file to the specified date for the
739
-	 * provided user
740
-	 *
741
-	 * @param string $objectType
742
-	 * @param string $objectId
743
-	 * @param \DateTime $dateTime
744
-	 * @param IUser $user
745
-	 * @since 9.0.0
746
-	 * @suppress SqlInjectionChecker
747
-	 */
748
-	public function setReadMark($objectType, $objectId, \DateTime $dateTime, IUser $user) {
749
-		$this->checkRoleParameters('Object', $objectType, $objectId);
750
-
751
-		$qb = $this->dbConn->getQueryBuilder();
752
-		$values = [
753
-			'user_id' => $qb->createNamedParameter($user->getUID()),
754
-			'marker_datetime' => $qb->createNamedParameter($dateTime, 'datetime'),
755
-			'object_type' => $qb->createNamedParameter($objectType),
756
-			'object_id' => $qb->createNamedParameter($objectId),
757
-		];
758
-
759
-		// Strategy: try to update, if this does not return affected rows, do an insert.
760
-		$affectedRows = $qb
761
-			->update('comments_read_markers')
762
-			->set('user_id', $values['user_id'])
763
-			->set('marker_datetime', $values['marker_datetime'])
764
-			->set('object_type', $values['object_type'])
765
-			->set('object_id', $values['object_id'])
766
-			->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
767
-			->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
768
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
769
-			->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR)
770
-			->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR)
771
-			->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR)
772
-			->execute();
773
-
774
-		if ($affectedRows > 0) {
775
-			return;
776
-		}
777
-
778
-		$qb->insert('comments_read_markers')
779
-			->values($values)
780
-			->execute();
781
-	}
782
-
783
-	/**
784
-	 * returns the read marker for a given file to the specified date for the
785
-	 * provided user. It returns null, when the marker is not present, i.e.
786
-	 * no comments were marked as read.
787
-	 *
788
-	 * @param string $objectType
789
-	 * @param string $objectId
790
-	 * @param IUser $user
791
-	 * @return \DateTime|null
792
-	 * @since 9.0.0
793
-	 */
794
-	public function getReadMark($objectType, $objectId, IUser $user) {
795
-		$qb = $this->dbConn->getQueryBuilder();
796
-		$resultStatement = $qb->select('marker_datetime')
797
-			->from('comments_read_markers')
798
-			->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
799
-			->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
800
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
801
-			->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR)
802
-			->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR)
803
-			->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR)
804
-			->execute();
805
-
806
-		$data = $resultStatement->fetch();
807
-		$resultStatement->closeCursor();
808
-		if (!$data || is_null($data['marker_datetime'])) {
809
-			return null;
810
-		}
811
-
812
-		return new \DateTime($data['marker_datetime']);
813
-	}
814
-
815
-	/**
816
-	 * deletes the read markers on the specified object
817
-	 *
818
-	 * @param string $objectType
819
-	 * @param string $objectId
820
-	 * @return bool
821
-	 * @since 9.0.0
822
-	 */
823
-	public function deleteReadMarksOnObject($objectType, $objectId) {
824
-		$this->checkRoleParameters('Object', $objectType, $objectId);
825
-
826
-		$qb = $this->dbConn->getQueryBuilder();
827
-		$query = $qb->delete('comments_read_markers')
828
-			->where($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
829
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
830
-			->setParameter('object_type', $objectType)
831
-			->setParameter('object_id', $objectId);
832
-
833
-		try {
834
-			$affectedRows = $query->execute();
835
-		} catch (DriverException $e) {
836
-			$this->logger->logException($e, ['app' => 'core_comments']);
837
-			return false;
838
-		}
839
-		return ($affectedRows > 0);
840
-	}
841
-
842
-	/**
843
-	 * registers an Entity to the manager, so event notifications can be send
844
-	 * to consumers of the comments infrastructure
845
-	 *
846
-	 * @param \Closure $closure
847
-	 */
848
-	public function registerEventHandler(\Closure $closure) {
849
-		$this->eventHandlerClosures[] = $closure;
850
-		$this->eventHandlers = [];
851
-	}
852
-
853
-	/**
854
-	 * registers a method that resolves an ID to a display name for a given type
855
-	 *
856
-	 * @param string $type
857
-	 * @param \Closure $closure
858
-	 * @throws \OutOfBoundsException
859
-	 * @since 11.0.0
860
-	 *
861
-	 * Only one resolver shall be registered per type. Otherwise a
862
-	 * \OutOfBoundsException has to thrown.
863
-	 */
864
-	public function registerDisplayNameResolver($type, \Closure $closure) {
865
-		if (!is_string($type)) {
866
-			throw new \InvalidArgumentException('String expected.');
867
-		}
868
-		if (isset($this->displayNameResolvers[$type])) {
869
-			throw new \OutOfBoundsException('Displayname resolver for this type already registered');
870
-		}
871
-		$this->displayNameResolvers[$type] = $closure;
872
-	}
873
-
874
-	/**
875
-	 * resolves a given ID of a given Type to a display name.
876
-	 *
877
-	 * @param string $type
878
-	 * @param string $id
879
-	 * @return string
880
-	 * @throws \OutOfBoundsException
881
-	 * @since 11.0.0
882
-	 *
883
-	 * If a provided type was not registered, an \OutOfBoundsException shall
884
-	 * be thrown. It is upon the resolver discretion what to return of the
885
-	 * provided ID is unknown. It must be ensured that a string is returned.
886
-	 */
887
-	public function resolveDisplayName($type, $id) {
888
-		if (!is_string($type)) {
889
-			throw new \InvalidArgumentException('String expected.');
890
-		}
891
-		if (!isset($this->displayNameResolvers[$type])) {
892
-			throw new \OutOfBoundsException('No Displayname resolver for this type registered');
893
-		}
894
-		return (string)$this->displayNameResolvers[$type]($id);
895
-	}
896
-
897
-	/**
898
-	 * returns valid, registered entities
899
-	 *
900
-	 * @return \OCP\Comments\ICommentsEventHandler[]
901
-	 */
902
-	private function getEventHandlers() {
903
-		if (!empty($this->eventHandlers)) {
904
-			return $this->eventHandlers;
905
-		}
906
-
907
-		$this->eventHandlers = [];
908
-		foreach ($this->eventHandlerClosures as $name => $closure) {
909
-			$entity = $closure();
910
-			if (!($entity instanceof ICommentsEventHandler)) {
911
-				throw new \InvalidArgumentException('The given entity does not implement the ICommentsEntity interface');
912
-			}
913
-			$this->eventHandlers[$name] = $entity;
914
-		}
915
-
916
-		return $this->eventHandlers;
917
-	}
918
-
919
-	/**
920
-	 * sends notifications to the registered entities
921
-	 *
922
-	 * @param $eventType
923
-	 * @param IComment $comment
924
-	 */
925
-	private function sendEvent($eventType, IComment $comment) {
926
-		$entities = $this->getEventHandlers();
927
-		$event = new CommentsEvent($eventType, $comment);
928
-		foreach ($entities as $entity) {
929
-			$entity->handle($event);
930
-		}
931
-	}
44
+    /** @var  IDBConnection */
45
+    protected $dbConn;
46
+
47
+    /** @var  ILogger */
48
+    protected $logger;
49
+
50
+    /** @var IConfig */
51
+    protected $config;
52
+
53
+    /** @var IComment[] */
54
+    protected $commentsCache = [];
55
+
56
+    /** @var  \Closure[] */
57
+    protected $eventHandlerClosures = [];
58
+
59
+    /** @var  ICommentsEventHandler[] */
60
+    protected $eventHandlers = [];
61
+
62
+    /** @var \Closure[] */
63
+    protected $displayNameResolvers = [];
64
+
65
+    /**
66
+     * Manager constructor.
67
+     *
68
+     * @param IDBConnection $dbConn
69
+     * @param ILogger $logger
70
+     * @param IConfig $config
71
+     */
72
+    public function __construct(
73
+        IDBConnection $dbConn,
74
+        ILogger $logger,
75
+        IConfig $config
76
+    ) {
77
+        $this->dbConn = $dbConn;
78
+        $this->logger = $logger;
79
+        $this->config = $config;
80
+    }
81
+
82
+    /**
83
+     * converts data base data into PHP native, proper types as defined by
84
+     * IComment interface.
85
+     *
86
+     * @param array $data
87
+     * @return array
88
+     */
89
+    protected function normalizeDatabaseData(array $data) {
90
+        $data['id'] = (string)$data['id'];
91
+        $data['parent_id'] = (string)$data['parent_id'];
92
+        $data['topmost_parent_id'] = (string)$data['topmost_parent_id'];
93
+        $data['creation_timestamp'] = new \DateTime($data['creation_timestamp']);
94
+        if (!is_null($data['latest_child_timestamp'])) {
95
+            $data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']);
96
+        }
97
+        $data['children_count'] = (int)$data['children_count'];
98
+        return $data;
99
+    }
100
+
101
+    /**
102
+     * prepares a comment for an insert or update operation after making sure
103
+     * all necessary fields have a value assigned.
104
+     *
105
+     * @param IComment $comment
106
+     * @return IComment returns the same updated IComment instance as provided
107
+     *                  by parameter for convenience
108
+     * @throws \UnexpectedValueException
109
+     */
110
+    protected function prepareCommentForDatabaseWrite(IComment $comment) {
111
+        if (!$comment->getActorType()
112
+            || !$comment->getActorId()
113
+            || !$comment->getObjectType()
114
+            || !$comment->getObjectId()
115
+            || !$comment->getVerb()
116
+        ) {
117
+            throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving');
118
+        }
119
+
120
+        if ($comment->getId() === '') {
121
+            $comment->setChildrenCount(0);
122
+            $comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC')));
123
+            $comment->setLatestChildDateTime(null);
124
+        }
125
+
126
+        if (is_null($comment->getCreationDateTime())) {
127
+            $comment->setCreationDateTime(new \DateTime());
128
+        }
129
+
130
+        if ($comment->getParentId() !== '0') {
131
+            $comment->setTopmostParentId($this->determineTopmostParentId($comment->getParentId()));
132
+        } else {
133
+            $comment->setTopmostParentId('0');
134
+        }
135
+
136
+        $this->cache($comment);
137
+
138
+        return $comment;
139
+    }
140
+
141
+    /**
142
+     * returns the topmost parent id of a given comment identified by ID
143
+     *
144
+     * @param string $id
145
+     * @return string
146
+     * @throws NotFoundException
147
+     */
148
+    protected function determineTopmostParentId($id) {
149
+        $comment = $this->get($id);
150
+        if ($comment->getParentId() === '0') {
151
+            return $comment->getId();
152
+        } else {
153
+            return $this->determineTopmostParentId($comment->getId());
154
+        }
155
+    }
156
+
157
+    /**
158
+     * updates child information of a comment
159
+     *
160
+     * @param string $id
161
+     * @param \DateTime $cDateTime the date time of the most recent child
162
+     * @throws NotFoundException
163
+     */
164
+    protected function updateChildrenInformation($id, \DateTime $cDateTime) {
165
+        $qb = $this->dbConn->getQueryBuilder();
166
+        $query = $qb->select($qb->createFunction('COUNT(`id`)'))
167
+            ->from('comments')
168
+            ->where($qb->expr()->eq('parent_id', $qb->createParameter('id')))
169
+            ->setParameter('id', $id);
170
+
171
+        $resultStatement = $query->execute();
172
+        $data = $resultStatement->fetch(\PDO::FETCH_NUM);
173
+        $resultStatement->closeCursor();
174
+        $children = (int)$data[0];
175
+
176
+        $comment = $this->get($id);
177
+        $comment->setChildrenCount($children);
178
+        $comment->setLatestChildDateTime($cDateTime);
179
+        $this->save($comment);
180
+    }
181
+
182
+    /**
183
+     * Tests whether actor or object type and id parameters are acceptable.
184
+     * Throws exception if not.
185
+     *
186
+     * @param string $role
187
+     * @param string $type
188
+     * @param string $id
189
+     * @throws \InvalidArgumentException
190
+     */
191
+    protected function checkRoleParameters($role, $type, $id) {
192
+        if (
193
+            !is_string($type) || empty($type)
194
+            || !is_string($id) || empty($id)
195
+        ) {
196
+            throw new \InvalidArgumentException($role . ' parameters must be string and not empty');
197
+        }
198
+    }
199
+
200
+    /**
201
+     * run-time caches a comment
202
+     *
203
+     * @param IComment $comment
204
+     */
205
+    protected function cache(IComment $comment) {
206
+        $id = $comment->getId();
207
+        if (empty($id)) {
208
+            return;
209
+        }
210
+        $this->commentsCache[(string)$id] = $comment;
211
+    }
212
+
213
+    /**
214
+     * removes an entry from the comments run time cache
215
+     *
216
+     * @param mixed $id the comment's id
217
+     */
218
+    protected function uncache($id) {
219
+        $id = (string)$id;
220
+        if (isset($this->commentsCache[$id])) {
221
+            unset($this->commentsCache[$id]);
222
+        }
223
+    }
224
+
225
+    /**
226
+     * returns a comment instance
227
+     *
228
+     * @param string $id the ID of the comment
229
+     * @return IComment
230
+     * @throws NotFoundException
231
+     * @throws \InvalidArgumentException
232
+     * @since 9.0.0
233
+     */
234
+    public function get($id) {
235
+        if ((int)$id === 0) {
236
+            throw new \InvalidArgumentException('IDs must be translatable to a number in this implementation.');
237
+        }
238
+
239
+        if (isset($this->commentsCache[$id])) {
240
+            return $this->commentsCache[$id];
241
+        }
242
+
243
+        $qb = $this->dbConn->getQueryBuilder();
244
+        $resultStatement = $qb->select('*')
245
+            ->from('comments')
246
+            ->where($qb->expr()->eq('id', $qb->createParameter('id')))
247
+            ->setParameter('id', $id, IQueryBuilder::PARAM_INT)
248
+            ->execute();
249
+
250
+        $data = $resultStatement->fetch();
251
+        $resultStatement->closeCursor();
252
+        if (!$data) {
253
+            throw new NotFoundException();
254
+        }
255
+
256
+        $comment = new Comment($this->normalizeDatabaseData($data));
257
+        $this->cache($comment);
258
+        return $comment;
259
+    }
260
+
261
+    /**
262
+     * returns the comment specified by the id and all it's child comments.
263
+     * At this point of time, we do only support one level depth.
264
+     *
265
+     * @param string $id
266
+     * @param int $limit max number of entries to return, 0 returns all
267
+     * @param int $offset the start entry
268
+     * @return array
269
+     * @since 9.0.0
270
+     *
271
+     * The return array looks like this
272
+     * [
273
+     *   'comment' => IComment, // root comment
274
+     *   'replies' =>
275
+     *   [
276
+     *     0 =>
277
+     *     [
278
+     *       'comment' => IComment,
279
+     *       'replies' => []
280
+     *     ]
281
+     *     1 =>
282
+     *     [
283
+     *       'comment' => IComment,
284
+     *       'replies'=> []
285
+     *     ],
286
+     *     …
287
+     *   ]
288
+     * ]
289
+     */
290
+    public function getTree($id, $limit = 0, $offset = 0) {
291
+        $tree = [];
292
+        $tree['comment'] = $this->get($id);
293
+        $tree['replies'] = [];
294
+
295
+        $qb = $this->dbConn->getQueryBuilder();
296
+        $query = $qb->select('*')
297
+            ->from('comments')
298
+            ->where($qb->expr()->eq('topmost_parent_id', $qb->createParameter('id')))
299
+            ->orderBy('creation_timestamp', 'DESC')
300
+            ->setParameter('id', $id);
301
+
302
+        if ($limit > 0) {
303
+            $query->setMaxResults($limit);
304
+        }
305
+        if ($offset > 0) {
306
+            $query->setFirstResult($offset);
307
+        }
308
+
309
+        $resultStatement = $query->execute();
310
+        while ($data = $resultStatement->fetch()) {
311
+            $comment = new Comment($this->normalizeDatabaseData($data));
312
+            $this->cache($comment);
313
+            $tree['replies'][] = [
314
+                'comment' => $comment,
315
+                'replies' => []
316
+            ];
317
+        }
318
+        $resultStatement->closeCursor();
319
+
320
+        return $tree;
321
+    }
322
+
323
+    /**
324
+     * returns comments for a specific object (e.g. a file).
325
+     *
326
+     * The sort order is always newest to oldest.
327
+     *
328
+     * @param string $objectType the object type, e.g. 'files'
329
+     * @param string $objectId the id of the object
330
+     * @param int $limit optional, number of maximum comments to be returned. if
331
+     * not specified, all comments are returned.
332
+     * @param int $offset optional, starting point
333
+     * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
334
+     * that may be returned
335
+     * @return IComment[]
336
+     * @since 9.0.0
337
+     */
338
+    public function getForObject(
339
+        $objectType,
340
+        $objectId,
341
+        $limit = 0,
342
+        $offset = 0,
343
+        \DateTime $notOlderThan = null
344
+    ) {
345
+        $comments = [];
346
+
347
+        $qb = $this->dbConn->getQueryBuilder();
348
+        $query = $qb->select('*')
349
+            ->from('comments')
350
+            ->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
351
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
352
+            ->orderBy('creation_timestamp', 'DESC')
353
+            ->setParameter('type', $objectType)
354
+            ->setParameter('id', $objectId);
355
+
356
+        if ($limit > 0) {
357
+            $query->setMaxResults($limit);
358
+        }
359
+        if ($offset > 0) {
360
+            $query->setFirstResult($offset);
361
+        }
362
+        if (!is_null($notOlderThan)) {
363
+            $query
364
+                ->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
365
+                ->setParameter('notOlderThan', $notOlderThan, 'datetime');
366
+        }
367
+
368
+        $resultStatement = $query->execute();
369
+        while ($data = $resultStatement->fetch()) {
370
+            $comment = new Comment($this->normalizeDatabaseData($data));
371
+            $this->cache($comment);
372
+            $comments[] = $comment;
373
+        }
374
+        $resultStatement->closeCursor();
375
+
376
+        return $comments;
377
+    }
378
+
379
+    /**
380
+     * Search for comments with a given content
381
+     *
382
+     * @param string $search content to search for
383
+     * @param string $objectType Limit the search by object type
384
+     * @param string $objectId Limit the search by object id
385
+     * @param string $verb Limit the verb of the comment
386
+     * @return IComment[]
387
+     */
388
+    public function search(string $search, string $objectType, string $objectId, string $verb): array {
389
+        $query = $this->dbConn->getQueryBuilder();
390
+
391
+        $query->select('*')
392
+            ->from('comments')
393
+            ->where($query->expr()->iLike('message', $query->createNamedParameter(
394
+                '%' . $this->dbConn->escapeLikeParameter($search). '%'
395
+            )))
396
+            ->orderBy('creation_timestamp', 'DESC')
397
+            ->addOrderBy('id', 'DESC');
398
+
399
+        if ($objectType !== '') {
400
+            $query->andWhere($query->expr()->eq('object_type', $query->createNamedParameter($objectType)));
401
+        }
402
+        if ($objectId !== '') {
403
+            $query->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
404
+        }
405
+        if ($verb !== '') {
406
+            $query->andWhere($query->expr()->eq('verb', $query->createNamedParameter($verb)));
407
+        }
408
+
409
+        $comments = [];
410
+        $result = $query->execute();
411
+        while ($data = $result->fetch()) {
412
+            $comment = new Comment($this->normalizeDatabaseData($data));
413
+            $this->cache($comment);
414
+            $comments[] = $comment;
415
+        }
416
+        $result->closeCursor();
417
+
418
+        return $comments;
419
+    }
420
+
421
+    /**
422
+     * @param $objectType string the object type, e.g. 'files'
423
+     * @param $objectId string the id of the object
424
+     * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
425
+     * that may be returned
426
+     * @return Int
427
+     * @since 9.0.0
428
+     */
429
+    public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null) {
430
+        $qb = $this->dbConn->getQueryBuilder();
431
+        $query = $qb->select($qb->createFunction('COUNT(`id`)'))
432
+            ->from('comments')
433
+            ->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
434
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
435
+            ->setParameter('type', $objectType)
436
+            ->setParameter('id', $objectId);
437
+
438
+        if (!is_null($notOlderThan)) {
439
+            $query
440
+                ->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
441
+                ->setParameter('notOlderThan', $notOlderThan, 'datetime');
442
+        }
443
+
444
+        $resultStatement = $query->execute();
445
+        $data = $resultStatement->fetch(\PDO::FETCH_NUM);
446
+        $resultStatement->closeCursor();
447
+        return (int)$data[0];
448
+    }
449
+
450
+    /**
451
+     * Get the number of unread comments for all files in a folder
452
+     *
453
+     * @param int $folderId
454
+     * @param IUser $user
455
+     * @return array [$fileId => $unreadCount]
456
+     */
457
+    public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user) {
458
+        $qb = $this->dbConn->getQueryBuilder();
459
+        $query = $qb->select('f.fileid')
460
+            ->selectAlias(
461
+                $qb->createFunction('COUNT(' . $qb->getColumnName('c.id') . ')'),
462
+                'num_ids'
463
+            )
464
+            ->from('comments', 'c')
465
+            ->innerJoin('c', 'filecache', 'f', $qb->expr()->andX(
466
+                $qb->expr()->eq('c.object_type', $qb->createNamedParameter('files')),
467
+                $qb->expr()->eq('f.fileid', $qb->expr()->castColumn('c.object_id', IQueryBuilder::PARAM_INT))
468
+            ))
469
+            ->leftJoin('c', 'comments_read_markers', 'm', $qb->expr()->andX(
470
+                $qb->expr()->eq('m.object_type', $qb->createNamedParameter('files')),
471
+                $qb->expr()->eq('m.object_id', 'c.object_id'),
472
+                $qb->expr()->eq('m.user_id', $qb->createNamedParameter($user->getUID()))
473
+            ))
474
+            ->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($folderId)))
475
+            ->andWhere($qb->expr()->orX(
476
+                $qb->expr()->gt('c.creation_timestamp', 'marker_datetime'),
477
+                $qb->expr()->isNull('marker_datetime')
478
+            ))
479
+            ->groupBy('f.fileid');
480
+
481
+        $resultStatement = $query->execute();
482
+
483
+        $results = [];
484
+        while ($row = $resultStatement->fetch()) {
485
+            $results[$row['fileid']] = (int) $row['num_ids'];
486
+        }
487
+        $resultStatement->closeCursor();
488
+        return $results;
489
+    }
490
+
491
+    /**
492
+     * creates a new comment and returns it. At this point of time, it is not
493
+     * saved in the used data storage. Use save() after setting other fields
494
+     * of the comment (e.g. message or verb).
495
+     *
496
+     * @param string $actorType the actor type (e.g. 'users')
497
+     * @param string $actorId a user id
498
+     * @param string $objectType the object type the comment is attached to
499
+     * @param string $objectId the object id the comment is attached to
500
+     * @return IComment
501
+     * @since 9.0.0
502
+     */
503
+    public function create($actorType, $actorId, $objectType, $objectId) {
504
+        $comment = new Comment();
505
+        $comment
506
+            ->setActor($actorType, $actorId)
507
+            ->setObject($objectType, $objectId);
508
+        return $comment;
509
+    }
510
+
511
+    /**
512
+     * permanently deletes the comment specified by the ID
513
+     *
514
+     * When the comment has child comments, their parent ID will be changed to
515
+     * the parent ID of the item that is to be deleted.
516
+     *
517
+     * @param string $id
518
+     * @return bool
519
+     * @throws \InvalidArgumentException
520
+     * @since 9.0.0
521
+     */
522
+    public function delete($id) {
523
+        if (!is_string($id)) {
524
+            throw new \InvalidArgumentException('Parameter must be string');
525
+        }
526
+
527
+        try {
528
+            $comment = $this->get($id);
529
+        } catch (\Exception $e) {
530
+            // Ignore exceptions, we just don't fire a hook then
531
+            $comment = null;
532
+        }
533
+
534
+        $qb = $this->dbConn->getQueryBuilder();
535
+        $query = $qb->delete('comments')
536
+            ->where($qb->expr()->eq('id', $qb->createParameter('id')))
537
+            ->setParameter('id', $id);
538
+
539
+        try {
540
+            $affectedRows = $query->execute();
541
+            $this->uncache($id);
542
+        } catch (DriverException $e) {
543
+            $this->logger->logException($e, ['app' => 'core_comments']);
544
+            return false;
545
+        }
546
+
547
+        if ($affectedRows > 0 && $comment instanceof IComment) {
548
+            $this->sendEvent(CommentsEvent::EVENT_DELETE, $comment);
549
+        }
550
+
551
+        return ($affectedRows > 0);
552
+    }
553
+
554
+    /**
555
+     * saves the comment permanently
556
+     *
557
+     * if the supplied comment has an empty ID, a new entry comment will be
558
+     * saved and the instance updated with the new ID.
559
+     *
560
+     * Otherwise, an existing comment will be updated.
561
+     *
562
+     * Throws NotFoundException when a comment that is to be updated does not
563
+     * exist anymore at this point of time.
564
+     *
565
+     * @param IComment $comment
566
+     * @return bool
567
+     * @throws NotFoundException
568
+     * @since 9.0.0
569
+     */
570
+    public function save(IComment $comment) {
571
+        if ($this->prepareCommentForDatabaseWrite($comment)->getId() === '') {
572
+            $result = $this->insert($comment);
573
+        } else {
574
+            $result = $this->update($comment);
575
+        }
576
+
577
+        if ($result && !!$comment->getParentId()) {
578
+            $this->updateChildrenInformation(
579
+                $comment->getParentId(),
580
+                $comment->getCreationDateTime()
581
+            );
582
+            $this->cache($comment);
583
+        }
584
+
585
+        return $result;
586
+    }
587
+
588
+    /**
589
+     * inserts the provided comment in the database
590
+     *
591
+     * @param IComment $comment
592
+     * @return bool
593
+     */
594
+    protected function insert(IComment &$comment) {
595
+        $qb = $this->dbConn->getQueryBuilder();
596
+        $affectedRows = $qb
597
+            ->insert('comments')
598
+            ->values([
599
+                'parent_id' => $qb->createNamedParameter($comment->getParentId()),
600
+                'topmost_parent_id' => $qb->createNamedParameter($comment->getTopmostParentId()),
601
+                'children_count' => $qb->createNamedParameter($comment->getChildrenCount()),
602
+                'actor_type' => $qb->createNamedParameter($comment->getActorType()),
603
+                'actor_id' => $qb->createNamedParameter($comment->getActorId()),
604
+                'message' => $qb->createNamedParameter($comment->getMessage()),
605
+                'verb' => $qb->createNamedParameter($comment->getVerb()),
606
+                'creation_timestamp' => $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'),
607
+                'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
608
+                'object_type' => $qb->createNamedParameter($comment->getObjectType()),
609
+                'object_id' => $qb->createNamedParameter($comment->getObjectId()),
610
+            ])
611
+            ->execute();
612
+
613
+        if ($affectedRows > 0) {
614
+            $comment->setId((string)$qb->getLastInsertId());
615
+            $this->sendEvent(CommentsEvent::EVENT_ADD, $comment);
616
+        }
617
+
618
+        return $affectedRows > 0;
619
+    }
620
+
621
+    /**
622
+     * updates a Comment data row
623
+     *
624
+     * @param IComment $comment
625
+     * @return bool
626
+     * @throws NotFoundException
627
+     */
628
+    protected function update(IComment $comment) {
629
+        // for properly working preUpdate Events we need the old comments as is
630
+        // in the DB and overcome caching. Also avoid that outdated information stays.
631
+        $this->uncache($comment->getId());
632
+        $this->sendEvent(CommentsEvent::EVENT_PRE_UPDATE, $this->get($comment->getId()));
633
+        $this->uncache($comment->getId());
634
+
635
+        $qb = $this->dbConn->getQueryBuilder();
636
+        $affectedRows = $qb
637
+            ->update('comments')
638
+            ->set('parent_id', $qb->createNamedParameter($comment->getParentId()))
639
+            ->set('topmost_parent_id', $qb->createNamedParameter($comment->getTopmostParentId()))
640
+            ->set('children_count', $qb->createNamedParameter($comment->getChildrenCount()))
641
+            ->set('actor_type', $qb->createNamedParameter($comment->getActorType()))
642
+            ->set('actor_id', $qb->createNamedParameter($comment->getActorId()))
643
+            ->set('message', $qb->createNamedParameter($comment->getMessage()))
644
+            ->set('verb', $qb->createNamedParameter($comment->getVerb()))
645
+            ->set('creation_timestamp', $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'))
646
+            ->set('latest_child_timestamp', $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'))
647
+            ->set('object_type', $qb->createNamedParameter($comment->getObjectType()))
648
+            ->set('object_id', $qb->createNamedParameter($comment->getObjectId()))
649
+            ->where($qb->expr()->eq('id', $qb->createParameter('id')))
650
+            ->setParameter('id', $comment->getId())
651
+            ->execute();
652
+
653
+        if ($affectedRows === 0) {
654
+            throw new NotFoundException('Comment to update does ceased to exist');
655
+        }
656
+
657
+        $this->sendEvent(CommentsEvent::EVENT_UPDATE, $comment);
658
+
659
+        return $affectedRows > 0;
660
+    }
661
+
662
+    /**
663
+     * removes references to specific actor (e.g. on user delete) of a comment.
664
+     * The comment itself must not get lost/deleted.
665
+     *
666
+     * @param string $actorType the actor type (e.g. 'users')
667
+     * @param string $actorId a user id
668
+     * @return boolean
669
+     * @since 9.0.0
670
+     */
671
+    public function deleteReferencesOfActor($actorType, $actorId) {
672
+        $this->checkRoleParameters('Actor', $actorType, $actorId);
673
+
674
+        $qb = $this->dbConn->getQueryBuilder();
675
+        $affectedRows = $qb
676
+            ->update('comments')
677
+            ->set('actor_type', $qb->createNamedParameter(ICommentsManager::DELETED_USER))
678
+            ->set('actor_id', $qb->createNamedParameter(ICommentsManager::DELETED_USER))
679
+            ->where($qb->expr()->eq('actor_type', $qb->createParameter('type')))
680
+            ->andWhere($qb->expr()->eq('actor_id', $qb->createParameter('id')))
681
+            ->setParameter('type', $actorType)
682
+            ->setParameter('id', $actorId)
683
+            ->execute();
684
+
685
+        $this->commentsCache = [];
686
+
687
+        return is_int($affectedRows);
688
+    }
689
+
690
+    /**
691
+     * deletes all comments made of a specific object (e.g. on file delete)
692
+     *
693
+     * @param string $objectType the object type (e.g. 'files')
694
+     * @param string $objectId e.g. the file id
695
+     * @return boolean
696
+     * @since 9.0.0
697
+     */
698
+    public function deleteCommentsAtObject($objectType, $objectId) {
699
+        $this->checkRoleParameters('Object', $objectType, $objectId);
700
+
701
+        $qb = $this->dbConn->getQueryBuilder();
702
+        $affectedRows = $qb
703
+            ->delete('comments')
704
+            ->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
705
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
706
+            ->setParameter('type', $objectType)
707
+            ->setParameter('id', $objectId)
708
+            ->execute();
709
+
710
+        $this->commentsCache = [];
711
+
712
+        return is_int($affectedRows);
713
+    }
714
+
715
+    /**
716
+     * deletes the read markers for the specified user
717
+     *
718
+     * @param \OCP\IUser $user
719
+     * @return bool
720
+     * @since 9.0.0
721
+     */
722
+    public function deleteReadMarksFromUser(IUser $user) {
723
+        $qb = $this->dbConn->getQueryBuilder();
724
+        $query = $qb->delete('comments_read_markers')
725
+            ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
726
+            ->setParameter('user_id', $user->getUID());
727
+
728
+        try {
729
+            $affectedRows = $query->execute();
730
+        } catch (DriverException $e) {
731
+            $this->logger->logException($e, ['app' => 'core_comments']);
732
+            return false;
733
+        }
734
+        return ($affectedRows > 0);
735
+    }
736
+
737
+    /**
738
+     * sets the read marker for a given file to the specified date for the
739
+     * provided user
740
+     *
741
+     * @param string $objectType
742
+     * @param string $objectId
743
+     * @param \DateTime $dateTime
744
+     * @param IUser $user
745
+     * @since 9.0.0
746
+     * @suppress SqlInjectionChecker
747
+     */
748
+    public function setReadMark($objectType, $objectId, \DateTime $dateTime, IUser $user) {
749
+        $this->checkRoleParameters('Object', $objectType, $objectId);
750
+
751
+        $qb = $this->dbConn->getQueryBuilder();
752
+        $values = [
753
+            'user_id' => $qb->createNamedParameter($user->getUID()),
754
+            'marker_datetime' => $qb->createNamedParameter($dateTime, 'datetime'),
755
+            'object_type' => $qb->createNamedParameter($objectType),
756
+            'object_id' => $qb->createNamedParameter($objectId),
757
+        ];
758
+
759
+        // Strategy: try to update, if this does not return affected rows, do an insert.
760
+        $affectedRows = $qb
761
+            ->update('comments_read_markers')
762
+            ->set('user_id', $values['user_id'])
763
+            ->set('marker_datetime', $values['marker_datetime'])
764
+            ->set('object_type', $values['object_type'])
765
+            ->set('object_id', $values['object_id'])
766
+            ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
767
+            ->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
768
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
769
+            ->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR)
770
+            ->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR)
771
+            ->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR)
772
+            ->execute();
773
+
774
+        if ($affectedRows > 0) {
775
+            return;
776
+        }
777
+
778
+        $qb->insert('comments_read_markers')
779
+            ->values($values)
780
+            ->execute();
781
+    }
782
+
783
+    /**
784
+     * returns the read marker for a given file to the specified date for the
785
+     * provided user. It returns null, when the marker is not present, i.e.
786
+     * no comments were marked as read.
787
+     *
788
+     * @param string $objectType
789
+     * @param string $objectId
790
+     * @param IUser $user
791
+     * @return \DateTime|null
792
+     * @since 9.0.0
793
+     */
794
+    public function getReadMark($objectType, $objectId, IUser $user) {
795
+        $qb = $this->dbConn->getQueryBuilder();
796
+        $resultStatement = $qb->select('marker_datetime')
797
+            ->from('comments_read_markers')
798
+            ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
799
+            ->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
800
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
801
+            ->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR)
802
+            ->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR)
803
+            ->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR)
804
+            ->execute();
805
+
806
+        $data = $resultStatement->fetch();
807
+        $resultStatement->closeCursor();
808
+        if (!$data || is_null($data['marker_datetime'])) {
809
+            return null;
810
+        }
811
+
812
+        return new \DateTime($data['marker_datetime']);
813
+    }
814
+
815
+    /**
816
+     * deletes the read markers on the specified object
817
+     *
818
+     * @param string $objectType
819
+     * @param string $objectId
820
+     * @return bool
821
+     * @since 9.0.0
822
+     */
823
+    public function deleteReadMarksOnObject($objectType, $objectId) {
824
+        $this->checkRoleParameters('Object', $objectType, $objectId);
825
+
826
+        $qb = $this->dbConn->getQueryBuilder();
827
+        $query = $qb->delete('comments_read_markers')
828
+            ->where($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
829
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
830
+            ->setParameter('object_type', $objectType)
831
+            ->setParameter('object_id', $objectId);
832
+
833
+        try {
834
+            $affectedRows = $query->execute();
835
+        } catch (DriverException $e) {
836
+            $this->logger->logException($e, ['app' => 'core_comments']);
837
+            return false;
838
+        }
839
+        return ($affectedRows > 0);
840
+    }
841
+
842
+    /**
843
+     * registers an Entity to the manager, so event notifications can be send
844
+     * to consumers of the comments infrastructure
845
+     *
846
+     * @param \Closure $closure
847
+     */
848
+    public function registerEventHandler(\Closure $closure) {
849
+        $this->eventHandlerClosures[] = $closure;
850
+        $this->eventHandlers = [];
851
+    }
852
+
853
+    /**
854
+     * registers a method that resolves an ID to a display name for a given type
855
+     *
856
+     * @param string $type
857
+     * @param \Closure $closure
858
+     * @throws \OutOfBoundsException
859
+     * @since 11.0.0
860
+     *
861
+     * Only one resolver shall be registered per type. Otherwise a
862
+     * \OutOfBoundsException has to thrown.
863
+     */
864
+    public function registerDisplayNameResolver($type, \Closure $closure) {
865
+        if (!is_string($type)) {
866
+            throw new \InvalidArgumentException('String expected.');
867
+        }
868
+        if (isset($this->displayNameResolvers[$type])) {
869
+            throw new \OutOfBoundsException('Displayname resolver for this type already registered');
870
+        }
871
+        $this->displayNameResolvers[$type] = $closure;
872
+    }
873
+
874
+    /**
875
+     * resolves a given ID of a given Type to a display name.
876
+     *
877
+     * @param string $type
878
+     * @param string $id
879
+     * @return string
880
+     * @throws \OutOfBoundsException
881
+     * @since 11.0.0
882
+     *
883
+     * If a provided type was not registered, an \OutOfBoundsException shall
884
+     * be thrown. It is upon the resolver discretion what to return of the
885
+     * provided ID is unknown. It must be ensured that a string is returned.
886
+     */
887
+    public function resolveDisplayName($type, $id) {
888
+        if (!is_string($type)) {
889
+            throw new \InvalidArgumentException('String expected.');
890
+        }
891
+        if (!isset($this->displayNameResolvers[$type])) {
892
+            throw new \OutOfBoundsException('No Displayname resolver for this type registered');
893
+        }
894
+        return (string)$this->displayNameResolvers[$type]($id);
895
+    }
896
+
897
+    /**
898
+     * returns valid, registered entities
899
+     *
900
+     * @return \OCP\Comments\ICommentsEventHandler[]
901
+     */
902
+    private function getEventHandlers() {
903
+        if (!empty($this->eventHandlers)) {
904
+            return $this->eventHandlers;
905
+        }
906
+
907
+        $this->eventHandlers = [];
908
+        foreach ($this->eventHandlerClosures as $name => $closure) {
909
+            $entity = $closure();
910
+            if (!($entity instanceof ICommentsEventHandler)) {
911
+                throw new \InvalidArgumentException('The given entity does not implement the ICommentsEntity interface');
912
+            }
913
+            $this->eventHandlers[$name] = $entity;
914
+        }
915
+
916
+        return $this->eventHandlers;
917
+    }
918
+
919
+    /**
920
+     * sends notifications to the registered entities
921
+     *
922
+     * @param $eventType
923
+     * @param IComment $comment
924
+     */
925
+    private function sendEvent($eventType, IComment $comment) {
926
+        $entities = $this->getEventHandlers();
927
+        $event = new CommentsEvent($eventType, $comment);
928
+        foreach ($entities as $entity) {
929
+            $entity->handle($event);
930
+        }
931
+    }
932 932
 }
Please login to merge, or discard this patch.
apps/comments/lib/AppInfo/Application.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -31,15 +31,15 @@
 block discarded – undo
31 31
 
32 32
 class Application extends App {
33 33
 
34
-	public function __construct (array $urlParams = array()) {
35
-		parent::__construct('comments', $urlParams);
36
-		$container = $this->getContainer();
34
+    public function __construct (array $urlParams = array()) {
35
+        parent::__construct('comments', $urlParams);
36
+        $container = $this->getContainer();
37 37
 
38
-		$container->registerAlias('NotificationsController', Notifications::class);
38
+        $container->registerAlias('NotificationsController', Notifications::class);
39 39
 
40
-		$jsSettingsHelper = new JSSettingsHelper($container->getServer());
41
-		Util::connectHook('\OCP\Config', 'js', $jsSettingsHelper, 'extend');
40
+        $jsSettingsHelper = new JSSettingsHelper($container->getServer());
41
+        Util::connectHook('\OCP\Config', 'js', $jsSettingsHelper, 'extend');
42 42
 
43
-		$container->getServer()->getSearch()->registerProvider(Provider::class, ['apps' => ['files']]);
44
-	}
43
+        $container->getServer()->getSearch()->registerProvider(Provider::class, ['apps' => ['files']]);
44
+    }
45 45
 }
Please login to merge, or discard this patch.
apps/comments/lib/Search/Provider.php 1 patch
Indentation   +52 added lines, -52 removed lines patch added patch discarded remove patch
@@ -30,66 +30,66 @@
 block discarded – undo
30 30
 
31 31
 class Provider extends \OCP\Search\Provider {
32 32
 
33
-	/**
34
-	 * Search for $query
35
-	 *
36
-	 * @param string $query
37
-	 * @return array An array of OCP\Search\Result's
38
-	 * @since 7.0.0
39
-	 */
40
-	public function search($query): array {
41
-		$cm = \OC::$server->getCommentsManager();
42
-		$us = \OC::$server->getUserSession();
33
+    /**
34
+     * Search for $query
35
+     *
36
+     * @param string $query
37
+     * @return array An array of OCP\Search\Result's
38
+     * @since 7.0.0
39
+     */
40
+    public function search($query): array {
41
+        $cm = \OC::$server->getCommentsManager();
42
+        $us = \OC::$server->getUserSession();
43 43
 
44
-		$user = $us->getUser();
45
-		if (!$user instanceof IUser) {
46
-			return [];
47
-		}
48
-		$uf = \OC::$server->getUserFolder($user->getUID());
44
+        $user = $us->getUser();
45
+        if (!$user instanceof IUser) {
46
+            return [];
47
+        }
48
+        $uf = \OC::$server->getUserFolder($user->getUID());
49 49
 
50
-		if ($uf === null) {
51
-			return [];
52
-		}
50
+        if ($uf === null) {
51
+            return [];
52
+        }
53 53
 
54
-		/** @var IComment[] $comments */
55
-		$comments = $cm->search('files', '', 'comment', $query);
54
+        /** @var IComment[] $comments */
55
+        $comments = $cm->search('files', '', 'comment', $query);
56 56
 
57 57
 
58
-		$result = [];
59
-		foreach ($comments as $comment) {
60
-			if ($comment->getActorType() !== 'users') {
61
-				continue;
62
-			}
58
+        $result = [];
59
+        foreach ($comments as $comment) {
60
+            if ($comment->getActorType() !== 'users') {
61
+                continue;
62
+            }
63 63
 
64
-			$displayName = $cm->resolveDisplayName($comment->getActorType(), $comment->getActorId());
64
+            $displayName = $cm->resolveDisplayName($comment->getActorType(), $comment->getActorId());
65 65
 
66
-			try {
67
-				$file = $this->getFileForComment($uf, $comment);
68
-				$result[] = new Result($query,
69
-					$comment,
70
-					$displayName,
71
-					$file->getPath()
72
-				);
73
-			} catch (NotFoundException $e) {
74
-				continue;
75
-			}
76
-		}
66
+            try {
67
+                $file = $this->getFileForComment($uf, $comment);
68
+                $result[] = new Result($query,
69
+                    $comment,
70
+                    $displayName,
71
+                    $file->getPath()
72
+                );
73
+            } catch (NotFoundException $e) {
74
+                continue;
75
+            }
76
+        }
77 77
 
78
-		return $result;
79
-	}
78
+        return $result;
79
+    }
80 80
 
81
-	/**
82
-	 * @param Folder $userFolder
83
-	 * @param IComment $comment
84
-	 * @return Node
85
-	 * @throws NotFoundException
86
-	 */
87
-	protected function getFileForComment(Folder $userFolder, IComment $comment): Node {
88
-		$nodes = $userFolder->getById((int) $comment->getObjectId());
89
-		if (empty($nodes)) {
90
-			throw new NotFoundException('File not found');
91
-		}
81
+    /**
82
+     * @param Folder $userFolder
83
+     * @param IComment $comment
84
+     * @return Node
85
+     * @throws NotFoundException
86
+     */
87
+    protected function getFileForComment(Folder $userFolder, IComment $comment): Node {
88
+        $nodes = $userFolder->getById((int) $comment->getObjectId());
89
+        if (empty($nodes)) {
90
+            throw new NotFoundException('File not found');
91
+        }
92 92
 
93
-		return array_shift($nodes);
94
-	}
93
+        return array_shift($nodes);
94
+    }
95 95
 }
Please login to merge, or discard this patch.
apps/comments/lib/Search/Result.php 1 patch
Indentation   +68 added lines, -68 removed lines patch added patch discarded remove patch
@@ -27,83 +27,83 @@
 block discarded – undo
27 27
 
28 28
 class Result extends BaseResult {
29 29
 
30
-	public $type = 'comment';
31
-	public $comment;
32
-	public $authorId;
33
-	public $authorName;
34
-	public $path;
35
-	public $fileName;
30
+    public $type = 'comment';
31
+    public $comment;
32
+    public $authorId;
33
+    public $authorName;
34
+    public $path;
35
+    public $fileName;
36 36
 
37
-	/**
38
-	 * @param string $search
39
-	 * @param IComment $comment
40
-	 * @param string $authorName
41
-	 * @param string $path
42
-	 * @throws NotFoundException
43
-	 */
44
-	public function __construct(string $search,
45
-								IComment $comment,
46
-								string $authorName,
47
-								string $path) {
48
-		parent::__construct(
49
-			(int) $comment->getId(),
50
-			$comment->getMessage()
51
-		/* @todo , [link to file] */
52
-		);
37
+    /**
38
+     * @param string $search
39
+     * @param IComment $comment
40
+     * @param string $authorName
41
+     * @param string $path
42
+     * @throws NotFoundException
43
+     */
44
+    public function __construct(string $search,
45
+                                IComment $comment,
46
+                                string $authorName,
47
+                                string $path) {
48
+        parent::__construct(
49
+            (int) $comment->getId(),
50
+            $comment->getMessage()
51
+        /* @todo , [link to file] */
52
+        );
53 53
 
54
-		$this->comment = $this->getRelevantMessagePart($comment->getMessage(), $search);
55
-		$this->authorId = $comment->getActorId();
56
-		$this->authorName = $authorName;
57
-		$this->fileName = basename($path);
58
-		$this->path = $this->getVisiblePath($path);
59
-	}
54
+        $this->comment = $this->getRelevantMessagePart($comment->getMessage(), $search);
55
+        $this->authorId = $comment->getActorId();
56
+        $this->authorName = $authorName;
57
+        $this->fileName = basename($path);
58
+        $this->path = $this->getVisiblePath($path);
59
+    }
60 60
 
61
-	/**
62
-	 * @param string $path
63
-	 * @return string
64
-	 * @throws NotFoundException
65
-	 */
66
-	protected function getVisiblePath(string $path): string {
67
-		$segments = explode('/', trim($path, '/'), 3);
61
+    /**
62
+     * @param string $path
63
+     * @return string
64
+     * @throws NotFoundException
65
+     */
66
+    protected function getVisiblePath(string $path): string {
67
+        $segments = explode('/', trim($path, '/'), 3);
68 68
 
69
-		if (!isset($segments[2])) {
70
-			throw new NotFoundException('Path not inside visible section');
71
-		}
69
+        if (!isset($segments[2])) {
70
+            throw new NotFoundException('Path not inside visible section');
71
+        }
72 72
 
73
-		return $segments[2];
74
-	}
73
+        return $segments[2];
74
+    }
75 75
 
76
-	/**
77
-	 * @param string $message
78
-	 * @param string $search
79
-	 * @return string
80
-	 * @throws NotFoundException
81
-	 */
82
-	protected function getRelevantMessagePart(string $message, string $search): string {
83
-		$start = stripos($message, $search);
84
-		if ($start === false) {
85
-			throw new NotFoundException('Comment section not found');
86
-		}
76
+    /**
77
+     * @param string $message
78
+     * @param string $search
79
+     * @return string
80
+     * @throws NotFoundException
81
+     */
82
+    protected function getRelevantMessagePart(string $message, string $search): string {
83
+        $start = stripos($message, $search);
84
+        if ($start === false) {
85
+            throw new NotFoundException('Comment section not found');
86
+        }
87 87
 
88
-		$end = $start + strlen($search);
88
+        $end = $start + strlen($search);
89 89
 
90
-		if ($start <= 25) {
91
-			$start = 0;
92
-			$prefix = '';
93
-		} else {
94
-			$start -= 25;
95
-			$prefix = '…';
96
-		}
90
+        if ($start <= 25) {
91
+            $start = 0;
92
+            $prefix = '';
93
+        } else {
94
+            $start -= 25;
95
+            $prefix = '…';
96
+        }
97 97
 
98
-		if ((strlen($message) - $end) <= 25) {
99
-			$end = strlen($message);
100
-			$suffix = '';
101
-		} else {
102
-			$end += 25;
103
-			$suffix = '…';
104
-		}
98
+        if ((strlen($message) - $end) <= 25) {
99
+            $end = strlen($message);
100
+            $suffix = '';
101
+        } else {
102
+            $end += 25;
103
+            $suffix = '…';
104
+        }
105 105
 
106
-		return $prefix . substr($message, $start, $end - $start) . $suffix;
107
-	}
106
+        return $prefix . substr($message, $start, $end - $start) . $suffix;
107
+    }
108 108
 
109 109
 }
Please login to merge, or discard this patch.