Completed
Push — stable9 ( 11047b...318578 )
by Lukas
20:03 queued 09:36
created
lib/private/appframework/utility/controllermethodreflector.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -46,7 +46,7 @@
 block discarded – undo
46 46
 
47 47
 
48 48
 	/**
49
-	 * @param object $object an object or classname
49
+	 * @param \OCP\AppFramework\Controller $object an object or classname
50 50
 	 * @param string $method the method which we want to inspect
51 51
 	 */
52 52
 	public function reflect($object, $method){
Please login to merge, or discard this patch.
Indentation   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -35,85 +35,85 @@
 block discarded – undo
35 35
  */
36 36
 class ControllerMethodReflector implements IControllerMethodReflector{
37 37
 
38
-	private $annotations;
39
-	private $types;
40
-	private $parameters;
41
-
42
-	public function __construct() {
43
-		$this->types = array();
44
-		$this->parameters = array();
45
-		$this->annotations = array();
46
-	}
47
-
48
-
49
-	/**
50
-	 * @param object $object an object or classname
51
-	 * @param string $method the method which we want to inspect
52
-	 */
53
-	public function reflect($object, $method){
54
-		$reflection = new \ReflectionMethod($object, $method);
55
-		$docs = $reflection->getDocComment();
56
-
57
-		// extract everything prefixed by @ and first letter uppercase
58
-		preg_match_all('/@([A-Z]\w+)/', $docs, $matches);
59
-		$this->annotations = $matches[1];
60
-
61
-		// extract type parameter information
62
-		preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches);
63
-		$this->types = array_combine($matches['var'], $matches['type']);
64
-
65
-		foreach ($reflection->getParameters() as $param) {
66
-			// extract type information from PHP 7 scalar types and prefer them
67
-			// over phpdoc annotations
68
-			if (method_exists($param, 'getType')) {
69
-				$type = $param->getType();
70
-				if ($type !== null) {
71
-					$this->types[$param->getName()] = (string) $type;
72
-				}
73
-			}
74
-
75
-			if($param->isOptional()) {
76
-				$default = $param->getDefaultValue();
77
-			} else {
78
-				$default = null;
79
-			}
80
-			$this->parameters[$param->name] = $default;
81
-		}
82
-	}
83
-
84
-
85
-	/**
86
-	 * Inspects the PHPDoc parameters for types
87
-	 * @param string $parameter the parameter whose type comments should be
88
-	 * parsed
89
-	 * @return string|null type in the type parameters (@param int $something)
90
-	 * would return int or null if not existing
91
-	 */
92
-	public function getType($parameter) {
93
-		if(array_key_exists($parameter, $this->types)) {
94
-			return $this->types[$parameter];
95
-		} else {
96
-			return null;
97
-		}
98
-	}
99
-
100
-
101
-	/**
102
-	 * @return array the arguments of the method with key => default value
103
-	 */
104
-	public function getParameters() {
105
-		return $this->parameters;
106
-	}
107
-
108
-
109
-	/**
110
-	 * Check if a method contains an annotation
111
-	 * @param string $name the name of the annotation
112
-	 * @return bool true if the annotation is found
113
-	 */
114
-	public function hasAnnotation($name){
115
-		return in_array($name, $this->annotations);
116
-	}
38
+    private $annotations;
39
+    private $types;
40
+    private $parameters;
41
+
42
+    public function __construct() {
43
+        $this->types = array();
44
+        $this->parameters = array();
45
+        $this->annotations = array();
46
+    }
47
+
48
+
49
+    /**
50
+     * @param object $object an object or classname
51
+     * @param string $method the method which we want to inspect
52
+     */
53
+    public function reflect($object, $method){
54
+        $reflection = new \ReflectionMethod($object, $method);
55
+        $docs = $reflection->getDocComment();
56
+
57
+        // extract everything prefixed by @ and first letter uppercase
58
+        preg_match_all('/@([A-Z]\w+)/', $docs, $matches);
59
+        $this->annotations = $matches[1];
60
+
61
+        // extract type parameter information
62
+        preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches);
63
+        $this->types = array_combine($matches['var'], $matches['type']);
64
+
65
+        foreach ($reflection->getParameters() as $param) {
66
+            // extract type information from PHP 7 scalar types and prefer them
67
+            // over phpdoc annotations
68
+            if (method_exists($param, 'getType')) {
69
+                $type = $param->getType();
70
+                if ($type !== null) {
71
+                    $this->types[$param->getName()] = (string) $type;
72
+                }
73
+            }
74
+
75
+            if($param->isOptional()) {
76
+                $default = $param->getDefaultValue();
77
+            } else {
78
+                $default = null;
79
+            }
80
+            $this->parameters[$param->name] = $default;
81
+        }
82
+    }
83
+
84
+
85
+    /**
86
+     * Inspects the PHPDoc parameters for types
87
+     * @param string $parameter the parameter whose type comments should be
88
+     * parsed
89
+     * @return string|null type in the type parameters (@param int $something)
90
+     * would return int or null if not existing
91
+     */
92
+    public function getType($parameter) {
93
+        if(array_key_exists($parameter, $this->types)) {
94
+            return $this->types[$parameter];
95
+        } else {
96
+            return null;
97
+        }
98
+    }
99
+
100
+
101
+    /**
102
+     * @return array the arguments of the method with key => default value
103
+     */
104
+    public function getParameters() {
105
+        return $this->parameters;
106
+    }
107
+
108
+
109
+    /**
110
+     * Check if a method contains an annotation
111
+     * @param string $name the name of the annotation
112
+     * @return bool true if the annotation is found
113
+     */
114
+    public function hasAnnotation($name){
115
+        return in_array($name, $this->annotations);
116
+    }
117 117
 
118 118
 
119 119
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -33,7 +33,7 @@  discard block
 block discarded – undo
33 33
 /**
34 34
  * Reads and parses annotations from doc comments
35 35
  */
36
-class ControllerMethodReflector implements IControllerMethodReflector{
36
+class ControllerMethodReflector implements IControllerMethodReflector {
37 37
 
38 38
 	private $annotations;
39 39
 	private $types;
@@ -50,7 +50,7 @@  discard block
 block discarded – undo
50 50
 	 * @param object $object an object or classname
51 51
 	 * @param string $method the method which we want to inspect
52 52
 	 */
53
-	public function reflect($object, $method){
53
+	public function reflect($object, $method) {
54 54
 		$reflection = new \ReflectionMethod($object, $method);
55 55
 		$docs = $reflection->getDocComment();
56 56
 
@@ -72,7 +72,7 @@  discard block
 block discarded – undo
72 72
 				}
73 73
 			}
74 74
 
75
-			if($param->isOptional()) {
75
+			if ($param->isOptional()) {
76 76
 				$default = $param->getDefaultValue();
77 77
 			} else {
78 78
 				$default = null;
@@ -90,7 +90,7 @@  discard block
 block discarded – undo
90 90
 	 * would return int or null if not existing
91 91
 	 */
92 92
 	public function getType($parameter) {
93
-		if(array_key_exists($parameter, $this->types)) {
93
+		if (array_key_exists($parameter, $this->types)) {
94 94
 			return $this->types[$parameter];
95 95
 		} else {
96 96
 			return null;
@@ -111,7 +111,7 @@  discard block
 block discarded – undo
111 111
 	 * @param string $name the name of the annotation
112 112
 	 * @return bool true if the annotation is found
113 113
 	 */
114
-	public function hasAnnotation($name){
114
+	public function hasAnnotation($name) {
115 115
 		return in_array($name, $this->annotations);
116 116
 	}
117 117
 
Please login to merge, or discard this patch.
lib/private/comments/manager.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -205,7 +205,7 @@
 block discarded – undo
205 205
 	/**
206 206
 	 * removes an entry from the comments run time cache
207 207
 	 *
208
-	 * @param mixed $id the comment's id
208
+	 * @param string $id the comment's id
209 209
 	 */
210 210
 	protected function uncache($id) {
211 211
 		$id = strval($id);
Please login to merge, or discard this patch.
Indentation   +714 added lines, -714 removed lines patch added patch discarded remove patch
@@ -37,718 +37,718 @@
 block discarded – undo
37 37
 
38 38
 class Manager implements ICommentsManager {
39 39
 
40
-	/** @var  IDBConnection */
41
-	protected $dbConn;
42
-
43
-	/** @var  ILogger */
44
-	protected $logger;
45
-
46
-	/** @var IConfig */
47
-	protected $config;
48
-
49
-	/** @var EventDispatcherInterface */
50
-	protected $dispatcher;
51
-
52
-	/** @var IComment[]  */
53
-	protected $commentsCache = [];
54
-
55
-	/**
56
-	 * Manager constructor.
57
-	 *
58
-	 * @param IDBConnection $dbConn
59
-	 * @param ILogger $logger
60
-	 * @param IConfig $config
61
-	 * @param EventDispatcherInterface $dispatcher
62
-	 */
63
-	public function __construct(
64
-		IDBConnection $dbConn,
65
-		ILogger $logger,
66
-		IConfig $config,
67
-		EventDispatcherInterface $dispatcher
68
-	) {
69
-		$this->dbConn = $dbConn;
70
-		$this->logger = $logger;
71
-		$this->config = $config;
72
-		$this->dispatcher = $dispatcher;
73
-	}
74
-
75
-	/**
76
-	 * converts data base data into PHP native, proper types as defined by
77
-	 * IComment interface.
78
-	 *
79
-	 * @param array $data
80
-	 * @return array
81
-	 */
82
-	protected function normalizeDatabaseData(array $data) {
83
-		$data['id'] = strval($data['id']);
84
-		$data['parent_id'] = strval($data['parent_id']);
85
-		$data['topmost_parent_id'] = strval($data['topmost_parent_id']);
86
-		$data['creation_timestamp'] = new \DateTime($data['creation_timestamp']);
87
-		if (!is_null($data['latest_child_timestamp'])) {
88
-			$data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']);
89
-		}
90
-		$data['children_count'] = intval($data['children_count']);
91
-		return $data;
92
-	}
93
-
94
-	/**
95
-	 * prepares a comment for an insert or update operation after making sure
96
-	 * all necessary fields have a value assigned.
97
-	 *
98
-	 * @param IComment $comment
99
-	 * @return IComment returns the same updated IComment instance as provided
100
-	 *                  by parameter for convenience
101
-	 * @throws \UnexpectedValueException
102
-	 */
103
-	protected function prepareCommentForDatabaseWrite(IComment $comment) {
104
-		if(    !$comment->getActorType()
105
-			|| !$comment->getActorId()
106
-			|| !$comment->getObjectType()
107
-			|| !$comment->getObjectId()
108
-			|| !$comment->getVerb()
109
-		) {
110
-			throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving');
111
-		}
112
-
113
-		if($comment->getId() === '') {
114
-			$comment->setChildrenCount(0);
115
-			$comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC')));
116
-			$comment->setLatestChildDateTime(null);
117
-		}
118
-
119
-		if(is_null($comment->getCreationDateTime())) {
120
-			$comment->setCreationDateTime(new \DateTime());
121
-		}
122
-
123
-		if($comment->getParentId() !== '0') {
124
-			$comment->setTopmostParentId($this->determineTopmostParentId($comment->getParentId()));
125
-		} else {
126
-			$comment->setTopmostParentId('0');
127
-		}
128
-
129
-		$this->cache($comment);
130
-
131
-		return $comment;
132
-	}
133
-
134
-	/**
135
-	 * returns the topmost parent id of a given comment identified by ID
136
-	 *
137
-	 * @param string $id
138
-	 * @return string
139
-	 * @throws NotFoundException
140
-	 */
141
-	protected function determineTopmostParentId($id) {
142
-		$comment = $this->get($id);
143
-		if($comment->getParentId() === '0') {
144
-			return $comment->getId();
145
-		} else {
146
-			return $this->determineTopmostParentId($comment->getId());
147
-		}
148
-	}
149
-
150
-	/**
151
-	 * updates child information of a comment
152
-	 *
153
-	 * @param string	$id
154
-	 * @param \DateTime	$cDateTime	the date time of the most recent child
155
-	 * @throws NotFoundException
156
-	 */
157
-	protected function updateChildrenInformation($id, \DateTime $cDateTime) {
158
-		$qb = $this->dbConn->getQueryBuilder();
159
-		$query = $qb->select($qb->createFunction('COUNT(`id`)'))
160
-				->from('comments')
161
-				->where($qb->expr()->eq('parent_id', $qb->createParameter('id')))
162
-				->setParameter('id', $id);
163
-
164
-		$resultStatement = $query->execute();
165
-		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
166
-		$resultStatement->closeCursor();
167
-		$children = intval($data[0]);
168
-
169
-		$comment = $this->get($id);
170
-		$comment->setChildrenCount($children);
171
-		$comment->setLatestChildDateTime($cDateTime);
172
-		$this->save($comment);
173
-	}
174
-
175
-	/**
176
-	 * Tests whether actor or object type and id parameters are acceptable.
177
-	 * Throws exception if not.
178
-	 *
179
-	 * @param string $role
180
-	 * @param string $type
181
-	 * @param string $id
182
-	 * @throws \InvalidArgumentException
183
-	 */
184
-	protected function checkRoleParameters($role, $type, $id) {
185
-		if(
186
-			   !is_string($type) || empty($type)
187
-			|| !is_string($id) || empty($id)
188
-		) {
189
-			throw new \InvalidArgumentException($role . ' parameters must be string and not empty');
190
-		}
191
-	}
192
-
193
-	/**
194
-	 * run-time caches a comment
195
-	 *
196
-	 * @param IComment $comment
197
-	 */
198
-	protected function cache(IComment $comment) {
199
-		$id = $comment->getId();
200
-		if(empty($id)) {
201
-			return;
202
-		}
203
-		$this->commentsCache[strval($id)] = $comment;
204
-	}
205
-
206
-	/**
207
-	 * removes an entry from the comments run time cache
208
-	 *
209
-	 * @param mixed $id the comment's id
210
-	 */
211
-	protected function uncache($id) {
212
-		$id = strval($id);
213
-		if (isset($this->commentsCache[$id])) {
214
-			unset($this->commentsCache[$id]);
215
-		}
216
-	}
217
-
218
-	/**
219
-	 * returns a comment instance
220
-	 *
221
-	 * @param string $id the ID of the comment
222
-	 * @return IComment
223
-	 * @throws NotFoundException
224
-	 * @throws \InvalidArgumentException
225
-	 * @since 9.0.0
226
-	 */
227
-	public function get($id) {
228
-		if(intval($id) === 0) {
229
-			throw new \InvalidArgumentException('IDs must be translatable to a number in this implementation.');
230
-		}
231
-
232
-		if(isset($this->commentsCache[$id])) {
233
-			return $this->commentsCache[$id];
234
-		}
235
-
236
-		$qb = $this->dbConn->getQueryBuilder();
237
-		$resultStatement = $qb->select('*')
238
-			->from('comments')
239
-			->where($qb->expr()->eq('id', $qb->createParameter('id')))
240
-			->setParameter('id', $id, IQueryBuilder::PARAM_INT)
241
-			->execute();
242
-
243
-		$data = $resultStatement->fetch();
244
-		$resultStatement->closeCursor();
245
-		if(!$data) {
246
-			throw new NotFoundException();
247
-		}
248
-
249
-		$comment = new Comment($this->normalizeDatabaseData($data));
250
-		$this->cache($comment);
251
-		return $comment;
252
-	}
253
-
254
-	/**
255
-	 * returns the comment specified by the id and all it's child comments.
256
-	 * At this point of time, we do only support one level depth.
257
-	 *
258
-	 * @param string $id
259
-	 * @param int $limit max number of entries to return, 0 returns all
260
-	 * @param int $offset the start entry
261
-	 * @return array
262
-	 * @since 9.0.0
263
-	 *
264
-	 * The return array looks like this
265
-	 * [
266
-	 *   'comment' => IComment, // root comment
267
-	 *   'replies' =>
268
-	 *   [
269
-	 *     0 =>
270
-	 *     [
271
-	 *       'comment' => IComment,
272
-	 *       'replies' => []
273
-	 *     ]
274
-	 *     1 =>
275
-	 *     [
276
-	 *       'comment' => IComment,
277
-	 *       'replies'=> []
278
-	 *     ],
279
-	 *     …
280
-	 *   ]
281
-	 * ]
282
-	 */
283
-	public function getTree($id, $limit = 0, $offset = 0) {
284
-		$tree = [];
285
-		$tree['comment'] = $this->get($id);
286
-		$tree['replies'] = [];
287
-
288
-		$qb = $this->dbConn->getQueryBuilder();
289
-		$query = $qb->select('*')
290
-				->from('comments')
291
-				->where($qb->expr()->eq('topmost_parent_id', $qb->createParameter('id')))
292
-				->orderBy('creation_timestamp', 'DESC')
293
-				->setParameter('id', $id);
294
-
295
-		if($limit > 0) {
296
-			$query->setMaxResults($limit);
297
-		}
298
-		if($offset > 0) {
299
-			$query->setFirstResult($offset);
300
-		}
301
-
302
-		$resultStatement = $query->execute();
303
-		while($data = $resultStatement->fetch()) {
304
-			$comment = new Comment($this->normalizeDatabaseData($data));
305
-			$this->cache($comment);
306
-			$tree['replies'][] = [
307
-				'comment' => $comment,
308
-				'replies' => []
309
-			];
310
-		}
311
-		$resultStatement->closeCursor();
312
-
313
-		return $tree;
314
-	}
315
-
316
-	/**
317
-	 * returns comments for a specific object (e.g. a file).
318
-	 *
319
-	 * The sort order is always newest to oldest.
320
-	 *
321
-	 * @param string $objectType the object type, e.g. 'files'
322
-	 * @param string $objectId the id of the object
323
-	 * @param int $limit optional, number of maximum comments to be returned. if
324
-	 * not specified, all comments are returned.
325
-	 * @param int $offset optional, starting point
326
-	 * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
327
-	 * that may be returned
328
-	 * @return IComment[]
329
-	 * @since 9.0.0
330
-	 */
331
-	public function getForObject(
332
-			$objectType,
333
-			$objectId,
334
-			$limit = 0,
335
-			$offset = 0,
336
-			\DateTime $notOlderThan = null
337
-	) {
338
-		$comments = [];
339
-
340
-		$qb = $this->dbConn->getQueryBuilder();
341
-		$query = $qb->select('*')
342
-				->from('comments')
343
-				->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
344
-				->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
345
-				->orderBy('creation_timestamp', 'DESC')
346
-				->setParameter('type', $objectType)
347
-				->setParameter('id', $objectId);
348
-
349
-		if($limit > 0) {
350
-			$query->setMaxResults($limit);
351
-		}
352
-		if($offset > 0) {
353
-			$query->setFirstResult($offset);
354
-		}
355
-		if(!is_null($notOlderThan)) {
356
-			$query
357
-				->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
358
-				->setParameter('notOlderThan', $notOlderThan, 'datetime');
359
-		}
360
-
361
-		$resultStatement = $query->execute();
362
-		while($data = $resultStatement->fetch()) {
363
-			$comment = new Comment($this->normalizeDatabaseData($data));
364
-			$this->cache($comment);
365
-			$comments[] = $comment;
366
-		}
367
-		$resultStatement->closeCursor();
368
-
369
-		return $comments;
370
-	}
371
-
372
-	/**
373
-	 * @param $objectType string the object type, e.g. 'files'
374
-	 * @param $objectId string the id of the object
375
-	 * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
376
-	 * that may be returned
377
-	 * @return Int
378
-	 * @since 9.0.0
379
-	 */
380
-	public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null) {
381
-		$qb = $this->dbConn->getQueryBuilder();
382
-		$query = $qb->select($qb->createFunction('COUNT(`id`)'))
383
-				->from('comments')
384
-				->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
385
-				->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
386
-				->setParameter('type', $objectType)
387
-				->setParameter('id', $objectId);
388
-
389
-		if(!is_null($notOlderThan)) {
390
-			$query
391
-				->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
392
-				->setParameter('notOlderThan', $notOlderThan, 'datetime');
393
-		}
394
-
395
-		$resultStatement = $query->execute();
396
-		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
397
-		$resultStatement->closeCursor();
398
-		return intval($data[0]);
399
-	}
400
-
401
-	/**
402
-	 * creates a new comment and returns it. At this point of time, it is not
403
-	 * saved in the used data storage. Use save() after setting other fields
404
-	 * of the comment (e.g. message or verb).
405
-	 *
406
-	 * @param string $actorType the actor type (e.g. 'users')
407
-	 * @param string $actorId a user id
408
-	 * @param string $objectType the object type the comment is attached to
409
-	 * @param string $objectId the object id the comment is attached to
410
-	 * @return IComment
411
-	 * @since 9.0.0
412
-	 */
413
-	public function create($actorType, $actorId, $objectType, $objectId) {
414
-		$comment = new Comment();
415
-		$comment
416
-			->setActor($actorType, $actorId)
417
-			->setObject($objectType, $objectId);
418
-		return $comment;
419
-	}
420
-
421
-	/**
422
-	 * permanently deletes the comment specified by the ID
423
-	 *
424
-	 * When the comment has child comments, their parent ID will be changed to
425
-	 * the parent ID of the item that is to be deleted.
426
-	 *
427
-	 * @param string $id
428
-	 * @return bool
429
-	 * @throws \InvalidArgumentException
430
-	 * @since 9.0.0
431
-	 */
432
-	public function delete($id) {
433
-		if(!is_string($id)) {
434
-			throw new \InvalidArgumentException('Parameter must be string');
435
-		}
436
-
437
-		try {
438
-			$comment = $this->get($id);
439
-		} catch (\Exception $e) {
440
-			// Ignore exceptions, we just don't fire a hook then
441
-			$comment = null;
442
-		}
443
-
444
-		$qb = $this->dbConn->getQueryBuilder();
445
-		$query = $qb->delete('comments')
446
-			->where($qb->expr()->eq('id', $qb->createParameter('id')))
447
-			->setParameter('id', $id);
448
-
449
-		try {
450
-			$affectedRows = $query->execute();
451
-			$this->uncache($id);
452
-		} catch (DriverException $e) {
453
-			$this->logger->logException($e, ['app' => 'core_comments']);
454
-			return false;
455
-		}
456
-
457
-		if ($affectedRows > 0 && $comment instanceof IComment) {
458
-			$this->dispatcher->dispatch(CommentsEvent::EVENT_DELETE, new CommentsEvent(
459
-				CommentsEvent::EVENT_DELETE,
460
-				$comment
461
-			));
462
-		}
463
-
464
-		return ($affectedRows > 0);
465
-	}
466
-
467
-	/**
468
-	 * saves the comment permanently
469
-	 *
470
-	 * if the supplied comment has an empty ID, a new entry comment will be
471
-	 * saved and the instance updated with the new ID.
472
-	 *
473
-	 * Otherwise, an existing comment will be updated.
474
-	 *
475
-	 * Throws NotFoundException when a comment that is to be updated does not
476
-	 * exist anymore at this point of time.
477
-	 *
478
-	 * @param IComment $comment
479
-	 * @return bool
480
-	 * @throws NotFoundException
481
-	 * @since 9.0.0
482
-	 */
483
-	public function save(IComment $comment) {
484
-		if($this->prepareCommentForDatabaseWrite($comment)->getId() === '') {
485
-			$result = $this->insert($comment);
486
-		} else {
487
-			$result = $this->update($comment);
488
-		}
489
-
490
-		if($result && !!$comment->getParentId()) {
491
-			$this->updateChildrenInformation(
492
-					$comment->getParentId(),
493
-					$comment->getCreationDateTime()
494
-			);
495
-			$this->cache($comment);
496
-		}
497
-
498
-		return $result;
499
-	}
500
-
501
-	/**
502
-	 * inserts the provided comment in the database
503
-	 *
504
-	 * @param IComment $comment
505
-	 * @return bool
506
-	 */
507
-	protected function insert(IComment &$comment) {
508
-		$qb = $this->dbConn->getQueryBuilder();
509
-		$affectedRows = $qb
510
-			->insert('comments')
511
-			->values([
512
-				'parent_id'					=> $qb->createNamedParameter($comment->getParentId()),
513
-				'topmost_parent_id' 		=> $qb->createNamedParameter($comment->getTopmostParentId()),
514
-				'children_count' 			=> $qb->createNamedParameter($comment->getChildrenCount()),
515
-				'actor_type' 				=> $qb->createNamedParameter($comment->getActorType()),
516
-				'actor_id' 					=> $qb->createNamedParameter($comment->getActorId()),
517
-				'message' 					=> $qb->createNamedParameter($comment->getMessage()),
518
-				'verb' 						=> $qb->createNamedParameter($comment->getVerb()),
519
-				'creation_timestamp' 		=> $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'),
520
-				'latest_child_timestamp'	=> $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
521
-				'object_type' 				=> $qb->createNamedParameter($comment->getObjectType()),
522
-				'object_id' 				=> $qb->createNamedParameter($comment->getObjectId()),
523
-			])
524
-			->execute();
525
-
526
-		if ($affectedRows > 0) {
527
-			$comment->setId(strval($qb->getLastInsertId()));
528
-		}
529
-
530
-		$this->dispatcher->dispatch(CommentsEvent::EVENT_ADD, new CommentsEvent(
531
-			CommentsEvent::EVENT_ADD,
532
-			$comment
533
-		));
534
-
535
-		return $affectedRows > 0;
536
-	}
537
-
538
-	/**
539
-	 * updates a Comment data row
540
-	 *
541
-	 * @param IComment $comment
542
-	 * @return bool
543
-	 * @throws NotFoundException
544
-	 */
545
-	protected function update(IComment $comment) {
546
-		$qb = $this->dbConn->getQueryBuilder();
547
-		$affectedRows = $qb
548
-			->update('comments')
549
-				->set('parent_id',				$qb->createNamedParameter($comment->getParentId()))
550
-				->set('topmost_parent_id', 		$qb->createNamedParameter($comment->getTopmostParentId()))
551
-				->set('children_count',			$qb->createNamedParameter($comment->getChildrenCount()))
552
-				->set('actor_type', 			$qb->createNamedParameter($comment->getActorType()))
553
-				->set('actor_id', 				$qb->createNamedParameter($comment->getActorId()))
554
-				->set('message',				$qb->createNamedParameter($comment->getMessage()))
555
-				->set('verb',					$qb->createNamedParameter($comment->getVerb()))
556
-				->set('creation_timestamp',		$qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'))
557
-				->set('latest_child_timestamp',	$qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'))
558
-				->set('object_type',			$qb->createNamedParameter($comment->getObjectType()))
559
-				->set('object_id',				$qb->createNamedParameter($comment->getObjectId()))
560
-			->where($qb->expr()->eq('id', $qb->createParameter('id')))
561
-			->setParameter('id', $comment->getId())
562
-			->execute();
563
-
564
-		if($affectedRows === 0) {
565
-			throw new NotFoundException('Comment to update does ceased to exist');
566
-		}
567
-
568
-		$this->dispatcher->dispatch(CommentsEvent::EVENT_UPDATE, new CommentsEvent(
569
-			CommentsEvent::EVENT_UPDATE,
570
-			$comment
571
-		));
572
-
573
-		return $affectedRows > 0;
574
-	}
575
-
576
-	/**
577
-	 * removes references to specific actor (e.g. on user delete) of a comment.
578
-	 * The comment itself must not get lost/deleted.
579
-	 *
580
-	 * @param string $actorType the actor type (e.g. 'users')
581
-	 * @param string $actorId a user id
582
-	 * @return boolean
583
-	 * @since 9.0.0
584
-	 */
585
-	public function deleteReferencesOfActor($actorType, $actorId) {
586
-		$this->checkRoleParameters('Actor', $actorType, $actorId);
587
-
588
-		$qb = $this->dbConn->getQueryBuilder();
589
-		$affectedRows = $qb
590
-			->update('comments')
591
-			->set('actor_type',	$qb->createNamedParameter(ICommentsManager::DELETED_USER))
592
-			->set('actor_id',	$qb->createNamedParameter(ICommentsManager::DELETED_USER))
593
-			->where($qb->expr()->eq('actor_type', $qb->createParameter('type')))
594
-			->andWhere($qb->expr()->eq('actor_id', $qb->createParameter('id')))
595
-			->setParameter('type', $actorType)
596
-			->setParameter('id', $actorId)
597
-			->execute();
598
-
599
-		$this->commentsCache = [];
600
-
601
-		return is_int($affectedRows);
602
-	}
603
-
604
-	/**
605
-	 * deletes all comments made of a specific object (e.g. on file delete)
606
-	 *
607
-	 * @param string $objectType the object type (e.g. 'files')
608
-	 * @param string $objectId e.g. the file id
609
-	 * @return boolean
610
-	 * @since 9.0.0
611
-	 */
612
-	public function deleteCommentsAtObject($objectType, $objectId) {
613
-		$this->checkRoleParameters('Object', $objectType, $objectId);
614
-
615
-		$qb = $this->dbConn->getQueryBuilder();
616
-		$affectedRows = $qb
617
-			->delete('comments')
618
-			->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
619
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
620
-			->setParameter('type', $objectType)
621
-			->setParameter('id', $objectId)
622
-			->execute();
623
-
624
-		$this->commentsCache = [];
625
-
626
-		return is_int($affectedRows);
627
-	}
628
-
629
-	/**
630
-	 * deletes the read markers for the specified user
631
-	 *
632
-	 * @param \OCP\IUser $user
633
-	 * @return bool
634
-	 * @since 9.0.0
635
-	 */
636
-	public function deleteReadMarksFromUser(IUser $user) {
637
-		$qb = $this->dbConn->getQueryBuilder();
638
-		$query = $qb->delete('comments_read_markers')
639
-			->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
640
-			->setParameter('user_id', $user->getUID());
641
-
642
-		try {
643
-			$affectedRows = $query->execute();
644
-		} catch (DriverException $e) {
645
-			$this->logger->logException($e, ['app' => 'core_comments']);
646
-			return false;
647
-		}
648
-		return ($affectedRows > 0);
649
-	}
650
-
651
-	/**
652
-	 * sets the read marker for a given file to the specified date for the
653
-	 * provided user
654
-	 *
655
-	 * @param string $objectType
656
-	 * @param string $objectId
657
-	 * @param \DateTime $dateTime
658
-	 * @param IUser $user
659
-	 * @since 9.0.0
660
-	 */
661
-	public function setReadMark($objectType, $objectId, \DateTime $dateTime, IUser $user) {
662
-		$this->checkRoleParameters('Object', $objectType, $objectId);
663
-
664
-		$qb = $this->dbConn->getQueryBuilder();
665
-		$values = [
666
-			'user_id'         => $qb->createNamedParameter($user->getUID()),
667
-			'marker_datetime' => $qb->createNamedParameter($dateTime, 'datetime'),
668
-			'object_type'     => $qb->createNamedParameter($objectType),
669
-			'object_id'       => $qb->createNamedParameter($objectId),
670
-		];
671
-
672
-		// Strategy: try to update, if this does not return affected rows, do an insert.
673
-		$affectedRows = $qb
674
-			->update('comments_read_markers')
675
-			->set('user_id',         $values['user_id'])
676
-			->set('marker_datetime', $values['marker_datetime'])
677
-			->set('object_type',     $values['object_type'])
678
-			->set('object_id',       $values['object_id'])
679
-			->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
680
-			->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
681
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
682
-			->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR)
683
-			->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR)
684
-			->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR)
685
-			->execute();
686
-
687
-		if ($affectedRows > 0) {
688
-			return;
689
-		}
690
-
691
-		$qb->insert('comments_read_markers')
692
-			->values($values)
693
-			->execute();
694
-	}
695
-
696
-	/**
697
-	 * returns the read marker for a given file to the specified date for the
698
-	 * provided user. It returns null, when the marker is not present, i.e.
699
-	 * no comments were marked as read.
700
-	 *
701
-	 * @param string $objectType
702
-	 * @param string $objectId
703
-	 * @param IUser $user
704
-	 * @return \DateTime|null
705
-	 * @since 9.0.0
706
-	 */
707
-	public function getReadMark($objectType, $objectId, IUser $user) {
708
-		$qb = $this->dbConn->getQueryBuilder();
709
-		$resultStatement = $qb->select('marker_datetime')
710
-			->from('comments_read_markers')
711
-			->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
712
-			->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
713
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
714
-			->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR)
715
-			->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR)
716
-			->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR)
717
-			->execute();
718
-
719
-		$data = $resultStatement->fetch();
720
-		$resultStatement->closeCursor();
721
-		if(!$data || is_null($data['marker_datetime'])) {
722
-			return null;
723
-		}
724
-
725
-		return new \DateTime($data['marker_datetime']);
726
-	}
727
-
728
-	/**
729
-	 * deletes the read markers on the specified object
730
-	 *
731
-	 * @param string $objectType
732
-	 * @param string $objectId
733
-	 * @return bool
734
-	 * @since 9.0.0
735
-	 */
736
-	public function deleteReadMarksOnObject($objectType, $objectId) {
737
-		$this->checkRoleParameters('Object', $objectType, $objectId);
738
-
739
-		$qb = $this->dbConn->getQueryBuilder();
740
-		$query = $qb->delete('comments_read_markers')
741
-			->where($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
742
-			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
743
-			->setParameter('object_type', $objectType)
744
-			->setParameter('object_id', $objectId);
745
-
746
-		try {
747
-			$affectedRows = $query->execute();
748
-		} catch (DriverException $e) {
749
-			$this->logger->logException($e, ['app' => 'core_comments']);
750
-			return false;
751
-		}
752
-		return ($affectedRows > 0);
753
-	}
40
+    /** @var  IDBConnection */
41
+    protected $dbConn;
42
+
43
+    /** @var  ILogger */
44
+    protected $logger;
45
+
46
+    /** @var IConfig */
47
+    protected $config;
48
+
49
+    /** @var EventDispatcherInterface */
50
+    protected $dispatcher;
51
+
52
+    /** @var IComment[]  */
53
+    protected $commentsCache = [];
54
+
55
+    /**
56
+     * Manager constructor.
57
+     *
58
+     * @param IDBConnection $dbConn
59
+     * @param ILogger $logger
60
+     * @param IConfig $config
61
+     * @param EventDispatcherInterface $dispatcher
62
+     */
63
+    public function __construct(
64
+        IDBConnection $dbConn,
65
+        ILogger $logger,
66
+        IConfig $config,
67
+        EventDispatcherInterface $dispatcher
68
+    ) {
69
+        $this->dbConn = $dbConn;
70
+        $this->logger = $logger;
71
+        $this->config = $config;
72
+        $this->dispatcher = $dispatcher;
73
+    }
74
+
75
+    /**
76
+     * converts data base data into PHP native, proper types as defined by
77
+     * IComment interface.
78
+     *
79
+     * @param array $data
80
+     * @return array
81
+     */
82
+    protected function normalizeDatabaseData(array $data) {
83
+        $data['id'] = strval($data['id']);
84
+        $data['parent_id'] = strval($data['parent_id']);
85
+        $data['topmost_parent_id'] = strval($data['topmost_parent_id']);
86
+        $data['creation_timestamp'] = new \DateTime($data['creation_timestamp']);
87
+        if (!is_null($data['latest_child_timestamp'])) {
88
+            $data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']);
89
+        }
90
+        $data['children_count'] = intval($data['children_count']);
91
+        return $data;
92
+    }
93
+
94
+    /**
95
+     * prepares a comment for an insert or update operation after making sure
96
+     * all necessary fields have a value assigned.
97
+     *
98
+     * @param IComment $comment
99
+     * @return IComment returns the same updated IComment instance as provided
100
+     *                  by parameter for convenience
101
+     * @throws \UnexpectedValueException
102
+     */
103
+    protected function prepareCommentForDatabaseWrite(IComment $comment) {
104
+        if(    !$comment->getActorType()
105
+            || !$comment->getActorId()
106
+            || !$comment->getObjectType()
107
+            || !$comment->getObjectId()
108
+            || !$comment->getVerb()
109
+        ) {
110
+            throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving');
111
+        }
112
+
113
+        if($comment->getId() === '') {
114
+            $comment->setChildrenCount(0);
115
+            $comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC')));
116
+            $comment->setLatestChildDateTime(null);
117
+        }
118
+
119
+        if(is_null($comment->getCreationDateTime())) {
120
+            $comment->setCreationDateTime(new \DateTime());
121
+        }
122
+
123
+        if($comment->getParentId() !== '0') {
124
+            $comment->setTopmostParentId($this->determineTopmostParentId($comment->getParentId()));
125
+        } else {
126
+            $comment->setTopmostParentId('0');
127
+        }
128
+
129
+        $this->cache($comment);
130
+
131
+        return $comment;
132
+    }
133
+
134
+    /**
135
+     * returns the topmost parent id of a given comment identified by ID
136
+     *
137
+     * @param string $id
138
+     * @return string
139
+     * @throws NotFoundException
140
+     */
141
+    protected function determineTopmostParentId($id) {
142
+        $comment = $this->get($id);
143
+        if($comment->getParentId() === '0') {
144
+            return $comment->getId();
145
+        } else {
146
+            return $this->determineTopmostParentId($comment->getId());
147
+        }
148
+    }
149
+
150
+    /**
151
+     * updates child information of a comment
152
+     *
153
+     * @param string	$id
154
+     * @param \DateTime	$cDateTime	the date time of the most recent child
155
+     * @throws NotFoundException
156
+     */
157
+    protected function updateChildrenInformation($id, \DateTime $cDateTime) {
158
+        $qb = $this->dbConn->getQueryBuilder();
159
+        $query = $qb->select($qb->createFunction('COUNT(`id`)'))
160
+                ->from('comments')
161
+                ->where($qb->expr()->eq('parent_id', $qb->createParameter('id')))
162
+                ->setParameter('id', $id);
163
+
164
+        $resultStatement = $query->execute();
165
+        $data = $resultStatement->fetch(\PDO::FETCH_NUM);
166
+        $resultStatement->closeCursor();
167
+        $children = intval($data[0]);
168
+
169
+        $comment = $this->get($id);
170
+        $comment->setChildrenCount($children);
171
+        $comment->setLatestChildDateTime($cDateTime);
172
+        $this->save($comment);
173
+    }
174
+
175
+    /**
176
+     * Tests whether actor or object type and id parameters are acceptable.
177
+     * Throws exception if not.
178
+     *
179
+     * @param string $role
180
+     * @param string $type
181
+     * @param string $id
182
+     * @throws \InvalidArgumentException
183
+     */
184
+    protected function checkRoleParameters($role, $type, $id) {
185
+        if(
186
+                !is_string($type) || empty($type)
187
+            || !is_string($id) || empty($id)
188
+        ) {
189
+            throw new \InvalidArgumentException($role . ' parameters must be string and not empty');
190
+        }
191
+    }
192
+
193
+    /**
194
+     * run-time caches a comment
195
+     *
196
+     * @param IComment $comment
197
+     */
198
+    protected function cache(IComment $comment) {
199
+        $id = $comment->getId();
200
+        if(empty($id)) {
201
+            return;
202
+        }
203
+        $this->commentsCache[strval($id)] = $comment;
204
+    }
205
+
206
+    /**
207
+     * removes an entry from the comments run time cache
208
+     *
209
+     * @param mixed $id the comment's id
210
+     */
211
+    protected function uncache($id) {
212
+        $id = strval($id);
213
+        if (isset($this->commentsCache[$id])) {
214
+            unset($this->commentsCache[$id]);
215
+        }
216
+    }
217
+
218
+    /**
219
+     * returns a comment instance
220
+     *
221
+     * @param string $id the ID of the comment
222
+     * @return IComment
223
+     * @throws NotFoundException
224
+     * @throws \InvalidArgumentException
225
+     * @since 9.0.0
226
+     */
227
+    public function get($id) {
228
+        if(intval($id) === 0) {
229
+            throw new \InvalidArgumentException('IDs must be translatable to a number in this implementation.');
230
+        }
231
+
232
+        if(isset($this->commentsCache[$id])) {
233
+            return $this->commentsCache[$id];
234
+        }
235
+
236
+        $qb = $this->dbConn->getQueryBuilder();
237
+        $resultStatement = $qb->select('*')
238
+            ->from('comments')
239
+            ->where($qb->expr()->eq('id', $qb->createParameter('id')))
240
+            ->setParameter('id', $id, IQueryBuilder::PARAM_INT)
241
+            ->execute();
242
+
243
+        $data = $resultStatement->fetch();
244
+        $resultStatement->closeCursor();
245
+        if(!$data) {
246
+            throw new NotFoundException();
247
+        }
248
+
249
+        $comment = new Comment($this->normalizeDatabaseData($data));
250
+        $this->cache($comment);
251
+        return $comment;
252
+    }
253
+
254
+    /**
255
+     * returns the comment specified by the id and all it's child comments.
256
+     * At this point of time, we do only support one level depth.
257
+     *
258
+     * @param string $id
259
+     * @param int $limit max number of entries to return, 0 returns all
260
+     * @param int $offset the start entry
261
+     * @return array
262
+     * @since 9.0.0
263
+     *
264
+     * The return array looks like this
265
+     * [
266
+     *   'comment' => IComment, // root comment
267
+     *   'replies' =>
268
+     *   [
269
+     *     0 =>
270
+     *     [
271
+     *       'comment' => IComment,
272
+     *       'replies' => []
273
+     *     ]
274
+     *     1 =>
275
+     *     [
276
+     *       'comment' => IComment,
277
+     *       'replies'=> []
278
+     *     ],
279
+     *     …
280
+     *   ]
281
+     * ]
282
+     */
283
+    public function getTree($id, $limit = 0, $offset = 0) {
284
+        $tree = [];
285
+        $tree['comment'] = $this->get($id);
286
+        $tree['replies'] = [];
287
+
288
+        $qb = $this->dbConn->getQueryBuilder();
289
+        $query = $qb->select('*')
290
+                ->from('comments')
291
+                ->where($qb->expr()->eq('topmost_parent_id', $qb->createParameter('id')))
292
+                ->orderBy('creation_timestamp', 'DESC')
293
+                ->setParameter('id', $id);
294
+
295
+        if($limit > 0) {
296
+            $query->setMaxResults($limit);
297
+        }
298
+        if($offset > 0) {
299
+            $query->setFirstResult($offset);
300
+        }
301
+
302
+        $resultStatement = $query->execute();
303
+        while($data = $resultStatement->fetch()) {
304
+            $comment = new Comment($this->normalizeDatabaseData($data));
305
+            $this->cache($comment);
306
+            $tree['replies'][] = [
307
+                'comment' => $comment,
308
+                'replies' => []
309
+            ];
310
+        }
311
+        $resultStatement->closeCursor();
312
+
313
+        return $tree;
314
+    }
315
+
316
+    /**
317
+     * returns comments for a specific object (e.g. a file).
318
+     *
319
+     * The sort order is always newest to oldest.
320
+     *
321
+     * @param string $objectType the object type, e.g. 'files'
322
+     * @param string $objectId the id of the object
323
+     * @param int $limit optional, number of maximum comments to be returned. if
324
+     * not specified, all comments are returned.
325
+     * @param int $offset optional, starting point
326
+     * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
327
+     * that may be returned
328
+     * @return IComment[]
329
+     * @since 9.0.0
330
+     */
331
+    public function getForObject(
332
+            $objectType,
333
+            $objectId,
334
+            $limit = 0,
335
+            $offset = 0,
336
+            \DateTime $notOlderThan = null
337
+    ) {
338
+        $comments = [];
339
+
340
+        $qb = $this->dbConn->getQueryBuilder();
341
+        $query = $qb->select('*')
342
+                ->from('comments')
343
+                ->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
344
+                ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
345
+                ->orderBy('creation_timestamp', 'DESC')
346
+                ->setParameter('type', $objectType)
347
+                ->setParameter('id', $objectId);
348
+
349
+        if($limit > 0) {
350
+            $query->setMaxResults($limit);
351
+        }
352
+        if($offset > 0) {
353
+            $query->setFirstResult($offset);
354
+        }
355
+        if(!is_null($notOlderThan)) {
356
+            $query
357
+                ->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
358
+                ->setParameter('notOlderThan', $notOlderThan, 'datetime');
359
+        }
360
+
361
+        $resultStatement = $query->execute();
362
+        while($data = $resultStatement->fetch()) {
363
+            $comment = new Comment($this->normalizeDatabaseData($data));
364
+            $this->cache($comment);
365
+            $comments[] = $comment;
366
+        }
367
+        $resultStatement->closeCursor();
368
+
369
+        return $comments;
370
+    }
371
+
372
+    /**
373
+     * @param $objectType string the object type, e.g. 'files'
374
+     * @param $objectId string the id of the object
375
+     * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
376
+     * that may be returned
377
+     * @return Int
378
+     * @since 9.0.0
379
+     */
380
+    public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null) {
381
+        $qb = $this->dbConn->getQueryBuilder();
382
+        $query = $qb->select($qb->createFunction('COUNT(`id`)'))
383
+                ->from('comments')
384
+                ->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
385
+                ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
386
+                ->setParameter('type', $objectType)
387
+                ->setParameter('id', $objectId);
388
+
389
+        if(!is_null($notOlderThan)) {
390
+            $query
391
+                ->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
392
+                ->setParameter('notOlderThan', $notOlderThan, 'datetime');
393
+        }
394
+
395
+        $resultStatement = $query->execute();
396
+        $data = $resultStatement->fetch(\PDO::FETCH_NUM);
397
+        $resultStatement->closeCursor();
398
+        return intval($data[0]);
399
+    }
400
+
401
+    /**
402
+     * creates a new comment and returns it. At this point of time, it is not
403
+     * saved in the used data storage. Use save() after setting other fields
404
+     * of the comment (e.g. message or verb).
405
+     *
406
+     * @param string $actorType the actor type (e.g. 'users')
407
+     * @param string $actorId a user id
408
+     * @param string $objectType the object type the comment is attached to
409
+     * @param string $objectId the object id the comment is attached to
410
+     * @return IComment
411
+     * @since 9.0.0
412
+     */
413
+    public function create($actorType, $actorId, $objectType, $objectId) {
414
+        $comment = new Comment();
415
+        $comment
416
+            ->setActor($actorType, $actorId)
417
+            ->setObject($objectType, $objectId);
418
+        return $comment;
419
+    }
420
+
421
+    /**
422
+     * permanently deletes the comment specified by the ID
423
+     *
424
+     * When the comment has child comments, their parent ID will be changed to
425
+     * the parent ID of the item that is to be deleted.
426
+     *
427
+     * @param string $id
428
+     * @return bool
429
+     * @throws \InvalidArgumentException
430
+     * @since 9.0.0
431
+     */
432
+    public function delete($id) {
433
+        if(!is_string($id)) {
434
+            throw new \InvalidArgumentException('Parameter must be string');
435
+        }
436
+
437
+        try {
438
+            $comment = $this->get($id);
439
+        } catch (\Exception $e) {
440
+            // Ignore exceptions, we just don't fire a hook then
441
+            $comment = null;
442
+        }
443
+
444
+        $qb = $this->dbConn->getQueryBuilder();
445
+        $query = $qb->delete('comments')
446
+            ->where($qb->expr()->eq('id', $qb->createParameter('id')))
447
+            ->setParameter('id', $id);
448
+
449
+        try {
450
+            $affectedRows = $query->execute();
451
+            $this->uncache($id);
452
+        } catch (DriverException $e) {
453
+            $this->logger->logException($e, ['app' => 'core_comments']);
454
+            return false;
455
+        }
456
+
457
+        if ($affectedRows > 0 && $comment instanceof IComment) {
458
+            $this->dispatcher->dispatch(CommentsEvent::EVENT_DELETE, new CommentsEvent(
459
+                CommentsEvent::EVENT_DELETE,
460
+                $comment
461
+            ));
462
+        }
463
+
464
+        return ($affectedRows > 0);
465
+    }
466
+
467
+    /**
468
+     * saves the comment permanently
469
+     *
470
+     * if the supplied comment has an empty ID, a new entry comment will be
471
+     * saved and the instance updated with the new ID.
472
+     *
473
+     * Otherwise, an existing comment will be updated.
474
+     *
475
+     * Throws NotFoundException when a comment that is to be updated does not
476
+     * exist anymore at this point of time.
477
+     *
478
+     * @param IComment $comment
479
+     * @return bool
480
+     * @throws NotFoundException
481
+     * @since 9.0.0
482
+     */
483
+    public function save(IComment $comment) {
484
+        if($this->prepareCommentForDatabaseWrite($comment)->getId() === '') {
485
+            $result = $this->insert($comment);
486
+        } else {
487
+            $result = $this->update($comment);
488
+        }
489
+
490
+        if($result && !!$comment->getParentId()) {
491
+            $this->updateChildrenInformation(
492
+                    $comment->getParentId(),
493
+                    $comment->getCreationDateTime()
494
+            );
495
+            $this->cache($comment);
496
+        }
497
+
498
+        return $result;
499
+    }
500
+
501
+    /**
502
+     * inserts the provided comment in the database
503
+     *
504
+     * @param IComment $comment
505
+     * @return bool
506
+     */
507
+    protected function insert(IComment &$comment) {
508
+        $qb = $this->dbConn->getQueryBuilder();
509
+        $affectedRows = $qb
510
+            ->insert('comments')
511
+            ->values([
512
+                'parent_id'					=> $qb->createNamedParameter($comment->getParentId()),
513
+                'topmost_parent_id' 		=> $qb->createNamedParameter($comment->getTopmostParentId()),
514
+                'children_count' 			=> $qb->createNamedParameter($comment->getChildrenCount()),
515
+                'actor_type' 				=> $qb->createNamedParameter($comment->getActorType()),
516
+                'actor_id' 					=> $qb->createNamedParameter($comment->getActorId()),
517
+                'message' 					=> $qb->createNamedParameter($comment->getMessage()),
518
+                'verb' 						=> $qb->createNamedParameter($comment->getVerb()),
519
+                'creation_timestamp' 		=> $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'),
520
+                'latest_child_timestamp'	=> $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
521
+                'object_type' 				=> $qb->createNamedParameter($comment->getObjectType()),
522
+                'object_id' 				=> $qb->createNamedParameter($comment->getObjectId()),
523
+            ])
524
+            ->execute();
525
+
526
+        if ($affectedRows > 0) {
527
+            $comment->setId(strval($qb->getLastInsertId()));
528
+        }
529
+
530
+        $this->dispatcher->dispatch(CommentsEvent::EVENT_ADD, new CommentsEvent(
531
+            CommentsEvent::EVENT_ADD,
532
+            $comment
533
+        ));
534
+
535
+        return $affectedRows > 0;
536
+    }
537
+
538
+    /**
539
+     * updates a Comment data row
540
+     *
541
+     * @param IComment $comment
542
+     * @return bool
543
+     * @throws NotFoundException
544
+     */
545
+    protected function update(IComment $comment) {
546
+        $qb = $this->dbConn->getQueryBuilder();
547
+        $affectedRows = $qb
548
+            ->update('comments')
549
+                ->set('parent_id',				$qb->createNamedParameter($comment->getParentId()))
550
+                ->set('topmost_parent_id', 		$qb->createNamedParameter($comment->getTopmostParentId()))
551
+                ->set('children_count',			$qb->createNamedParameter($comment->getChildrenCount()))
552
+                ->set('actor_type', 			$qb->createNamedParameter($comment->getActorType()))
553
+                ->set('actor_id', 				$qb->createNamedParameter($comment->getActorId()))
554
+                ->set('message',				$qb->createNamedParameter($comment->getMessage()))
555
+                ->set('verb',					$qb->createNamedParameter($comment->getVerb()))
556
+                ->set('creation_timestamp',		$qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'))
557
+                ->set('latest_child_timestamp',	$qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'))
558
+                ->set('object_type',			$qb->createNamedParameter($comment->getObjectType()))
559
+                ->set('object_id',				$qb->createNamedParameter($comment->getObjectId()))
560
+            ->where($qb->expr()->eq('id', $qb->createParameter('id')))
561
+            ->setParameter('id', $comment->getId())
562
+            ->execute();
563
+
564
+        if($affectedRows === 0) {
565
+            throw new NotFoundException('Comment to update does ceased to exist');
566
+        }
567
+
568
+        $this->dispatcher->dispatch(CommentsEvent::EVENT_UPDATE, new CommentsEvent(
569
+            CommentsEvent::EVENT_UPDATE,
570
+            $comment
571
+        ));
572
+
573
+        return $affectedRows > 0;
574
+    }
575
+
576
+    /**
577
+     * removes references to specific actor (e.g. on user delete) of a comment.
578
+     * The comment itself must not get lost/deleted.
579
+     *
580
+     * @param string $actorType the actor type (e.g. 'users')
581
+     * @param string $actorId a user id
582
+     * @return boolean
583
+     * @since 9.0.0
584
+     */
585
+    public function deleteReferencesOfActor($actorType, $actorId) {
586
+        $this->checkRoleParameters('Actor', $actorType, $actorId);
587
+
588
+        $qb = $this->dbConn->getQueryBuilder();
589
+        $affectedRows = $qb
590
+            ->update('comments')
591
+            ->set('actor_type',	$qb->createNamedParameter(ICommentsManager::DELETED_USER))
592
+            ->set('actor_id',	$qb->createNamedParameter(ICommentsManager::DELETED_USER))
593
+            ->where($qb->expr()->eq('actor_type', $qb->createParameter('type')))
594
+            ->andWhere($qb->expr()->eq('actor_id', $qb->createParameter('id')))
595
+            ->setParameter('type', $actorType)
596
+            ->setParameter('id', $actorId)
597
+            ->execute();
598
+
599
+        $this->commentsCache = [];
600
+
601
+        return is_int($affectedRows);
602
+    }
603
+
604
+    /**
605
+     * deletes all comments made of a specific object (e.g. on file delete)
606
+     *
607
+     * @param string $objectType the object type (e.g. 'files')
608
+     * @param string $objectId e.g. the file id
609
+     * @return boolean
610
+     * @since 9.0.0
611
+     */
612
+    public function deleteCommentsAtObject($objectType, $objectId) {
613
+        $this->checkRoleParameters('Object', $objectType, $objectId);
614
+
615
+        $qb = $this->dbConn->getQueryBuilder();
616
+        $affectedRows = $qb
617
+            ->delete('comments')
618
+            ->where($qb->expr()->eq('object_type', $qb->createParameter('type')))
619
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('id')))
620
+            ->setParameter('type', $objectType)
621
+            ->setParameter('id', $objectId)
622
+            ->execute();
623
+
624
+        $this->commentsCache = [];
625
+
626
+        return is_int($affectedRows);
627
+    }
628
+
629
+    /**
630
+     * deletes the read markers for the specified user
631
+     *
632
+     * @param \OCP\IUser $user
633
+     * @return bool
634
+     * @since 9.0.0
635
+     */
636
+    public function deleteReadMarksFromUser(IUser $user) {
637
+        $qb = $this->dbConn->getQueryBuilder();
638
+        $query = $qb->delete('comments_read_markers')
639
+            ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
640
+            ->setParameter('user_id', $user->getUID());
641
+
642
+        try {
643
+            $affectedRows = $query->execute();
644
+        } catch (DriverException $e) {
645
+            $this->logger->logException($e, ['app' => 'core_comments']);
646
+            return false;
647
+        }
648
+        return ($affectedRows > 0);
649
+    }
650
+
651
+    /**
652
+     * sets the read marker for a given file to the specified date for the
653
+     * provided user
654
+     *
655
+     * @param string $objectType
656
+     * @param string $objectId
657
+     * @param \DateTime $dateTime
658
+     * @param IUser $user
659
+     * @since 9.0.0
660
+     */
661
+    public function setReadMark($objectType, $objectId, \DateTime $dateTime, IUser $user) {
662
+        $this->checkRoleParameters('Object', $objectType, $objectId);
663
+
664
+        $qb = $this->dbConn->getQueryBuilder();
665
+        $values = [
666
+            'user_id'         => $qb->createNamedParameter($user->getUID()),
667
+            'marker_datetime' => $qb->createNamedParameter($dateTime, 'datetime'),
668
+            'object_type'     => $qb->createNamedParameter($objectType),
669
+            'object_id'       => $qb->createNamedParameter($objectId),
670
+        ];
671
+
672
+        // Strategy: try to update, if this does not return affected rows, do an insert.
673
+        $affectedRows = $qb
674
+            ->update('comments_read_markers')
675
+            ->set('user_id',         $values['user_id'])
676
+            ->set('marker_datetime', $values['marker_datetime'])
677
+            ->set('object_type',     $values['object_type'])
678
+            ->set('object_id',       $values['object_id'])
679
+            ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
680
+            ->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
681
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
682
+            ->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR)
683
+            ->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR)
684
+            ->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR)
685
+            ->execute();
686
+
687
+        if ($affectedRows > 0) {
688
+            return;
689
+        }
690
+
691
+        $qb->insert('comments_read_markers')
692
+            ->values($values)
693
+            ->execute();
694
+    }
695
+
696
+    /**
697
+     * returns the read marker for a given file to the specified date for the
698
+     * provided user. It returns null, when the marker is not present, i.e.
699
+     * no comments were marked as read.
700
+     *
701
+     * @param string $objectType
702
+     * @param string $objectId
703
+     * @param IUser $user
704
+     * @return \DateTime|null
705
+     * @since 9.0.0
706
+     */
707
+    public function getReadMark($objectType, $objectId, IUser $user) {
708
+        $qb = $this->dbConn->getQueryBuilder();
709
+        $resultStatement = $qb->select('marker_datetime')
710
+            ->from('comments_read_markers')
711
+            ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
712
+            ->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
713
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
714
+            ->setParameter('user_id', $user->getUID(), IQueryBuilder::PARAM_STR)
715
+            ->setParameter('object_type', $objectType, IQueryBuilder::PARAM_STR)
716
+            ->setParameter('object_id', $objectId, IQueryBuilder::PARAM_STR)
717
+            ->execute();
718
+
719
+        $data = $resultStatement->fetch();
720
+        $resultStatement->closeCursor();
721
+        if(!$data || is_null($data['marker_datetime'])) {
722
+            return null;
723
+        }
724
+
725
+        return new \DateTime($data['marker_datetime']);
726
+    }
727
+
728
+    /**
729
+     * deletes the read markers on the specified object
730
+     *
731
+     * @param string $objectType
732
+     * @param string $objectId
733
+     * @return bool
734
+     * @since 9.0.0
735
+     */
736
+    public function deleteReadMarksOnObject($objectType, $objectId) {
737
+        $this->checkRoleParameters('Object', $objectType, $objectId);
738
+
739
+        $qb = $this->dbConn->getQueryBuilder();
740
+        $query = $qb->delete('comments_read_markers')
741
+            ->where($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
742
+            ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
743
+            ->setParameter('object_type', $objectType)
744
+            ->setParameter('object_id', $objectId);
745
+
746
+        try {
747
+            $affectedRows = $query->execute();
748
+        } catch (DriverException $e) {
749
+            $this->logger->logException($e, ['app' => 'core_comments']);
750
+            return false;
751
+        }
752
+        return ($affectedRows > 0);
753
+    }
754 754
 }
Please login to merge, or discard this patch.
Spacing   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -101,7 +101,7 @@  discard block
 block discarded – undo
101 101
 	 * @throws \UnexpectedValueException
102 102
 	 */
103 103
 	protected function prepareCommentForDatabaseWrite(IComment $comment) {
104
-		if(    !$comment->getActorType()
104
+		if (!$comment->getActorType()
105 105
 			|| !$comment->getActorId()
106 106
 			|| !$comment->getObjectType()
107 107
 			|| !$comment->getObjectId()
@@ -110,17 +110,17 @@  discard block
 block discarded – undo
110 110
 			throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving');
111 111
 		}
112 112
 
113
-		if($comment->getId() === '') {
113
+		if ($comment->getId() === '') {
114 114
 			$comment->setChildrenCount(0);
115 115
 			$comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC')));
116 116
 			$comment->setLatestChildDateTime(null);
117 117
 		}
118 118
 
119
-		if(is_null($comment->getCreationDateTime())) {
119
+		if (is_null($comment->getCreationDateTime())) {
120 120
 			$comment->setCreationDateTime(new \DateTime());
121 121
 		}
122 122
 
123
-		if($comment->getParentId() !== '0') {
123
+		if ($comment->getParentId() !== '0') {
124 124
 			$comment->setTopmostParentId($this->determineTopmostParentId($comment->getParentId()));
125 125
 		} else {
126 126
 			$comment->setTopmostParentId('0');
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
 	 */
141 141
 	protected function determineTopmostParentId($id) {
142 142
 		$comment = $this->get($id);
143
-		if($comment->getParentId() === '0') {
143
+		if ($comment->getParentId() === '0') {
144 144
 			return $comment->getId();
145 145
 		} else {
146 146
 			return $this->determineTopmostParentId($comment->getId());
@@ -182,11 +182,11 @@  discard block
 block discarded – undo
182 182
 	 * @throws \InvalidArgumentException
183 183
 	 */
184 184
 	protected function checkRoleParameters($role, $type, $id) {
185
-		if(
185
+		if (
186 186
 			   !is_string($type) || empty($type)
187 187
 			|| !is_string($id) || empty($id)
188 188
 		) {
189
-			throw new \InvalidArgumentException($role . ' parameters must be string and not empty');
189
+			throw new \InvalidArgumentException($role.' parameters must be string and not empty');
190 190
 		}
191 191
 	}
192 192
 
@@ -197,7 +197,7 @@  discard block
 block discarded – undo
197 197
 	 */
198 198
 	protected function cache(IComment $comment) {
199 199
 		$id = $comment->getId();
200
-		if(empty($id)) {
200
+		if (empty($id)) {
201 201
 			return;
202 202
 		}
203 203
 		$this->commentsCache[strval($id)] = $comment;
@@ -225,11 +225,11 @@  discard block
 block discarded – undo
225 225
 	 * @since 9.0.0
226 226
 	 */
227 227
 	public function get($id) {
228
-		if(intval($id) === 0) {
228
+		if (intval($id) === 0) {
229 229
 			throw new \InvalidArgumentException('IDs must be translatable to a number in this implementation.');
230 230
 		}
231 231
 
232
-		if(isset($this->commentsCache[$id])) {
232
+		if (isset($this->commentsCache[$id])) {
233 233
 			return $this->commentsCache[$id];
234 234
 		}
235 235
 
@@ -242,7 +242,7 @@  discard block
 block discarded – undo
242 242
 
243 243
 		$data = $resultStatement->fetch();
244 244
 		$resultStatement->closeCursor();
245
-		if(!$data) {
245
+		if (!$data) {
246 246
 			throw new NotFoundException();
247 247
 		}
248 248
 
@@ -292,15 +292,15 @@  discard block
 block discarded – undo
292 292
 				->orderBy('creation_timestamp', 'DESC')
293 293
 				->setParameter('id', $id);
294 294
 
295
-		if($limit > 0) {
295
+		if ($limit > 0) {
296 296
 			$query->setMaxResults($limit);
297 297
 		}
298
-		if($offset > 0) {
298
+		if ($offset > 0) {
299 299
 			$query->setFirstResult($offset);
300 300
 		}
301 301
 
302 302
 		$resultStatement = $query->execute();
303
-		while($data = $resultStatement->fetch()) {
303
+		while ($data = $resultStatement->fetch()) {
304 304
 			$comment = new Comment($this->normalizeDatabaseData($data));
305 305
 			$this->cache($comment);
306 306
 			$tree['replies'][] = [
@@ -346,20 +346,20 @@  discard block
 block discarded – undo
346 346
 				->setParameter('type', $objectType)
347 347
 				->setParameter('id', $objectId);
348 348
 
349
-		if($limit > 0) {
349
+		if ($limit > 0) {
350 350
 			$query->setMaxResults($limit);
351 351
 		}
352
-		if($offset > 0) {
352
+		if ($offset > 0) {
353 353
 			$query->setFirstResult($offset);
354 354
 		}
355
-		if(!is_null($notOlderThan)) {
355
+		if (!is_null($notOlderThan)) {
356 356
 			$query
357 357
 				->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
358 358
 				->setParameter('notOlderThan', $notOlderThan, 'datetime');
359 359
 		}
360 360
 
361 361
 		$resultStatement = $query->execute();
362
-		while($data = $resultStatement->fetch()) {
362
+		while ($data = $resultStatement->fetch()) {
363 363
 			$comment = new Comment($this->normalizeDatabaseData($data));
364 364
 			$this->cache($comment);
365 365
 			$comments[] = $comment;
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
 				->setParameter('type', $objectType)
387 387
 				->setParameter('id', $objectId);
388 388
 
389
-		if(!is_null($notOlderThan)) {
389
+		if (!is_null($notOlderThan)) {
390 390
 			$query
391 391
 				->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
392 392
 				->setParameter('notOlderThan', $notOlderThan, 'datetime');
@@ -430,7 +430,7 @@  discard block
 block discarded – undo
430 430
 	 * @since 9.0.0
431 431
 	 */
432 432
 	public function delete($id) {
433
-		if(!is_string($id)) {
433
+		if (!is_string($id)) {
434 434
 			throw new \InvalidArgumentException('Parameter must be string');
435 435
 		}
436 436
 
@@ -481,13 +481,13 @@  discard block
 block discarded – undo
481 481
 	 * @since 9.0.0
482 482
 	 */
483 483
 	public function save(IComment $comment) {
484
-		if($this->prepareCommentForDatabaseWrite($comment)->getId() === '') {
484
+		if ($this->prepareCommentForDatabaseWrite($comment)->getId() === '') {
485 485
 			$result = $this->insert($comment);
486 486
 		} else {
487 487
 			$result = $this->update($comment);
488 488
 		}
489 489
 
490
-		if($result && !!$comment->getParentId()) {
490
+		if ($result && !!$comment->getParentId()) {
491 491
 			$this->updateChildrenInformation(
492 492
 					$comment->getParentId(),
493 493
 					$comment->getCreationDateTime()
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
 	 * @param IComment $comment
505 505
 	 * @return bool
506 506
 	 */
507
-	protected function insert(IComment &$comment) {
507
+	protected function insert(IComment & $comment) {
508 508
 		$qb = $this->dbConn->getQueryBuilder();
509 509
 		$affectedRows = $qb
510 510
 			->insert('comments')
@@ -546,22 +546,22 @@  discard block
 block discarded – undo
546 546
 		$qb = $this->dbConn->getQueryBuilder();
547 547
 		$affectedRows = $qb
548 548
 			->update('comments')
549
-				->set('parent_id',				$qb->createNamedParameter($comment->getParentId()))
550
-				->set('topmost_parent_id', 		$qb->createNamedParameter($comment->getTopmostParentId()))
551
-				->set('children_count',			$qb->createNamedParameter($comment->getChildrenCount()))
552
-				->set('actor_type', 			$qb->createNamedParameter($comment->getActorType()))
553
-				->set('actor_id', 				$qb->createNamedParameter($comment->getActorId()))
554
-				->set('message',				$qb->createNamedParameter($comment->getMessage()))
555
-				->set('verb',					$qb->createNamedParameter($comment->getVerb()))
556
-				->set('creation_timestamp',		$qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'))
557
-				->set('latest_child_timestamp',	$qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'))
558
-				->set('object_type',			$qb->createNamedParameter($comment->getObjectType()))
559
-				->set('object_id',				$qb->createNamedParameter($comment->getObjectId()))
549
+				->set('parent_id', $qb->createNamedParameter($comment->getParentId()))
550
+				->set('topmost_parent_id', $qb->createNamedParameter($comment->getTopmostParentId()))
551
+				->set('children_count', $qb->createNamedParameter($comment->getChildrenCount()))
552
+				->set('actor_type', $qb->createNamedParameter($comment->getActorType()))
553
+				->set('actor_id', $qb->createNamedParameter($comment->getActorId()))
554
+				->set('message', $qb->createNamedParameter($comment->getMessage()))
555
+				->set('verb', $qb->createNamedParameter($comment->getVerb()))
556
+				->set('creation_timestamp', $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'))
557
+				->set('latest_child_timestamp', $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'))
558
+				->set('object_type', $qb->createNamedParameter($comment->getObjectType()))
559
+				->set('object_id', $qb->createNamedParameter($comment->getObjectId()))
560 560
 			->where($qb->expr()->eq('id', $qb->createParameter('id')))
561 561
 			->setParameter('id', $comment->getId())
562 562
 			->execute();
563 563
 
564
-		if($affectedRows === 0) {
564
+		if ($affectedRows === 0) {
565 565
 			throw new NotFoundException('Comment to update does ceased to exist');
566 566
 		}
567 567
 
@@ -588,8 +588,8 @@  discard block
 block discarded – undo
588 588
 		$qb = $this->dbConn->getQueryBuilder();
589 589
 		$affectedRows = $qb
590 590
 			->update('comments')
591
-			->set('actor_type',	$qb->createNamedParameter(ICommentsManager::DELETED_USER))
592
-			->set('actor_id',	$qb->createNamedParameter(ICommentsManager::DELETED_USER))
591
+			->set('actor_type', $qb->createNamedParameter(ICommentsManager::DELETED_USER))
592
+			->set('actor_id', $qb->createNamedParameter(ICommentsManager::DELETED_USER))
593 593
 			->where($qb->expr()->eq('actor_type', $qb->createParameter('type')))
594 594
 			->andWhere($qb->expr()->eq('actor_id', $qb->createParameter('id')))
595 595
 			->setParameter('type', $actorType)
@@ -672,10 +672,10 @@  discard block
 block discarded – undo
672 672
 		// Strategy: try to update, if this does not return affected rows, do an insert.
673 673
 		$affectedRows = $qb
674 674
 			->update('comments_read_markers')
675
-			->set('user_id',         $values['user_id'])
675
+			->set('user_id', $values['user_id'])
676 676
 			->set('marker_datetime', $values['marker_datetime'])
677
-			->set('object_type',     $values['object_type'])
678
-			->set('object_id',       $values['object_id'])
677
+			->set('object_type', $values['object_type'])
678
+			->set('object_id', $values['object_id'])
679 679
 			->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
680 680
 			->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
681 681
 			->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
@@ -718,7 +718,7 @@  discard block
 block discarded – undo
718 718
 
719 719
 		$data = $resultStatement->fetch();
720 720
 		$resultStatement->closeCursor();
721
-		if(!$data || is_null($data['marker_datetime'])) {
721
+		if (!$data || is_null($data['marker_datetime'])) {
722 722
 			return null;
723 723
 		}
724 724
 
Please login to merge, or discard this patch.
lib/private/datetimeformatter.php 3 patches
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -93,7 +93,7 @@  discard block
 block discarded – undo
93 93
 	/**
94 94
 	 * Formats the date of the given timestamp
95 95
 	 *
96
-	 * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
96
+	 * @param integer	$timestamp	Either a Unix timestamp or DateTime object
97 97
 	 * @param string	$format			Either 'full', 'long', 'medium' or 'short'
98 98
 	 * 				full:	e.g. 'EEEE, MMMM d, y'	=> 'Wednesday, August 20, 2014'
99 99
 	 * 				long:	e.g. 'MMMM d, y'		=> 'August 20, 2014'
@@ -192,7 +192,7 @@  discard block
 block discarded – undo
192 192
 	/**
193 193
 	 * Gives the relative past time of the timestamp
194 194
 	 *
195
-	 * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
195
+	 * @param integer	$timestamp	Either a Unix timestamp or DateTime object
196 196
 	 * @param int|\DateTime	$baseTimestamp	Timestamp to compare $timestamp against, defaults to current time
197 197
 	 * @return string	Dates returned are:
198 198
 	 * 				< 60 sec	=> seconds ago
@@ -228,7 +228,7 @@  discard block
 block discarded – undo
228 228
 	/**
229 229
 	 * Formats the date and time of the given timestamp
230 230
 	 *
231
-	 * @param int|\DateTime $timestamp	Either a Unix timestamp or DateTime object
231
+	 * @param integer $timestamp	Either a Unix timestamp or DateTime object
232 232
 	 * @param string		$formatDate		See formatDate() for description
233 233
 	 * @param string		$formatTime		See formatTime() for description
234 234
 	 * @param \DateTimeZone	$timeZone	The timezone to use
Please login to merge, or discard this patch.
Indentation   +232 added lines, -232 removed lines patch added patch discarded remove patch
@@ -25,257 +25,257 @@
 block discarded – undo
25 25
 namespace OC;
26 26
 
27 27
 class DateTimeFormatter implements \OCP\IDateTimeFormatter {
28
-	/** @var \DateTimeZone */
29
-	protected $defaultTimeZone;
28
+    /** @var \DateTimeZone */
29
+    protected $defaultTimeZone;
30 30
 
31
-	/** @var \OCP\IL10N */
32
-	protected $defaultL10N;
31
+    /** @var \OCP\IL10N */
32
+    protected $defaultL10N;
33 33
 
34
-	/**
35
-	 * Constructor
36
-	 *
37
-	 * @param \DateTimeZone $defaultTimeZone Set the timezone for the format
38
-	 * @param \OCP\IL10N $defaultL10N Set the language for the format
39
-	 */
40
-	public function __construct(\DateTimeZone $defaultTimeZone, \OCP\IL10N $defaultL10N) {
41
-		$this->defaultTimeZone = $defaultTimeZone;
42
-		$this->defaultL10N = $defaultL10N;
43
-	}
34
+    /**
35
+     * Constructor
36
+     *
37
+     * @param \DateTimeZone $defaultTimeZone Set the timezone for the format
38
+     * @param \OCP\IL10N $defaultL10N Set the language for the format
39
+     */
40
+    public function __construct(\DateTimeZone $defaultTimeZone, \OCP\IL10N $defaultL10N) {
41
+        $this->defaultTimeZone = $defaultTimeZone;
42
+        $this->defaultL10N = $defaultL10N;
43
+    }
44 44
 
45
-	/**
46
-	 * Get TimeZone to use
47
-	 *
48
-	 * @param \DateTimeZone $timeZone	The timezone to use
49
-	 * @return \DateTimeZone		The timezone to use, falling back to the current user's timezone
50
-	 */
51
-	protected function getTimeZone($timeZone = null) {
52
-		if ($timeZone === null) {
53
-			$timeZone = $this->defaultTimeZone;
54
-		}
45
+    /**
46
+     * Get TimeZone to use
47
+     *
48
+     * @param \DateTimeZone $timeZone	The timezone to use
49
+     * @return \DateTimeZone		The timezone to use, falling back to the current user's timezone
50
+     */
51
+    protected function getTimeZone($timeZone = null) {
52
+        if ($timeZone === null) {
53
+            $timeZone = $this->defaultTimeZone;
54
+        }
55 55
 
56
-		return $timeZone;
57
-	}
56
+        return $timeZone;
57
+    }
58 58
 
59
-	/**
60
-	 * Get \OCP\IL10N to use
61
-	 *
62
-	 * @param \OCP\IL10N $l	The locale to use
63
-	 * @return \OCP\IL10N		The locale to use, falling back to the current user's locale
64
-	 */
65
-	protected function getLocale($l = null) {
66
-		if ($l === null) {
67
-			$l = $this->defaultL10N;
68
-		}
59
+    /**
60
+     * Get \OCP\IL10N to use
61
+     *
62
+     * @param \OCP\IL10N $l	The locale to use
63
+     * @return \OCP\IL10N		The locale to use, falling back to the current user's locale
64
+     */
65
+    protected function getLocale($l = null) {
66
+        if ($l === null) {
67
+            $l = $this->defaultL10N;
68
+        }
69 69
 
70
-		return $l;
71
-	}
70
+        return $l;
71
+    }
72 72
 
73
-	/**
74
-	 * Generates a DateTime object with the given timestamp and TimeZone
75
-	 *
76
-	 * @param mixed $timestamp
77
-	 * @param \DateTimeZone $timeZone	The timezone to use
78
-	 * @return \DateTime
79
-	 */
80
-	protected function getDateTime($timestamp, \DateTimeZone $timeZone = null) {
81
-		if ($timestamp === null) {
82
-			return new \DateTime('now', $timeZone);
83
-		} else if (!$timestamp instanceof \DateTime) {
84
-			$dateTime = new \DateTime('now', $timeZone);
85
-			$dateTime->setTimestamp($timestamp);
86
-			return $dateTime;
87
-		}
88
-		if ($timeZone) {
89
-			$timestamp->setTimezone($timeZone);
90
-		}
91
-		return $timestamp;
92
-	}
73
+    /**
74
+     * Generates a DateTime object with the given timestamp and TimeZone
75
+     *
76
+     * @param mixed $timestamp
77
+     * @param \DateTimeZone $timeZone	The timezone to use
78
+     * @return \DateTime
79
+     */
80
+    protected function getDateTime($timestamp, \DateTimeZone $timeZone = null) {
81
+        if ($timestamp === null) {
82
+            return new \DateTime('now', $timeZone);
83
+        } else if (!$timestamp instanceof \DateTime) {
84
+            $dateTime = new \DateTime('now', $timeZone);
85
+            $dateTime->setTimestamp($timestamp);
86
+            return $dateTime;
87
+        }
88
+        if ($timeZone) {
89
+            $timestamp->setTimezone($timeZone);
90
+        }
91
+        return $timestamp;
92
+    }
93 93
 
94
-	/**
95
-	 * Formats the date of the given timestamp
96
-	 *
97
-	 * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
98
-	 * @param string	$format			Either 'full', 'long', 'medium' or 'short'
99
-	 * 				full:	e.g. 'EEEE, MMMM d, y'	=> 'Wednesday, August 20, 2014'
100
-	 * 				long:	e.g. 'MMMM d, y'		=> 'August 20, 2014'
101
-	 * 				medium:	e.g. 'MMM d, y'			=> 'Aug 20, 2014'
102
-	 * 				short:	e.g. 'M/d/yy'			=> '8/20/14'
103
-	 * 				The exact format is dependent on the language
104
-	 * @param \DateTimeZone	$timeZone	The timezone to use
105
-	 * @param \OCP\IL10N	$l			The locale to use
106
-	 * @return string Formatted date string
107
-	 */
108
-	public function formatDate($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
109
-		return $this->format($timestamp, 'date', $format, $timeZone, $l);
110
-	}
94
+    /**
95
+     * Formats the date of the given timestamp
96
+     *
97
+     * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
98
+     * @param string	$format			Either 'full', 'long', 'medium' or 'short'
99
+     * 				full:	e.g. 'EEEE, MMMM d, y'	=> 'Wednesday, August 20, 2014'
100
+     * 				long:	e.g. 'MMMM d, y'		=> 'August 20, 2014'
101
+     * 				medium:	e.g. 'MMM d, y'			=> 'Aug 20, 2014'
102
+     * 				short:	e.g. 'M/d/yy'			=> '8/20/14'
103
+     * 				The exact format is dependent on the language
104
+     * @param \DateTimeZone	$timeZone	The timezone to use
105
+     * @param \OCP\IL10N	$l			The locale to use
106
+     * @return string Formatted date string
107
+     */
108
+    public function formatDate($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
109
+        return $this->format($timestamp, 'date', $format, $timeZone, $l);
110
+    }
111 111
 
112
-	/**
113
-	 * Formats the date of the given timestamp
114
-	 *
115
-	 * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
116
-	 * @param string	$format			Either 'full', 'long', 'medium' or 'short'
117
-	 * 				full:	e.g. 'EEEE, MMMM d, y'	=> 'Wednesday, August 20, 2014'
118
-	 * 				long:	e.g. 'MMMM d, y'		=> 'August 20, 2014'
119
-	 * 				medium:	e.g. 'MMM d, y'			=> 'Aug 20, 2014'
120
-	 * 				short:	e.g. 'M/d/yy'			=> '8/20/14'
121
-	 * 				The exact format is dependent on the language
122
-	 * 					Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable
123
-	 * @param \DateTimeZone	$timeZone	The timezone to use
124
-	 * @param \OCP\IL10N	$l			The locale to use
125
-	 * @return string Formatted relative date string
126
-	 */
127
-	public function formatDateRelativeDay($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
128
-		if (substr($format, -1) !== '*' && substr($format, -1) !== '*') {
129
-			$format .= '^';
130
-		}
112
+    /**
113
+     * Formats the date of the given timestamp
114
+     *
115
+     * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
116
+     * @param string	$format			Either 'full', 'long', 'medium' or 'short'
117
+     * 				full:	e.g. 'EEEE, MMMM d, y'	=> 'Wednesday, August 20, 2014'
118
+     * 				long:	e.g. 'MMMM d, y'		=> 'August 20, 2014'
119
+     * 				medium:	e.g. 'MMM d, y'			=> 'Aug 20, 2014'
120
+     * 				short:	e.g. 'M/d/yy'			=> '8/20/14'
121
+     * 				The exact format is dependent on the language
122
+     * 					Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable
123
+     * @param \DateTimeZone	$timeZone	The timezone to use
124
+     * @param \OCP\IL10N	$l			The locale to use
125
+     * @return string Formatted relative date string
126
+     */
127
+    public function formatDateRelativeDay($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
128
+        if (substr($format, -1) !== '*' && substr($format, -1) !== '*') {
129
+            $format .= '^';
130
+        }
131 131
 
132
-		return $this->format($timestamp, 'date', $format, $timeZone, $l);
133
-	}
132
+        return $this->format($timestamp, 'date', $format, $timeZone, $l);
133
+    }
134 134
 
135
-	/**
136
-	 * Gives the relative date of the timestamp
137
-	 * Only works for past dates
138
-	 *
139
-	 * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
140
-	 * @param int|\DateTime	$baseTimestamp	Timestamp to compare $timestamp against, defaults to current time
141
-	 * @return string	Dates returned are:
142
-	 * 				<  1 month	=> Today, Yesterday, n days ago
143
-	 * 				< 13 month	=> last month, n months ago
144
-	 * 				>= 13 month	=> last year, n years ago
145
-	 * @param \OCP\IL10N	$l			The locale to use
146
-	 * @return string Formatted date span
147
-	 */
148
-	public function formatDateSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null) {
149
-		$l = $this->getLocale($l);
150
-		$timestamp = $this->getDateTime($timestamp);
151
-		$timestamp->setTime(0, 0, 0);
152
-		if ($baseTimestamp === null) {
153
-			$baseTimestamp = time();
154
-		}
155
-		$baseTimestamp = $this->getDateTime($baseTimestamp);
156
-		$baseTimestamp->setTime(0, 0, 0);
157
-		$dateInterval = $timestamp->diff($baseTimestamp);
135
+    /**
136
+     * Gives the relative date of the timestamp
137
+     * Only works for past dates
138
+     *
139
+     * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
140
+     * @param int|\DateTime	$baseTimestamp	Timestamp to compare $timestamp against, defaults to current time
141
+     * @return string	Dates returned are:
142
+     * 				<  1 month	=> Today, Yesterday, n days ago
143
+     * 				< 13 month	=> last month, n months ago
144
+     * 				>= 13 month	=> last year, n years ago
145
+     * @param \OCP\IL10N	$l			The locale to use
146
+     * @return string Formatted date span
147
+     */
148
+    public function formatDateSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null) {
149
+        $l = $this->getLocale($l);
150
+        $timestamp = $this->getDateTime($timestamp);
151
+        $timestamp->setTime(0, 0, 0);
152
+        if ($baseTimestamp === null) {
153
+            $baseTimestamp = time();
154
+        }
155
+        $baseTimestamp = $this->getDateTime($baseTimestamp);
156
+        $baseTimestamp->setTime(0, 0, 0);
157
+        $dateInterval = $timestamp->diff($baseTimestamp);
158 158
 
159
-		if ($dateInterval->y == 0 && $dateInterval->m == 0 && $dateInterval->d == 0) {
160
-			return (string) $l->t('today');
161
-		} else if ($dateInterval->y == 0 && $dateInterval->m == 0 && $dateInterval->d == 1) {
162
-			return (string) $l->t('yesterday');
163
-		} else if ($dateInterval->y == 0 && $dateInterval->m == 0) {
164
-			return (string) $l->n('%n day ago', '%n days ago', $dateInterval->d);
165
-		} else if ($dateInterval->y == 0 && $dateInterval->m == 1) {
166
-			return (string) $l->t('last month');
167
-		} else if ($dateInterval->y == 0) {
168
-			return (string) $l->n('%n month ago', '%n months ago', $dateInterval->m);
169
-		} else if ($dateInterval->y == 1) {
170
-			return (string) $l->t('last year');
171
-		}
172
-		return (string) $l->n('%n year ago', '%n years ago', $dateInterval->y);
173
-	}
159
+        if ($dateInterval->y == 0 && $dateInterval->m == 0 && $dateInterval->d == 0) {
160
+            return (string) $l->t('today');
161
+        } else if ($dateInterval->y == 0 && $dateInterval->m == 0 && $dateInterval->d == 1) {
162
+            return (string) $l->t('yesterday');
163
+        } else if ($dateInterval->y == 0 && $dateInterval->m == 0) {
164
+            return (string) $l->n('%n day ago', '%n days ago', $dateInterval->d);
165
+        } else if ($dateInterval->y == 0 && $dateInterval->m == 1) {
166
+            return (string) $l->t('last month');
167
+        } else if ($dateInterval->y == 0) {
168
+            return (string) $l->n('%n month ago', '%n months ago', $dateInterval->m);
169
+        } else if ($dateInterval->y == 1) {
170
+            return (string) $l->t('last year');
171
+        }
172
+        return (string) $l->n('%n year ago', '%n years ago', $dateInterval->y);
173
+    }
174 174
 
175
-	/**
176
-	 * Formats the time of the given timestamp
177
-	 *
178
-	 * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
179
-	 * @param string	$format			Either 'full', 'long', 'medium' or 'short'
180
-	 * 				full:	e.g. 'h:mm:ss a zzzz'	=> '11:42:13 AM GMT+0:00'
181
-	 * 				long:	e.g. 'h:mm:ss a z'		=> '11:42:13 AM GMT'
182
-	 * 				medium:	e.g. 'h:mm:ss a'		=> '11:42:13 AM'
183
-	 * 				short:	e.g. 'h:mm a'			=> '11:42 AM'
184
-	 * 				The exact format is dependent on the language
185
-	 * @param \DateTimeZone	$timeZone	The timezone to use
186
-	 * @param \OCP\IL10N	$l			The locale to use
187
-	 * @return string Formatted time string
188
-	 */
189
-	public function formatTime($timestamp, $format = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
190
-		return $this->format($timestamp, 'time', $format, $timeZone, $l);
191
-	}
175
+    /**
176
+     * Formats the time of the given timestamp
177
+     *
178
+     * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
179
+     * @param string	$format			Either 'full', 'long', 'medium' or 'short'
180
+     * 				full:	e.g. 'h:mm:ss a zzzz'	=> '11:42:13 AM GMT+0:00'
181
+     * 				long:	e.g. 'h:mm:ss a z'		=> '11:42:13 AM GMT'
182
+     * 				medium:	e.g. 'h:mm:ss a'		=> '11:42:13 AM'
183
+     * 				short:	e.g. 'h:mm a'			=> '11:42 AM'
184
+     * 				The exact format is dependent on the language
185
+     * @param \DateTimeZone	$timeZone	The timezone to use
186
+     * @param \OCP\IL10N	$l			The locale to use
187
+     * @return string Formatted time string
188
+     */
189
+    public function formatTime($timestamp, $format = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
190
+        return $this->format($timestamp, 'time', $format, $timeZone, $l);
191
+    }
192 192
 
193
-	/**
194
-	 * Gives the relative past time of the timestamp
195
-	 *
196
-	 * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
197
-	 * @param int|\DateTime	$baseTimestamp	Timestamp to compare $timestamp against, defaults to current time
198
-	 * @return string	Dates returned are:
199
-	 * 				< 60 sec	=> seconds ago
200
-	 * 				<  1 hour	=> n minutes ago
201
-	 * 				<  1 day	=> n hours ago
202
-	 * 				<  1 month	=> Yesterday, n days ago
203
-	 * 				< 13 month	=> last month, n months ago
204
-	 * 				>= 13 month	=> last year, n years ago
205
-	 * @param \OCP\IL10N	$l			The locale to use
206
-	 * @return string Formatted time span
207
-	 */
208
-	public function formatTimeSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null) {
209
-		$l = $this->getLocale($l);
210
-		$timestamp = $this->getDateTime($timestamp);
211
-		if ($baseTimestamp === null) {
212
-			$baseTimestamp = time();
213
-		}
214
-		$baseTimestamp = $this->getDateTime($baseTimestamp);
193
+    /**
194
+     * Gives the relative past time of the timestamp
195
+     *
196
+     * @param int|\DateTime	$timestamp	Either a Unix timestamp or DateTime object
197
+     * @param int|\DateTime	$baseTimestamp	Timestamp to compare $timestamp against, defaults to current time
198
+     * @return string	Dates returned are:
199
+     * 				< 60 sec	=> seconds ago
200
+     * 				<  1 hour	=> n minutes ago
201
+     * 				<  1 day	=> n hours ago
202
+     * 				<  1 month	=> Yesterday, n days ago
203
+     * 				< 13 month	=> last month, n months ago
204
+     * 				>= 13 month	=> last year, n years ago
205
+     * @param \OCP\IL10N	$l			The locale to use
206
+     * @return string Formatted time span
207
+     */
208
+    public function formatTimeSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null) {
209
+        $l = $this->getLocale($l);
210
+        $timestamp = $this->getDateTime($timestamp);
211
+        if ($baseTimestamp === null) {
212
+            $baseTimestamp = time();
213
+        }
214
+        $baseTimestamp = $this->getDateTime($baseTimestamp);
215 215
 
216
-		$diff = $timestamp->diff($baseTimestamp);
217
-		if ($diff->y > 0 || $diff->m > 0 || $diff->d > 0) {
218
-			return (string) $this->formatDateSpan($timestamp, $baseTimestamp, $l);
219
-		}
216
+        $diff = $timestamp->diff($baseTimestamp);
217
+        if ($diff->y > 0 || $diff->m > 0 || $diff->d > 0) {
218
+            return (string) $this->formatDateSpan($timestamp, $baseTimestamp, $l);
219
+        }
220 220
 
221
-		if ($diff->h > 0) {
222
-			return (string) $l->n('%n hour ago', '%n hours ago', $diff->h);
223
-		} else if ($diff->i > 0) {
224
-			return (string) $l->n('%n minute ago', '%n minutes ago', $diff->i);
225
-		}
226
-		return (string) $l->t('seconds ago');
227
-	}
221
+        if ($diff->h > 0) {
222
+            return (string) $l->n('%n hour ago', '%n hours ago', $diff->h);
223
+        } else if ($diff->i > 0) {
224
+            return (string) $l->n('%n minute ago', '%n minutes ago', $diff->i);
225
+        }
226
+        return (string) $l->t('seconds ago');
227
+    }
228 228
 
229
-	/**
230
-	 * Formats the date and time of the given timestamp
231
-	 *
232
-	 * @param int|\DateTime $timestamp	Either a Unix timestamp or DateTime object
233
-	 * @param string		$formatDate		See formatDate() for description
234
-	 * @param string		$formatTime		See formatTime() for description
235
-	 * @param \DateTimeZone	$timeZone	The timezone to use
236
-	 * @param \OCP\IL10N	$l			The locale to use
237
-	 * @return string Formatted date and time string
238
-	 */
239
-	public function formatDateTime($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
240
-		return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l);
241
-	}
229
+    /**
230
+     * Formats the date and time of the given timestamp
231
+     *
232
+     * @param int|\DateTime $timestamp	Either a Unix timestamp or DateTime object
233
+     * @param string		$formatDate		See formatDate() for description
234
+     * @param string		$formatTime		See formatTime() for description
235
+     * @param \DateTimeZone	$timeZone	The timezone to use
236
+     * @param \OCP\IL10N	$l			The locale to use
237
+     * @return string Formatted date and time string
238
+     */
239
+    public function formatDateTime($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
240
+        return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l);
241
+    }
242 242
 
243
-	/**
244
-	 * Formats the date and time of the given timestamp
245
-	 *
246
-	 * @param int|\DateTime $timestamp	Either a Unix timestamp or DateTime object
247
-	 * @param string	$formatDate		See formatDate() for description
248
-	 * 					Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable
249
-	 * @param string	$formatTime		See formatTime() for description
250
-	 * @param \DateTimeZone	$timeZone	The timezone to use
251
-	 * @param \OCP\IL10N	$l			The locale to use
252
-	 * @return string Formatted relative date and time string
253
-	 */
254
-	public function formatDateTimeRelativeDay($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
255
-		if (substr($formatDate, -1) !== '^' && substr($formatDate, -1) !== '*') {
256
-			$formatDate .= '^';
257
-		}
243
+    /**
244
+     * Formats the date and time of the given timestamp
245
+     *
246
+     * @param int|\DateTime $timestamp	Either a Unix timestamp or DateTime object
247
+     * @param string	$formatDate		See formatDate() for description
248
+     * 					Uses 'Today', 'Yesterday' and 'Tomorrow' when applicable
249
+     * @param string	$formatTime		See formatTime() for description
250
+     * @param \DateTimeZone	$timeZone	The timezone to use
251
+     * @param \OCP\IL10N	$l			The locale to use
252
+     * @return string Formatted relative date and time string
253
+     */
254
+    public function formatDateTimeRelativeDay($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
255
+        if (substr($formatDate, -1) !== '^' && substr($formatDate, -1) !== '*') {
256
+            $formatDate .= '^';
257
+        }
258 258
 
259
-		return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l);
260
-	}
259
+        return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l);
260
+    }
261 261
 
262
-	/**
263
-	 * Formats the date and time of the given timestamp
264
-	 *
265
-	 * @param int|\DateTime $timestamp	Either a Unix timestamp or DateTime object
266
-	 * @param string		$type		One of 'date', 'datetime' or 'time'
267
-	 * @param string		$format		Format string
268
-	 * @param \DateTimeZone	$timeZone	The timezone to use
269
-	 * @param \OCP\IL10N	$l			The locale to use
270
-	 * @return string Formatted date and time string
271
-	 */
272
-	protected function format($timestamp, $type, $format, \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
273
-		$l = $this->getLocale($l);
274
-		$timeZone = $this->getTimeZone($timeZone);
275
-		$timestamp = $this->getDateTime($timestamp, $timeZone);
262
+    /**
263
+     * Formats the date and time of the given timestamp
264
+     *
265
+     * @param int|\DateTime $timestamp	Either a Unix timestamp or DateTime object
266
+     * @param string		$type		One of 'date', 'datetime' or 'time'
267
+     * @param string		$format		Format string
268
+     * @param \DateTimeZone	$timeZone	The timezone to use
269
+     * @param \OCP\IL10N	$l			The locale to use
270
+     * @return string Formatted date and time string
271
+     */
272
+    protected function format($timestamp, $type, $format, \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
273
+        $l = $this->getLocale($l);
274
+        $timeZone = $this->getTimeZone($timeZone);
275
+        $timestamp = $this->getDateTime($timestamp, $timeZone);
276 276
 
277
-		return (string) $l->l($type, $timestamp, array(
278
-			'width' => $format,
279
-		));
280
-	}
277
+        return (string) $l->l($type, $timestamp, array(
278
+            'width' => $format,
279
+        ));
280
+    }
281 281
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -237,7 +237,7 @@  discard block
 block discarded – undo
237 237
 	 * @return string Formatted date and time string
238 238
 	 */
239 239
 	public function formatDateTime($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) {
240
-		return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l);
240
+		return $this->format($timestamp, 'datetime', $formatDate.'|'.$formatTime, $timeZone, $l);
241 241
 	}
242 242
 
243 243
 	/**
@@ -256,7 +256,7 @@  discard block
 block discarded – undo
256 256
 			$formatDate .= '^';
257 257
 		}
258 258
 
259
-		return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l);
259
+		return $this->format($timestamp, 'datetime', $formatDate.'|'.$formatTime, $timeZone, $l);
260 260
 	}
261 261
 
262 262
 	/**
Please login to merge, or discard this patch.
lib/private/db/connection.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -173,7 +173,7 @@  discard block
 block discarded – undo
173 173
 	 * If an SQLLogger is configured, the execution is logged.
174 174
 	 *
175 175
 	 * @param string                                      $query  The SQL query to execute.
176
-	 * @param array                                       $params The parameters to bind to the query, if any.
176
+	 * @param string[]                                       $params The parameters to bind to the query, if any.
177 177
 	 * @param array                                       $types  The types the previous parameters are in.
178 178
 	 * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
179 179
 	 *
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
 	 * columns or sequences.
219 219
 	 *
220 220
 	 * @param string $seqName Name of the sequence object from which the ID should be returned.
221
-	 * @return string A string representation of the last inserted ID.
221
+	 * @return integer A string representation of the last inserted ID.
222 222
 	 */
223 223
 	public function lastInsertId($seqName = null) {
224 224
 		if ($seqName) {
Please login to merge, or discard this patch.
Indentation   +306 added lines, -306 removed lines patch added patch discarded remove patch
@@ -37,338 +37,338 @@
 block discarded – undo
37 37
 use OCP\PreconditionNotMetException;
38 38
 
39 39
 class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
40
-	/**
41
-	 * @var string $tablePrefix
42
-	 */
43
-	protected $tablePrefix;
40
+    /**
41
+     * @var string $tablePrefix
42
+     */
43
+    protected $tablePrefix;
44 44
 
45
-	/**
46
-	 * @var \OC\DB\Adapter $adapter
47
-	 */
48
-	protected $adapter;
45
+    /**
46
+     * @var \OC\DB\Adapter $adapter
47
+     */
48
+    protected $adapter;
49 49
 
50
-	public function connect() {
51
-		try {
52
-			return parent::connect();
53
-		} catch (DBALException $e) {
54
-			// throw a new exception to prevent leaking info from the stacktrace
55
-			throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
56
-		}
57
-	}
50
+    public function connect() {
51
+        try {
52
+            return parent::connect();
53
+        } catch (DBALException $e) {
54
+            // throw a new exception to prevent leaking info from the stacktrace
55
+            throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
56
+        }
57
+    }
58 58
 
59
-	/**
60
-	 * Returns a QueryBuilder for the connection.
61
-	 *
62
-	 * @return \OCP\DB\QueryBuilder\IQueryBuilder
63
-	 */
64
-	public function getQueryBuilder() {
65
-		return new QueryBuilder($this);
66
-	}
59
+    /**
60
+     * Returns a QueryBuilder for the connection.
61
+     *
62
+     * @return \OCP\DB\QueryBuilder\IQueryBuilder
63
+     */
64
+    public function getQueryBuilder() {
65
+        return new QueryBuilder($this);
66
+    }
67 67
 
68
-	/**
69
-	 * Gets the QueryBuilder for the connection.
70
-	 *
71
-	 * @return \Doctrine\DBAL\Query\QueryBuilder
72
-	 * @deprecated please use $this->getQueryBuilder() instead
73
-	 */
74
-	public function createQueryBuilder() {
75
-		$backtrace = $this->getCallerBacktrace();
76
-		\OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
77
-		return parent::createQueryBuilder();
78
-	}
68
+    /**
69
+     * Gets the QueryBuilder for the connection.
70
+     *
71
+     * @return \Doctrine\DBAL\Query\QueryBuilder
72
+     * @deprecated please use $this->getQueryBuilder() instead
73
+     */
74
+    public function createQueryBuilder() {
75
+        $backtrace = $this->getCallerBacktrace();
76
+        \OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
77
+        return parent::createQueryBuilder();
78
+    }
79 79
 
80
-	/**
81
-	 * Gets the ExpressionBuilder for the connection.
82
-	 *
83
-	 * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
84
-	 * @deprecated please use $this->getQueryBuilder()->expr() instead
85
-	 */
86
-	public function getExpressionBuilder() {
87
-		$backtrace = $this->getCallerBacktrace();
88
-		\OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
89
-		return parent::getExpressionBuilder();
90
-	}
80
+    /**
81
+     * Gets the ExpressionBuilder for the connection.
82
+     *
83
+     * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
84
+     * @deprecated please use $this->getQueryBuilder()->expr() instead
85
+     */
86
+    public function getExpressionBuilder() {
87
+        $backtrace = $this->getCallerBacktrace();
88
+        \OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
89
+        return parent::getExpressionBuilder();
90
+    }
91 91
 
92
-	/**
93
-	 * Get the file and line that called the method where `getCallerBacktrace()` was used
94
-	 *
95
-	 * @return string
96
-	 */
97
-	protected function getCallerBacktrace() {
98
-		$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
92
+    /**
93
+     * Get the file and line that called the method where `getCallerBacktrace()` was used
94
+     *
95
+     * @return string
96
+     */
97
+    protected function getCallerBacktrace() {
98
+        $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
99 99
 
100
-		// 0 is the method where we use `getCallerBacktrace`
101
-		// 1 is the target method which uses the method we want to log
102
-		if (isset($traces[1])) {
103
-			return $traces[1]['file'] . ':' . $traces[1]['line'];
104
-		}
100
+        // 0 is the method where we use `getCallerBacktrace`
101
+        // 1 is the target method which uses the method we want to log
102
+        if (isset($traces[1])) {
103
+            return $traces[1]['file'] . ':' . $traces[1]['line'];
104
+        }
105 105
 
106
-		return '';
107
-	}
106
+        return '';
107
+    }
108 108
 
109
-	/**
110
-	 * @return string
111
-	 */
112
-	public function getPrefix() {
113
-		return $this->tablePrefix;
114
-	}
109
+    /**
110
+     * @return string
111
+     */
112
+    public function getPrefix() {
113
+        return $this->tablePrefix;
114
+    }
115 115
 
116
-	/**
117
-	 * Initializes a new instance of the Connection class.
118
-	 *
119
-	 * @param array $params  The connection parameters.
120
-	 * @param \Doctrine\DBAL\Driver $driver
121
-	 * @param \Doctrine\DBAL\Configuration $config
122
-	 * @param \Doctrine\Common\EventManager $eventManager
123
-	 * @throws \Exception
124
-	 */
125
-	public function __construct(array $params, Driver $driver, Configuration $config = null,
126
-		EventManager $eventManager = null)
127
-	{
128
-		if (!isset($params['adapter'])) {
129
-			throw new \Exception('adapter not set');
130
-		}
131
-		if (!isset($params['tablePrefix'])) {
132
-			throw new \Exception('tablePrefix not set');
133
-		}
134
-		parent::__construct($params, $driver, $config, $eventManager);
135
-		$this->adapter = new $params['adapter']($this);
136
-		$this->tablePrefix = $params['tablePrefix'];
116
+    /**
117
+     * Initializes a new instance of the Connection class.
118
+     *
119
+     * @param array $params  The connection parameters.
120
+     * @param \Doctrine\DBAL\Driver $driver
121
+     * @param \Doctrine\DBAL\Configuration $config
122
+     * @param \Doctrine\Common\EventManager $eventManager
123
+     * @throws \Exception
124
+     */
125
+    public function __construct(array $params, Driver $driver, Configuration $config = null,
126
+        EventManager $eventManager = null)
127
+    {
128
+        if (!isset($params['adapter'])) {
129
+            throw new \Exception('adapter not set');
130
+        }
131
+        if (!isset($params['tablePrefix'])) {
132
+            throw new \Exception('tablePrefix not set');
133
+        }
134
+        parent::__construct($params, $driver, $config, $eventManager);
135
+        $this->adapter = new $params['adapter']($this);
136
+        $this->tablePrefix = $params['tablePrefix'];
137 137
 
138
-		parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED);
139
-	}
138
+        parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED);
139
+    }
140 140
 
141
-	/**
142
-	 * Prepares an SQL statement.
143
-	 *
144
-	 * @param string $statement The SQL statement to prepare.
145
-	 * @param int $limit
146
-	 * @param int $offset
147
-	 * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
148
-	 */
149
-	public function prepare( $statement, $limit=null, $offset=null ) {
150
-		if ($limit === -1) {
151
-			$limit = null;
152
-		}
153
-		if (!is_null($limit)) {
154
-			$platform = $this->getDatabasePlatform();
155
-			$statement = $platform->modifyLimitQuery($statement, $limit, $offset);
156
-		}
157
-		$statement = $this->replaceTablePrefix($statement);
158
-		$statement = $this->adapter->fixupStatement($statement);
141
+    /**
142
+     * Prepares an SQL statement.
143
+     *
144
+     * @param string $statement The SQL statement to prepare.
145
+     * @param int $limit
146
+     * @param int $offset
147
+     * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
148
+     */
149
+    public function prepare( $statement, $limit=null, $offset=null ) {
150
+        if ($limit === -1) {
151
+            $limit = null;
152
+        }
153
+        if (!is_null($limit)) {
154
+            $platform = $this->getDatabasePlatform();
155
+            $statement = $platform->modifyLimitQuery($statement, $limit, $offset);
156
+        }
157
+        $statement = $this->replaceTablePrefix($statement);
158
+        $statement = $this->adapter->fixupStatement($statement);
159 159
 
160
-		if(\OC::$server->getSystemConfig()->getValue( 'log_query', false)) {
161
-			\OCP\Util::writeLog('core', 'DB prepare : '.$statement, \OCP\Util::DEBUG);
162
-		}
163
-		return parent::prepare($statement);
164
-	}
160
+        if(\OC::$server->getSystemConfig()->getValue( 'log_query', false)) {
161
+            \OCP\Util::writeLog('core', 'DB prepare : '.$statement, \OCP\Util::DEBUG);
162
+        }
163
+        return parent::prepare($statement);
164
+    }
165 165
 
166
-	/**
167
-	 * Executes an, optionally parametrized, SQL query.
168
-	 *
169
-	 * If the query is parametrized, a prepared statement is used.
170
-	 * If an SQLLogger is configured, the execution is logged.
171
-	 *
172
-	 * @param string                                      $query  The SQL query to execute.
173
-	 * @param array                                       $params The parameters to bind to the query, if any.
174
-	 * @param array                                       $types  The types the previous parameters are in.
175
-	 * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
176
-	 *
177
-	 * @return \Doctrine\DBAL\Driver\Statement The executed statement.
178
-	 *
179
-	 * @throws \Doctrine\DBAL\DBALException
180
-	 */
181
-	public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null)
182
-	{
183
-		$query = $this->replaceTablePrefix($query);
184
-		$query = $this->adapter->fixupStatement($query);
185
-		return parent::executeQuery($query, $params, $types, $qcp);
186
-	}
166
+    /**
167
+     * Executes an, optionally parametrized, SQL query.
168
+     *
169
+     * If the query is parametrized, a prepared statement is used.
170
+     * If an SQLLogger is configured, the execution is logged.
171
+     *
172
+     * @param string                                      $query  The SQL query to execute.
173
+     * @param array                                       $params The parameters to bind to the query, if any.
174
+     * @param array                                       $types  The types the previous parameters are in.
175
+     * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
176
+     *
177
+     * @return \Doctrine\DBAL\Driver\Statement The executed statement.
178
+     *
179
+     * @throws \Doctrine\DBAL\DBALException
180
+     */
181
+    public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null)
182
+    {
183
+        $query = $this->replaceTablePrefix($query);
184
+        $query = $this->adapter->fixupStatement($query);
185
+        return parent::executeQuery($query, $params, $types, $qcp);
186
+    }
187 187
 
188
-	/**
189
-	 * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
190
-	 * and returns the number of affected rows.
191
-	 *
192
-	 * This method supports PDO binding types as well as DBAL mapping types.
193
-	 *
194
-	 * @param string $query  The SQL query.
195
-	 * @param array  $params The query parameters.
196
-	 * @param array  $types  The parameter types.
197
-	 *
198
-	 * @return integer The number of affected rows.
199
-	 *
200
-	 * @throws \Doctrine\DBAL\DBALException
201
-	 */
202
-	public function executeUpdate($query, array $params = array(), array $types = array())
203
-	{
204
-		$query = $this->replaceTablePrefix($query);
205
-		$query = $this->adapter->fixupStatement($query);
206
-		return parent::executeUpdate($query, $params, $types);
207
-	}
188
+    /**
189
+     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
190
+     * and returns the number of affected rows.
191
+     *
192
+     * This method supports PDO binding types as well as DBAL mapping types.
193
+     *
194
+     * @param string $query  The SQL query.
195
+     * @param array  $params The query parameters.
196
+     * @param array  $types  The parameter types.
197
+     *
198
+     * @return integer The number of affected rows.
199
+     *
200
+     * @throws \Doctrine\DBAL\DBALException
201
+     */
202
+    public function executeUpdate($query, array $params = array(), array $types = array())
203
+    {
204
+        $query = $this->replaceTablePrefix($query);
205
+        $query = $this->adapter->fixupStatement($query);
206
+        return parent::executeUpdate($query, $params, $types);
207
+    }
208 208
 
209
-	/**
210
-	 * Returns the ID of the last inserted row, or the last value from a sequence object,
211
-	 * depending on the underlying driver.
212
-	 *
213
-	 * Note: This method may not return a meaningful or consistent result across different drivers,
214
-	 * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
215
-	 * columns or sequences.
216
-	 *
217
-	 * @param string $seqName Name of the sequence object from which the ID should be returned.
218
-	 * @return string A string representation of the last inserted ID.
219
-	 */
220
-	public function lastInsertId($seqName = null) {
221
-		if ($seqName) {
222
-			$seqName = $this->replaceTablePrefix($seqName);
223
-		}
224
-		return $this->adapter->lastInsertId($seqName);
225
-	}
209
+    /**
210
+     * Returns the ID of the last inserted row, or the last value from a sequence object,
211
+     * depending on the underlying driver.
212
+     *
213
+     * Note: This method may not return a meaningful or consistent result across different drivers,
214
+     * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
215
+     * columns or sequences.
216
+     *
217
+     * @param string $seqName Name of the sequence object from which the ID should be returned.
218
+     * @return string A string representation of the last inserted ID.
219
+     */
220
+    public function lastInsertId($seqName = null) {
221
+        if ($seqName) {
222
+            $seqName = $this->replaceTablePrefix($seqName);
223
+        }
224
+        return $this->adapter->lastInsertId($seqName);
225
+    }
226 226
 
227
-	// internal use
228
-	public function realLastInsertId($seqName = null) {
229
-		return parent::lastInsertId($seqName);
230
-	}
227
+    // internal use
228
+    public function realLastInsertId($seqName = null) {
229
+        return parent::lastInsertId($seqName);
230
+    }
231 231
 
232
-	/**
233
-	 * Insert a row if the matching row does not exists.
234
-	 *
235
-	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
236
-	 * @param array $input data that should be inserted into the table  (column name => value)
237
-	 * @param array|null $compare List of values that should be checked for "if not exists"
238
-	 *				If this is null or an empty array, all keys of $input will be compared
239
-	 *				Please note: text fields (clob) must not be used in the compare array
240
-	 * @return int number of inserted rows
241
-	 * @throws \Doctrine\DBAL\DBALException
242
-	 */
243
-	public function insertIfNotExist($table, $input, array $compare = null) {
244
-		return $this->adapter->insertIfNotExist($table, $input, $compare);
245
-	}
232
+    /**
233
+     * Insert a row if the matching row does not exists.
234
+     *
235
+     * @param string $table The table name (will replace *PREFIX* with the actual prefix)
236
+     * @param array $input data that should be inserted into the table  (column name => value)
237
+     * @param array|null $compare List of values that should be checked for "if not exists"
238
+     *				If this is null or an empty array, all keys of $input will be compared
239
+     *				Please note: text fields (clob) must not be used in the compare array
240
+     * @return int number of inserted rows
241
+     * @throws \Doctrine\DBAL\DBALException
242
+     */
243
+    public function insertIfNotExist($table, $input, array $compare = null) {
244
+        return $this->adapter->insertIfNotExist($table, $input, $compare);
245
+    }
246 246
 
247
-	private function getType($value) {
248
-		if (is_bool($value)) {
249
-			return IQueryBuilder::PARAM_BOOL;
250
-		} else if (is_int($value)) {
251
-			return IQueryBuilder::PARAM_INT;
252
-		} else {
253
-			return IQueryBuilder::PARAM_STR;
254
-		}
255
-	}
247
+    private function getType($value) {
248
+        if (is_bool($value)) {
249
+            return IQueryBuilder::PARAM_BOOL;
250
+        } else if (is_int($value)) {
251
+            return IQueryBuilder::PARAM_INT;
252
+        } else {
253
+            return IQueryBuilder::PARAM_STR;
254
+        }
255
+    }
256 256
 
257
-	/**
258
-	 * Insert or update a row value
259
-	 *
260
-	 * @param string $table
261
-	 * @param array $keys (column name => value)
262
-	 * @param array $values (column name => value)
263
-	 * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
264
-	 * @return int number of new rows
265
-	 * @throws \Doctrine\DBAL\DBALException
266
-	 * @throws PreconditionNotMetException
267
-	 */
268
-	public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
269
-		try {
270
-			$insertQb = $this->getQueryBuilder();
271
-			$insertQb->insert($table)
272
-				->values(
273
-					array_map(function($value) use ($insertQb) {
274
-						return $insertQb->createNamedParameter($value, $this->getType($value));
275
-					}, array_merge($keys, $values))
276
-				);
277
-			return $insertQb->execute();
278
-		} catch (\Doctrine\DBAL\Exception\ConstraintViolationException $e) {
279
-			// value already exists, try update
280
-			$updateQb = $this->getQueryBuilder();
281
-			$updateQb->update($table);
282
-			foreach ($values as $name => $value) {
283
-				$updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
284
-			}
285
-			$where = $updateQb->expr()->andx();
286
-			$whereValues = array_merge($keys, $updatePreconditionValues);
287
-			foreach ($whereValues as $name => $value) {
288
-				$where->add($updateQb->expr()->eq(
289
-					$name,
290
-					$updateQb->createNamedParameter($value, $this->getType($value)),
291
-					$this->getType($value)
292
-				));
293
-			}
294
-			$updateQb->where($where);
295
-			$affected = $updateQb->execute();
257
+    /**
258
+     * Insert or update a row value
259
+     *
260
+     * @param string $table
261
+     * @param array $keys (column name => value)
262
+     * @param array $values (column name => value)
263
+     * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
264
+     * @return int number of new rows
265
+     * @throws \Doctrine\DBAL\DBALException
266
+     * @throws PreconditionNotMetException
267
+     */
268
+    public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
269
+        try {
270
+            $insertQb = $this->getQueryBuilder();
271
+            $insertQb->insert($table)
272
+                ->values(
273
+                    array_map(function($value) use ($insertQb) {
274
+                        return $insertQb->createNamedParameter($value, $this->getType($value));
275
+                    }, array_merge($keys, $values))
276
+                );
277
+            return $insertQb->execute();
278
+        } catch (\Doctrine\DBAL\Exception\ConstraintViolationException $e) {
279
+            // value already exists, try update
280
+            $updateQb = $this->getQueryBuilder();
281
+            $updateQb->update($table);
282
+            foreach ($values as $name => $value) {
283
+                $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
284
+            }
285
+            $where = $updateQb->expr()->andx();
286
+            $whereValues = array_merge($keys, $updatePreconditionValues);
287
+            foreach ($whereValues as $name => $value) {
288
+                $where->add($updateQb->expr()->eq(
289
+                    $name,
290
+                    $updateQb->createNamedParameter($value, $this->getType($value)),
291
+                    $this->getType($value)
292
+                ));
293
+            }
294
+            $updateQb->where($where);
295
+            $affected = $updateQb->execute();
296 296
 
297
-			if ($affected === 0 && !empty($updatePreconditionValues)) {
298
-				throw new PreconditionNotMetException();
299
-			}
297
+            if ($affected === 0 && !empty($updatePreconditionValues)) {
298
+                throw new PreconditionNotMetException();
299
+            }
300 300
 
301
-			return 0;
302
-		}
303
-	}
301
+            return 0;
302
+        }
303
+    }
304 304
 
305
-	/**
306
-	 * returns the error code and message as a string for logging
307
-	 * works with DoctrineException
308
-	 * @return string
309
-	 */
310
-	public function getError() {
311
-		$msg = $this->errorCode() . ': ';
312
-		$errorInfo = $this->errorInfo();
313
-		if (is_array($errorInfo)) {
314
-			$msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
315
-			$msg .= 'Driver Code = '.$errorInfo[1] . ', ';
316
-			$msg .= 'Driver Message = '.$errorInfo[2];
317
-		}
318
-		return $msg;
319
-	}
305
+    /**
306
+     * returns the error code and message as a string for logging
307
+     * works with DoctrineException
308
+     * @return string
309
+     */
310
+    public function getError() {
311
+        $msg = $this->errorCode() . ': ';
312
+        $errorInfo = $this->errorInfo();
313
+        if (is_array($errorInfo)) {
314
+            $msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
315
+            $msg .= 'Driver Code = '.$errorInfo[1] . ', ';
316
+            $msg .= 'Driver Message = '.$errorInfo[2];
317
+        }
318
+        return $msg;
319
+    }
320 320
 
321
-	/**
322
-	 * Drop a table from the database if it exists
323
-	 *
324
-	 * @param string $table table name without the prefix
325
-	 */
326
-	public function dropTable($table) {
327
-		$table = $this->tablePrefix . trim($table);
328
-		$schema = $this->getSchemaManager();
329
-		if($schema->tablesExist(array($table))) {
330
-			$schema->dropTable($table);
331
-		}
332
-	}
321
+    /**
322
+     * Drop a table from the database if it exists
323
+     *
324
+     * @param string $table table name without the prefix
325
+     */
326
+    public function dropTable($table) {
327
+        $table = $this->tablePrefix . trim($table);
328
+        $schema = $this->getSchemaManager();
329
+        if($schema->tablesExist(array($table))) {
330
+            $schema->dropTable($table);
331
+        }
332
+    }
333 333
 
334
-	/**
335
-	 * Check if a table exists
336
-	 *
337
-	 * @param string $table table name without the prefix
338
-	 * @return bool
339
-	 */
340
-	public function tableExists($table){
341
-		$table = $this->tablePrefix . trim($table);
342
-		$schema = $this->getSchemaManager();
343
-		return $schema->tablesExist(array($table));
344
-	}
334
+    /**
335
+     * Check if a table exists
336
+     *
337
+     * @param string $table table name without the prefix
338
+     * @return bool
339
+     */
340
+    public function tableExists($table){
341
+        $table = $this->tablePrefix . trim($table);
342
+        $schema = $this->getSchemaManager();
343
+        return $schema->tablesExist(array($table));
344
+    }
345 345
 
346
-	// internal use
347
-	/**
348
-	 * @param string $statement
349
-	 * @return string
350
-	 */
351
-	protected function replaceTablePrefix($statement) {
352
-		return str_replace( '*PREFIX*', $this->tablePrefix, $statement );
353
-	}
346
+    // internal use
347
+    /**
348
+     * @param string $statement
349
+     * @return string
350
+     */
351
+    protected function replaceTablePrefix($statement) {
352
+        return str_replace( '*PREFIX*', $this->tablePrefix, $statement );
353
+    }
354 354
 
355
-	/**
356
-	 * Check if a transaction is active
357
-	 *
358
-	 * @return bool
359
-	 * @since 8.2.0
360
-	 */
361
-	public function inTransaction() {
362
-		return $this->getTransactionNestingLevel() > 0;
363
-	}
355
+    /**
356
+     * Check if a transaction is active
357
+     *
358
+     * @return bool
359
+     * @since 8.2.0
360
+     */
361
+    public function inTransaction() {
362
+        return $this->getTransactionNestingLevel() > 0;
363
+    }
364 364
 
365
-	/**
366
-	 * Espace a parameter to be used in a LIKE query
367
-	 *
368
-	 * @param string $param
369
-	 * @return string
370
-	 */
371
-	public function escapeLikeParameter($param) {
372
-		return addcslashes($param, '\\_%');
373
-	}
365
+    /**
366
+     * Espace a parameter to be used in a LIKE query
367
+     *
368
+     * @param string $param
369
+     * @return string
370
+     */
371
+    public function escapeLikeParameter($param) {
372
+        return addcslashes($param, '\\_%');
373
+    }
374 374
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -52,7 +52,7 @@  discard block
 block discarded – undo
52 52
 			return parent::connect();
53 53
 		} catch (DBALException $e) {
54 54
 			// throw a new exception to prevent leaking info from the stacktrace
55
-			throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
55
+			throw new DBALException('Failed to connect to the database: '.$e->getMessage(), $e->getCode());
56 56
 		}
57 57
 	}
58 58
 
@@ -100,7 +100,7 @@  discard block
 block discarded – undo
100 100
 		// 0 is the method where we use `getCallerBacktrace`
101 101
 		// 1 is the target method which uses the method we want to log
102 102
 		if (isset($traces[1])) {
103
-			return $traces[1]['file'] . ':' . $traces[1]['line'];
103
+			return $traces[1]['file'].':'.$traces[1]['line'];
104 104
 		}
105 105
 
106 106
 		return '';
@@ -146,7 +146,7 @@  discard block
 block discarded – undo
146 146
 	 * @param int $offset
147 147
 	 * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
148 148
 	 */
149
-	public function prepare( $statement, $limit=null, $offset=null ) {
149
+	public function prepare($statement, $limit = null, $offset = null) {
150 150
 		if ($limit === -1) {
151 151
 			$limit = null;
152 152
 		}
@@ -157,7 +157,7 @@  discard block
 block discarded – undo
157 157
 		$statement = $this->replaceTablePrefix($statement);
158 158
 		$statement = $this->adapter->fixupStatement($statement);
159 159
 
160
-		if(\OC::$server->getSystemConfig()->getValue( 'log_query', false)) {
160
+		if (\OC::$server->getSystemConfig()->getValue('log_query', false)) {
161 161
 			\OCP\Util::writeLog('core', 'DB prepare : '.$statement, \OCP\Util::DEBUG);
162 162
 		}
163 163
 		return parent::prepare($statement);
@@ -308,11 +308,11 @@  discard block
 block discarded – undo
308 308
 	 * @return string
309 309
 	 */
310 310
 	public function getError() {
311
-		$msg = $this->errorCode() . ': ';
311
+		$msg = $this->errorCode().': ';
312 312
 		$errorInfo = $this->errorInfo();
313 313
 		if (is_array($errorInfo)) {
314
-			$msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
315
-			$msg .= 'Driver Code = '.$errorInfo[1] . ', ';
314
+			$msg .= 'SQLSTATE = '.$errorInfo[0].', ';
315
+			$msg .= 'Driver Code = '.$errorInfo[1].', ';
316 316
 			$msg .= 'Driver Message = '.$errorInfo[2];
317 317
 		}
318 318
 		return $msg;
@@ -324,9 +324,9 @@  discard block
 block discarded – undo
324 324
 	 * @param string $table table name without the prefix
325 325
 	 */
326 326
 	public function dropTable($table) {
327
-		$table = $this->tablePrefix . trim($table);
327
+		$table = $this->tablePrefix.trim($table);
328 328
 		$schema = $this->getSchemaManager();
329
-		if($schema->tablesExist(array($table))) {
329
+		if ($schema->tablesExist(array($table))) {
330 330
 			$schema->dropTable($table);
331 331
 		}
332 332
 	}
@@ -337,8 +337,8 @@  discard block
 block discarded – undo
337 337
 	 * @param string $table table name without the prefix
338 338
 	 * @return bool
339 339
 	 */
340
-	public function tableExists($table){
341
-		$table = $this->tablePrefix . trim($table);
340
+	public function tableExists($table) {
341
+		$table = $this->tablePrefix.trim($table);
342 342
 		$schema = $this->getSchemaManager();
343 343
 		return $schema->tablesExist(array($table));
344 344
 	}
@@ -349,7 +349,7 @@  discard block
 block discarded – undo
349 349
 	 * @return string
350 350
 	 */
351 351
 	protected function replaceTablePrefix($statement) {
352
-		return str_replace( '*PREFIX*', $this->tablePrefix, $statement );
352
+		return str_replace('*PREFIX*', $this->tablePrefix, $statement);
353 353
 	}
354 354
 
355 355
 	/**
Please login to merge, or discard this patch.
lib/private/encryption/util.php 3 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -30,7 +30,6 @@
 block discarded – undo
30 30
 use OC\Files\Filesystem;
31 31
 use OC\Files\View;
32 32
 use OCP\Encryption\IEncryptionModule;
33
-use OCP\Files\Storage;
34 33
 use OCP\IConfig;
35 34
 
36 35
 class Util {
Please login to merge, or discard this patch.
Indentation   +354 added lines, -354 removed lines patch added patch discarded remove patch
@@ -36,359 +36,359 @@
 block discarded – undo
36 36
 
37 37
 class Util {
38 38
 
39
-	const HEADER_START = 'HBEGIN';
40
-	const HEADER_END = 'HEND';
41
-	const HEADER_PADDING_CHAR = '-';
42
-
43
-	const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module';
44
-
45
-	/**
46
-	 * block size will always be 8192 for a PHP stream
47
-	 * @see https://bugs.php.net/bug.php?id=21641
48
-	 * @var integer
49
-	 */
50
-	protected $headerSize = 8192;
51
-
52
-	/**
53
-	 * block size will always be 8192 for a PHP stream
54
-	 * @see https://bugs.php.net/bug.php?id=21641
55
-	 * @var integer
56
-	 */
57
-	protected $blockSize = 8192;
58
-
59
-	/** @var View */
60
-	protected $rootView;
61
-
62
-	/** @var array */
63
-	protected $ocHeaderKeys;
64
-
65
-	/** @var \OC\User\Manager */
66
-	protected $userManager;
67
-
68
-	/** @var IConfig */
69
-	protected $config;
70
-
71
-	/** @var array paths excluded from encryption */
72
-	protected $excludedPaths;
73
-
74
-	/** @var \OC\Group\Manager $manager */
75
-	protected $groupManager;
76
-
77
-	/**
78
-	 *
79
-	 * @param View $rootView
80
-	 * @param \OC\User\Manager $userManager
81
-	 * @param \OC\Group\Manager $groupManager
82
-	 * @param IConfig $config
83
-	 */
84
-	public function __construct(
85
-		View $rootView,
86
-		\OC\User\Manager $userManager,
87
-		\OC\Group\Manager $groupManager,
88
-		IConfig $config) {
89
-
90
-		$this->ocHeaderKeys = [
91
-			self::HEADER_ENCRYPTION_MODULE_KEY
92
-		];
93
-
94
-		$this->rootView = $rootView;
95
-		$this->userManager = $userManager;
96
-		$this->groupManager = $groupManager;
97
-		$this->config = $config;
98
-
99
-		$this->excludedPaths[] = 'files_encryption';
100
-	}
101
-
102
-	/**
103
-	 * read encryption module ID from header
104
-	 *
105
-	 * @param array $header
106
-	 * @return string
107
-	 * @throws ModuleDoesNotExistsException
108
-	 */
109
-	public function getEncryptionModuleId(array $header = null) {
110
-		$id = '';
111
-		$encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY;
112
-
113
-		if (isset($header[$encryptionModuleKey])) {
114
-			$id = $header[$encryptionModuleKey];
115
-		} elseif (isset($header['cipher'])) {
116
-			if (class_exists('\OCA\Encryption\Crypto\Encryption')) {
117
-				// fall back to default encryption if the user migrated from
118
-				// ownCloud <= 8.0 with the old encryption
119
-				$id = \OCA\Encryption\Crypto\Encryption::ID;
120
-			} else {
121
-				throw new ModuleDoesNotExistsException('Default encryption module missing');
122
-			}
123
-		}
124
-
125
-		return $id;
126
-	}
127
-
128
-	/**
129
-	 * create header for encrypted file
130
-	 *
131
-	 * @param array $headerData
132
-	 * @param IEncryptionModule $encryptionModule
133
-	 * @return string
134
-	 * @throws EncryptionHeaderToLargeException if header has to many arguments
135
-	 * @throws EncryptionHeaderKeyExistsException if header key is already in use
136
-	 */
137
-	public function createHeader(array $headerData, IEncryptionModule $encryptionModule) {
138
-		$header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':';
139
-		foreach ($headerData as $key => $value) {
140
-			if (in_array($key, $this->ocHeaderKeys)) {
141
-				throw new EncryptionHeaderKeyExistsException($key);
142
-			}
143
-			$header .= $key . ':' . $value . ':';
144
-		}
145
-		$header .= self::HEADER_END;
146
-
147
-		if (strlen($header) > $this->getHeaderSize()) {
148
-			throw new EncryptionHeaderToLargeException();
149
-		}
150
-
151
-		$paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT);
152
-
153
-		return $paddedHeader;
154
-	}
155
-
156
-	/**
157
-	 * go recursively through a dir and collect all files and sub files.
158
-	 *
159
-	 * @param string $dir relative to the users files folder
160
-	 * @return array with list of files relative to the users files folder
161
-	 */
162
-	public function getAllFiles($dir) {
163
-		$result = array();
164
-		$dirList = array($dir);
165
-
166
-		while ($dirList) {
167
-			$dir = array_pop($dirList);
168
-			$content = $this->rootView->getDirectoryContent($dir);
169
-
170
-			foreach ($content as $c) {
171
-				if ($c->getType() === 'dir') {
172
-					$dirList[] = $c->getPath();
173
-				} else {
174
-					$result[] =  $c->getPath();
175
-				}
176
-			}
177
-
178
-		}
179
-
180
-		return $result;
181
-	}
182
-
183
-	/**
184
-	 * check if it is a file uploaded by the user stored in data/user/files
185
-	 * or a metadata file
186
-	 *
187
-	 * @param string $path relative to the data/ folder
188
-	 * @return boolean
189
-	 */
190
-	public function isFile($path) {
191
-		$parts = explode('/', Filesystem::normalizePath($path), 4);
192
-		if (isset($parts[2]) && $parts[2] === 'files') {
193
-			return true;
194
-		}
195
-		return false;
196
-	}
197
-
198
-	/**
199
-	 * return size of encryption header
200
-	 *
201
-	 * @return integer
202
-	 */
203
-	public function getHeaderSize() {
204
-		return $this->headerSize;
205
-	}
206
-
207
-	/**
208
-	 * return size of block read by a PHP stream
209
-	 *
210
-	 * @return integer
211
-	 */
212
-	public function getBlockSize() {
213
-		return $this->blockSize;
214
-	}
215
-
216
-	/**
217
-	 * get the owner and the path for the file relative to the owners files folder
218
-	 *
219
-	 * @param string $path
220
-	 * @return array
221
-	 * @throws \BadMethodCallException
222
-	 */
223
-	public function getUidAndFilename($path) {
224
-
225
-		$parts = explode('/', $path);
226
-		$uid = '';
227
-		if (count($parts) > 2) {
228
-			$uid = $parts[1];
229
-		}
230
-		if (!$this->userManager->userExists($uid)) {
231
-			throw new \BadMethodCallException(
232
-				'path needs to be relative to the system wide data folder and point to a user specific file'
233
-			);
234
-		}
235
-
236
-		$ownerPath = implode('/', array_slice($parts, 2));
237
-
238
-		return array($uid, Filesystem::normalizePath($ownerPath));
239
-
240
-	}
241
-
242
-	/**
243
-	 * Remove .path extension from a file path
244
-	 * @param string $path Path that may identify a .part file
245
-	 * @return string File path without .part extension
246
-	 * @note this is needed for reusing keys
247
-	 */
248
-	public function stripPartialFileExtension($path) {
249
-		$extension = pathinfo($path, PATHINFO_EXTENSION);
250
-
251
-		if ( $extension === 'part') {
252
-
253
-			$newLength = strlen($path) - 5; // 5 = strlen(".part")
254
-			$fPath = substr($path, 0, $newLength);
255
-
256
-			// if path also contains a transaction id, we remove it too
257
-			$extension = pathinfo($fPath, PATHINFO_EXTENSION);
258
-			if(substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
259
-				$newLength = strlen($fPath) - strlen($extension) -1;
260
-				$fPath = substr($fPath, 0, $newLength);
261
-			}
262
-			return $fPath;
263
-
264
-		} else {
265
-			return $path;
266
-		}
267
-	}
268
-
269
-	public function getUserWithAccessToMountPoint($users, $groups) {
270
-		$result = array();
271
-		if (in_array('all', $users)) {
272
-			$result = \OCP\User::getUsers();
273
-		} else {
274
-			$result = array_merge($result, $users);
275
-			foreach ($groups as $group) {
276
-				$result = array_merge($result, \OC_Group::usersInGroup($group));
277
-			}
278
-		}
279
-
280
-		return $result;
281
-	}
282
-
283
-	/**
284
-	 * check if the file is stored on a system wide mount point
285
-	 * @param string $path relative to /data/user with leading '/'
286
-	 * @param string $uid
287
-	 * @return boolean
288
-	 */
289
-	public function isSystemWideMountPoint($path, $uid) {
290
-		if (\OCP\App::isEnabled("files_external")) {
291
-			$mounts = \OC_Mount_Config::getSystemMountPoints();
292
-			foreach ($mounts as $mount) {
293
-				if (strpos($path, '/files/' . $mount['mountpoint']) === 0) {
294
-					if ($this->isMountPointApplicableToUser($mount, $uid)) {
295
-						return true;
296
-					}
297
-				}
298
-			}
299
-		}
300
-		return false;
301
-	}
302
-
303
-	/**
304
-	 * check if mount point is applicable to user
305
-	 *
306
-	 * @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups']
307
-	 * @param string $uid
308
-	 * @return boolean
309
-	 */
310
-	private function isMountPointApplicableToUser($mount, $uid) {
311
-		$acceptedUids = array('all', $uid);
312
-		// check if mount point is applicable for the user
313
-		$intersection = array_intersect($acceptedUids, $mount['applicable']['users']);
314
-		if (!empty($intersection)) {
315
-			return true;
316
-		}
317
-		// check if mount point is applicable for group where the user is a member
318
-		foreach ($mount['applicable']['groups'] as $gid) {
319
-			if ($this->groupManager->isInGroup($uid, $gid)) {
320
-				return true;
321
-			}
322
-		}
323
-		return false;
324
-	}
325
-
326
-	/**
327
-	 * check if it is a path which is excluded by ownCloud from encryption
328
-	 *
329
-	 * @param string $path
330
-	 * @return boolean
331
-	 */
332
-	public function isExcluded($path) {
333
-		$normalizedPath = Filesystem::normalizePath($path);
334
-		$root = explode('/', $normalizedPath, 4);
335
-		if (count($root) > 1) {
336
-
337
-			// detect alternative key storage root
338
-			$rootDir = $this->getKeyStorageRoot();
339
-			if ($rootDir !== '' &&
340
-				0 === strpos(
341
-					Filesystem::normalizePath($path),
342
-					Filesystem::normalizePath($rootDir)
343
-				)
344
-			) {
345
-				return true;
346
-			}
347
-
348
-
349
-			//detect system wide folders
350
-			if (in_array($root[1], $this->excludedPaths)) {
351
-				return true;
352
-			}
353
-
354
-			// detect user specific folders
355
-			if ($this->userManager->userExists($root[1])
356
-				&& in_array($root[2], $this->excludedPaths)) {
357
-
358
-				return true;
359
-			}
360
-		}
361
-		return false;
362
-	}
363
-
364
-	/**
365
-	 * check if recovery key is enabled for user
366
-	 *
367
-	 * @param string $uid
368
-	 * @return boolean
369
-	 */
370
-	public function recoveryEnabled($uid) {
371
-		$enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
372
-
373
-		return ($enabled === '1') ? true : false;
374
-	}
375
-
376
-	/**
377
-	 * set new key storage root
378
-	 *
379
-	 * @param string $root new key store root relative to the data folder
380
-	 */
381
-	public function setKeyStorageRoot($root) {
382
-		$this->config->setAppValue('core', 'encryption_key_storage_root', $root);
383
-	}
384
-
385
-	/**
386
-	 * get key storage root
387
-	 *
388
-	 * @return string key storage root
389
-	 */
390
-	public function getKeyStorageRoot() {
391
-		return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
392
-	}
39
+    const HEADER_START = 'HBEGIN';
40
+    const HEADER_END = 'HEND';
41
+    const HEADER_PADDING_CHAR = '-';
42
+
43
+    const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module';
44
+
45
+    /**
46
+     * block size will always be 8192 for a PHP stream
47
+     * @see https://bugs.php.net/bug.php?id=21641
48
+     * @var integer
49
+     */
50
+    protected $headerSize = 8192;
51
+
52
+    /**
53
+     * block size will always be 8192 for a PHP stream
54
+     * @see https://bugs.php.net/bug.php?id=21641
55
+     * @var integer
56
+     */
57
+    protected $blockSize = 8192;
58
+
59
+    /** @var View */
60
+    protected $rootView;
61
+
62
+    /** @var array */
63
+    protected $ocHeaderKeys;
64
+
65
+    /** @var \OC\User\Manager */
66
+    protected $userManager;
67
+
68
+    /** @var IConfig */
69
+    protected $config;
70
+
71
+    /** @var array paths excluded from encryption */
72
+    protected $excludedPaths;
73
+
74
+    /** @var \OC\Group\Manager $manager */
75
+    protected $groupManager;
76
+
77
+    /**
78
+     *
79
+     * @param View $rootView
80
+     * @param \OC\User\Manager $userManager
81
+     * @param \OC\Group\Manager $groupManager
82
+     * @param IConfig $config
83
+     */
84
+    public function __construct(
85
+        View $rootView,
86
+        \OC\User\Manager $userManager,
87
+        \OC\Group\Manager $groupManager,
88
+        IConfig $config) {
89
+
90
+        $this->ocHeaderKeys = [
91
+            self::HEADER_ENCRYPTION_MODULE_KEY
92
+        ];
93
+
94
+        $this->rootView = $rootView;
95
+        $this->userManager = $userManager;
96
+        $this->groupManager = $groupManager;
97
+        $this->config = $config;
98
+
99
+        $this->excludedPaths[] = 'files_encryption';
100
+    }
101
+
102
+    /**
103
+     * read encryption module ID from header
104
+     *
105
+     * @param array $header
106
+     * @return string
107
+     * @throws ModuleDoesNotExistsException
108
+     */
109
+    public function getEncryptionModuleId(array $header = null) {
110
+        $id = '';
111
+        $encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY;
112
+
113
+        if (isset($header[$encryptionModuleKey])) {
114
+            $id = $header[$encryptionModuleKey];
115
+        } elseif (isset($header['cipher'])) {
116
+            if (class_exists('\OCA\Encryption\Crypto\Encryption')) {
117
+                // fall back to default encryption if the user migrated from
118
+                // ownCloud <= 8.0 with the old encryption
119
+                $id = \OCA\Encryption\Crypto\Encryption::ID;
120
+            } else {
121
+                throw new ModuleDoesNotExistsException('Default encryption module missing');
122
+            }
123
+        }
124
+
125
+        return $id;
126
+    }
127
+
128
+    /**
129
+     * create header for encrypted file
130
+     *
131
+     * @param array $headerData
132
+     * @param IEncryptionModule $encryptionModule
133
+     * @return string
134
+     * @throws EncryptionHeaderToLargeException if header has to many arguments
135
+     * @throws EncryptionHeaderKeyExistsException if header key is already in use
136
+     */
137
+    public function createHeader(array $headerData, IEncryptionModule $encryptionModule) {
138
+        $header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':';
139
+        foreach ($headerData as $key => $value) {
140
+            if (in_array($key, $this->ocHeaderKeys)) {
141
+                throw new EncryptionHeaderKeyExistsException($key);
142
+            }
143
+            $header .= $key . ':' . $value . ':';
144
+        }
145
+        $header .= self::HEADER_END;
146
+
147
+        if (strlen($header) > $this->getHeaderSize()) {
148
+            throw new EncryptionHeaderToLargeException();
149
+        }
150
+
151
+        $paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT);
152
+
153
+        return $paddedHeader;
154
+    }
155
+
156
+    /**
157
+     * go recursively through a dir and collect all files and sub files.
158
+     *
159
+     * @param string $dir relative to the users files folder
160
+     * @return array with list of files relative to the users files folder
161
+     */
162
+    public function getAllFiles($dir) {
163
+        $result = array();
164
+        $dirList = array($dir);
165
+
166
+        while ($dirList) {
167
+            $dir = array_pop($dirList);
168
+            $content = $this->rootView->getDirectoryContent($dir);
169
+
170
+            foreach ($content as $c) {
171
+                if ($c->getType() === 'dir') {
172
+                    $dirList[] = $c->getPath();
173
+                } else {
174
+                    $result[] =  $c->getPath();
175
+                }
176
+            }
177
+
178
+        }
179
+
180
+        return $result;
181
+    }
182
+
183
+    /**
184
+     * check if it is a file uploaded by the user stored in data/user/files
185
+     * or a metadata file
186
+     *
187
+     * @param string $path relative to the data/ folder
188
+     * @return boolean
189
+     */
190
+    public function isFile($path) {
191
+        $parts = explode('/', Filesystem::normalizePath($path), 4);
192
+        if (isset($parts[2]) && $parts[2] === 'files') {
193
+            return true;
194
+        }
195
+        return false;
196
+    }
197
+
198
+    /**
199
+     * return size of encryption header
200
+     *
201
+     * @return integer
202
+     */
203
+    public function getHeaderSize() {
204
+        return $this->headerSize;
205
+    }
206
+
207
+    /**
208
+     * return size of block read by a PHP stream
209
+     *
210
+     * @return integer
211
+     */
212
+    public function getBlockSize() {
213
+        return $this->blockSize;
214
+    }
215
+
216
+    /**
217
+     * get the owner and the path for the file relative to the owners files folder
218
+     *
219
+     * @param string $path
220
+     * @return array
221
+     * @throws \BadMethodCallException
222
+     */
223
+    public function getUidAndFilename($path) {
224
+
225
+        $parts = explode('/', $path);
226
+        $uid = '';
227
+        if (count($parts) > 2) {
228
+            $uid = $parts[1];
229
+        }
230
+        if (!$this->userManager->userExists($uid)) {
231
+            throw new \BadMethodCallException(
232
+                'path needs to be relative to the system wide data folder and point to a user specific file'
233
+            );
234
+        }
235
+
236
+        $ownerPath = implode('/', array_slice($parts, 2));
237
+
238
+        return array($uid, Filesystem::normalizePath($ownerPath));
239
+
240
+    }
241
+
242
+    /**
243
+     * Remove .path extension from a file path
244
+     * @param string $path Path that may identify a .part file
245
+     * @return string File path without .part extension
246
+     * @note this is needed for reusing keys
247
+     */
248
+    public function stripPartialFileExtension($path) {
249
+        $extension = pathinfo($path, PATHINFO_EXTENSION);
250
+
251
+        if ( $extension === 'part') {
252
+
253
+            $newLength = strlen($path) - 5; // 5 = strlen(".part")
254
+            $fPath = substr($path, 0, $newLength);
255
+
256
+            // if path also contains a transaction id, we remove it too
257
+            $extension = pathinfo($fPath, PATHINFO_EXTENSION);
258
+            if(substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
259
+                $newLength = strlen($fPath) - strlen($extension) -1;
260
+                $fPath = substr($fPath, 0, $newLength);
261
+            }
262
+            return $fPath;
263
+
264
+        } else {
265
+            return $path;
266
+        }
267
+    }
268
+
269
+    public function getUserWithAccessToMountPoint($users, $groups) {
270
+        $result = array();
271
+        if (in_array('all', $users)) {
272
+            $result = \OCP\User::getUsers();
273
+        } else {
274
+            $result = array_merge($result, $users);
275
+            foreach ($groups as $group) {
276
+                $result = array_merge($result, \OC_Group::usersInGroup($group));
277
+            }
278
+        }
279
+
280
+        return $result;
281
+    }
282
+
283
+    /**
284
+     * check if the file is stored on a system wide mount point
285
+     * @param string $path relative to /data/user with leading '/'
286
+     * @param string $uid
287
+     * @return boolean
288
+     */
289
+    public function isSystemWideMountPoint($path, $uid) {
290
+        if (\OCP\App::isEnabled("files_external")) {
291
+            $mounts = \OC_Mount_Config::getSystemMountPoints();
292
+            foreach ($mounts as $mount) {
293
+                if (strpos($path, '/files/' . $mount['mountpoint']) === 0) {
294
+                    if ($this->isMountPointApplicableToUser($mount, $uid)) {
295
+                        return true;
296
+                    }
297
+                }
298
+            }
299
+        }
300
+        return false;
301
+    }
302
+
303
+    /**
304
+     * check if mount point is applicable to user
305
+     *
306
+     * @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups']
307
+     * @param string $uid
308
+     * @return boolean
309
+     */
310
+    private function isMountPointApplicableToUser($mount, $uid) {
311
+        $acceptedUids = array('all', $uid);
312
+        // check if mount point is applicable for the user
313
+        $intersection = array_intersect($acceptedUids, $mount['applicable']['users']);
314
+        if (!empty($intersection)) {
315
+            return true;
316
+        }
317
+        // check if mount point is applicable for group where the user is a member
318
+        foreach ($mount['applicable']['groups'] as $gid) {
319
+            if ($this->groupManager->isInGroup($uid, $gid)) {
320
+                return true;
321
+            }
322
+        }
323
+        return false;
324
+    }
325
+
326
+    /**
327
+     * check if it is a path which is excluded by ownCloud from encryption
328
+     *
329
+     * @param string $path
330
+     * @return boolean
331
+     */
332
+    public function isExcluded($path) {
333
+        $normalizedPath = Filesystem::normalizePath($path);
334
+        $root = explode('/', $normalizedPath, 4);
335
+        if (count($root) > 1) {
336
+
337
+            // detect alternative key storage root
338
+            $rootDir = $this->getKeyStorageRoot();
339
+            if ($rootDir !== '' &&
340
+                0 === strpos(
341
+                    Filesystem::normalizePath($path),
342
+                    Filesystem::normalizePath($rootDir)
343
+                )
344
+            ) {
345
+                return true;
346
+            }
347
+
348
+
349
+            //detect system wide folders
350
+            if (in_array($root[1], $this->excludedPaths)) {
351
+                return true;
352
+            }
353
+
354
+            // detect user specific folders
355
+            if ($this->userManager->userExists($root[1])
356
+                && in_array($root[2], $this->excludedPaths)) {
357
+
358
+                return true;
359
+            }
360
+        }
361
+        return false;
362
+    }
363
+
364
+    /**
365
+     * check if recovery key is enabled for user
366
+     *
367
+     * @param string $uid
368
+     * @return boolean
369
+     */
370
+    public function recoveryEnabled($uid) {
371
+        $enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
372
+
373
+        return ($enabled === '1') ? true : false;
374
+    }
375
+
376
+    /**
377
+     * set new key storage root
378
+     *
379
+     * @param string $root new key store root relative to the data folder
380
+     */
381
+    public function setKeyStorageRoot($root) {
382
+        $this->config->setAppValue('core', 'encryption_key_storage_root', $root);
383
+    }
384
+
385
+    /**
386
+     * get key storage root
387
+     *
388
+     * @return string key storage root
389
+     */
390
+    public function getKeyStorageRoot() {
391
+        return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
392
+    }
393 393
 
394 394
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -135,12 +135,12 @@  discard block
 block discarded – undo
135 135
 	 * @throws EncryptionHeaderKeyExistsException if header key is already in use
136 136
 	 */
137 137
 	public function createHeader(array $headerData, IEncryptionModule $encryptionModule) {
138
-		$header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':';
138
+		$header = self::HEADER_START.':'.self::HEADER_ENCRYPTION_MODULE_KEY.':'.$encryptionModule->getId().':';
139 139
 		foreach ($headerData as $key => $value) {
140 140
 			if (in_array($key, $this->ocHeaderKeys)) {
141 141
 				throw new EncryptionHeaderKeyExistsException($key);
142 142
 			}
143
-			$header .= $key . ':' . $value . ':';
143
+			$header .= $key.':'.$value.':';
144 144
 		}
145 145
 		$header .= self::HEADER_END;
146 146
 
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
 				if ($c->getType() === 'dir') {
172 172
 					$dirList[] = $c->getPath();
173 173
 				} else {
174
-					$result[] =  $c->getPath();
174
+					$result[] = $c->getPath();
175 175
 				}
176 176
 			}
177 177
 
@@ -248,15 +248,15 @@  discard block
 block discarded – undo
248 248
 	public function stripPartialFileExtension($path) {
249 249
 		$extension = pathinfo($path, PATHINFO_EXTENSION);
250 250
 
251
-		if ( $extension === 'part') {
251
+		if ($extension === 'part') {
252 252
 
253 253
 			$newLength = strlen($path) - 5; // 5 = strlen(".part")
254 254
 			$fPath = substr($path, 0, $newLength);
255 255
 
256 256
 			// if path also contains a transaction id, we remove it too
257 257
 			$extension = pathinfo($fPath, PATHINFO_EXTENSION);
258
-			if(substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
259
-				$newLength = strlen($fPath) - strlen($extension) -1;
258
+			if (substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
259
+				$newLength = strlen($fPath) - strlen($extension) - 1;
260 260
 				$fPath = substr($fPath, 0, $newLength);
261 261
 			}
262 262
 			return $fPath;
@@ -290,7 +290,7 @@  discard block
 block discarded – undo
290 290
 		if (\OCP\App::isEnabled("files_external")) {
291 291
 			$mounts = \OC_Mount_Config::getSystemMountPoints();
292 292
 			foreach ($mounts as $mount) {
293
-				if (strpos($path, '/files/' . $mount['mountpoint']) === 0) {
293
+				if (strpos($path, '/files/'.$mount['mountpoint']) === 0) {
294 294
 					if ($this->isMountPointApplicableToUser($mount, $uid)) {
295 295
 						return true;
296 296
 					}
Please login to merge, or discard this patch.
lib/private/eventsource.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -88,7 +88,7 @@
 block discarded – undo
88 88
 	 * send a message to the client
89 89
 	 *
90 90
 	 * @param string $type
91
-	 * @param mixed $data
91
+	 * @param string $data
92 92
 	 *
93 93
 	 * @throws \BadMethodCallException
94 94
 	 * if only one parameter is given, a typeless message will be send with that parameter as data
Please login to merge, or discard this patch.
Indentation   +88 added lines, -88 removed lines patch added patch discarded remove patch
@@ -33,98 +33,98 @@
 block discarded – undo
33 33
  * use server side events with caution, to many open requests can hang the server
34 34
  */
35 35
 class OC_EventSource implements \OCP\IEventSource {
36
-	/**
37
-	 * @var bool
38
-	 */
39
-	private $fallback;
36
+    /**
37
+     * @var bool
38
+     */
39
+    private $fallback;
40 40
 
41
-	/**
42
-	 * @var int
43
-	 */
44
-	private $fallBackId = 0;
41
+    /**
42
+     * @var int
43
+     */
44
+    private $fallBackId = 0;
45 45
 
46
-	/**
47
-	 * @var bool
48
-	 */
49
-	private $started = false;
46
+    /**
47
+     * @var bool
48
+     */
49
+    private $started = false;
50 50
 
51
-	protected function init() {
52
-		if ($this->started) {
53
-			return;
54
-		}
55
-		$this->started = true;
51
+    protected function init() {
52
+        if ($this->started) {
53
+            return;
54
+        }
55
+        $this->started = true;
56 56
 
57
-		// prevent php output buffering, caching and nginx buffering
58
-		OC_Util::obEnd();
59
-		header('Cache-Control: no-cache');
60
-		header('X-Accel-Buffering: no');
61
-		$this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true';
62
-		if ($this->fallback) {
63
-			$this->fallBackId = (int)$_GET['fallback_id'];
64
-			/**
65
-			 * FIXME: The default content-security-policy of ownCloud forbids inline
66
-			 * JavaScript for security reasons. IE starting on Windows 10 will
67
-			 * however also obey the CSP which will break the event source fallback.
68
-			 *
69
-			 * As a workaround thus we set a custom policy which allows the execution
70
-			 * of inline JavaScript.
71
-			 *
72
-			 * @link https://github.com/owncloud/core/issues/14286
73
-			 */
74
-			header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'");
75
-			header("Content-Type: text/html");
76
-			echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy
77
-		} else {
78
-			header("Content-Type: text/event-stream");
79
-		}
80
-		if(!\OC::$server->getRequest()->passesStrictCookieCheck()) {
81
-			header('Location: '.\OC::$WEBROOT);
82
-			exit();
83
-		}
84
-		if (!(\OC::$server->getRequest()->passesCSRFCheck())) {
85
-			$this->send('error', 'Possible CSRF attack. Connection will be closed.');
86
-			$this->close();
87
-			exit();
88
-		}
89
-		flush();
90
-	}
57
+        // prevent php output buffering, caching and nginx buffering
58
+        OC_Util::obEnd();
59
+        header('Cache-Control: no-cache');
60
+        header('X-Accel-Buffering: no');
61
+        $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true';
62
+        if ($this->fallback) {
63
+            $this->fallBackId = (int)$_GET['fallback_id'];
64
+            /**
65
+             * FIXME: The default content-security-policy of ownCloud forbids inline
66
+             * JavaScript for security reasons. IE starting on Windows 10 will
67
+             * however also obey the CSP which will break the event source fallback.
68
+             *
69
+             * As a workaround thus we set a custom policy which allows the execution
70
+             * of inline JavaScript.
71
+             *
72
+             * @link https://github.com/owncloud/core/issues/14286
73
+             */
74
+            header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'");
75
+            header("Content-Type: text/html");
76
+            echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy
77
+        } else {
78
+            header("Content-Type: text/event-stream");
79
+        }
80
+        if(!\OC::$server->getRequest()->passesStrictCookieCheck()) {
81
+            header('Location: '.\OC::$WEBROOT);
82
+            exit();
83
+        }
84
+        if (!(\OC::$server->getRequest()->passesCSRFCheck())) {
85
+            $this->send('error', 'Possible CSRF attack. Connection will be closed.');
86
+            $this->close();
87
+            exit();
88
+        }
89
+        flush();
90
+    }
91 91
 
92
-	/**
93
-	 * send a message to the client
94
-	 *
95
-	 * @param string $type
96
-	 * @param mixed $data
97
-	 *
98
-	 * @throws \BadMethodCallException
99
-	 * if only one parameter is given, a typeless message will be send with that parameter as data
100
-	 */
101
-	public function send($type, $data = null) {
102
-		if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) {
103
-			throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')');
104
-		}
105
-		$this->init();
106
-		if (is_null($data)) {
107
-			$data = $type;
108
-			$type = null;
109
-		}
110
-		if ($this->fallback) {
111
-			$response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
112
-				. $this->fallBackId . ',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL;
113
-			echo $response;
114
-		} else {
115
-			if ($type) {
116
-				echo 'event: ' . $type . PHP_EOL;
117
-			}
118
-			echo 'data: ' . OCP\JSON::encode($data) . PHP_EOL;
119
-		}
120
-		echo PHP_EOL;
121
-		flush();
122
-	}
92
+    /**
93
+     * send a message to the client
94
+     *
95
+     * @param string $type
96
+     * @param mixed $data
97
+     *
98
+     * @throws \BadMethodCallException
99
+     * if only one parameter is given, a typeless message will be send with that parameter as data
100
+     */
101
+    public function send($type, $data = null) {
102
+        if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) {
103
+            throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')');
104
+        }
105
+        $this->init();
106
+        if (is_null($data)) {
107
+            $data = $type;
108
+            $type = null;
109
+        }
110
+        if ($this->fallback) {
111
+            $response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
112
+                . $this->fallBackId . ',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL;
113
+            echo $response;
114
+        } else {
115
+            if ($type) {
116
+                echo 'event: ' . $type . PHP_EOL;
117
+            }
118
+            echo 'data: ' . OCP\JSON::encode($data) . PHP_EOL;
119
+        }
120
+        echo PHP_EOL;
121
+        flush();
122
+    }
123 123
 
124
-	/**
125
-	 * close the connection of the event source
126
-	 */
127
-	public function close() {
128
-		$this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it
129
-	}
124
+    /**
125
+     * close the connection of the event source
126
+     */
127
+    public function close() {
128
+        $this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it
129
+    }
130 130
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -60,7 +60,7 @@  discard block
 block discarded – undo
60 60
 		header('X-Accel-Buffering: no');
61 61
 		$this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true';
62 62
 		if ($this->fallback) {
63
-			$this->fallBackId = (int)$_GET['fallback_id'];
63
+			$this->fallBackId = (int) $_GET['fallback_id'];
64 64
 			/**
65 65
 			 * FIXME: The default content-security-policy of ownCloud forbids inline
66 66
 			 * JavaScript for security reasons. IE starting on Windows 10 will
@@ -73,11 +73,11 @@  discard block
 block discarded – undo
73 73
 			 */
74 74
 			header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'");
75 75
 			header("Content-Type: text/html");
76
-			echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy
76
+			echo str_repeat('<span></span>'.PHP_EOL, 10); //dummy data to keep IE happy
77 77
 		} else {
78 78
 			header("Content-Type: text/event-stream");
79 79
 		}
80
-		if(!\OC::$server->getRequest()->passesStrictCookieCheck()) {
80
+		if (!\OC::$server->getRequest()->passesStrictCookieCheck()) {
81 81
 			header('Location: '.\OC::$WEBROOT);
82 82
 			exit();
83 83
 		}
@@ -100,7 +100,7 @@  discard block
 block discarded – undo
100 100
 	 */
101 101
 	public function send($type, $data = null) {
102 102
 		if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) {
103
-			throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')');
103
+			throw new BadMethodCallException('Type needs to be alphanumeric ('.$type.')');
104 104
 		}
105 105
 		$this->init();
106 106
 		if (is_null($data)) {
@@ -109,13 +109,13 @@  discard block
 block discarded – undo
109 109
 		}
110 110
 		if ($this->fallback) {
111 111
 			$response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
112
-				. $this->fallBackId . ',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL;
112
+				. $this->fallBackId.',"'.$type.'",'.OCP\JSON::encode($data).')</script>'.PHP_EOL;
113 113
 			echo $response;
114 114
 		} else {
115 115
 			if ($type) {
116
-				echo 'event: ' . $type . PHP_EOL;
116
+				echo 'event: '.$type.PHP_EOL;
117 117
 			}
118
-			echo 'data: ' . OCP\JSON::encode($data) . PHP_EOL;
118
+			echo 'data: '.OCP\JSON::encode($data).PHP_EOL;
119 119
 		}
120 120
 		echo PHP_EOL;
121 121
 		flush();
Please login to merge, or discard this patch.
lib/private/files/cache/scanner.php 3 patches
Doc Comments   +3 added lines patch added patch discarded remove patch
@@ -476,6 +476,9 @@
 block discarded – undo
476 476
 		}
477 477
 	}
478 478
 
479
+	/**
480
+	 * @param string|boolean $path
481
+	 */
479 482
 	private function runBackgroundScanJob(callable $callback, $path) {
480 483
 		try {
481 484
 			$callback();
Please login to merge, or discard this patch.
Indentation   +465 added lines, -465 removed lines patch added patch discarded remove patch
@@ -54,473 +54,473 @@
 block discarded – undo
54 54
  * @package OC\Files\Cache
55 55
  */
56 56
 class Scanner extends BasicEmitter implements IScanner {
57
-	/**
58
-	 * @var \OC\Files\Storage\Storage $storage
59
-	 */
60
-	protected $storage;
61
-
62
-	/**
63
-	 * @var string $storageId
64
-	 */
65
-	protected $storageId;
66
-
67
-	/**
68
-	 * @var \OC\Files\Cache\Cache $cache
69
-	 */
70
-	protected $cache;
71
-
72
-	/**
73
-	 * @var boolean $cacheActive If true, perform cache operations, if false, do not affect cache
74
-	 */
75
-	protected $cacheActive;
76
-
77
-	/**
78
-	 * @var bool $useTransactions whether to use transactions
79
-	 */
80
-	protected $useTransactions = true;
81
-
82
-	/**
83
-	 * @var \OCP\Lock\ILockingProvider
84
-	 */
85
-	protected $lockingProvider;
86
-
87
-	public function __construct(\OC\Files\Storage\Storage $storage) {
88
-		$this->storage = $storage;
89
-		$this->storageId = $this->storage->getId();
90
-		$this->cache = $storage->getCache();
91
-		$this->cacheActive = !Config::getSystemValue('filesystem_cache_readonly', false);
92
-		$this->lockingProvider = \OC::$server->getLockingProvider();
93
-	}
94
-
95
-	/**
96
-	 * Whether to wrap the scanning of a folder in a database transaction
97
-	 * On default transactions are used
98
-	 *
99
-	 * @param bool $useTransactions
100
-	 */
101
-	public function setUseTransactions($useTransactions) {
102
-		$this->useTransactions = $useTransactions;
103
-	}
104
-
105
-	/**
106
-	 * get all the metadata of a file or folder
107
-	 * *
108
-	 *
109
-	 * @param string $path
110
-	 * @return array an array of metadata of the file
111
-	 */
112
-	protected function getData($path) {
113
-		$data = $this->storage->getMetaData($path);
114
-		if (is_null($data)) {
115
-			\OCP\Util::writeLog('OC\Files\Cache\Scanner', "!!! Path '$path' is not accessible or present !!!", \OCP\Util::DEBUG);
116
-		}
117
-		return $data;
118
-	}
119
-
120
-	/**
121
-	 * scan a single file and store it in the cache
122
-	 *
123
-	 * @param string $file
124
-	 * @param int $reuseExisting
125
-	 * @param int $parentId
126
-	 * @param array | null $cacheData existing data in the cache for the file to be scanned
127
-	 * @param bool $lock set to false to disable getting an additional read lock during scanning
128
-	 * @return array an array of metadata of the scanned file
129
-	 * @throws \OC\ServerNotAvailableException
130
-	 * @throws \OCP\Lock\LockedException
131
-	 */
132
-	public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) {
133
-
134
-		// verify database - e.g. mysql only 3-byte chars
135
-		if (preg_match('%(?:
57
+    /**
58
+     * @var \OC\Files\Storage\Storage $storage
59
+     */
60
+    protected $storage;
61
+
62
+    /**
63
+     * @var string $storageId
64
+     */
65
+    protected $storageId;
66
+
67
+    /**
68
+     * @var \OC\Files\Cache\Cache $cache
69
+     */
70
+    protected $cache;
71
+
72
+    /**
73
+     * @var boolean $cacheActive If true, perform cache operations, if false, do not affect cache
74
+     */
75
+    protected $cacheActive;
76
+
77
+    /**
78
+     * @var bool $useTransactions whether to use transactions
79
+     */
80
+    protected $useTransactions = true;
81
+
82
+    /**
83
+     * @var \OCP\Lock\ILockingProvider
84
+     */
85
+    protected $lockingProvider;
86
+
87
+    public function __construct(\OC\Files\Storage\Storage $storage) {
88
+        $this->storage = $storage;
89
+        $this->storageId = $this->storage->getId();
90
+        $this->cache = $storage->getCache();
91
+        $this->cacheActive = !Config::getSystemValue('filesystem_cache_readonly', false);
92
+        $this->lockingProvider = \OC::$server->getLockingProvider();
93
+    }
94
+
95
+    /**
96
+     * Whether to wrap the scanning of a folder in a database transaction
97
+     * On default transactions are used
98
+     *
99
+     * @param bool $useTransactions
100
+     */
101
+    public function setUseTransactions($useTransactions) {
102
+        $this->useTransactions = $useTransactions;
103
+    }
104
+
105
+    /**
106
+     * get all the metadata of a file or folder
107
+     * *
108
+     *
109
+     * @param string $path
110
+     * @return array an array of metadata of the file
111
+     */
112
+    protected function getData($path) {
113
+        $data = $this->storage->getMetaData($path);
114
+        if (is_null($data)) {
115
+            \OCP\Util::writeLog('OC\Files\Cache\Scanner', "!!! Path '$path' is not accessible or present !!!", \OCP\Util::DEBUG);
116
+        }
117
+        return $data;
118
+    }
119
+
120
+    /**
121
+     * scan a single file and store it in the cache
122
+     *
123
+     * @param string $file
124
+     * @param int $reuseExisting
125
+     * @param int $parentId
126
+     * @param array | null $cacheData existing data in the cache for the file to be scanned
127
+     * @param bool $lock set to false to disable getting an additional read lock during scanning
128
+     * @return array an array of metadata of the scanned file
129
+     * @throws \OC\ServerNotAvailableException
130
+     * @throws \OCP\Lock\LockedException
131
+     */
132
+    public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) {
133
+
134
+        // verify database - e.g. mysql only 3-byte chars
135
+        if (preg_match('%(?:
136 136
       \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
137 137
     | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
138 138
     | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
139 139
 )%xs', $file)) {
140
-			// 4-byte characters are not supported in file names
141
-			return null;
142
-		}
143
-
144
-		try {
145
-			$this->storage->verifyPath(dirname($file), basename($file));
146
-		} catch (\Exception $e) {
147
-			return null;
148
-		}
149
-
150
-		// only proceed if $file is not a partial file nor a blacklisted file
151
-		if (!self::isPartialFile($file) and !Filesystem::isFileBlacklisted($file)) {
152
-
153
-			//acquire a lock
154
-			if ($lock) {
155
-				if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
156
-					$this->storage->acquireLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
157
-				}
158
-			}
159
-
160
-			$data = $this->getData($file);
161
-
162
-			if ($data) {
163
-
164
-				// pre-emit only if it was a file. By that we avoid counting/treating folders as files
165
-				if ($data['mimetype'] !== 'httpd/unix-directory') {
166
-					$this->emit('\OC\Files\Cache\Scanner', 'scanFile', array($file, $this->storageId));
167
-					\OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId));
168
-				}
169
-
170
-				$parent = dirname($file);
171
-				if ($parent === '.' or $parent === '/') {
172
-					$parent = '';
173
-				}
174
-				if ($parentId === -1) {
175
-					$parentId = $this->cache->getId($parent);
176
-				}
177
-
178
-				// scan the parent if it's not in the cache (id -1) and the current file is not the root folder
179
-				if ($file and $parentId === -1) {
180
-					$parentData = $this->scanFile($parent);
181
-					if (!$parentData) {
182
-						return null;
183
-					}
184
-					$parentId = $parentData['fileid'];
185
-				}
186
-				if ($parent) {
187
-					$data['parent'] = $parentId;
188
-				}
189
-				if (is_null($cacheData)) {
190
-					/** @var CacheEntry $cacheData */
191
-					$cacheData = $this->cache->get($file);
192
-				}
193
-				if ($cacheData and $reuseExisting and isset($cacheData['fileid'])) {
194
-					// prevent empty etag
195
-					if (empty($cacheData['etag'])) {
196
-						$etag = $data['etag'];
197
-					} else {
198
-						$etag = $cacheData['etag'];
199
-					}
200
-					$fileId = $cacheData['fileid'];
201
-					$data['fileid'] = $fileId;
202
-					// only reuse data if the file hasn't explicitly changed
203
-					if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) {
204
-						$data['mtime'] = $cacheData['mtime'];
205
-						if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) {
206
-							$data['size'] = $cacheData['size'];
207
-						}
208
-						if ($reuseExisting & self::REUSE_ETAG) {
209
-							$data['etag'] = $etag;
210
-						}
211
-					}
212
-					// Only update metadata that has changed
213
-					$newData = array_diff_assoc($data, $cacheData->getData());
214
-				} else {
215
-					$newData = $data;
216
-					$fileId = -1;
217
-				}
218
-				if (!empty($newData)) {
219
-					// Reset the checksum if the data has changed
220
-					$newData['checksum'] = '';
221
-					$data['fileid'] = $this->addToCache($file, $newData, $fileId);
222
-				}
223
-				if (isset($cacheData['size'])) {
224
-					$data['oldSize'] = $cacheData['size'];
225
-				} else {
226
-					$data['oldSize'] = 0;
227
-				}
228
-
229
-				if (isset($cacheData['encrypted'])) {
230
-					$data['encrypted'] = $cacheData['encrypted'];
231
-				}
232
-
233
-				// post-emit only if it was a file. By that we avoid counting/treating folders as files
234
-				if ($data['mimetype'] !== 'httpd/unix-directory') {
235
-					$this->emit('\OC\Files\Cache\Scanner', 'postScanFile', array($file, $this->storageId));
236
-					\OC_Hook::emit('\OC\Files\Cache\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId));
237
-				}
238
-
239
-			} else {
240
-				$this->removeFromCache($file);
241
-			}
242
-
243
-			//release the acquired lock
244
-			if ($lock) {
245
-				if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
246
-					$this->storage->releaseLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
247
-				}
248
-			}
249
-
250
-			if ($data && !isset($data['encrypted'])) {
251
-				$data['encrypted'] = false;
252
-			}
253
-			return $data;
254
-		}
255
-
256
-		return null;
257
-	}
258
-
259
-	protected function removeFromCache($path) {
260
-		\OC_Hook::emit('Scanner', 'removeFromCache', array('file' => $path));
261
-		$this->emit('\OC\Files\Cache\Scanner', 'removeFromCache', array($path));
262
-		if ($this->cacheActive) {
263
-			$this->cache->remove($path);
264
-		}
265
-	}
266
-
267
-	/**
268
-	 * @param string $path
269
-	 * @param array $data
270
-	 * @param int $fileId
271
-	 * @return int the id of the added file
272
-	 */
273
-	protected function addToCache($path, $data, $fileId = -1) {
274
-		\OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data));
275
-		$this->emit('\OC\Files\Cache\Scanner', 'addToCache', array($path, $this->storageId, $data));
276
-		if ($this->cacheActive) {
277
-			if ($fileId !== -1) {
278
-				$this->cache->update($fileId, $data);
279
-				return $fileId;
280
-			} else {
281
-				return $this->cache->put($path, $data);
282
-			}
283
-		} else {
284
-			return -1;
285
-		}
286
-	}
287
-
288
-	/**
289
-	 * @param string $path
290
-	 * @param array $data
291
-	 * @param int $fileId
292
-	 */
293
-	protected function updateCache($path, $data, $fileId = -1) {
294
-		\OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data));
295
-		$this->emit('\OC\Files\Cache\Scanner', 'updateCache', array($path, $this->storageId, $data));
296
-		if ($this->cacheActive) {
297
-			if ($fileId !== -1) {
298
-				$this->cache->update($fileId, $data);
299
-			} else {
300
-				$this->cache->put($path, $data);
301
-			}
302
-		}
303
-	}
304
-
305
-	/**
306
-	 * scan a folder and all it's children
307
-	 *
308
-	 * @param string $path
309
-	 * @param bool $recursive
310
-	 * @param int $reuse
311
-	 * @param bool $lock set to false to disable getting an additional read lock during scanning
312
-	 * @return array an array of the meta data of the scanned file or folder
313
-	 */
314
-	public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) {
315
-		if ($reuse === -1) {
316
-			$reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG;
317
-		}
318
-		if ($lock) {
319
-			if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
320
-				$this->storage->acquireLock('scanner::' . $path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider);
321
-				$this->storage->acquireLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
322
-			}
323
-		}
324
-		$data = $this->scanFile($path, $reuse, -1, null, $lock);
325
-		if ($data and $data['mimetype'] === 'httpd/unix-directory') {
326
-			$size = $this->scanChildren($path, $recursive, $reuse, $data, $lock);
327
-			$data['size'] = $size;
328
-		}
329
-		if ($lock) {
330
-			if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
331
-				$this->storage->releaseLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
332
-				$this->storage->releaseLock('scanner::' . $path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider);
333
-			}
334
-		}
335
-		return $data;
336
-	}
337
-
338
-	/**
339
-	 * Get the children currently in the cache
340
-	 *
341
-	 * @param int $folderId
342
-	 * @return array[]
343
-	 */
344
-	protected function getExistingChildren($folderId) {
345
-		$existingChildren = array();
346
-		$children = $this->cache->getFolderContentsById($folderId);
347
-		foreach ($children as $child) {
348
-			$existingChildren[$child['name']] = $child;
349
-		}
350
-		return $existingChildren;
351
-	}
352
-
353
-	/**
354
-	 * Get the children from the storage
355
-	 *
356
-	 * @param string $folder
357
-	 * @return string[]
358
-	 */
359
-	protected function getNewChildren($folder) {
360
-		$children = array();
361
-		if ($dh = $this->storage->opendir($folder)) {
362
-			if (is_resource($dh)) {
363
-				while (($file = readdir($dh)) !== false) {
364
-					if (!Filesystem::isIgnoredDir($file)) {
365
-						$children[] = $file;
366
-					}
367
-				}
368
-			}
369
-		}
370
-		return $children;
371
-	}
372
-
373
-	/**
374
-	 * scan all the files and folders in a folder
375
-	 *
376
-	 * @param string $path
377
-	 * @param bool $recursive
378
-	 * @param int $reuse
379
-	 * @param array $folderData existing cache data for the folder to be scanned
380
-	 * @param bool $lock set to false to disable getting an additional read lock during scanning
381
-	 * @return int the size of the scanned folder or -1 if the size is unknown at this stage
382
-	 */
383
-	protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderData = null, $lock = true) {
384
-		if ($reuse === -1) {
385
-			$reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG;
386
-		}
387
-		$this->emit('\OC\Files\Cache\Scanner', 'scanFolder', array($path, $this->storageId));
388
-		$size = 0;
389
-		$childQueue = array();
390
-		if (is_array($folderData) and isset($folderData['fileid'])) {
391
-			$folderId = $folderData['fileid'];
392
-		} else {
393
-			$folderId = $this->cache->getId($path);
394
-		}
395
-		$existingChildren = $this->getExistingChildren($folderId);
396
-		$newChildren = $this->getNewChildren($path);
397
-
398
-		if ($this->useTransactions) {
399
-			\OC::$server->getDatabaseConnection()->beginTransaction();
400
-		}
401
-		$exceptionOccurred = false;
402
-		foreach ($newChildren as $file) {
403
-			$child = ($path) ? $path . '/' . $file : $file;
404
-			try {
405
-				$existingData = isset($existingChildren[$file]) ? $existingChildren[$file] : null;
406
-				$data = $this->scanFile($child, $reuse, $folderId, $existingData, $lock);
407
-				if ($data) {
408
-					if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE) {
409
-						$childQueue[$child] = $data;
410
-					} else if ($data['size'] === -1) {
411
-						$size = -1;
412
-					} else if ($size !== -1) {
413
-						$size += $data['size'];
414
-					}
415
-				}
416
-			} catch (\Doctrine\DBAL\DBALException $ex) {
417
-				// might happen if inserting duplicate while a scanning
418
-				// process is running in parallel
419
-				// log and ignore
420
-				\OCP\Util::writeLog('core', 'Exception while scanning file "' . $child . '": ' . $ex->getMessage(), \OCP\Util::DEBUG);
421
-				$exceptionOccurred = true;
422
-			} catch (\OCP\Lock\LockedException $e) {
423
-				if ($this->useTransactions) {
424
-					\OC::$server->getDatabaseConnection()->rollback();
425
-				}
426
-				throw $e;
427
-			}
428
-		}
429
-		$removedChildren = \array_diff(array_keys($existingChildren), $newChildren);
430
-		foreach ($removedChildren as $childName) {
431
-			$child = ($path) ? $path . '/' . $childName : $childName;
432
-			$this->removeFromCache($child);
433
-		}
434
-		if ($this->useTransactions) {
435
-			\OC::$server->getDatabaseConnection()->commit();
436
-		}
437
-		if ($exceptionOccurred) {
438
-			// It might happen that the parallel scan process has already
439
-			// inserted mimetypes but those weren't available yet inside the transaction
440
-			// To make sure to have the updated mime types in such cases,
441
-			// we reload them here
442
-			\OC::$server->getMimeTypeLoader()->reset();
443
-		}
444
-
445
-		foreach ($childQueue as $child => $childData) {
446
-			$childSize = $this->scanChildren($child, self::SCAN_RECURSIVE, $reuse, $childData, $lock);
447
-			if ($childSize === -1) {
448
-				$size = -1;
449
-			} else if ($size !== -1) {
450
-				$size += $childSize;
451
-			}
452
-		}
453
-		if (!is_array($folderData) or !isset($folderData['size']) or $folderData['size'] !== $size) {
454
-			$this->updateCache($path, array('size' => $size), $folderId);
455
-		}
456
-		$this->emit('\OC\Files\Cache\Scanner', 'postScanFolder', array($path, $this->storageId));
457
-		return $size;
458
-	}
459
-
460
-	/**
461
-	 * check if the file should be ignored when scanning
462
-	 * NOTE: files with a '.part' extension are ignored as well!
463
-	 *       prevents unfinished put requests to be scanned
464
-	 *
465
-	 * @param string $file
466
-	 * @return boolean
467
-	 */
468
-	public static function isPartialFile($file) {
469
-		if (pathinfo($file, PATHINFO_EXTENSION) === 'part') {
470
-			return true;
471
-		}
472
-		if (strpos($file, '.part/') !== false) {
473
-			return true;
474
-		}
475
-
476
-		return false;
477
-	}
478
-
479
-	/**
480
-	 * walk over any folders that are not fully scanned yet and scan them
481
-	 */
482
-	public function backgroundScan() {
483
-		if (!$this->cache->inCache('')) {
484
-			$this->runBackgroundScanJob(function () {
485
-				$this->scan('', self::SCAN_RECURSIVE, self::REUSE_ETAG);
486
-			}, '');
487
-		} else {
488
-			$lastPath = null;
489
-			while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) {
490
-				$this->runBackgroundScanJob(function() use ($path) {
491
-					$this->scan($path, self::SCAN_RECURSIVE, self::REUSE_ETAG);
492
-				}, $path);
493
-				// FIXME: this won't proceed with the next item, needs revamping of getIncomplete()
494
-				// to make this possible
495
-				$lastPath = $path;
496
-			}
497
-		}
498
-	}
499
-
500
-	private function runBackgroundScanJob(callable $callback, $path) {
501
-		try {
502
-			$callback();
503
-			\OC_Hook::emit('Scanner', 'correctFolderSize', array('path' => $path));
504
-			if ($this->cacheActive && $this->cache instanceof Cache) {
505
-				$this->cache->correctFolderSize($path);
506
-			}
507
-		} catch (\OCP\Files\StorageInvalidException $e) {
508
-			// skip unavailable storages
509
-		} catch (\OCP\Files\StorageNotAvailableException $e) {
510
-			// skip unavailable storages
511
-		} catch (\OCP\Files\ForbiddenException $e) {
512
-			// skip forbidden storages
513
-		} catch (\OCP\Lock\LockedException $e) {
514
-			// skip unavailable storages
515
-		}
516
-	}
517
-
518
-	/**
519
-	 * Set whether the cache is affected by scan operations
520
-	 *
521
-	 * @param boolean $active The active state of the cache
522
-	 */
523
-	public function setCacheActive($active) {
524
-		$this->cacheActive = $active;
525
-	}
140
+            // 4-byte characters are not supported in file names
141
+            return null;
142
+        }
143
+
144
+        try {
145
+            $this->storage->verifyPath(dirname($file), basename($file));
146
+        } catch (\Exception $e) {
147
+            return null;
148
+        }
149
+
150
+        // only proceed if $file is not a partial file nor a blacklisted file
151
+        if (!self::isPartialFile($file) and !Filesystem::isFileBlacklisted($file)) {
152
+
153
+            //acquire a lock
154
+            if ($lock) {
155
+                if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
156
+                    $this->storage->acquireLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
157
+                }
158
+            }
159
+
160
+            $data = $this->getData($file);
161
+
162
+            if ($data) {
163
+
164
+                // pre-emit only if it was a file. By that we avoid counting/treating folders as files
165
+                if ($data['mimetype'] !== 'httpd/unix-directory') {
166
+                    $this->emit('\OC\Files\Cache\Scanner', 'scanFile', array($file, $this->storageId));
167
+                    \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId));
168
+                }
169
+
170
+                $parent = dirname($file);
171
+                if ($parent === '.' or $parent === '/') {
172
+                    $parent = '';
173
+                }
174
+                if ($parentId === -1) {
175
+                    $parentId = $this->cache->getId($parent);
176
+                }
177
+
178
+                // scan the parent if it's not in the cache (id -1) and the current file is not the root folder
179
+                if ($file and $parentId === -1) {
180
+                    $parentData = $this->scanFile($parent);
181
+                    if (!$parentData) {
182
+                        return null;
183
+                    }
184
+                    $parentId = $parentData['fileid'];
185
+                }
186
+                if ($parent) {
187
+                    $data['parent'] = $parentId;
188
+                }
189
+                if (is_null($cacheData)) {
190
+                    /** @var CacheEntry $cacheData */
191
+                    $cacheData = $this->cache->get($file);
192
+                }
193
+                if ($cacheData and $reuseExisting and isset($cacheData['fileid'])) {
194
+                    // prevent empty etag
195
+                    if (empty($cacheData['etag'])) {
196
+                        $etag = $data['etag'];
197
+                    } else {
198
+                        $etag = $cacheData['etag'];
199
+                    }
200
+                    $fileId = $cacheData['fileid'];
201
+                    $data['fileid'] = $fileId;
202
+                    // only reuse data if the file hasn't explicitly changed
203
+                    if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) {
204
+                        $data['mtime'] = $cacheData['mtime'];
205
+                        if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) {
206
+                            $data['size'] = $cacheData['size'];
207
+                        }
208
+                        if ($reuseExisting & self::REUSE_ETAG) {
209
+                            $data['etag'] = $etag;
210
+                        }
211
+                    }
212
+                    // Only update metadata that has changed
213
+                    $newData = array_diff_assoc($data, $cacheData->getData());
214
+                } else {
215
+                    $newData = $data;
216
+                    $fileId = -1;
217
+                }
218
+                if (!empty($newData)) {
219
+                    // Reset the checksum if the data has changed
220
+                    $newData['checksum'] = '';
221
+                    $data['fileid'] = $this->addToCache($file, $newData, $fileId);
222
+                }
223
+                if (isset($cacheData['size'])) {
224
+                    $data['oldSize'] = $cacheData['size'];
225
+                } else {
226
+                    $data['oldSize'] = 0;
227
+                }
228
+
229
+                if (isset($cacheData['encrypted'])) {
230
+                    $data['encrypted'] = $cacheData['encrypted'];
231
+                }
232
+
233
+                // post-emit only if it was a file. By that we avoid counting/treating folders as files
234
+                if ($data['mimetype'] !== 'httpd/unix-directory') {
235
+                    $this->emit('\OC\Files\Cache\Scanner', 'postScanFile', array($file, $this->storageId));
236
+                    \OC_Hook::emit('\OC\Files\Cache\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId));
237
+                }
238
+
239
+            } else {
240
+                $this->removeFromCache($file);
241
+            }
242
+
243
+            //release the acquired lock
244
+            if ($lock) {
245
+                if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
246
+                    $this->storage->releaseLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
247
+                }
248
+            }
249
+
250
+            if ($data && !isset($data['encrypted'])) {
251
+                $data['encrypted'] = false;
252
+            }
253
+            return $data;
254
+        }
255
+
256
+        return null;
257
+    }
258
+
259
+    protected function removeFromCache($path) {
260
+        \OC_Hook::emit('Scanner', 'removeFromCache', array('file' => $path));
261
+        $this->emit('\OC\Files\Cache\Scanner', 'removeFromCache', array($path));
262
+        if ($this->cacheActive) {
263
+            $this->cache->remove($path);
264
+        }
265
+    }
266
+
267
+    /**
268
+     * @param string $path
269
+     * @param array $data
270
+     * @param int $fileId
271
+     * @return int the id of the added file
272
+     */
273
+    protected function addToCache($path, $data, $fileId = -1) {
274
+        \OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data));
275
+        $this->emit('\OC\Files\Cache\Scanner', 'addToCache', array($path, $this->storageId, $data));
276
+        if ($this->cacheActive) {
277
+            if ($fileId !== -1) {
278
+                $this->cache->update($fileId, $data);
279
+                return $fileId;
280
+            } else {
281
+                return $this->cache->put($path, $data);
282
+            }
283
+        } else {
284
+            return -1;
285
+        }
286
+    }
287
+
288
+    /**
289
+     * @param string $path
290
+     * @param array $data
291
+     * @param int $fileId
292
+     */
293
+    protected function updateCache($path, $data, $fileId = -1) {
294
+        \OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data));
295
+        $this->emit('\OC\Files\Cache\Scanner', 'updateCache', array($path, $this->storageId, $data));
296
+        if ($this->cacheActive) {
297
+            if ($fileId !== -1) {
298
+                $this->cache->update($fileId, $data);
299
+            } else {
300
+                $this->cache->put($path, $data);
301
+            }
302
+        }
303
+    }
304
+
305
+    /**
306
+     * scan a folder and all it's children
307
+     *
308
+     * @param string $path
309
+     * @param bool $recursive
310
+     * @param int $reuse
311
+     * @param bool $lock set to false to disable getting an additional read lock during scanning
312
+     * @return array an array of the meta data of the scanned file or folder
313
+     */
314
+    public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) {
315
+        if ($reuse === -1) {
316
+            $reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG;
317
+        }
318
+        if ($lock) {
319
+            if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
320
+                $this->storage->acquireLock('scanner::' . $path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider);
321
+                $this->storage->acquireLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
322
+            }
323
+        }
324
+        $data = $this->scanFile($path, $reuse, -1, null, $lock);
325
+        if ($data and $data['mimetype'] === 'httpd/unix-directory') {
326
+            $size = $this->scanChildren($path, $recursive, $reuse, $data, $lock);
327
+            $data['size'] = $size;
328
+        }
329
+        if ($lock) {
330
+            if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
331
+                $this->storage->releaseLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
332
+                $this->storage->releaseLock('scanner::' . $path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider);
333
+            }
334
+        }
335
+        return $data;
336
+    }
337
+
338
+    /**
339
+     * Get the children currently in the cache
340
+     *
341
+     * @param int $folderId
342
+     * @return array[]
343
+     */
344
+    protected function getExistingChildren($folderId) {
345
+        $existingChildren = array();
346
+        $children = $this->cache->getFolderContentsById($folderId);
347
+        foreach ($children as $child) {
348
+            $existingChildren[$child['name']] = $child;
349
+        }
350
+        return $existingChildren;
351
+    }
352
+
353
+    /**
354
+     * Get the children from the storage
355
+     *
356
+     * @param string $folder
357
+     * @return string[]
358
+     */
359
+    protected function getNewChildren($folder) {
360
+        $children = array();
361
+        if ($dh = $this->storage->opendir($folder)) {
362
+            if (is_resource($dh)) {
363
+                while (($file = readdir($dh)) !== false) {
364
+                    if (!Filesystem::isIgnoredDir($file)) {
365
+                        $children[] = $file;
366
+                    }
367
+                }
368
+            }
369
+        }
370
+        return $children;
371
+    }
372
+
373
+    /**
374
+     * scan all the files and folders in a folder
375
+     *
376
+     * @param string $path
377
+     * @param bool $recursive
378
+     * @param int $reuse
379
+     * @param array $folderData existing cache data for the folder to be scanned
380
+     * @param bool $lock set to false to disable getting an additional read lock during scanning
381
+     * @return int the size of the scanned folder or -1 if the size is unknown at this stage
382
+     */
383
+    protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderData = null, $lock = true) {
384
+        if ($reuse === -1) {
385
+            $reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG;
386
+        }
387
+        $this->emit('\OC\Files\Cache\Scanner', 'scanFolder', array($path, $this->storageId));
388
+        $size = 0;
389
+        $childQueue = array();
390
+        if (is_array($folderData) and isset($folderData['fileid'])) {
391
+            $folderId = $folderData['fileid'];
392
+        } else {
393
+            $folderId = $this->cache->getId($path);
394
+        }
395
+        $existingChildren = $this->getExistingChildren($folderId);
396
+        $newChildren = $this->getNewChildren($path);
397
+
398
+        if ($this->useTransactions) {
399
+            \OC::$server->getDatabaseConnection()->beginTransaction();
400
+        }
401
+        $exceptionOccurred = false;
402
+        foreach ($newChildren as $file) {
403
+            $child = ($path) ? $path . '/' . $file : $file;
404
+            try {
405
+                $existingData = isset($existingChildren[$file]) ? $existingChildren[$file] : null;
406
+                $data = $this->scanFile($child, $reuse, $folderId, $existingData, $lock);
407
+                if ($data) {
408
+                    if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE) {
409
+                        $childQueue[$child] = $data;
410
+                    } else if ($data['size'] === -1) {
411
+                        $size = -1;
412
+                    } else if ($size !== -1) {
413
+                        $size += $data['size'];
414
+                    }
415
+                }
416
+            } catch (\Doctrine\DBAL\DBALException $ex) {
417
+                // might happen if inserting duplicate while a scanning
418
+                // process is running in parallel
419
+                // log and ignore
420
+                \OCP\Util::writeLog('core', 'Exception while scanning file "' . $child . '": ' . $ex->getMessage(), \OCP\Util::DEBUG);
421
+                $exceptionOccurred = true;
422
+            } catch (\OCP\Lock\LockedException $e) {
423
+                if ($this->useTransactions) {
424
+                    \OC::$server->getDatabaseConnection()->rollback();
425
+                }
426
+                throw $e;
427
+            }
428
+        }
429
+        $removedChildren = \array_diff(array_keys($existingChildren), $newChildren);
430
+        foreach ($removedChildren as $childName) {
431
+            $child = ($path) ? $path . '/' . $childName : $childName;
432
+            $this->removeFromCache($child);
433
+        }
434
+        if ($this->useTransactions) {
435
+            \OC::$server->getDatabaseConnection()->commit();
436
+        }
437
+        if ($exceptionOccurred) {
438
+            // It might happen that the parallel scan process has already
439
+            // inserted mimetypes but those weren't available yet inside the transaction
440
+            // To make sure to have the updated mime types in such cases,
441
+            // we reload them here
442
+            \OC::$server->getMimeTypeLoader()->reset();
443
+        }
444
+
445
+        foreach ($childQueue as $child => $childData) {
446
+            $childSize = $this->scanChildren($child, self::SCAN_RECURSIVE, $reuse, $childData, $lock);
447
+            if ($childSize === -1) {
448
+                $size = -1;
449
+            } else if ($size !== -1) {
450
+                $size += $childSize;
451
+            }
452
+        }
453
+        if (!is_array($folderData) or !isset($folderData['size']) or $folderData['size'] !== $size) {
454
+            $this->updateCache($path, array('size' => $size), $folderId);
455
+        }
456
+        $this->emit('\OC\Files\Cache\Scanner', 'postScanFolder', array($path, $this->storageId));
457
+        return $size;
458
+    }
459
+
460
+    /**
461
+     * check if the file should be ignored when scanning
462
+     * NOTE: files with a '.part' extension are ignored as well!
463
+     *       prevents unfinished put requests to be scanned
464
+     *
465
+     * @param string $file
466
+     * @return boolean
467
+     */
468
+    public static function isPartialFile($file) {
469
+        if (pathinfo($file, PATHINFO_EXTENSION) === 'part') {
470
+            return true;
471
+        }
472
+        if (strpos($file, '.part/') !== false) {
473
+            return true;
474
+        }
475
+
476
+        return false;
477
+    }
478
+
479
+    /**
480
+     * walk over any folders that are not fully scanned yet and scan them
481
+     */
482
+    public function backgroundScan() {
483
+        if (!$this->cache->inCache('')) {
484
+            $this->runBackgroundScanJob(function () {
485
+                $this->scan('', self::SCAN_RECURSIVE, self::REUSE_ETAG);
486
+            }, '');
487
+        } else {
488
+            $lastPath = null;
489
+            while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) {
490
+                $this->runBackgroundScanJob(function() use ($path) {
491
+                    $this->scan($path, self::SCAN_RECURSIVE, self::REUSE_ETAG);
492
+                }, $path);
493
+                // FIXME: this won't proceed with the next item, needs revamping of getIncomplete()
494
+                // to make this possible
495
+                $lastPath = $path;
496
+            }
497
+        }
498
+    }
499
+
500
+    private function runBackgroundScanJob(callable $callback, $path) {
501
+        try {
502
+            $callback();
503
+            \OC_Hook::emit('Scanner', 'correctFolderSize', array('path' => $path));
504
+            if ($this->cacheActive && $this->cache instanceof Cache) {
505
+                $this->cache->correctFolderSize($path);
506
+            }
507
+        } catch (\OCP\Files\StorageInvalidException $e) {
508
+            // skip unavailable storages
509
+        } catch (\OCP\Files\StorageNotAvailableException $e) {
510
+            // skip unavailable storages
511
+        } catch (\OCP\Files\ForbiddenException $e) {
512
+            // skip forbidden storages
513
+        } catch (\OCP\Lock\LockedException $e) {
514
+            // skip unavailable storages
515
+        }
516
+    }
517
+
518
+    /**
519
+     * Set whether the cache is affected by scan operations
520
+     *
521
+     * @param boolean $active The active state of the cache
522
+     */
523
+    public function setCacheActive($active) {
524
+        $this->cacheActive = $active;
525
+    }
526 526
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -317,7 +317,7 @@  discard block
 block discarded – undo
317 317
 		}
318 318
 		if ($lock) {
319 319
 			if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
320
-				$this->storage->acquireLock('scanner::' . $path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider);
320
+				$this->storage->acquireLock('scanner::'.$path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider);
321 321
 				$this->storage->acquireLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
322 322
 			}
323 323
 		}
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
 		if ($lock) {
330 330
 			if ($this->storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
331 331
 				$this->storage->releaseLock($path, ILockingProvider::LOCK_SHARED, $this->lockingProvider);
332
-				$this->storage->releaseLock('scanner::' . $path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider);
332
+				$this->storage->releaseLock('scanner::'.$path, ILockingProvider::LOCK_EXCLUSIVE, $this->lockingProvider);
333 333
 			}
334 334
 		}
335 335
 		return $data;
@@ -400,7 +400,7 @@  discard block
 block discarded – undo
400 400
 		}
401 401
 		$exceptionOccurred = false;
402 402
 		foreach ($newChildren as $file) {
403
-			$child = ($path) ? $path . '/' . $file : $file;
403
+			$child = ($path) ? $path.'/'.$file : $file;
404 404
 			try {
405 405
 				$existingData = isset($existingChildren[$file]) ? $existingChildren[$file] : null;
406 406
 				$data = $this->scanFile($child, $reuse, $folderId, $existingData, $lock);
@@ -417,7 +417,7 @@  discard block
 block discarded – undo
417 417
 				// might happen if inserting duplicate while a scanning
418 418
 				// process is running in parallel
419 419
 				// log and ignore
420
-				\OCP\Util::writeLog('core', 'Exception while scanning file "' . $child . '": ' . $ex->getMessage(), \OCP\Util::DEBUG);
420
+				\OCP\Util::writeLog('core', 'Exception while scanning file "'.$child.'": '.$ex->getMessage(), \OCP\Util::DEBUG);
421 421
 				$exceptionOccurred = true;
422 422
 			} catch (\OCP\Lock\LockedException $e) {
423 423
 				if ($this->useTransactions) {
@@ -428,7 +428,7 @@  discard block
 block discarded – undo
428 428
 		}
429 429
 		$removedChildren = \array_diff(array_keys($existingChildren), $newChildren);
430 430
 		foreach ($removedChildren as $childName) {
431
-			$child = ($path) ? $path . '/' . $childName : $childName;
431
+			$child = ($path) ? $path.'/'.$childName : $childName;
432 432
 			$this->removeFromCache($child);
433 433
 		}
434 434
 		if ($this->useTransactions) {
@@ -481,7 +481,7 @@  discard block
 block discarded – undo
481 481
 	 */
482 482
 	public function backgroundScan() {
483 483
 		if (!$this->cache->inCache('')) {
484
-			$this->runBackgroundScanJob(function () {
484
+			$this->runBackgroundScanJob(function() {
485 485
 				$this->scan('', self::SCAN_RECURSIVE, self::REUSE_ETAG);
486 486
 			}, '');
487 487
 		} else {
Please login to merge, or discard this patch.
lib/private/files/config/usermountcache.php 4 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -206,7 +206,7 @@
 block discarded – undo
206 206
 	}
207 207
 
208 208
 	/**
209
-	 * @param $fileId
209
+	 * @param integer $fileId
210 210
 	 * @return array
211 211
 	 * @throws \OCP\Files\NotFoundException
212 212
 	 */
Please login to merge, or discard this patch.
Unused Use Statements   -2 removed lines patch added patch discarded remove patch
@@ -22,8 +22,6 @@
 block discarded – undo
22 22
 
23 23
 namespace OC\Files\Config;
24 24
 
25
-use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
26
-use OC\Files\Filesystem;
27 25
 use OCA\Files_Sharing\SharedMount;
28 26
 use OCP\DB\QueryBuilder\IQueryBuilder;
29 27
 use OCP\Files\Config\ICachedMountInfo;
Please login to merge, or discard this patch.
Indentation   +264 added lines, -264 removed lines patch added patch discarded remove patch
@@ -43,268 +43,268 @@
 block discarded – undo
43 43
  * Cache mounts points per user in the cache so we can easilly look them up
44 44
  */
45 45
 class UserMountCache implements IUserMountCache {
46
-	/**
47
-	 * @var IDBConnection
48
-	 */
49
-	private $connection;
50
-
51
-	/**
52
-	 * @var IUserManager
53
-	 */
54
-	private $userManager;
55
-
56
-	/**
57
-	 * Cached mount info.
58
-	 * Map of $userId to ICachedMountInfo.
59
-	 *
60
-	 * @var ICache
61
-	 **/
62
-	private $mountsForUsers;
63
-
64
-	/**
65
-	 * @var ILogger
66
-	 */
67
-	private $logger;
68
-
69
-	/**
70
-	 * @var ICache
71
-	 */
72
-	private $cacheInfoCache;
73
-
74
-	/**
75
-	 * UserMountCache constructor.
76
-	 *
77
-	 * @param IDBConnection $connection
78
-	 * @param IUserManager $userManager
79
-	 * @param ILogger $logger
80
-	 */
81
-	public function __construct(IDBConnection $connection, IUserManager $userManager, ILogger $logger) {
82
-		$this->connection = $connection;
83
-		$this->userManager = $userManager;
84
-		$this->logger = $logger;
85
-		$this->cacheInfoCache = new CappedMemoryCache();
86
-		$this->mountsForUsers = new CappedMemoryCache();
87
-	}
88
-
89
-	public function registerMounts(IUser $user, array $mounts) {
90
-		// filter out non-proper storages coming from unit tests
91
-		$mounts = array_filter($mounts, function (IMountPoint $mount) {
92
-			return $mount instanceof SharedMount || $mount->getStorage() && $mount->getStorage()->getCache();
93
-		});
94
-		/** @var ICachedMountInfo[] $newMounts */
95
-		$newMounts = array_map(function (IMountPoint $mount) use ($user) {
96
-			$storage = $mount->getStorage();
97
-			if ($storage->instanceOfStorage('\OC\Files\Storage\Shared')) {
98
-				$rootId = (int)$storage->getShare()['file_source'];
99
-			} else {
100
-				$rootId = (int)$storage->getCache()->getId('');
101
-			}
102
-			$storageId = (int)$storage->getStorageCache()->getNumericId();
103
-			// filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
104
-			if ($rootId === -1) {
105
-				return null;
106
-			} else {
107
-				return new CachedMountInfo($user, $storageId, $rootId, $mount->getMountPoint());
108
-			}
109
-		}, $mounts);
110
-		$newMounts = array_values(array_filter($newMounts));
111
-
112
-		$cachedMounts = $this->getMountsForUser($user);
113
-		$mountDiff = function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
114
-			// since we are only looking for mounts for a specific user comparing on root id is enough
115
-			return $mount1->getRootId() - $mount2->getRootId();
116
-		};
117
-
118
-		/** @var ICachedMountInfo[] $addedMounts */
119
-		$addedMounts = array_udiff($newMounts, $cachedMounts, $mountDiff);
120
-		/** @var ICachedMountInfo[] $removedMounts */
121
-		$removedMounts = array_udiff($cachedMounts, $newMounts, $mountDiff);
122
-
123
-		$changedMounts = array_uintersect($newMounts, $cachedMounts, function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
124
-			// filter mounts with the same root id and different mountpoints
125
-			if ($mount1->getRootId() !== $mount2->getRootId()) {
126
-				return -1;
127
-			}
128
-			return ($mount1->getMountPoint() !== $mount2->getMountPoint()) ? 0 : 1;
129
-		});
130
-
131
-		foreach ($addedMounts as $mount) {
132
-			$this->addToCache($mount);
133
-			$this->mountsForUsers[$user->getUID()][] = $mount;
134
-		}
135
-		foreach ($removedMounts as $mount) {
136
-			$this->removeFromCache($mount);
137
-			$index = array_search($mount, $this->mountsForUsers[$user->getUID()]);
138
-			unset($this->mountsForUsers[$user->getUID()][$index]);
139
-		}
140
-		foreach ($changedMounts as $mount) {
141
-			$this->setMountPoint($mount);
142
-		}
143
-	}
144
-
145
-	private function addToCache(ICachedMountInfo $mount) {
146
-		$this->connection->insertIfNotExist('*PREFIX*mounts', [
147
-			'storage_id' => $mount->getStorageId(),
148
-			'root_id' => $mount->getRootId(),
149
-			'user_id' => $mount->getUser()->getUID(),
150
-			'mount_point' => $mount->getMountPoint()
151
-		], ['root_id', 'user_id']);
152
-	}
153
-
154
-	private function setMountPoint(ICachedMountInfo $mount) {
155
-		$builder = $this->connection->getQueryBuilder();
156
-
157
-		$query = $builder->update('mounts')
158
-			->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
159
-			->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
160
-			->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)));
161
-
162
-		$query->execute();
163
-	}
164
-
165
-	private function removeFromCache(ICachedMountInfo $mount) {
166
-		$builder = $this->connection->getQueryBuilder();
167
-
168
-		$query = $builder->delete('mounts')
169
-			->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
170
-			->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)));
171
-		$query->execute();
172
-	}
173
-
174
-	private function dbRowToMountInfo(array $row) {
175
-		$user = $this->userManager->get($row['user_id']);
176
-		return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point']);
177
-	}
178
-
179
-	/**
180
-	 * @param IUser $user
181
-	 * @return ICachedMountInfo[]
182
-	 */
183
-	public function getMountsForUser(IUser $user) {
184
-		if (!isset($this->mountsForUsers[$user->getUID()])) {
185
-			$builder = $this->connection->getQueryBuilder();
186
-			$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
187
-				->from('mounts')
188
-				->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID())));
189
-
190
-			$rows = $query->execute()->fetchAll();
191
-
192
-			$this->mountsForUsers[$user->getUID()] = array_map([$this, 'dbRowToMountInfo'], $rows);
193
-		}
194
-		return $this->mountsForUsers[$user->getUID()];
195
-	}
196
-
197
-	/**
198
-	 * @param int $numericStorageId
199
-	 * @return CachedMountInfo[]
200
-	 */
201
-	public function getMountsForStorageId($numericStorageId) {
202
-		$builder = $this->connection->getQueryBuilder();
203
-		$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
204
-			->from('mounts')
205
-			->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, IQueryBuilder::PARAM_INT)));
206
-
207
-		$rows = $query->execute()->fetchAll();
208
-
209
-		return array_map([$this, 'dbRowToMountInfo'], $rows);
210
-	}
211
-
212
-	/**
213
-	 * @param int $rootFileId
214
-	 * @return CachedMountInfo[]
215
-	 */
216
-	public function getMountsForRootId($rootFileId) {
217
-		$builder = $this->connection->getQueryBuilder();
218
-		$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
219
-			->from('mounts')
220
-			->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, IQueryBuilder::PARAM_INT)));
221
-
222
-		$rows = $query->execute()->fetchAll();
223
-
224
-		return array_map([$this, 'dbRowToMountInfo'], $rows);
225
-	}
226
-
227
-	/**
228
-	 * @param $fileId
229
-	 * @return array
230
-	 * @throws \OCP\Files\NotFoundException
231
-	 */
232
-	private function getCacheInfoFromFileId($fileId) {
233
-		if (!isset($this->cacheInfoCache[$fileId])) {
234
-			$builder = $this->connection->getQueryBuilder();
235
-			$query = $builder->select('storage', 'path')
236
-				->from('filecache')
237
-				->where($builder->expr()->eq('fileid', $builder->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
238
-
239
-			$row = $query->execute()->fetch();
240
-			if (is_array($row)) {
241
-				$this->cacheInfoCache[$fileId] = [
242
-					(int)$row['storage'],
243
-					$row['path']
244
-				];
245
-			} else {
246
-				throw new NotFoundException('File with id "' . $fileId . '" not found');
247
-			}
248
-		}
249
-		return $this->cacheInfoCache[$fileId];
250
-	}
251
-
252
-	/**
253
-	 * @param int $fileId
254
-	 * @return ICachedMountInfo[]
255
-	 * @since 9.0.0
256
-	 */
257
-	public function getMountsForFileId($fileId) {
258
-		try {
259
-			list($storageId, $internalPath) = $this->getCacheInfoFromFileId($fileId);
260
-		} catch (NotFoundException $e) {
261
-			return [];
262
-		}
263
-		$mountsForStorage = $this->getMountsForStorageId($storageId);
264
-
265
-		// filter mounts that are from the same storage but a different directory
266
-		return array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) {
267
-			if ($fileId === $mount->getRootId()) {
268
-				return true;
269
-			}
270
-			try {
271
-				list(, $internalMountPath) = $this->getCacheInfoFromFileId($mount->getRootId());
272
-			} catch (NotFoundException $e) {
273
-				return false;
274
-			}
275
-
276
-			return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/';
277
-		});
278
-
279
-	}
280
-
281
-	/**
282
-	 * Remove all cached mounts for a user
283
-	 *
284
-	 * @param IUser $user
285
-	 */
286
-	public function removeUserMounts(IUser $user) {
287
-		$builder = $this->connection->getQueryBuilder();
288
-
289
-		$query = $builder->delete('mounts')
290
-			->where($builder->expr()->eq('user_id', $builder->createNamedParameter($user->getUID())));
291
-		$query->execute();
292
-	}
293
-
294
-	public function removeUserStorageMount($storageId, $userId) {
295
-		$builder = $this->connection->getQueryBuilder();
296
-
297
-		$query = $builder->delete('mounts')
298
-			->where($builder->expr()->eq('user_id', $builder->createNamedParameter($userId)))
299
-			->andWhere($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
300
-		$query->execute();
301
-	}
302
-
303
-	public function remoteStorageMounts($storageId) {
304
-		$builder = $this->connection->getQueryBuilder();
305
-
306
-		$query = $builder->delete('mounts')
307
-			->where($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
308
-		$query->execute();
309
-	}
46
+    /**
47
+     * @var IDBConnection
48
+     */
49
+    private $connection;
50
+
51
+    /**
52
+     * @var IUserManager
53
+     */
54
+    private $userManager;
55
+
56
+    /**
57
+     * Cached mount info.
58
+     * Map of $userId to ICachedMountInfo.
59
+     *
60
+     * @var ICache
61
+     **/
62
+    private $mountsForUsers;
63
+
64
+    /**
65
+     * @var ILogger
66
+     */
67
+    private $logger;
68
+
69
+    /**
70
+     * @var ICache
71
+     */
72
+    private $cacheInfoCache;
73
+
74
+    /**
75
+     * UserMountCache constructor.
76
+     *
77
+     * @param IDBConnection $connection
78
+     * @param IUserManager $userManager
79
+     * @param ILogger $logger
80
+     */
81
+    public function __construct(IDBConnection $connection, IUserManager $userManager, ILogger $logger) {
82
+        $this->connection = $connection;
83
+        $this->userManager = $userManager;
84
+        $this->logger = $logger;
85
+        $this->cacheInfoCache = new CappedMemoryCache();
86
+        $this->mountsForUsers = new CappedMemoryCache();
87
+    }
88
+
89
+    public function registerMounts(IUser $user, array $mounts) {
90
+        // filter out non-proper storages coming from unit tests
91
+        $mounts = array_filter($mounts, function (IMountPoint $mount) {
92
+            return $mount instanceof SharedMount || $mount->getStorage() && $mount->getStorage()->getCache();
93
+        });
94
+        /** @var ICachedMountInfo[] $newMounts */
95
+        $newMounts = array_map(function (IMountPoint $mount) use ($user) {
96
+            $storage = $mount->getStorage();
97
+            if ($storage->instanceOfStorage('\OC\Files\Storage\Shared')) {
98
+                $rootId = (int)$storage->getShare()['file_source'];
99
+            } else {
100
+                $rootId = (int)$storage->getCache()->getId('');
101
+            }
102
+            $storageId = (int)$storage->getStorageCache()->getNumericId();
103
+            // filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
104
+            if ($rootId === -1) {
105
+                return null;
106
+            } else {
107
+                return new CachedMountInfo($user, $storageId, $rootId, $mount->getMountPoint());
108
+            }
109
+        }, $mounts);
110
+        $newMounts = array_values(array_filter($newMounts));
111
+
112
+        $cachedMounts = $this->getMountsForUser($user);
113
+        $mountDiff = function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
114
+            // since we are only looking for mounts for a specific user comparing on root id is enough
115
+            return $mount1->getRootId() - $mount2->getRootId();
116
+        };
117
+
118
+        /** @var ICachedMountInfo[] $addedMounts */
119
+        $addedMounts = array_udiff($newMounts, $cachedMounts, $mountDiff);
120
+        /** @var ICachedMountInfo[] $removedMounts */
121
+        $removedMounts = array_udiff($cachedMounts, $newMounts, $mountDiff);
122
+
123
+        $changedMounts = array_uintersect($newMounts, $cachedMounts, function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
124
+            // filter mounts with the same root id and different mountpoints
125
+            if ($mount1->getRootId() !== $mount2->getRootId()) {
126
+                return -1;
127
+            }
128
+            return ($mount1->getMountPoint() !== $mount2->getMountPoint()) ? 0 : 1;
129
+        });
130
+
131
+        foreach ($addedMounts as $mount) {
132
+            $this->addToCache($mount);
133
+            $this->mountsForUsers[$user->getUID()][] = $mount;
134
+        }
135
+        foreach ($removedMounts as $mount) {
136
+            $this->removeFromCache($mount);
137
+            $index = array_search($mount, $this->mountsForUsers[$user->getUID()]);
138
+            unset($this->mountsForUsers[$user->getUID()][$index]);
139
+        }
140
+        foreach ($changedMounts as $mount) {
141
+            $this->setMountPoint($mount);
142
+        }
143
+    }
144
+
145
+    private function addToCache(ICachedMountInfo $mount) {
146
+        $this->connection->insertIfNotExist('*PREFIX*mounts', [
147
+            'storage_id' => $mount->getStorageId(),
148
+            'root_id' => $mount->getRootId(),
149
+            'user_id' => $mount->getUser()->getUID(),
150
+            'mount_point' => $mount->getMountPoint()
151
+        ], ['root_id', 'user_id']);
152
+    }
153
+
154
+    private function setMountPoint(ICachedMountInfo $mount) {
155
+        $builder = $this->connection->getQueryBuilder();
156
+
157
+        $query = $builder->update('mounts')
158
+            ->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
159
+            ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
160
+            ->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)));
161
+
162
+        $query->execute();
163
+    }
164
+
165
+    private function removeFromCache(ICachedMountInfo $mount) {
166
+        $builder = $this->connection->getQueryBuilder();
167
+
168
+        $query = $builder->delete('mounts')
169
+            ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
170
+            ->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)));
171
+        $query->execute();
172
+    }
173
+
174
+    private function dbRowToMountInfo(array $row) {
175
+        $user = $this->userManager->get($row['user_id']);
176
+        return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point']);
177
+    }
178
+
179
+    /**
180
+     * @param IUser $user
181
+     * @return ICachedMountInfo[]
182
+     */
183
+    public function getMountsForUser(IUser $user) {
184
+        if (!isset($this->mountsForUsers[$user->getUID()])) {
185
+            $builder = $this->connection->getQueryBuilder();
186
+            $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
187
+                ->from('mounts')
188
+                ->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID())));
189
+
190
+            $rows = $query->execute()->fetchAll();
191
+
192
+            $this->mountsForUsers[$user->getUID()] = array_map([$this, 'dbRowToMountInfo'], $rows);
193
+        }
194
+        return $this->mountsForUsers[$user->getUID()];
195
+    }
196
+
197
+    /**
198
+     * @param int $numericStorageId
199
+     * @return CachedMountInfo[]
200
+     */
201
+    public function getMountsForStorageId($numericStorageId) {
202
+        $builder = $this->connection->getQueryBuilder();
203
+        $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
204
+            ->from('mounts')
205
+            ->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, IQueryBuilder::PARAM_INT)));
206
+
207
+        $rows = $query->execute()->fetchAll();
208
+
209
+        return array_map([$this, 'dbRowToMountInfo'], $rows);
210
+    }
211
+
212
+    /**
213
+     * @param int $rootFileId
214
+     * @return CachedMountInfo[]
215
+     */
216
+    public function getMountsForRootId($rootFileId) {
217
+        $builder = $this->connection->getQueryBuilder();
218
+        $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point')
219
+            ->from('mounts')
220
+            ->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, IQueryBuilder::PARAM_INT)));
221
+
222
+        $rows = $query->execute()->fetchAll();
223
+
224
+        return array_map([$this, 'dbRowToMountInfo'], $rows);
225
+    }
226
+
227
+    /**
228
+     * @param $fileId
229
+     * @return array
230
+     * @throws \OCP\Files\NotFoundException
231
+     */
232
+    private function getCacheInfoFromFileId($fileId) {
233
+        if (!isset($this->cacheInfoCache[$fileId])) {
234
+            $builder = $this->connection->getQueryBuilder();
235
+            $query = $builder->select('storage', 'path')
236
+                ->from('filecache')
237
+                ->where($builder->expr()->eq('fileid', $builder->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
238
+
239
+            $row = $query->execute()->fetch();
240
+            if (is_array($row)) {
241
+                $this->cacheInfoCache[$fileId] = [
242
+                    (int)$row['storage'],
243
+                    $row['path']
244
+                ];
245
+            } else {
246
+                throw new NotFoundException('File with id "' . $fileId . '" not found');
247
+            }
248
+        }
249
+        return $this->cacheInfoCache[$fileId];
250
+    }
251
+
252
+    /**
253
+     * @param int $fileId
254
+     * @return ICachedMountInfo[]
255
+     * @since 9.0.0
256
+     */
257
+    public function getMountsForFileId($fileId) {
258
+        try {
259
+            list($storageId, $internalPath) = $this->getCacheInfoFromFileId($fileId);
260
+        } catch (NotFoundException $e) {
261
+            return [];
262
+        }
263
+        $mountsForStorage = $this->getMountsForStorageId($storageId);
264
+
265
+        // filter mounts that are from the same storage but a different directory
266
+        return array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) {
267
+            if ($fileId === $mount->getRootId()) {
268
+                return true;
269
+            }
270
+            try {
271
+                list(, $internalMountPath) = $this->getCacheInfoFromFileId($mount->getRootId());
272
+            } catch (NotFoundException $e) {
273
+                return false;
274
+            }
275
+
276
+            return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/';
277
+        });
278
+
279
+    }
280
+
281
+    /**
282
+     * Remove all cached mounts for a user
283
+     *
284
+     * @param IUser $user
285
+     */
286
+    public function removeUserMounts(IUser $user) {
287
+        $builder = $this->connection->getQueryBuilder();
288
+
289
+        $query = $builder->delete('mounts')
290
+            ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($user->getUID())));
291
+        $query->execute();
292
+    }
293
+
294
+    public function removeUserStorageMount($storageId, $userId) {
295
+        $builder = $this->connection->getQueryBuilder();
296
+
297
+        $query = $builder->delete('mounts')
298
+            ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($userId)))
299
+            ->andWhere($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
300
+        $query->execute();
301
+    }
302
+
303
+    public function remoteStorageMounts($storageId) {
304
+        $builder = $this->connection->getQueryBuilder();
305
+
306
+        $query = $builder->delete('mounts')
307
+            ->where($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
308
+        $query->execute();
309
+    }
310 310
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -88,18 +88,18 @@  discard block
 block discarded – undo
88 88
 
89 89
 	public function registerMounts(IUser $user, array $mounts) {
90 90
 		// filter out non-proper storages coming from unit tests
91
-		$mounts = array_filter($mounts, function (IMountPoint $mount) {
91
+		$mounts = array_filter($mounts, function(IMountPoint $mount) {
92 92
 			return $mount instanceof SharedMount || $mount->getStorage() && $mount->getStorage()->getCache();
93 93
 		});
94 94
 		/** @var ICachedMountInfo[] $newMounts */
95
-		$newMounts = array_map(function (IMountPoint $mount) use ($user) {
95
+		$newMounts = array_map(function(IMountPoint $mount) use ($user) {
96 96
 			$storage = $mount->getStorage();
97 97
 			if ($storage->instanceOfStorage('\OC\Files\Storage\Shared')) {
98
-				$rootId = (int)$storage->getShare()['file_source'];
98
+				$rootId = (int) $storage->getShare()['file_source'];
99 99
 			} else {
100
-				$rootId = (int)$storage->getCache()->getId('');
100
+				$rootId = (int) $storage->getCache()->getId('');
101 101
 			}
102
-			$storageId = (int)$storage->getStorageCache()->getNumericId();
102
+			$storageId = (int) $storage->getStorageCache()->getNumericId();
103 103
 			// filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
104 104
 			if ($rootId === -1) {
105 105
 				return null;
@@ -110,7 +110,7 @@  discard block
 block discarded – undo
110 110
 		$newMounts = array_values(array_filter($newMounts));
111 111
 
112 112
 		$cachedMounts = $this->getMountsForUser($user);
113
-		$mountDiff = function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
113
+		$mountDiff = function(ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
114 114
 			// since we are only looking for mounts for a specific user comparing on root id is enough
115 115
 			return $mount1->getRootId() - $mount2->getRootId();
116 116
 		};
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
 		/** @var ICachedMountInfo[] $removedMounts */
121 121
 		$removedMounts = array_udiff($cachedMounts, $newMounts, $mountDiff);
122 122
 
123
-		$changedMounts = array_uintersect($newMounts, $cachedMounts, function (ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
123
+		$changedMounts = array_uintersect($newMounts, $cachedMounts, function(ICachedMountInfo $mount1, ICachedMountInfo $mount2) {
124 124
 			// filter mounts with the same root id and different mountpoints
125 125
 			if ($mount1->getRootId() !== $mount2->getRootId()) {
126 126
 				return -1;
@@ -173,7 +173,7 @@  discard block
 block discarded – undo
173 173
 
174 174
 	private function dbRowToMountInfo(array $row) {
175 175
 		$user = $this->userManager->get($row['user_id']);
176
-		return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point']);
176
+		return new CachedMountInfo($user, (int) $row['storage_id'], (int) $row['root_id'], $row['mount_point']);
177 177
 	}
178 178
 
179 179
 	/**
@@ -239,11 +239,11 @@  discard block
 block discarded – undo
239 239
 			$row = $query->execute()->fetch();
240 240
 			if (is_array($row)) {
241 241
 				$this->cacheInfoCache[$fileId] = [
242
-					(int)$row['storage'],
242
+					(int) $row['storage'],
243 243
 					$row['path']
244 244
 				];
245 245
 			} else {
246
-				throw new NotFoundException('File with id "' . $fileId . '" not found');
246
+				throw new NotFoundException('File with id "'.$fileId.'" not found');
247 247
 			}
248 248
 		}
249 249
 		return $this->cacheInfoCache[$fileId];
@@ -263,7 +263,7 @@  discard block
 block discarded – undo
263 263
 		$mountsForStorage = $this->getMountsForStorageId($storageId);
264 264
 
265 265
 		// filter mounts that are from the same storage but a different directory
266
-		return array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) {
266
+		return array_filter($mountsForStorage, function(ICachedMountInfo $mount) use ($internalPath, $fileId) {
267 267
 			if ($fileId === $mount->getRootId()) {
268 268
 				return true;
269 269
 			}
@@ -273,7 +273,7 @@  discard block
 block discarded – undo
273 273
 				return false;
274 274
 			}
275 275
 
276
-			return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/';
276
+			return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath.'/';
277 277
 		});
278 278
 
279 279
 	}
Please login to merge, or discard this patch.
lib/private/files/filesystem.php 4 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -62,7 +62,6 @@
 block discarded – undo
62 62
 use OC\Files\Mount\MountPoint;
63 63
 use OC\Files\Storage\StorageFactory;
64 64
 use OCP\Files\Config\IMountProvider;
65
-use OCP\Files\Mount\IMountPoint;
66 65
 use OCP\Files\NotFoundException;
67 66
 use OCP\IUserManager;
68 67
 
Please login to merge, or discard this patch.
Doc Comments   +28 added lines, -2 removed lines patch added patch discarded remove patch
@@ -338,6 +338,9 @@  discard block
 block discarded – undo
338 338
 		}
339 339
 	}
340 340
 
341
+	/**
342
+	 * @param string $root
343
+	 */
341 344
 	static public function init($user, $root) {
342 345
 		if (self::$defaultInstance) {
343 346
 			return false;
@@ -521,7 +524,7 @@  discard block
 block discarded – undo
521 524
 	/**
522 525
 	 * mount an \OC\Files\Storage\Storage in our virtual filesystem
523 526
 	 *
524
-	 * @param \OC\Files\Storage\Storage|string $class
527
+	 * @param string $class
525 528
 	 * @param array $arguments
526 529
 	 * @param string $mountpoint
527 530
 	 */
@@ -653,6 +656,9 @@  discard block
 block discarded – undo
653 656
 		return self::$defaultInstance->is_dir($path);
654 657
 	}
655 658
 
659
+	/**
660
+	 * @param string $path
661
+	 */
656 662
 	static public function is_file($path) {
657 663
 		return self::$defaultInstance->is_file($path);
658 664
 	}
@@ -665,6 +671,9 @@  discard block
 block discarded – undo
665 671
 		return self::$defaultInstance->filetype($path);
666 672
 	}
667 673
 
674
+	/**
675
+	 * @param string $path
676
+	 */
668 677
 	static public function filesize($path) {
669 678
 		return self::$defaultInstance->filesize($path);
670 679
 	}
@@ -677,6 +686,9 @@  discard block
 block discarded – undo
677 686
 		return self::$defaultInstance->isCreatable($path);
678 687
 	}
679 688
 
689
+	/**
690
+	 * @param string $path
691
+	 */
680 692
 	static public function isReadable($path) {
681 693
 		return self::$defaultInstance->isReadable($path);
682 694
 	}
@@ -689,6 +701,9 @@  discard block
 block discarded – undo
689 701
 		return self::$defaultInstance->isDeletable($path);
690 702
 	}
691 703
 
704
+	/**
705
+	 * @param string $path
706
+	 */
692 707
 	static public function isSharable($path) {
693 708
 		return self::$defaultInstance->isSharable($path);
694 709
 	}
@@ -706,6 +721,7 @@  discard block
 block discarded – undo
706 721
 	}
707 722
 
708 723
 	/**
724
+	 * @param string $path
709 725
 	 * @return string
710 726
 	 */
711 727
 	static public function file_get_contents($path) {
@@ -728,6 +744,10 @@  discard block
 block discarded – undo
728 744
 		return self::$defaultInstance->copy($path1, $path2);
729 745
 	}
730 746
 
747
+	/**
748
+	 * @param string $path
749
+	 * @param string $mode
750
+	 */
731 751
 	static public function fopen($path, $mode) {
732 752
 		return self::$defaultInstance->fopen($path, $mode);
733 753
 	}
@@ -743,6 +763,9 @@  discard block
 block discarded – undo
743 763
 		return self::$defaultInstance->fromTmpFile($tmpFile, $path);
744 764
 	}
745 765
 
766
+	/**
767
+	 * @param string $path
768
+	 */
746 769
 	static public function getMimeType($path) {
747 770
 		return self::$defaultInstance->getMimeType($path);
748 771
 	}
@@ -755,6 +778,9 @@  discard block
 block discarded – undo
755 778
 		return self::$defaultInstance->free_space($path);
756 779
 	}
757 780
 
781
+	/**
782
+	 * @param string $query
783
+	 */
758 784
 	static public function search($query) {
759 785
 		return self::$defaultInstance->search($query);
760 786
 	}
@@ -868,7 +894,7 @@  discard block
 block discarded – undo
868 894
 	 * @param string $path
869 895
 	 * @param boolean $includeMountPoints whether to add mountpoint sizes,
870 896
 	 * defaults to true
871
-	 * @return \OC\Files\FileInfo|bool False if file does not exist
897
+	 * @return \OCP\Files\FileInfo|null False if file does not exist
872 898
 	 */
873 899
 	public static function getFileInfo($path, $includeMountPoints = true) {
874 900
 		return self::$defaultInstance->getFileInfo($path, $includeMountPoints);
Please login to merge, or discard this patch.
Indentation   +861 added lines, -861 removed lines patch added patch discarded remove patch
@@ -70,865 +70,865 @@
 block discarded – undo
70 70
 
71 71
 class Filesystem {
72 72
 
73
-	/**
74
-	 * @var Mount\Manager $mounts
75
-	 */
76
-	private static $mounts;
77
-
78
-	public static $loaded = false;
79
-	/**
80
-	 * @var \OC\Files\View $defaultInstance
81
-	 */
82
-	static private $defaultInstance;
83
-
84
-	static private $usersSetup = array();
85
-
86
-	static private $normalizedPathCache = null;
87
-
88
-	static private $listeningForProviders = false;
89
-
90
-	/**
91
-	 * classname which used for hooks handling
92
-	 * used as signalclass in OC_Hooks::emit()
93
-	 */
94
-	const CLASSNAME = 'OC_Filesystem';
95
-
96
-	/**
97
-	 * signalname emitted before file renaming
98
-	 *
99
-	 * @param string $oldpath
100
-	 * @param string $newpath
101
-	 */
102
-	const signal_rename = 'rename';
103
-
104
-	/**
105
-	 * signal emitted after file renaming
106
-	 *
107
-	 * @param string $oldpath
108
-	 * @param string $newpath
109
-	 */
110
-	const signal_post_rename = 'post_rename';
111
-
112
-	/**
113
-	 * signal emitted before file/dir creation
114
-	 *
115
-	 * @param string $path
116
-	 * @param bool $run changing this flag to false in hook handler will cancel event
117
-	 */
118
-	const signal_create = 'create';
119
-
120
-	/**
121
-	 * signal emitted after file/dir creation
122
-	 *
123
-	 * @param string $path
124
-	 * @param bool $run changing this flag to false in hook handler will cancel event
125
-	 */
126
-	const signal_post_create = 'post_create';
127
-
128
-	/**
129
-	 * signal emits before file/dir copy
130
-	 *
131
-	 * @param string $oldpath
132
-	 * @param string $newpath
133
-	 * @param bool $run changing this flag to false in hook handler will cancel event
134
-	 */
135
-	const signal_copy = 'copy';
136
-
137
-	/**
138
-	 * signal emits after file/dir copy
139
-	 *
140
-	 * @param string $oldpath
141
-	 * @param string $newpath
142
-	 */
143
-	const signal_post_copy = 'post_copy';
144
-
145
-	/**
146
-	 * signal emits before file/dir save
147
-	 *
148
-	 * @param string $path
149
-	 * @param bool $run changing this flag to false in hook handler will cancel event
150
-	 */
151
-	const signal_write = 'write';
152
-
153
-	/**
154
-	 * signal emits after file/dir save
155
-	 *
156
-	 * @param string $path
157
-	 */
158
-	const signal_post_write = 'post_write';
159
-
160
-	/**
161
-	 * signal emitted before file/dir update
162
-	 *
163
-	 * @param string $path
164
-	 * @param bool $run changing this flag to false in hook handler will cancel event
165
-	 */
166
-	const signal_update = 'update';
167
-
168
-	/**
169
-	 * signal emitted after file/dir update
170
-	 *
171
-	 * @param string $path
172
-	 * @param bool $run changing this flag to false in hook handler will cancel event
173
-	 */
174
-	const signal_post_update = 'post_update';
175
-
176
-	/**
177
-	 * signal emits when reading file/dir
178
-	 *
179
-	 * @param string $path
180
-	 */
181
-	const signal_read = 'read';
182
-
183
-	/**
184
-	 * signal emits when removing file/dir
185
-	 *
186
-	 * @param string $path
187
-	 */
188
-	const signal_delete = 'delete';
189
-
190
-	/**
191
-	 * parameters definitions for signals
192
-	 */
193
-	const signal_param_path = 'path';
194
-	const signal_param_oldpath = 'oldpath';
195
-	const signal_param_newpath = 'newpath';
196
-
197
-	/**
198
-	 * run - changing this flag to false in hook handler will cancel event
199
-	 */
200
-	const signal_param_run = 'run';
201
-
202
-	const signal_create_mount = 'create_mount';
203
-	const signal_delete_mount = 'delete_mount';
204
-	const signal_param_mount_type = 'mounttype';
205
-	const signal_param_users = 'users';
206
-
207
-	/**
208
-	 * @var \OC\Files\Storage\StorageFactory $loader
209
-	 */
210
-	private static $loader;
211
-
212
-	/**
213
-	 * @param string $wrapperName
214
-	 * @param callable $wrapper
215
-	 * @param int $priority
216
-	 */
217
-	public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) {
218
-		$mounts = self::getMountManager()->getAll();
219
-		if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) {
220
-			// do not re-wrap if storage with this name already existed
221
-			return;
222
-		}
223
-	}
224
-
225
-	/**
226
-	 * Returns the storage factory
227
-	 *
228
-	 * @return \OCP\Files\Storage\IStorageFactory
229
-	 */
230
-	public static function getLoader() {
231
-		if (!self::$loader) {
232
-			self::$loader = new StorageFactory();
233
-		}
234
-		return self::$loader;
235
-	}
236
-
237
-	/**
238
-	 * Returns the mount manager
239
-	 *
240
-	 * @return \OC\Files\Mount\Manager
241
-	 */
242
-	public static function getMountManager($user = '') {
243
-		if (!self::$mounts) {
244
-			\OC_Util::setupFS($user);
245
-		}
246
-		return self::$mounts;
247
-	}
248
-
249
-	/**
250
-	 * get the mountpoint of the storage object for a path
251
-	 * ( note: because a storage is not always mounted inside the fakeroot, the
252
-	 * returned mountpoint is relative to the absolute root of the filesystem
253
-	 * and doesn't take the chroot into account )
254
-	 *
255
-	 * @param string $path
256
-	 * @return string
257
-	 */
258
-	static public function getMountPoint($path) {
259
-		if (!self::$mounts) {
260
-			\OC_Util::setupFS();
261
-		}
262
-		$mount = self::$mounts->find($path);
263
-		if ($mount) {
264
-			return $mount->getMountPoint();
265
-		} else {
266
-			return '';
267
-		}
268
-	}
269
-
270
-	/**
271
-	 * get a list of all mount points in a directory
272
-	 *
273
-	 * @param string $path
274
-	 * @return string[]
275
-	 */
276
-	static public function getMountPoints($path) {
277
-		if (!self::$mounts) {
278
-			\OC_Util::setupFS();
279
-		}
280
-		$result = array();
281
-		$mounts = self::$mounts->findIn($path);
282
-		foreach ($mounts as $mount) {
283
-			$result[] = $mount->getMountPoint();
284
-		}
285
-		return $result;
286
-	}
287
-
288
-	/**
289
-	 * get the storage mounted at $mountPoint
290
-	 *
291
-	 * @param string $mountPoint
292
-	 * @return \OC\Files\Storage\Storage
293
-	 */
294
-	public static function getStorage($mountPoint) {
295
-		if (!self::$mounts) {
296
-			\OC_Util::setupFS();
297
-		}
298
-		$mount = self::$mounts->find($mountPoint);
299
-		return $mount->getStorage();
300
-	}
301
-
302
-	/**
303
-	 * @param string $id
304
-	 * @return Mount\MountPoint[]
305
-	 */
306
-	public static function getMountByStorageId($id) {
307
-		if (!self::$mounts) {
308
-			\OC_Util::setupFS();
309
-		}
310
-		return self::$mounts->findByStorageId($id);
311
-	}
312
-
313
-	/**
314
-	 * @param int $id
315
-	 * @return Mount\MountPoint[]
316
-	 */
317
-	public static function getMountByNumericId($id) {
318
-		if (!self::$mounts) {
319
-			\OC_Util::setupFS();
320
-		}
321
-		return self::$mounts->findByNumericId($id);
322
-	}
323
-
324
-	/**
325
-	 * resolve a path to a storage and internal path
326
-	 *
327
-	 * @param string $path
328
-	 * @return array an array consisting of the storage and the internal path
329
-	 */
330
-	static public function resolvePath($path) {
331
-		if (!self::$mounts) {
332
-			\OC_Util::setupFS();
333
-		}
334
-		$mount = self::$mounts->find($path);
335
-		if ($mount) {
336
-			return array($mount->getStorage(), rtrim($mount->getInternalPath($path), '/'));
337
-		} else {
338
-			return array(null, null);
339
-		}
340
-	}
341
-
342
-	static public function init($user, $root) {
343
-		if (self::$defaultInstance) {
344
-			return false;
345
-		}
346
-		self::getLoader();
347
-		self::$defaultInstance = new View($root);
348
-
349
-		if (!self::$mounts) {
350
-			self::$mounts = \OC::$server->getMountManager();
351
-		}
352
-
353
-		//load custom mount config
354
-		self::initMountPoints($user);
355
-
356
-		self::$loaded = true;
357
-
358
-		return true;
359
-	}
360
-
361
-	static public function initMountManager() {
362
-		if (!self::$mounts) {
363
-			self::$mounts = \OC::$server->getMountManager();
364
-		}
365
-	}
366
-
367
-	/**
368
-	 * Initialize system and personal mount points for a user
369
-	 *
370
-	 * @param string $user
371
-	 * @throws \OC\User\NoUserException if the user is not available
372
-	 */
373
-	public static function initMountPoints($user = '') {
374
-		if ($user == '') {
375
-			$user = \OC_User::getUser();
376
-		}
377
-		if ($user === null || $user === false || $user === '') {
378
-			throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session');
379
-		}
380
-		if (isset(self::$usersSetup[$user])) {
381
-			return;
382
-		}
383
-		self::$usersSetup[$user] = true;
384
-
385
-		$root = \OC_User::getHome($user);
386
-
387
-		$userManager = \OC::$server->getUserManager();
388
-		$userObject = $userManager->get($user);
389
-
390
-		if (is_null($userObject)) {
391
-			\OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR);
392
-			throw new \OC\User\NoUserException('Backends provided no user object for ' . $user);
393
-		}
394
-
395
-		$homeStorage = \OC::$server->getConfig()->getSystemValue('objectstore');
396
-		if (!empty($homeStorage)) {
397
-			// sanity checks
398
-			if (empty($homeStorage['class'])) {
399
-				\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
400
-			}
401
-			if (!isset($homeStorage['arguments'])) {
402
-				$homeStorage['arguments'] = array();
403
-			}
404
-			// instantiate object store implementation
405
-			$homeStorage['arguments']['objectstore'] = new $homeStorage['class']($homeStorage['arguments']);
406
-			// mount with home object store implementation
407
-			$homeStorage['class'] = '\OC\Files\ObjectStore\HomeObjectStoreStorage';
408
-		} else {
409
-			$homeStorage = array(
410
-				//default home storage configuration:
411
-				'class' => '\OC\Files\Storage\Home',
412
-				'arguments' => array()
413
-			);
414
-		}
415
-		$homeStorage['arguments']['user'] = $userObject;
416
-
417
-		// check for legacy home id (<= 5.0.12)
418
-		if (\OC\Files\Cache\Storage::exists('local::' . $root . '/')) {
419
-			$homeStorage['arguments']['legacy'] = true;
420
-		}
421
-
422
-		$mount = new MountPoint($homeStorage['class'], '/' . $user, $homeStorage['arguments'], self::getLoader());
423
-		self::getMountManager()->addMount($mount);
424
-
425
-		$home = \OC\Files\Filesystem::getStorage($user);
426
-
427
-		self::mountCacheDir($user);
428
-
429
-		// Chance to mount for other storages
430
-		/** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */
431
-		$mountConfigManager = \OC::$server->getMountProviderCollection();
432
-		if ($userObject) {
433
-			$mounts = $mountConfigManager->getMountsForUser($userObject);
434
-			array_walk($mounts, array(self::$mounts, 'addMount'));
435
-			$mounts[] = $mount;
436
-			$mountConfigManager->registerMounts($userObject, $mounts);
437
-		}
438
-
439
-		self::listenForNewMountProviders($mountConfigManager, $userManager);
440
-		\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user, 'user_dir' => $root));
441
-	}
442
-
443
-	/**
444
-	 * Get mounts from mount providers that are registered after setup
445
-	 *
446
-	 * @param MountProviderCollection $mountConfigManager
447
-	 * @param IUserManager $userManager
448
-	 */
449
-	private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) {
450
-		if (!self::$listeningForProviders) {
451
-			self::$listeningForProviders = true;
452
-			$mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) {
453
-				foreach (Filesystem::$usersSetup as $user => $setup) {
454
-					$userObject = $userManager->get($user);
455
-					if ($userObject) {
456
-						$mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader());
457
-						array_walk($mounts, array(self::$mounts, 'addMount'));
458
-					}
459
-				}
460
-			});
461
-		}
462
-	}
463
-
464
-	/**
465
-	 * Mounts the cache directory
466
-	 *
467
-	 * @param string $user user name
468
-	 */
469
-	private static function mountCacheDir($user) {
470
-		$cacheBaseDir = \OC::$server->getConfig()->getSystemValue('cache_path', '');
471
-		if ($cacheBaseDir !== '') {
472
-			$cacheDir = rtrim($cacheBaseDir, '/') . '/' . $user;
473
-			if (!file_exists($cacheDir)) {
474
-				mkdir($cacheDir, 0770, true);
475
-			}
476
-			// mount external cache dir to "/$user/cache" mount point
477
-			self::mount('\OC\Files\Storage\Local', array('datadir' => $cacheDir), '/' . $user . '/cache');
478
-		}
479
-	}
480
-
481
-	/**
482
-	 * get the default filesystem view
483
-	 *
484
-	 * @return View
485
-	 */
486
-	static public function getView() {
487
-		return self::$defaultInstance;
488
-	}
489
-
490
-	/**
491
-	 * tear down the filesystem, removing all storage providers
492
-	 */
493
-	static public function tearDown() {
494
-		self::clearMounts();
495
-		self::$defaultInstance = null;
496
-	}
497
-
498
-	/**
499
-	 * get the relative path of the root data directory for the current user
500
-	 *
501
-	 * @return string
502
-	 *
503
-	 * Returns path like /admin/files
504
-	 */
505
-	static public function getRoot() {
506
-		if (!self::$defaultInstance) {
507
-			return null;
508
-		}
509
-		return self::$defaultInstance->getRoot();
510
-	}
511
-
512
-	/**
513
-	 * clear all mounts and storage backends
514
-	 */
515
-	public static function clearMounts() {
516
-		if (self::$mounts) {
517
-			self::$usersSetup = array();
518
-			self::$mounts->clear();
519
-		}
520
-	}
521
-
522
-	/**
523
-	 * mount an \OC\Files\Storage\Storage in our virtual filesystem
524
-	 *
525
-	 * @param \OC\Files\Storage\Storage|string $class
526
-	 * @param array $arguments
527
-	 * @param string $mountpoint
528
-	 */
529
-	static public function mount($class, $arguments, $mountpoint) {
530
-		if (!self::$mounts) {
531
-			\OC_Util::setupFS();
532
-		}
533
-		$mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader());
534
-		self::$mounts->addMount($mount);
535
-	}
536
-
537
-	/**
538
-	 * return the path to a local version of the file
539
-	 * we need this because we can't know if a file is stored local or not from
540
-	 * outside the filestorage and for some purposes a local file is needed
541
-	 *
542
-	 * @param string $path
543
-	 * @return string
544
-	 */
545
-	static public function getLocalFile($path) {
546
-		return self::$defaultInstance->getLocalFile($path);
547
-	}
548
-
549
-	/**
550
-	 * @param string $path
551
-	 * @return string
552
-	 */
553
-	static public function getLocalFolder($path) {
554
-		return self::$defaultInstance->getLocalFolder($path);
555
-	}
556
-
557
-	/**
558
-	 * return path to file which reflects one visible in browser
559
-	 *
560
-	 * @param string $path
561
-	 * @return string
562
-	 */
563
-	static public function getLocalPath($path) {
564
-		$datadir = \OC_User::getHome(\OC_User::getUser()) . '/files';
565
-		$newpath = $path;
566
-		if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
567
-			$newpath = substr($path, strlen($datadir));
568
-		}
569
-		return $newpath;
570
-	}
571
-
572
-	/**
573
-	 * check if the requested path is valid
574
-	 *
575
-	 * @param string $path
576
-	 * @return bool
577
-	 */
578
-	static public function isValidPath($path) {
579
-		$path = self::normalizePath($path);
580
-		if (!$path || $path[0] !== '/') {
581
-			$path = '/' . $path;
582
-		}
583
-		if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') {
584
-			return false;
585
-		}
586
-		return true;
587
-	}
588
-
589
-	/**
590
-	 * checks if a file is blacklisted for storage in the filesystem
591
-	 * Listens to write and rename hooks
592
-	 *
593
-	 * @param array $data from hook
594
-	 */
595
-	static public function isBlacklisted($data) {
596
-		if (isset($data['path'])) {
597
-			$path = $data['path'];
598
-		} else if (isset($data['newpath'])) {
599
-			$path = $data['newpath'];
600
-		}
601
-		if (isset($path)) {
602
-			if (self::isFileBlacklisted($path)) {
603
-				$data['run'] = false;
604
-			}
605
-		}
606
-	}
607
-
608
-	/**
609
-	 * @param string $filename
610
-	 * @return bool
611
-	 */
612
-	static public function isFileBlacklisted($filename) {
613
-		$filename = self::normalizePath($filename);
614
-
615
-		$blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', array('.htaccess'));
616
-		$filename = strtolower(basename($filename));
617
-		return in_array($filename, $blacklist);
618
-	}
619
-
620
-	/**
621
-	 * check if the directory should be ignored when scanning
622
-	 * NOTE: the special directories . and .. would cause never ending recursion
623
-	 *
624
-	 * @param String $dir
625
-	 * @return boolean
626
-	 */
627
-	static public function isIgnoredDir($dir) {
628
-		if ($dir === '.' || $dir === '..') {
629
-			return true;
630
-		}
631
-		return false;
632
-	}
633
-
634
-	/**
635
-	 * following functions are equivalent to their php builtin equivalents for arguments/return values.
636
-	 */
637
-	static public function mkdir($path) {
638
-		return self::$defaultInstance->mkdir($path);
639
-	}
640
-
641
-	static public function rmdir($path) {
642
-		return self::$defaultInstance->rmdir($path);
643
-	}
644
-
645
-	static public function opendir($path) {
646
-		return self::$defaultInstance->opendir($path);
647
-	}
648
-
649
-	static public function readdir($path) {
650
-		return self::$defaultInstance->readdir($path);
651
-	}
652
-
653
-	static public function is_dir($path) {
654
-		return self::$defaultInstance->is_dir($path);
655
-	}
656
-
657
-	static public function is_file($path) {
658
-		return self::$defaultInstance->is_file($path);
659
-	}
660
-
661
-	static public function stat($path) {
662
-		return self::$defaultInstance->stat($path);
663
-	}
664
-
665
-	static public function filetype($path) {
666
-		return self::$defaultInstance->filetype($path);
667
-	}
668
-
669
-	static public function filesize($path) {
670
-		return self::$defaultInstance->filesize($path);
671
-	}
672
-
673
-	static public function readfile($path) {
674
-		return self::$defaultInstance->readfile($path);
675
-	}
676
-
677
-	static public function isCreatable($path) {
678
-		return self::$defaultInstance->isCreatable($path);
679
-	}
680
-
681
-	static public function isReadable($path) {
682
-		return self::$defaultInstance->isReadable($path);
683
-	}
684
-
685
-	static public function isUpdatable($path) {
686
-		return self::$defaultInstance->isUpdatable($path);
687
-	}
688
-
689
-	static public function isDeletable($path) {
690
-		return self::$defaultInstance->isDeletable($path);
691
-	}
692
-
693
-	static public function isSharable($path) {
694
-		return self::$defaultInstance->isSharable($path);
695
-	}
696
-
697
-	static public function file_exists($path) {
698
-		return self::$defaultInstance->file_exists($path);
699
-	}
700
-
701
-	static public function filemtime($path) {
702
-		return self::$defaultInstance->filemtime($path);
703
-	}
704
-
705
-	static public function touch($path, $mtime = null) {
706
-		return self::$defaultInstance->touch($path, $mtime);
707
-	}
708
-
709
-	/**
710
-	 * @return string
711
-	 */
712
-	static public function file_get_contents($path) {
713
-		return self::$defaultInstance->file_get_contents($path);
714
-	}
715
-
716
-	static public function file_put_contents($path, $data) {
717
-		return self::$defaultInstance->file_put_contents($path, $data);
718
-	}
719
-
720
-	static public function unlink($path) {
721
-		return self::$defaultInstance->unlink($path);
722
-	}
723
-
724
-	static public function rename($path1, $path2) {
725
-		return self::$defaultInstance->rename($path1, $path2);
726
-	}
727
-
728
-	static public function copy($path1, $path2) {
729
-		return self::$defaultInstance->copy($path1, $path2);
730
-	}
731
-
732
-	static public function fopen($path, $mode) {
733
-		return self::$defaultInstance->fopen($path, $mode);
734
-	}
735
-
736
-	/**
737
-	 * @return string
738
-	 */
739
-	static public function toTmpFile($path) {
740
-		return self::$defaultInstance->toTmpFile($path);
741
-	}
742
-
743
-	static public function fromTmpFile($tmpFile, $path) {
744
-		return self::$defaultInstance->fromTmpFile($tmpFile, $path);
745
-	}
746
-
747
-	static public function getMimeType($path) {
748
-		return self::$defaultInstance->getMimeType($path);
749
-	}
750
-
751
-	static public function hash($type, $path, $raw = false) {
752
-		return self::$defaultInstance->hash($type, $path, $raw);
753
-	}
754
-
755
-	static public function free_space($path = '/') {
756
-		return self::$defaultInstance->free_space($path);
757
-	}
758
-
759
-	static public function search($query) {
760
-		return self::$defaultInstance->search($query);
761
-	}
762
-
763
-	/**
764
-	 * @param string $query
765
-	 */
766
-	static public function searchByMime($query) {
767
-		return self::$defaultInstance->searchByMime($query);
768
-	}
769
-
770
-	/**
771
-	 * @param string|int $tag name or tag id
772
-	 * @param string $userId owner of the tags
773
-	 * @return FileInfo[] array or file info
774
-	 */
775
-	static public function searchByTag($tag, $userId) {
776
-		return self::$defaultInstance->searchByTag($tag, $userId);
777
-	}
778
-
779
-	/**
780
-	 * check if a file or folder has been updated since $time
781
-	 *
782
-	 * @param string $path
783
-	 * @param int $time
784
-	 * @return bool
785
-	 */
786
-	static public function hasUpdated($path, $time) {
787
-		return self::$defaultInstance->hasUpdated($path, $time);
788
-	}
789
-
790
-	/**
791
-	 * Fix common problems with a file path
792
-	 *
793
-	 * @param string $path
794
-	 * @param bool $stripTrailingSlash
795
-	 * @param bool $isAbsolutePath
796
-	 * @return string
797
-	 */
798
-	public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false) {
799
-		if (is_null(self::$normalizedPathCache)) {
800
-			self::$normalizedPathCache = new CappedMemoryCache();
801
-		}
802
-
803
-		/**
804
-		 * FIXME: This is a workaround for existing classes and files which call
805
-		 *        this function with another type than a valid string. This
806
-		 *        conversion should get removed as soon as all existing
807
-		 *        function calls have been fixed.
808
-		 */
809
-		$path = (string)$path;
810
-
811
-		$cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath]);
812
-
813
-		if (isset(self::$normalizedPathCache[$cacheKey])) {
814
-			return self::$normalizedPathCache[$cacheKey];
815
-		}
816
-
817
-		if ($path == '') {
818
-			return '/';
819
-		}
820
-
821
-		//normalize unicode if possible
822
-		$path = \OC_Util::normalizeUnicode($path);
823
-
824
-		//no windows style slashes
825
-		$path = str_replace('\\', '/', $path);
826
-
827
-		// When normalizing an absolute path, we need to ensure that the drive-letter
828
-		// is still at the beginning on windows
829
-		$windows_drive_letter = '';
830
-		if ($isAbsolutePath && \OC_Util::runningOnWindows() && preg_match('#^([a-zA-Z])$#', $path[0]) && $path[1] == ':' && $path[2] == '/') {
831
-			$windows_drive_letter = substr($path, 0, 2);
832
-			$path = substr($path, 2);
833
-		}
834
-
835
-		//add leading slash
836
-		if ($path[0] !== '/') {
837
-			$path = '/' . $path;
838
-		}
839
-
840
-		// remove '/./'
841
-		// ugly, but str_replace() can't replace them all in one go
842
-		// as the replacement itself is part of the search string
843
-		// which will only be found during the next iteration
844
-		while (strpos($path, '/./') !== false) {
845
-			$path = str_replace('/./', '/', $path);
846
-		}
847
-		// remove sequences of slashes
848
-		$path = preg_replace('#/{2,}#', '/', $path);
849
-
850
-		//remove trailing slash
851
-		if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') {
852
-			$path = substr($path, 0, -1);
853
-		}
854
-
855
-		// remove trailing '/.'
856
-		if (substr($path, -2) == '/.') {
857
-			$path = substr($path, 0, -2);
858
-		}
859
-
860
-		$normalizedPath = $windows_drive_letter . $path;
861
-		self::$normalizedPathCache[$cacheKey] = $normalizedPath;
862
-
863
-		return $normalizedPath;
864
-	}
865
-
866
-	/**
867
-	 * get the filesystem info
868
-	 *
869
-	 * @param string $path
870
-	 * @param boolean $includeMountPoints whether to add mountpoint sizes,
871
-	 * defaults to true
872
-	 * @return \OC\Files\FileInfo|bool False if file does not exist
873
-	 */
874
-	public static function getFileInfo($path, $includeMountPoints = true) {
875
-		return self::$defaultInstance->getFileInfo($path, $includeMountPoints);
876
-	}
877
-
878
-	/**
879
-	 * change file metadata
880
-	 *
881
-	 * @param string $path
882
-	 * @param array $data
883
-	 * @return int
884
-	 *
885
-	 * returns the fileid of the updated file
886
-	 */
887
-	public static function putFileInfo($path, $data) {
888
-		return self::$defaultInstance->putFileInfo($path, $data);
889
-	}
890
-
891
-	/**
892
-	 * get the content of a directory
893
-	 *
894
-	 * @param string $directory path under datadirectory
895
-	 * @param string $mimetype_filter limit returned content to this mimetype or mimepart
896
-	 * @return \OC\Files\FileInfo[]
897
-	 */
898
-	public static function getDirectoryContent($directory, $mimetype_filter = '') {
899
-		return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
900
-	}
901
-
902
-	/**
903
-	 * Get the path of a file by id
904
-	 *
905
-	 * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
906
-	 *
907
-	 * @param int $id
908
-	 * @throws NotFoundException
909
-	 * @return string
910
-	 */
911
-	public static function getPath($id) {
912
-		return self::$defaultInstance->getPath($id);
913
-	}
914
-
915
-	/**
916
-	 * Get the owner for a file or folder
917
-	 *
918
-	 * @param string $path
919
-	 * @return string
920
-	 */
921
-	public static function getOwner($path) {
922
-		return self::$defaultInstance->getOwner($path);
923
-	}
924
-
925
-	/**
926
-	 * get the ETag for a file or folder
927
-	 *
928
-	 * @param string $path
929
-	 * @return string
930
-	 */
931
-	static public function getETag($path) {
932
-		return self::$defaultInstance->getETag($path);
933
-	}
73
+    /**
74
+     * @var Mount\Manager $mounts
75
+     */
76
+    private static $mounts;
77
+
78
+    public static $loaded = false;
79
+    /**
80
+     * @var \OC\Files\View $defaultInstance
81
+     */
82
+    static private $defaultInstance;
83
+
84
+    static private $usersSetup = array();
85
+
86
+    static private $normalizedPathCache = null;
87
+
88
+    static private $listeningForProviders = false;
89
+
90
+    /**
91
+     * classname which used for hooks handling
92
+     * used as signalclass in OC_Hooks::emit()
93
+     */
94
+    const CLASSNAME = 'OC_Filesystem';
95
+
96
+    /**
97
+     * signalname emitted before file renaming
98
+     *
99
+     * @param string $oldpath
100
+     * @param string $newpath
101
+     */
102
+    const signal_rename = 'rename';
103
+
104
+    /**
105
+     * signal emitted after file renaming
106
+     *
107
+     * @param string $oldpath
108
+     * @param string $newpath
109
+     */
110
+    const signal_post_rename = 'post_rename';
111
+
112
+    /**
113
+     * signal emitted before file/dir creation
114
+     *
115
+     * @param string $path
116
+     * @param bool $run changing this flag to false in hook handler will cancel event
117
+     */
118
+    const signal_create = 'create';
119
+
120
+    /**
121
+     * signal emitted after file/dir creation
122
+     *
123
+     * @param string $path
124
+     * @param bool $run changing this flag to false in hook handler will cancel event
125
+     */
126
+    const signal_post_create = 'post_create';
127
+
128
+    /**
129
+     * signal emits before file/dir copy
130
+     *
131
+     * @param string $oldpath
132
+     * @param string $newpath
133
+     * @param bool $run changing this flag to false in hook handler will cancel event
134
+     */
135
+    const signal_copy = 'copy';
136
+
137
+    /**
138
+     * signal emits after file/dir copy
139
+     *
140
+     * @param string $oldpath
141
+     * @param string $newpath
142
+     */
143
+    const signal_post_copy = 'post_copy';
144
+
145
+    /**
146
+     * signal emits before file/dir save
147
+     *
148
+     * @param string $path
149
+     * @param bool $run changing this flag to false in hook handler will cancel event
150
+     */
151
+    const signal_write = 'write';
152
+
153
+    /**
154
+     * signal emits after file/dir save
155
+     *
156
+     * @param string $path
157
+     */
158
+    const signal_post_write = 'post_write';
159
+
160
+    /**
161
+     * signal emitted before file/dir update
162
+     *
163
+     * @param string $path
164
+     * @param bool $run changing this flag to false in hook handler will cancel event
165
+     */
166
+    const signal_update = 'update';
167
+
168
+    /**
169
+     * signal emitted after file/dir update
170
+     *
171
+     * @param string $path
172
+     * @param bool $run changing this flag to false in hook handler will cancel event
173
+     */
174
+    const signal_post_update = 'post_update';
175
+
176
+    /**
177
+     * signal emits when reading file/dir
178
+     *
179
+     * @param string $path
180
+     */
181
+    const signal_read = 'read';
182
+
183
+    /**
184
+     * signal emits when removing file/dir
185
+     *
186
+     * @param string $path
187
+     */
188
+    const signal_delete = 'delete';
189
+
190
+    /**
191
+     * parameters definitions for signals
192
+     */
193
+    const signal_param_path = 'path';
194
+    const signal_param_oldpath = 'oldpath';
195
+    const signal_param_newpath = 'newpath';
196
+
197
+    /**
198
+     * run - changing this flag to false in hook handler will cancel event
199
+     */
200
+    const signal_param_run = 'run';
201
+
202
+    const signal_create_mount = 'create_mount';
203
+    const signal_delete_mount = 'delete_mount';
204
+    const signal_param_mount_type = 'mounttype';
205
+    const signal_param_users = 'users';
206
+
207
+    /**
208
+     * @var \OC\Files\Storage\StorageFactory $loader
209
+     */
210
+    private static $loader;
211
+
212
+    /**
213
+     * @param string $wrapperName
214
+     * @param callable $wrapper
215
+     * @param int $priority
216
+     */
217
+    public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) {
218
+        $mounts = self::getMountManager()->getAll();
219
+        if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) {
220
+            // do not re-wrap if storage with this name already existed
221
+            return;
222
+        }
223
+    }
224
+
225
+    /**
226
+     * Returns the storage factory
227
+     *
228
+     * @return \OCP\Files\Storage\IStorageFactory
229
+     */
230
+    public static function getLoader() {
231
+        if (!self::$loader) {
232
+            self::$loader = new StorageFactory();
233
+        }
234
+        return self::$loader;
235
+    }
236
+
237
+    /**
238
+     * Returns the mount manager
239
+     *
240
+     * @return \OC\Files\Mount\Manager
241
+     */
242
+    public static function getMountManager($user = '') {
243
+        if (!self::$mounts) {
244
+            \OC_Util::setupFS($user);
245
+        }
246
+        return self::$mounts;
247
+    }
248
+
249
+    /**
250
+     * get the mountpoint of the storage object for a path
251
+     * ( note: because a storage is not always mounted inside the fakeroot, the
252
+     * returned mountpoint is relative to the absolute root of the filesystem
253
+     * and doesn't take the chroot into account )
254
+     *
255
+     * @param string $path
256
+     * @return string
257
+     */
258
+    static public function getMountPoint($path) {
259
+        if (!self::$mounts) {
260
+            \OC_Util::setupFS();
261
+        }
262
+        $mount = self::$mounts->find($path);
263
+        if ($mount) {
264
+            return $mount->getMountPoint();
265
+        } else {
266
+            return '';
267
+        }
268
+    }
269
+
270
+    /**
271
+     * get a list of all mount points in a directory
272
+     *
273
+     * @param string $path
274
+     * @return string[]
275
+     */
276
+    static public function getMountPoints($path) {
277
+        if (!self::$mounts) {
278
+            \OC_Util::setupFS();
279
+        }
280
+        $result = array();
281
+        $mounts = self::$mounts->findIn($path);
282
+        foreach ($mounts as $mount) {
283
+            $result[] = $mount->getMountPoint();
284
+        }
285
+        return $result;
286
+    }
287
+
288
+    /**
289
+     * get the storage mounted at $mountPoint
290
+     *
291
+     * @param string $mountPoint
292
+     * @return \OC\Files\Storage\Storage
293
+     */
294
+    public static function getStorage($mountPoint) {
295
+        if (!self::$mounts) {
296
+            \OC_Util::setupFS();
297
+        }
298
+        $mount = self::$mounts->find($mountPoint);
299
+        return $mount->getStorage();
300
+    }
301
+
302
+    /**
303
+     * @param string $id
304
+     * @return Mount\MountPoint[]
305
+     */
306
+    public static function getMountByStorageId($id) {
307
+        if (!self::$mounts) {
308
+            \OC_Util::setupFS();
309
+        }
310
+        return self::$mounts->findByStorageId($id);
311
+    }
312
+
313
+    /**
314
+     * @param int $id
315
+     * @return Mount\MountPoint[]
316
+     */
317
+    public static function getMountByNumericId($id) {
318
+        if (!self::$mounts) {
319
+            \OC_Util::setupFS();
320
+        }
321
+        return self::$mounts->findByNumericId($id);
322
+    }
323
+
324
+    /**
325
+     * resolve a path to a storage and internal path
326
+     *
327
+     * @param string $path
328
+     * @return array an array consisting of the storage and the internal path
329
+     */
330
+    static public function resolvePath($path) {
331
+        if (!self::$mounts) {
332
+            \OC_Util::setupFS();
333
+        }
334
+        $mount = self::$mounts->find($path);
335
+        if ($mount) {
336
+            return array($mount->getStorage(), rtrim($mount->getInternalPath($path), '/'));
337
+        } else {
338
+            return array(null, null);
339
+        }
340
+    }
341
+
342
+    static public function init($user, $root) {
343
+        if (self::$defaultInstance) {
344
+            return false;
345
+        }
346
+        self::getLoader();
347
+        self::$defaultInstance = new View($root);
348
+
349
+        if (!self::$mounts) {
350
+            self::$mounts = \OC::$server->getMountManager();
351
+        }
352
+
353
+        //load custom mount config
354
+        self::initMountPoints($user);
355
+
356
+        self::$loaded = true;
357
+
358
+        return true;
359
+    }
360
+
361
+    static public function initMountManager() {
362
+        if (!self::$mounts) {
363
+            self::$mounts = \OC::$server->getMountManager();
364
+        }
365
+    }
366
+
367
+    /**
368
+     * Initialize system and personal mount points for a user
369
+     *
370
+     * @param string $user
371
+     * @throws \OC\User\NoUserException if the user is not available
372
+     */
373
+    public static function initMountPoints($user = '') {
374
+        if ($user == '') {
375
+            $user = \OC_User::getUser();
376
+        }
377
+        if ($user === null || $user === false || $user === '') {
378
+            throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session');
379
+        }
380
+        if (isset(self::$usersSetup[$user])) {
381
+            return;
382
+        }
383
+        self::$usersSetup[$user] = true;
384
+
385
+        $root = \OC_User::getHome($user);
386
+
387
+        $userManager = \OC::$server->getUserManager();
388
+        $userObject = $userManager->get($user);
389
+
390
+        if (is_null($userObject)) {
391
+            \OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR);
392
+            throw new \OC\User\NoUserException('Backends provided no user object for ' . $user);
393
+        }
394
+
395
+        $homeStorage = \OC::$server->getConfig()->getSystemValue('objectstore');
396
+        if (!empty($homeStorage)) {
397
+            // sanity checks
398
+            if (empty($homeStorage['class'])) {
399
+                \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
400
+            }
401
+            if (!isset($homeStorage['arguments'])) {
402
+                $homeStorage['arguments'] = array();
403
+            }
404
+            // instantiate object store implementation
405
+            $homeStorage['arguments']['objectstore'] = new $homeStorage['class']($homeStorage['arguments']);
406
+            // mount with home object store implementation
407
+            $homeStorage['class'] = '\OC\Files\ObjectStore\HomeObjectStoreStorage';
408
+        } else {
409
+            $homeStorage = array(
410
+                //default home storage configuration:
411
+                'class' => '\OC\Files\Storage\Home',
412
+                'arguments' => array()
413
+            );
414
+        }
415
+        $homeStorage['arguments']['user'] = $userObject;
416
+
417
+        // check for legacy home id (<= 5.0.12)
418
+        if (\OC\Files\Cache\Storage::exists('local::' . $root . '/')) {
419
+            $homeStorage['arguments']['legacy'] = true;
420
+        }
421
+
422
+        $mount = new MountPoint($homeStorage['class'], '/' . $user, $homeStorage['arguments'], self::getLoader());
423
+        self::getMountManager()->addMount($mount);
424
+
425
+        $home = \OC\Files\Filesystem::getStorage($user);
426
+
427
+        self::mountCacheDir($user);
428
+
429
+        // Chance to mount for other storages
430
+        /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */
431
+        $mountConfigManager = \OC::$server->getMountProviderCollection();
432
+        if ($userObject) {
433
+            $mounts = $mountConfigManager->getMountsForUser($userObject);
434
+            array_walk($mounts, array(self::$mounts, 'addMount'));
435
+            $mounts[] = $mount;
436
+            $mountConfigManager->registerMounts($userObject, $mounts);
437
+        }
438
+
439
+        self::listenForNewMountProviders($mountConfigManager, $userManager);
440
+        \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user, 'user_dir' => $root));
441
+    }
442
+
443
+    /**
444
+     * Get mounts from mount providers that are registered after setup
445
+     *
446
+     * @param MountProviderCollection $mountConfigManager
447
+     * @param IUserManager $userManager
448
+     */
449
+    private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) {
450
+        if (!self::$listeningForProviders) {
451
+            self::$listeningForProviders = true;
452
+            $mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) {
453
+                foreach (Filesystem::$usersSetup as $user => $setup) {
454
+                    $userObject = $userManager->get($user);
455
+                    if ($userObject) {
456
+                        $mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader());
457
+                        array_walk($mounts, array(self::$mounts, 'addMount'));
458
+                    }
459
+                }
460
+            });
461
+        }
462
+    }
463
+
464
+    /**
465
+     * Mounts the cache directory
466
+     *
467
+     * @param string $user user name
468
+     */
469
+    private static function mountCacheDir($user) {
470
+        $cacheBaseDir = \OC::$server->getConfig()->getSystemValue('cache_path', '');
471
+        if ($cacheBaseDir !== '') {
472
+            $cacheDir = rtrim($cacheBaseDir, '/') . '/' . $user;
473
+            if (!file_exists($cacheDir)) {
474
+                mkdir($cacheDir, 0770, true);
475
+            }
476
+            // mount external cache dir to "/$user/cache" mount point
477
+            self::mount('\OC\Files\Storage\Local', array('datadir' => $cacheDir), '/' . $user . '/cache');
478
+        }
479
+    }
480
+
481
+    /**
482
+     * get the default filesystem view
483
+     *
484
+     * @return View
485
+     */
486
+    static public function getView() {
487
+        return self::$defaultInstance;
488
+    }
489
+
490
+    /**
491
+     * tear down the filesystem, removing all storage providers
492
+     */
493
+    static public function tearDown() {
494
+        self::clearMounts();
495
+        self::$defaultInstance = null;
496
+    }
497
+
498
+    /**
499
+     * get the relative path of the root data directory for the current user
500
+     *
501
+     * @return string
502
+     *
503
+     * Returns path like /admin/files
504
+     */
505
+    static public function getRoot() {
506
+        if (!self::$defaultInstance) {
507
+            return null;
508
+        }
509
+        return self::$defaultInstance->getRoot();
510
+    }
511
+
512
+    /**
513
+     * clear all mounts and storage backends
514
+     */
515
+    public static function clearMounts() {
516
+        if (self::$mounts) {
517
+            self::$usersSetup = array();
518
+            self::$mounts->clear();
519
+        }
520
+    }
521
+
522
+    /**
523
+     * mount an \OC\Files\Storage\Storage in our virtual filesystem
524
+     *
525
+     * @param \OC\Files\Storage\Storage|string $class
526
+     * @param array $arguments
527
+     * @param string $mountpoint
528
+     */
529
+    static public function mount($class, $arguments, $mountpoint) {
530
+        if (!self::$mounts) {
531
+            \OC_Util::setupFS();
532
+        }
533
+        $mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader());
534
+        self::$mounts->addMount($mount);
535
+    }
536
+
537
+    /**
538
+     * return the path to a local version of the file
539
+     * we need this because we can't know if a file is stored local or not from
540
+     * outside the filestorage and for some purposes a local file is needed
541
+     *
542
+     * @param string $path
543
+     * @return string
544
+     */
545
+    static public function getLocalFile($path) {
546
+        return self::$defaultInstance->getLocalFile($path);
547
+    }
548
+
549
+    /**
550
+     * @param string $path
551
+     * @return string
552
+     */
553
+    static public function getLocalFolder($path) {
554
+        return self::$defaultInstance->getLocalFolder($path);
555
+    }
556
+
557
+    /**
558
+     * return path to file which reflects one visible in browser
559
+     *
560
+     * @param string $path
561
+     * @return string
562
+     */
563
+    static public function getLocalPath($path) {
564
+        $datadir = \OC_User::getHome(\OC_User::getUser()) . '/files';
565
+        $newpath = $path;
566
+        if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
567
+            $newpath = substr($path, strlen($datadir));
568
+        }
569
+        return $newpath;
570
+    }
571
+
572
+    /**
573
+     * check if the requested path is valid
574
+     *
575
+     * @param string $path
576
+     * @return bool
577
+     */
578
+    static public function isValidPath($path) {
579
+        $path = self::normalizePath($path);
580
+        if (!$path || $path[0] !== '/') {
581
+            $path = '/' . $path;
582
+        }
583
+        if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') {
584
+            return false;
585
+        }
586
+        return true;
587
+    }
588
+
589
+    /**
590
+     * checks if a file is blacklisted for storage in the filesystem
591
+     * Listens to write and rename hooks
592
+     *
593
+     * @param array $data from hook
594
+     */
595
+    static public function isBlacklisted($data) {
596
+        if (isset($data['path'])) {
597
+            $path = $data['path'];
598
+        } else if (isset($data['newpath'])) {
599
+            $path = $data['newpath'];
600
+        }
601
+        if (isset($path)) {
602
+            if (self::isFileBlacklisted($path)) {
603
+                $data['run'] = false;
604
+            }
605
+        }
606
+    }
607
+
608
+    /**
609
+     * @param string $filename
610
+     * @return bool
611
+     */
612
+    static public function isFileBlacklisted($filename) {
613
+        $filename = self::normalizePath($filename);
614
+
615
+        $blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', array('.htaccess'));
616
+        $filename = strtolower(basename($filename));
617
+        return in_array($filename, $blacklist);
618
+    }
619
+
620
+    /**
621
+     * check if the directory should be ignored when scanning
622
+     * NOTE: the special directories . and .. would cause never ending recursion
623
+     *
624
+     * @param String $dir
625
+     * @return boolean
626
+     */
627
+    static public function isIgnoredDir($dir) {
628
+        if ($dir === '.' || $dir === '..') {
629
+            return true;
630
+        }
631
+        return false;
632
+    }
633
+
634
+    /**
635
+     * following functions are equivalent to their php builtin equivalents for arguments/return values.
636
+     */
637
+    static public function mkdir($path) {
638
+        return self::$defaultInstance->mkdir($path);
639
+    }
640
+
641
+    static public function rmdir($path) {
642
+        return self::$defaultInstance->rmdir($path);
643
+    }
644
+
645
+    static public function opendir($path) {
646
+        return self::$defaultInstance->opendir($path);
647
+    }
648
+
649
+    static public function readdir($path) {
650
+        return self::$defaultInstance->readdir($path);
651
+    }
652
+
653
+    static public function is_dir($path) {
654
+        return self::$defaultInstance->is_dir($path);
655
+    }
656
+
657
+    static public function is_file($path) {
658
+        return self::$defaultInstance->is_file($path);
659
+    }
660
+
661
+    static public function stat($path) {
662
+        return self::$defaultInstance->stat($path);
663
+    }
664
+
665
+    static public function filetype($path) {
666
+        return self::$defaultInstance->filetype($path);
667
+    }
668
+
669
+    static public function filesize($path) {
670
+        return self::$defaultInstance->filesize($path);
671
+    }
672
+
673
+    static public function readfile($path) {
674
+        return self::$defaultInstance->readfile($path);
675
+    }
676
+
677
+    static public function isCreatable($path) {
678
+        return self::$defaultInstance->isCreatable($path);
679
+    }
680
+
681
+    static public function isReadable($path) {
682
+        return self::$defaultInstance->isReadable($path);
683
+    }
684
+
685
+    static public function isUpdatable($path) {
686
+        return self::$defaultInstance->isUpdatable($path);
687
+    }
688
+
689
+    static public function isDeletable($path) {
690
+        return self::$defaultInstance->isDeletable($path);
691
+    }
692
+
693
+    static public function isSharable($path) {
694
+        return self::$defaultInstance->isSharable($path);
695
+    }
696
+
697
+    static public function file_exists($path) {
698
+        return self::$defaultInstance->file_exists($path);
699
+    }
700
+
701
+    static public function filemtime($path) {
702
+        return self::$defaultInstance->filemtime($path);
703
+    }
704
+
705
+    static public function touch($path, $mtime = null) {
706
+        return self::$defaultInstance->touch($path, $mtime);
707
+    }
708
+
709
+    /**
710
+     * @return string
711
+     */
712
+    static public function file_get_contents($path) {
713
+        return self::$defaultInstance->file_get_contents($path);
714
+    }
715
+
716
+    static public function file_put_contents($path, $data) {
717
+        return self::$defaultInstance->file_put_contents($path, $data);
718
+    }
719
+
720
+    static public function unlink($path) {
721
+        return self::$defaultInstance->unlink($path);
722
+    }
723
+
724
+    static public function rename($path1, $path2) {
725
+        return self::$defaultInstance->rename($path1, $path2);
726
+    }
727
+
728
+    static public function copy($path1, $path2) {
729
+        return self::$defaultInstance->copy($path1, $path2);
730
+    }
731
+
732
+    static public function fopen($path, $mode) {
733
+        return self::$defaultInstance->fopen($path, $mode);
734
+    }
735
+
736
+    /**
737
+     * @return string
738
+     */
739
+    static public function toTmpFile($path) {
740
+        return self::$defaultInstance->toTmpFile($path);
741
+    }
742
+
743
+    static public function fromTmpFile($tmpFile, $path) {
744
+        return self::$defaultInstance->fromTmpFile($tmpFile, $path);
745
+    }
746
+
747
+    static public function getMimeType($path) {
748
+        return self::$defaultInstance->getMimeType($path);
749
+    }
750
+
751
+    static public function hash($type, $path, $raw = false) {
752
+        return self::$defaultInstance->hash($type, $path, $raw);
753
+    }
754
+
755
+    static public function free_space($path = '/') {
756
+        return self::$defaultInstance->free_space($path);
757
+    }
758
+
759
+    static public function search($query) {
760
+        return self::$defaultInstance->search($query);
761
+    }
762
+
763
+    /**
764
+     * @param string $query
765
+     */
766
+    static public function searchByMime($query) {
767
+        return self::$defaultInstance->searchByMime($query);
768
+    }
769
+
770
+    /**
771
+     * @param string|int $tag name or tag id
772
+     * @param string $userId owner of the tags
773
+     * @return FileInfo[] array or file info
774
+     */
775
+    static public function searchByTag($tag, $userId) {
776
+        return self::$defaultInstance->searchByTag($tag, $userId);
777
+    }
778
+
779
+    /**
780
+     * check if a file or folder has been updated since $time
781
+     *
782
+     * @param string $path
783
+     * @param int $time
784
+     * @return bool
785
+     */
786
+    static public function hasUpdated($path, $time) {
787
+        return self::$defaultInstance->hasUpdated($path, $time);
788
+    }
789
+
790
+    /**
791
+     * Fix common problems with a file path
792
+     *
793
+     * @param string $path
794
+     * @param bool $stripTrailingSlash
795
+     * @param bool $isAbsolutePath
796
+     * @return string
797
+     */
798
+    public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false) {
799
+        if (is_null(self::$normalizedPathCache)) {
800
+            self::$normalizedPathCache = new CappedMemoryCache();
801
+        }
802
+
803
+        /**
804
+         * FIXME: This is a workaround for existing classes and files which call
805
+         *        this function with another type than a valid string. This
806
+         *        conversion should get removed as soon as all existing
807
+         *        function calls have been fixed.
808
+         */
809
+        $path = (string)$path;
810
+
811
+        $cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath]);
812
+
813
+        if (isset(self::$normalizedPathCache[$cacheKey])) {
814
+            return self::$normalizedPathCache[$cacheKey];
815
+        }
816
+
817
+        if ($path == '') {
818
+            return '/';
819
+        }
820
+
821
+        //normalize unicode if possible
822
+        $path = \OC_Util::normalizeUnicode($path);
823
+
824
+        //no windows style slashes
825
+        $path = str_replace('\\', '/', $path);
826
+
827
+        // When normalizing an absolute path, we need to ensure that the drive-letter
828
+        // is still at the beginning on windows
829
+        $windows_drive_letter = '';
830
+        if ($isAbsolutePath && \OC_Util::runningOnWindows() && preg_match('#^([a-zA-Z])$#', $path[0]) && $path[1] == ':' && $path[2] == '/') {
831
+            $windows_drive_letter = substr($path, 0, 2);
832
+            $path = substr($path, 2);
833
+        }
834
+
835
+        //add leading slash
836
+        if ($path[0] !== '/') {
837
+            $path = '/' . $path;
838
+        }
839
+
840
+        // remove '/./'
841
+        // ugly, but str_replace() can't replace them all in one go
842
+        // as the replacement itself is part of the search string
843
+        // which will only be found during the next iteration
844
+        while (strpos($path, '/./') !== false) {
845
+            $path = str_replace('/./', '/', $path);
846
+        }
847
+        // remove sequences of slashes
848
+        $path = preg_replace('#/{2,}#', '/', $path);
849
+
850
+        //remove trailing slash
851
+        if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') {
852
+            $path = substr($path, 0, -1);
853
+        }
854
+
855
+        // remove trailing '/.'
856
+        if (substr($path, -2) == '/.') {
857
+            $path = substr($path, 0, -2);
858
+        }
859
+
860
+        $normalizedPath = $windows_drive_letter . $path;
861
+        self::$normalizedPathCache[$cacheKey] = $normalizedPath;
862
+
863
+        return $normalizedPath;
864
+    }
865
+
866
+    /**
867
+     * get the filesystem info
868
+     *
869
+     * @param string $path
870
+     * @param boolean $includeMountPoints whether to add mountpoint sizes,
871
+     * defaults to true
872
+     * @return \OC\Files\FileInfo|bool False if file does not exist
873
+     */
874
+    public static function getFileInfo($path, $includeMountPoints = true) {
875
+        return self::$defaultInstance->getFileInfo($path, $includeMountPoints);
876
+    }
877
+
878
+    /**
879
+     * change file metadata
880
+     *
881
+     * @param string $path
882
+     * @param array $data
883
+     * @return int
884
+     *
885
+     * returns the fileid of the updated file
886
+     */
887
+    public static function putFileInfo($path, $data) {
888
+        return self::$defaultInstance->putFileInfo($path, $data);
889
+    }
890
+
891
+    /**
892
+     * get the content of a directory
893
+     *
894
+     * @param string $directory path under datadirectory
895
+     * @param string $mimetype_filter limit returned content to this mimetype or mimepart
896
+     * @return \OC\Files\FileInfo[]
897
+     */
898
+    public static function getDirectoryContent($directory, $mimetype_filter = '') {
899
+        return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
900
+    }
901
+
902
+    /**
903
+     * Get the path of a file by id
904
+     *
905
+     * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
906
+     *
907
+     * @param int $id
908
+     * @throws NotFoundException
909
+     * @return string
910
+     */
911
+    public static function getPath($id) {
912
+        return self::$defaultInstance->getPath($id);
913
+    }
914
+
915
+    /**
916
+     * Get the owner for a file or folder
917
+     *
918
+     * @param string $path
919
+     * @return string
920
+     */
921
+    public static function getOwner($path) {
922
+        return self::$defaultInstance->getOwner($path);
923
+    }
924
+
925
+    /**
926
+     * get the ETag for a file or folder
927
+     *
928
+     * @param string $path
929
+     * @return string
930
+     */
931
+    static public function getETag($path) {
932
+        return self::$defaultInstance->getETag($path);
933
+    }
934 934
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -388,8 +388,8 @@  discard block
 block discarded – undo
388 388
 		$userObject = $userManager->get($user);
389 389
 
390 390
 		if (is_null($userObject)) {
391
-			\OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR);
392
-			throw new \OC\User\NoUserException('Backends provided no user object for ' . $user);
391
+			\OCP\Util::writeLog('files', ' Backends provided no user object for '.$user, \OCP\Util::ERROR);
392
+			throw new \OC\User\NoUserException('Backends provided no user object for '.$user);
393 393
 		}
394 394
 
395 395
 		$homeStorage = \OC::$server->getConfig()->getSystemValue('objectstore');
@@ -415,11 +415,11 @@  discard block
 block discarded – undo
415 415
 		$homeStorage['arguments']['user'] = $userObject;
416 416
 
417 417
 		// check for legacy home id (<= 5.0.12)
418
-		if (\OC\Files\Cache\Storage::exists('local::' . $root . '/')) {
418
+		if (\OC\Files\Cache\Storage::exists('local::'.$root.'/')) {
419 419
 			$homeStorage['arguments']['legacy'] = true;
420 420
 		}
421 421
 
422
-		$mount = new MountPoint($homeStorage['class'], '/' . $user, $homeStorage['arguments'], self::getLoader());
422
+		$mount = new MountPoint($homeStorage['class'], '/'.$user, $homeStorage['arguments'], self::getLoader());
423 423
 		self::getMountManager()->addMount($mount);
424 424
 
425 425
 		$home = \OC\Files\Filesystem::getStorage($user);
@@ -449,7 +449,7 @@  discard block
 block discarded – undo
449 449
 	private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) {
450 450
 		if (!self::$listeningForProviders) {
451 451
 			self::$listeningForProviders = true;
452
-			$mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) {
452
+			$mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function(IMountProvider $provider) use ($userManager) {
453 453
 				foreach (Filesystem::$usersSetup as $user => $setup) {
454 454
 					$userObject = $userManager->get($user);
455 455
 					if ($userObject) {
@@ -469,12 +469,12 @@  discard block
 block discarded – undo
469 469
 	private static function mountCacheDir($user) {
470 470
 		$cacheBaseDir = \OC::$server->getConfig()->getSystemValue('cache_path', '');
471 471
 		if ($cacheBaseDir !== '') {
472
-			$cacheDir = rtrim($cacheBaseDir, '/') . '/' . $user;
472
+			$cacheDir = rtrim($cacheBaseDir, '/').'/'.$user;
473 473
 			if (!file_exists($cacheDir)) {
474 474
 				mkdir($cacheDir, 0770, true);
475 475
 			}
476 476
 			// mount external cache dir to "/$user/cache" mount point
477
-			self::mount('\OC\Files\Storage\Local', array('datadir' => $cacheDir), '/' . $user . '/cache');
477
+			self::mount('\OC\Files\Storage\Local', array('datadir' => $cacheDir), '/'.$user.'/cache');
478 478
 		}
479 479
 	}
480 480
 
@@ -561,7 +561,7 @@  discard block
 block discarded – undo
561 561
 	 * @return string
562 562
 	 */
563 563
 	static public function getLocalPath($path) {
564
-		$datadir = \OC_User::getHome(\OC_User::getUser()) . '/files';
564
+		$datadir = \OC_User::getHome(\OC_User::getUser()).'/files';
565 565
 		$newpath = $path;
566 566
 		if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
567 567
 			$newpath = substr($path, strlen($datadir));
@@ -578,7 +578,7 @@  discard block
 block discarded – undo
578 578
 	static public function isValidPath($path) {
579 579
 		$path = self::normalizePath($path);
580 580
 		if (!$path || $path[0] !== '/') {
581
-			$path = '/' . $path;
581
+			$path = '/'.$path;
582 582
 		}
583 583
 		if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') {
584 584
 			return false;
@@ -806,7 +806,7 @@  discard block
 block discarded – undo
806 806
 		 *        conversion should get removed as soon as all existing
807 807
 		 *        function calls have been fixed.
808 808
 		 */
809
-		$path = (string)$path;
809
+		$path = (string) $path;
810 810
 
811 811
 		$cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath]);
812 812
 
@@ -834,7 +834,7 @@  discard block
 block discarded – undo
834 834
 
835 835
 		//add leading slash
836 836
 		if ($path[0] !== '/') {
837
-			$path = '/' . $path;
837
+			$path = '/'.$path;
838 838
 		}
839 839
 
840 840
 		// remove '/./'
@@ -857,7 +857,7 @@  discard block
 block discarded – undo
857 857
 			$path = substr($path, 0, -2);
858 858
 		}
859 859
 
860
-		$normalizedPath = $windows_drive_letter . $path;
860
+		$normalizedPath = $windows_drive_letter.$path;
861 861
 		self::$normalizedPathCache[$cacheKey] = $normalizedPath;
862 862
 
863 863
 		return $normalizedPath;
Please login to merge, or discard this patch.