Passed
Push — master ( c5c251...4e8552 )
by Morris
17:39 queued 06:15
created
lib/private/Share/Share.php 2 patches
Indentation   +1090 added lines, -1090 removed lines patch added patch discarded remove patch
@@ -50,1094 +50,1094 @@
 block discarded – undo
50 50
  */
51 51
 class Share extends Constants {
52 52
 
53
-	/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
54
-	 * Construct permissions for share() and setPermissions with Or (|) e.g.
55
-	 * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
56
-	 *
57
-	 * Check if permission is granted with And (&) e.g. Check if delete is
58
-	 * granted: if ($permissions & PERMISSION_DELETE)
59
-	 *
60
-	 * Remove permissions with And (&) and Not (~) e.g. Remove the update
61
-	 * permission: $permissions &= ~PERMISSION_UPDATE
62
-	 *
63
-	 * Apps are required to handle permissions on their own, this class only
64
-	 * stores and manages the permissions of shares
65
-	 * @see lib/public/constants.php
66
-	 */
67
-
68
-	/**
69
-	 * Register a sharing backend class that implements OCP\Share_Backend for an item type
70
-	 * @param string $itemType Item type
71
-	 * @param string $class Backend class
72
-	 * @param string $collectionOf (optional) Depends on item type
73
-	 * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
74
-	 * @return boolean true if backend is registered or false if error
75
-	 */
76
-	public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
77
-		if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') == 'yes') {
78
-			if (!isset(self::$backendTypes[$itemType])) {
79
-				self::$backendTypes[$itemType] = [
80
-					'class' => $class,
81
-					'collectionOf' => $collectionOf,
82
-					'supportedFileExtensions' => $supportedFileExtensions
83
-				];
84
-				return true;
85
-			}
86
-			\OCP\Util::writeLog('OCP\Share',
87
-				'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
88
-				.' is already registered for '.$itemType,
89
-				ILogger::WARN);
90
-		}
91
-		return false;
92
-	}
93
-
94
-	/**
95
-	 * Get the items of item type shared with the current user
96
-	 * @param string $itemType
97
-	 * @param int $format (optional) Format type must be defined by the backend
98
-	 * @param mixed $parameters (optional)
99
-	 * @param int $limit Number of items to return (optional) Returns all by default
100
-	 * @param boolean $includeCollections (optional)
101
-	 * @return mixed Return depends on format
102
-	 * @deprecated TESTS ONLY - this methods is only used by tests
103
-	 * called like this:
104
-	 * \OC\Share\Share::getItemsSharedWith('folder'); (apps/files_sharing/tests/UpdaterTest.php)
105
-	 */
106
-	public static function getItemsSharedWith() {
107
-		return self::getItems('folder', null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, self::FORMAT_NONE,
108
-			null, -1, false);
109
-	}
110
-
111
-	/**
112
-	 * Get the items of item type shared with a user
113
-	 * @param string $itemType
114
-	 * @param string $user id for which user we want the shares
115
-	 * @param int $format (optional) Format type must be defined by the backend
116
-	 * @param mixed $parameters (optional)
117
-	 * @param int $limit Number of items to return (optional) Returns all by default
118
-	 * @param boolean $includeCollections (optional)
119
-	 * @return mixed Return depends on format
120
-	 */
121
-	public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
122
-												  $parameters = null, $limit = -1, $includeCollections = false) {
123
-		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
124
-			$parameters, $limit, $includeCollections);
125
-	}
126
-
127
-	/**
128
-	 * Get the item of item type shared with a given user by source
129
-	 * @param string $itemType
130
-	 * @param string $itemSource
131
-	 * @param string $user User to whom the item was shared
132
-	 * @param string $owner Owner of the share
133
-	 * @param int $shareType only look for a specific share type
134
-	 * @return array Return list of items with file_target, permissions and expiration
135
-	 */
136
-	public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
137
-		$shares = [];
138
-		$fileDependent = false;
139
-
140
-		$where = 'WHERE';
141
-		$fileDependentWhere = '';
142
-		if ($itemType === 'file' || $itemType === 'folder') {
143
-			$fileDependent = true;
144
-			$column = 'file_source';
145
-			$fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
146
-			$fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
147
-		} else {
148
-			$column = 'item_source';
149
-		}
150
-
151
-		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
152
-
153
-		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
154
-		$arguments = [$itemSource, $itemType];
155
-		// for link shares $user === null
156
-		if ($user !== null) {
157
-			$where .= ' AND `share_with` = ? ';
158
-			$arguments[] = $user;
159
-		}
160
-
161
-		if ($shareType !== null) {
162
-			$where .= ' AND `share_type` = ? ';
163
-			$arguments[] = $shareType;
164
-		}
165
-
166
-		if ($owner !== null) {
167
-			$where .= ' AND `uid_owner` = ? ';
168
-			$arguments[] = $owner;
169
-		}
170
-
171
-		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
172
-
173
-		$result = \OC_DB::executeAudited($query, $arguments);
174
-
175
-		while ($row = $result->fetchRow()) {
176
-			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
177
-				continue;
178
-			}
179
-			if ($fileDependent && (int)$row['file_parent'] === -1) {
180
-				// if it is a mount point we need to get the path from the mount manager
181
-				$mountManager = \OC\Files\Filesystem::getMountManager();
182
-				$mountPoint = $mountManager->findByStorageId($row['storage_id']);
183
-				if (!empty($mountPoint)) {
184
-					$path = $mountPoint[0]->getMountPoint();
185
-					$path = trim($path, '/');
186
-					$path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
187
-					$row['path'] = $path;
188
-				} else {
189
-					\OC::$server->getLogger()->warning(
190
-						'Could not resolve mount point for ' . $row['storage_id'],
191
-						['app' => 'OCP\Share']
192
-					);
193
-				}
194
-			}
195
-			$shares[] = $row;
196
-		}
197
-
198
-		//if didn't found a result than let's look for a group share.
199
-		if (empty($shares) && $user !== null) {
200
-			$userObject = \OC::$server->getUserManager()->get($user);
201
-			$groups = [];
202
-			if ($userObject) {
203
-				$groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
204
-			}
205
-
206
-			if (!empty($groups)) {
207
-				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
208
-				$arguments = [$itemSource, $itemType, $groups];
209
-				$types = [null, null, IQueryBuilder::PARAM_STR_ARRAY];
210
-
211
-				if ($owner !== null) {
212
-					$where .= ' AND `uid_owner` = ?';
213
-					$arguments[] = $owner;
214
-					$types[] = null;
215
-				}
216
-
217
-				// TODO: inject connection, hopefully one day in the future when this
218
-				// class isn't static anymore...
219
-				$conn = \OC::$server->getDatabaseConnection();
220
-				$result = $conn->executeQuery(
221
-					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
222
-					$arguments,
223
-					$types
224
-				);
225
-
226
-				while ($row = $result->fetch()) {
227
-					$shares[] = $row;
228
-				}
229
-			}
230
-		}
231
-
232
-		return $shares;
233
-	}
234
-
235
-	/**
236
-	 * Get the item of item type shared with the current user by source
237
-	 * @param string $itemType
238
-	 * @param string $itemSource
239
-	 * @param int $format (optional) Format type must be defined by the backend
240
-	 * @param mixed $parameters
241
-	 * @param boolean $includeCollections
242
-	 * @param string $shareWith (optional) define against which user should be checked, default: current user
243
-	 * @return array
244
-	 */
245
-	public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
246
-													 $parameters = null, $includeCollections = false, $shareWith = null) {
247
-		$shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
248
-		return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
249
-			$parameters, 1, $includeCollections, true);
250
-	}
251
-
252
-	/**
253
-	 * Get the shared item of item type owned by the current user
254
-	 * @param string $itemType
255
-	 * @param string $itemSource
256
-	 * @param int $format (optional) Format type must be defined by the backend
257
-	 * @param mixed $parameters
258
-	 * @param boolean $includeCollections
259
-	 * @return mixed Return depends on format
260
-	 */
261
-	public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
262
-										 $parameters = null, $includeCollections = false) {
263
-		return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
264
-			$parameters, -1, $includeCollections);
265
-	}
266
-
267
-	/**
268
-	 * Unshare an item from a user, group, or delete a private link
269
-	 * @param string $itemType
270
-	 * @param string $itemSource
271
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
272
-	 * @param string $shareWith User or group the item is being shared with
273
-	 * @param string $owner owner of the share, if null the current user is used
274
-	 * @return boolean true on success or false on failure
275
-	 */
276
-	public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
277
-
278
-		// check if it is a valid itemType
279
-		self::getBackend($itemType);
280
-
281
-		$items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
282
-
283
-		$toDelete = [];
284
-		$newParent = null;
285
-		$currentUser = $owner ? $owner : \OC_User::getUser();
286
-		foreach ($items as $item) {
287
-			// delete the item with the expected share_type and owner
288
-			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
289
-				$toDelete = $item;
290
-			// if there is more then one result we don't have to delete the children
291
-				// but update their parent. For group shares the new parent should always be
292
-				// the original group share and not the db entry with the unique name
293
-			} elseif ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
294
-				$newParent = $item['parent'];
295
-			} else {
296
-				$newParent = $item['id'];
297
-			}
298
-		}
299
-
300
-		if (!empty($toDelete)) {
301
-			self::unshareItem($toDelete, $newParent);
302
-			return true;
303
-		}
304
-		return false;
305
-	}
306
-
307
-	/**
308
-	 * Checks whether a share has expired, calls unshareItem() if yes.
309
-	 * @param array $item Share data (usually database row)
310
-	 * @return boolean True if item was expired, false otherwise.
311
-	 */
312
-	protected static function expireItem(array $item) {
313
-		$result = false;
314
-
315
-		// only use default expiration date for link shares
316
-		if ((int) $item['share_type'] === IShare::TYPE_LINK) {
317
-
318
-			// calculate expiration date
319
-			if (!empty($item['expiration'])) {
320
-				$userDefinedExpire = new \DateTime($item['expiration']);
321
-				$expires = $userDefinedExpire->getTimestamp();
322
-			} else {
323
-				$expires = null;
324
-			}
325
-
326
-
327
-			// get default expiration settings
328
-			$defaultSettings = Helper::getDefaultExpireSetting();
329
-			$expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
330
-
331
-
332
-			if (is_int($expires)) {
333
-				$now = time();
334
-				if ($now > $expires) {
335
-					self::unshareItem($item);
336
-					$result = true;
337
-				}
338
-			}
339
-		}
340
-		return $result;
341
-	}
342
-
343
-	/**
344
-	 * Unshares a share given a share data array
345
-	 * @param array $item Share data (usually database row)
346
-	 * @param int $newParent parent ID
347
-	 * @return null
348
-	 */
349
-	protected static function unshareItem(array $item, $newParent = null) {
350
-		$shareType = (int)$item['share_type'];
351
-		$shareWith = null;
352
-		if ($shareType !== IShare::TYPE_LINK) {
353
-			$shareWith = $item['share_with'];
354
-		}
355
-
356
-		// Pass all the vars we have for now, they may be useful
357
-		$hookParams = [
358
-			'id'            => $item['id'],
359
-			'itemType'      => $item['item_type'],
360
-			'itemSource'    => $item['item_source'],
361
-			'shareType'     => $shareType,
362
-			'shareWith'     => $shareWith,
363
-			'itemParent'    => $item['parent'],
364
-			'uidOwner'      => $item['uid_owner'],
365
-		];
366
-		if ($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
367
-			$hookParams['fileSource'] = $item['file_source'];
368
-			$hookParams['fileTarget'] = $item['file_target'];
369
-		}
370
-
371
-		\OC_Hook::emit(\OCP\Share::class, 'pre_unshare', $hookParams);
372
-		$deletedShares = Helper::delete($item['id'], false, null, $newParent);
373
-		$deletedShares[] = $hookParams;
374
-		$hookParams['deletedShares'] = $deletedShares;
375
-		\OC_Hook::emit(\OCP\Share::class, 'post_unshare', $hookParams);
376
-		if ((int)$item['share_type'] === IShare::TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
377
-			list(, $remote) = Helper::splitUserRemote($item['share_with']);
378
-			self::sendRemoteUnshare($remote, $item['id'], $item['token']);
379
-		}
380
-	}
381
-
382
-	/**
383
-	 * Get the backend class for the specified item type
384
-	 * @param string $itemType
385
-	 * @throws \Exception
386
-	 * @return \OCP\Share_Backend
387
-	 */
388
-	public static function getBackend($itemType) {
389
-		$l = \OC::$server->getL10N('lib');
390
-		if (isset(self::$backends[$itemType])) {
391
-			return self::$backends[$itemType];
392
-		} elseif (isset(self::$backendTypes[$itemType]['class'])) {
393
-			$class = self::$backendTypes[$itemType]['class'];
394
-			if (class_exists($class)) {
395
-				self::$backends[$itemType] = new $class;
396
-				if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
397
-					$message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
398
-					$message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', [$class]);
399
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), ILogger::ERROR);
400
-					throw new \Exception($message_t);
401
-				}
402
-				return self::$backends[$itemType];
403
-			} else {
404
-				$message = 'Sharing backend %s not found';
405
-				$message_t = $l->t('Sharing backend %s not found', [$class]);
406
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), ILogger::ERROR);
407
-				throw new \Exception($message_t);
408
-			}
409
-		}
410
-		$message = 'Sharing backend for %s not found';
411
-		$message_t = $l->t('Sharing backend for %s not found', [$itemType]);
412
-		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), ILogger::ERROR);
413
-		throw new \Exception($message_t);
414
-	}
415
-
416
-	/**
417
-	 * Check if resharing is allowed
418
-	 * @return boolean true if allowed or false
419
-	 *
420
-	 * Resharing is allowed by default if not configured
421
-	 */
422
-	public static function isResharingAllowed() {
423
-		if (!isset(self::$isResharingAllowed)) {
424
-			if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
425
-				self::$isResharingAllowed = true;
426
-			} else {
427
-				self::$isResharingAllowed = false;
428
-			}
429
-		}
430
-		return self::$isResharingAllowed;
431
-	}
432
-
433
-	/**
434
-	 * Get a list of collection item types for the specified item type
435
-	 * @param string $itemType
436
-	 * @return array
437
-	 */
438
-	private static function getCollectionItemTypes($itemType) {
439
-		$collectionTypes = [$itemType];
440
-		foreach (self::$backendTypes as $type => $backend) {
441
-			if (in_array($backend['collectionOf'], $collectionTypes)) {
442
-				$collectionTypes[] = $type;
443
-			}
444
-		}
445
-		// TODO Add option for collections to be collection of themselves, only 'folder' does it now...
446
-		if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
447
-			unset($collectionTypes[0]);
448
-		}
449
-		// Return array if collections were found or the item type is a
450
-		// collection itself - collections can be inside collections
451
-		if (count($collectionTypes) > 0) {
452
-			return $collectionTypes;
453
-		}
454
-		return false;
455
-	}
456
-
457
-	/**
458
-	 * Get shared items from the database
459
-	 * @param string $itemType
460
-	 * @param string $item Item source or target (optional)
461
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
462
-	 * @param string $shareWith User or group the item is being shared with
463
-	 * @param string $uidOwner User that is the owner of shared items (optional)
464
-	 * @param int $format Format to convert items to with formatItems() (optional)
465
-	 * @param mixed $parameters to pass to formatItems() (optional)
466
-	 * @param int $limit Number of items to return, -1 to return all matches (optional)
467
-	 * @param boolean $includeCollections Include collection item types (optional)
468
-	 * @param boolean $itemShareWithBySource (optional)
469
-	 * @param boolean $checkExpireDate
470
-	 * @return array
471
-	 *
472
-	 * See public functions getItem(s)... for parameter usage
473
-	 *
474
-	 */
475
-	public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
476
-									$uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
477
-									$includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
478
-		if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') != 'yes') {
479
-			return [];
480
-		}
481
-		$backend = self::getBackend($itemType);
482
-		$collectionTypes = false;
483
-		// Get filesystem root to add it to the file target and remove from the
484
-		// file source, match file_source with the file cache
485
-		if ($itemType == 'file' || $itemType == 'folder') {
486
-			if (!is_null($uidOwner)) {
487
-				$root = \OC\Files\Filesystem::getRoot();
488
-			} else {
489
-				$root = '';
490
-			}
491
-			$where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
492
-			if (!isset($item)) {
493
-				$where .= ' AND `file_target` IS NOT NULL ';
494
-			}
495
-			$where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
496
-			$fileDependent = true;
497
-			$queryArgs = [];
498
-		} else {
499
-			$fileDependent = false;
500
-			$root = '';
501
-			$collectionTypes = self::getCollectionItemTypes($itemType);
502
-			if ($includeCollections && !isset($item) && $collectionTypes) {
503
-				// If includeCollections is true, find collections of this item type, e.g. a music album contains songs
504
-				if (!in_array($itemType, $collectionTypes)) {
505
-					$itemTypes = array_merge([$itemType], $collectionTypes);
506
-				} else {
507
-					$itemTypes = $collectionTypes;
508
-				}
509
-				$placeholders = implode(',', array_fill(0, count($itemTypes), '?'));
510
-				$where = ' WHERE `item_type` IN ('.$placeholders.'))';
511
-				$queryArgs = $itemTypes;
512
-			} else {
513
-				$where = ' WHERE `item_type` = ?';
514
-				$queryArgs = [$itemType];
515
-			}
516
-		}
517
-		if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
518
-			$where .= ' AND `share_type` != ?';
519
-			$queryArgs[] = IShare::TYPE_LINK;
520
-		}
521
-		if (isset($shareType)) {
522
-			// Include all user and group items
523
-			if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
524
-				$where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
525
-				$queryArgs[] = IShare::TYPE_USER;
526
-				$queryArgs[] = self::$shareTypeGroupUserUnique;
527
-				$queryArgs[] = $shareWith;
528
-
529
-				$user = \OC::$server->getUserManager()->get($shareWith);
530
-				$groups = [];
531
-				if ($user) {
532
-					$groups = \OC::$server->getGroupManager()->getUserGroupIds($user);
533
-				}
534
-				if (!empty($groups)) {
535
-					$placeholders = implode(',', array_fill(0, count($groups), '?'));
536
-					$where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
537
-					$queryArgs[] = IShare::TYPE_GROUP;
538
-					$queryArgs = array_merge($queryArgs, $groups);
539
-				}
540
-				$where .= ')';
541
-				// Don't include own group shares
542
-				$where .= ' AND `uid_owner` != ?';
543
-				$queryArgs[] = $shareWith;
544
-			} else {
545
-				$where .= ' AND `share_type` = ?';
546
-				$queryArgs[] = $shareType;
547
-				if (isset($shareWith)) {
548
-					$where .= ' AND `share_with` = ?';
549
-					$queryArgs[] = $shareWith;
550
-				}
551
-			}
552
-		}
553
-		if (isset($uidOwner)) {
554
-			$where .= ' AND `uid_owner` = ?';
555
-			$queryArgs[] = $uidOwner;
556
-			if (!isset($shareType)) {
557
-				// Prevent unique user targets for group shares from being selected
558
-				$where .= ' AND `share_type` != ?';
559
-				$queryArgs[] = self::$shareTypeGroupUserUnique;
560
-			}
561
-			if ($fileDependent) {
562
-				$column = 'file_source';
563
-			} else {
564
-				$column = 'item_source';
565
-			}
566
-		} else {
567
-			if ($fileDependent) {
568
-				$column = 'file_target';
569
-			} else {
570
-				$column = 'item_target';
571
-			}
572
-		}
573
-		if (isset($item)) {
574
-			$collectionTypes = self::getCollectionItemTypes($itemType);
575
-			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
576
-				$where .= ' AND (';
577
-			} else {
578
-				$where .= ' AND';
579
-			}
580
-			// If looking for own shared items, check item_source else check item_target
581
-			if (isset($uidOwner) || $itemShareWithBySource) {
582
-				// If item type is a file, file source needs to be checked in case the item was converted
583
-				if ($fileDependent) {
584
-					$where .= ' `file_source` = ?';
585
-					$column = 'file_source';
586
-				} else {
587
-					$where .= ' `item_source` = ?';
588
-					$column = 'item_source';
589
-				}
590
-			} else {
591
-				if ($fileDependent) {
592
-					$where .= ' `file_target` = ?';
593
-					$item = \OC\Files\Filesystem::normalizePath($item);
594
-				} else {
595
-					$where .= ' `item_target` = ?';
596
-				}
597
-			}
598
-			$queryArgs[] = $item;
599
-			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
600
-				$placeholders = implode(',', array_fill(0, count($collectionTypes), '?'));
601
-				$where .= ' OR `item_type` IN ('.$placeholders.'))';
602
-				$queryArgs = array_merge($queryArgs, $collectionTypes);
603
-			}
604
-		}
605
-
606
-		if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
607
-			// Make sure the unique user target is returned if it exists,
608
-			// unique targets should follow the group share in the database
609
-			// If the limit is not 1, the filtering can be done later
610
-			$where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
611
-		} else {
612
-			$where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
613
-		}
614
-
615
-		if ($limit != -1 && !$includeCollections) {
616
-			// The limit must be at least 3, because filtering needs to be done
617
-			if ($limit < 3) {
618
-				$queryLimit = 3;
619
-			} else {
620
-				$queryLimit = $limit;
621
-			}
622
-		} else {
623
-			$queryLimit = null;
624
-		}
625
-		$select = self::createSelectStatement($format, $fileDependent, $uidOwner);
626
-		$root = strlen($root);
627
-		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
628
-		$result = $query->execute($queryArgs);
629
-		if ($result === false) {
630
-			\OCP\Util::writeLog('OCP\Share',
631
-				\OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
632
-				ILogger::ERROR);
633
-		}
634
-		$items = [];
635
-		$targets = [];
636
-		$switchedItems = [];
637
-		$mounts = [];
638
-		while ($row = $result->fetchRow()) {
639
-			self::transformDBResults($row);
640
-			// Filter out duplicate group shares for users with unique targets
641
-			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
642
-				continue;
643
-			}
644
-			if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
645
-				$row['share_type'] = IShare::TYPE_GROUP;
646
-				$row['unique_name'] = true; // remember that we use a unique name for this user
647
-				$row['share_with'] = $items[$row['parent']]['share_with'];
648
-				// if the group share was unshared from the user we keep the permission, otherwise
649
-				// we take the permission from the parent because this is always the up-to-date
650
-				// permission for the group share
651
-				if ($row['permissions'] > 0) {
652
-					$row['permissions'] = $items[$row['parent']]['permissions'];
653
-				}
654
-				// Remove the parent group share
655
-				unset($items[$row['parent']]);
656
-				if ($row['permissions'] == 0) {
657
-					continue;
658
-				}
659
-			} elseif (!isset($uidOwner)) {
660
-				// Check if the same target already exists
661
-				if (isset($targets[$row['id']])) {
662
-					// Check if the same owner shared with the user twice
663
-					// through a group and user share - this is allowed
664
-					$id = $targets[$row['id']];
665
-					if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
666
-						// Switch to group share type to ensure resharing conditions aren't bypassed
667
-						if ($items[$id]['share_type'] != IShare::TYPE_GROUP) {
668
-							$items[$id]['share_type'] = IShare::TYPE_GROUP;
669
-							$items[$id]['share_with'] = $row['share_with'];
670
-						}
671
-						// Switch ids if sharing permission is granted on only
672
-						// one share to ensure correct parent is used if resharing
673
-						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
674
-							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
675
-							$items[$row['id']] = $items[$id];
676
-							$switchedItems[$id] = $row['id'];
677
-							unset($items[$id]);
678
-							$id = $row['id'];
679
-						}
680
-						$items[$id]['permissions'] |= (int)$row['permissions'];
681
-					}
682
-					continue;
683
-				} elseif (!empty($row['parent'])) {
684
-					$targets[$row['parent']] = $row['id'];
685
-				}
686
-			}
687
-			// Remove root from file source paths if retrieving own shared items
688
-			if (isset($uidOwner) && isset($row['path'])) {
689
-				if (isset($row['parent'])) {
690
-					$query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
691
-					$parentResult = $query->execute([$row['parent']]);
692
-					if ($result === false) {
693
-						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
694
-							\OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
695
-							ILogger::ERROR);
696
-					} else {
697
-						$parentRow = $parentResult->fetchRow();
698
-						$tmpPath = $parentRow['file_target'];
699
-						// find the right position where the row path continues from the target path
700
-						$pos = strrpos($row['path'], $parentRow['file_target']);
701
-						$subPath = substr($row['path'], $pos);
702
-						$splitPath = explode('/', $subPath);
703
-						foreach (array_slice($splitPath, 2) as $pathPart) {
704
-							$tmpPath = $tmpPath . '/' . $pathPart;
705
-						}
706
-						$row['path'] = $tmpPath;
707
-					}
708
-				} else {
709
-					if (!isset($mounts[$row['storage']])) {
710
-						$mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
711
-						if (is_array($mountPoints) && !empty($mountPoints)) {
712
-							$mounts[$row['storage']] = current($mountPoints);
713
-						}
714
-					}
715
-					if (!empty($mounts[$row['storage']])) {
716
-						$path = $mounts[$row['storage']]->getMountPoint().$row['path'];
717
-						$relPath = substr($path, $root); // path relative to data/user
718
-						$row['path'] = rtrim($relPath, '/');
719
-					}
720
-				}
721
-			}
722
-
723
-			if ($checkExpireDate) {
724
-				if (self::expireItem($row)) {
725
-					continue;
726
-				}
727
-			}
728
-			// Check if resharing is allowed, if not remove share permission
729
-			if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) {
730
-				$row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
731
-			}
732
-			// Add display names to result
733
-			$row['share_with_displayname'] = $row['share_with'];
734
-			if (isset($row['share_with']) && $row['share_with'] != '' &&
735
-				$row['share_type'] === IShare::TYPE_USER) {
736
-				$shareWithUser = \OC::$server->getUserManager()->get($row['share_with']);
737
-				$row['share_with_displayname'] = $shareWithUser === null ? $row['share_with'] : $shareWithUser->getDisplayName();
738
-			} elseif (isset($row['share_with']) && $row['share_with'] != '' &&
739
-				$row['share_type'] === IShare::TYPE_REMOTE) {
740
-				$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
741
-				foreach ($addressBookEntries as $entry) {
742
-					foreach ($entry['CLOUD'] as $cloudID) {
743
-						if ($cloudID === $row['share_with']) {
744
-							$row['share_with_displayname'] = $entry['FN'];
745
-						}
746
-					}
747
-				}
748
-			}
749
-			if (isset($row['uid_owner']) && $row['uid_owner'] != '') {
750
-				$ownerUser = \OC::$server->getUserManager()->get($row['uid_owner']);
751
-				$row['displayname_owner'] = $ownerUser === null ? $row['uid_owner'] : $ownerUser->getDisplayName();
752
-			}
753
-
754
-			if ($row['permissions'] > 0) {
755
-				$items[$row['id']] = $row;
756
-			}
757
-		}
758
-
759
-		// group items if we are looking for items shared with the current user
760
-		if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
761
-			$items = self::groupItems($items, $itemType);
762
-		}
763
-
764
-		if (!empty($items)) {
765
-			$collectionItems = [];
766
-			foreach ($items as &$row) {
767
-				// Return only the item instead of a 2-dimensional array
768
-				if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
769
-					if ($format == self::FORMAT_NONE) {
770
-						return $row;
771
-					} else {
772
-						break;
773
-					}
774
-				}
775
-				// Check if this is a collection of the requested item type
776
-				if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
777
-					if (($collectionBackend = self::getBackend($row['item_type']))
778
-						&& $collectionBackend instanceof \OCP\Share_Backend_Collection) {
779
-						// Collections can be inside collections, check if the item is a collection
780
-						if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
781
-							$collectionItems[] = $row;
782
-						} else {
783
-							$collection = [];
784
-							$collection['item_type'] = $row['item_type'];
785
-							if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
786
-								$collection['path'] = basename($row['path']);
787
-							}
788
-							$row['collection'] = $collection;
789
-							// Fetch all of the children sources
790
-							$children = $collectionBackend->getChildren($row[$column]);
791
-							foreach ($children as $child) {
792
-								$childItem = $row;
793
-								$childItem['item_type'] = $itemType;
794
-								if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
795
-									$childItem['item_source'] = $child['source'];
796
-									$childItem['item_target'] = $child['target'];
797
-								}
798
-								if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
799
-									if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
800
-										$childItem['file_source'] = $child['source'];
801
-									} else { // TODO is this really needed if we already know that we use the file backend?
802
-										$meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
803
-										$childItem['file_source'] = $meta['fileid'];
804
-									}
805
-									$childItem['file_target'] =
806
-										\OC\Files\Filesystem::normalizePath($child['file_path']);
807
-								}
808
-								if (isset($item)) {
809
-									if ($childItem[$column] == $item) {
810
-										// Return only the item instead of a 2-dimensional array
811
-										if ($limit == 1) {
812
-											if ($format == self::FORMAT_NONE) {
813
-												return $childItem;
814
-											} else {
815
-												// Unset the items array and break out of both loops
816
-												$items = [];
817
-												$items[] = $childItem;
818
-												break 2;
819
-											}
820
-										} else {
821
-											$collectionItems[] = $childItem;
822
-										}
823
-									}
824
-								} else {
825
-									$collectionItems[] = $childItem;
826
-								}
827
-							}
828
-						}
829
-					}
830
-					// Remove collection item
831
-					$toRemove = $row['id'];
832
-					if (array_key_exists($toRemove, $switchedItems)) {
833
-						$toRemove = $switchedItems[$toRemove];
834
-					}
835
-					unset($items[$toRemove]);
836
-				} elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
837
-					// FIXME: Thats a dirty hack to improve file sharing performance,
838
-					// see github issue #10588 for more details
839
-					// Need to find a solution which works for all back-ends
840
-					$collectionBackend = self::getBackend($row['item_type']);
841
-					$sharedParents = $collectionBackend->getParents($row['item_source']);
842
-					foreach ($sharedParents as $parent) {
843
-						$collectionItems[] = $parent;
844
-					}
845
-				}
846
-			}
847
-			if (!empty($collectionItems)) {
848
-				$collectionItems = array_unique($collectionItems, SORT_REGULAR);
849
-				$items = array_merge($items, $collectionItems);
850
-			}
851
-
852
-			// filter out invalid items, these can appear when subshare entries exist
853
-			// for a group in which the requested user isn't a member any more
854
-			$items = array_filter($items, function ($item) {
855
-				return $item['share_type'] !== self::$shareTypeGroupUserUnique;
856
-			});
857
-
858
-			return self::formatResult($items, $column, $backend, $format, $parameters);
859
-		} elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
860
-			// FIXME: Thats a dirty hack to improve file sharing performance,
861
-			// see github issue #10588 for more details
862
-			// Need to find a solution which works for all back-ends
863
-			$collectionItems = [];
864
-			$collectionBackend = self::getBackend('folder');
865
-			$sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
866
-			foreach ($sharedParents as $parent) {
867
-				$collectionItems[] = $parent;
868
-			}
869
-			if ($limit === 1) {
870
-				return reset($collectionItems);
871
-			}
872
-			return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
873
-		}
874
-
875
-		return [];
876
-	}
877
-
878
-	/**
879
-	 * group items with link to the same source
880
-	 *
881
-	 * @param array $items
882
-	 * @param string $itemType
883
-	 * @return array of grouped items
884
-	 */
885
-	protected static function groupItems($items, $itemType) {
886
-		$fileSharing = $itemType === 'file' || $itemType === 'folder';
887
-
888
-		$result = [];
889
-
890
-		foreach ($items as $item) {
891
-			$grouped = false;
892
-			foreach ($result as $key => $r) {
893
-				// for file/folder shares we need to compare file_source, otherwise we compare item_source
894
-				// only group shares if they already point to the same target, otherwise the file where shared
895
-				// before grouping of shares was added. In this case we don't group them toi avoid confusions
896
-				if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
897
-					(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
898
-					// add the first item to the list of grouped shares
899
-					if (!isset($result[$key]['grouped'])) {
900
-						$result[$key]['grouped'][] = $result[$key];
901
-					}
902
-					$result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
903
-					$result[$key]['grouped'][] = $item;
904
-					$grouped = true;
905
-					break;
906
-				}
907
-			}
908
-
909
-			if (!$grouped) {
910
-				$result[] = $item;
911
-			}
912
-		}
913
-
914
-		return $result;
915
-	}
916
-
917
-	/**
918
-	 * construct select statement
919
-	 * @param int $format
920
-	 * @param boolean $fileDependent ist it a file/folder share or a generla share
921
-	 * @param string $uidOwner
922
-	 * @return string select statement
923
-	 */
924
-	private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
925
-		$select = '*';
926
-		if ($format == self::FORMAT_STATUSES) {
927
-			if ($fileDependent) {
928
-				$select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
929
-					. '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
930
-					. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
931
-					. '`uid_initiator`';
932
-			} else {
933
-				$select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
934
-			}
935
-		} else {
936
-			if (isset($uidOwner)) {
937
-				if ($fileDependent) {
938
-					$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
939
-						. ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
940
-						. ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
941
-						. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
942
-				} else {
943
-					$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
944
-						. ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
945
-				}
946
-			} else {
947
-				if ($fileDependent) {
948
-					if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
949
-						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
950
-							. '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
951
-							. '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
952
-							. '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
953
-					} else {
954
-						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
955
-							. '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
956
-							. '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
957
-							. '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
958
-							. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
959
-					}
960
-				}
961
-			}
962
-		}
963
-		return $select;
964
-	}
965
-
966
-
967
-	/**
968
-	 * transform db results
969
-	 * @param array $row result
970
-	 */
971
-	private static function transformDBResults(&$row) {
972
-		if (isset($row['id'])) {
973
-			$row['id'] = (int) $row['id'];
974
-		}
975
-		if (isset($row['share_type'])) {
976
-			$row['share_type'] = (int) $row['share_type'];
977
-		}
978
-		if (isset($row['parent'])) {
979
-			$row['parent'] = (int) $row['parent'];
980
-		}
981
-		if (isset($row['file_parent'])) {
982
-			$row['file_parent'] = (int) $row['file_parent'];
983
-		}
984
-		if (isset($row['file_source'])) {
985
-			$row['file_source'] = (int) $row['file_source'];
986
-		}
987
-		if (isset($row['permissions'])) {
988
-			$row['permissions'] = (int) $row['permissions'];
989
-		}
990
-		if (isset($row['storage'])) {
991
-			$row['storage'] = (int) $row['storage'];
992
-		}
993
-		if (isset($row['stime'])) {
994
-			$row['stime'] = (int) $row['stime'];
995
-		}
996
-		if (isset($row['expiration']) && $row['share_type'] !== IShare::TYPE_LINK) {
997
-			// discard expiration date for non-link shares, which might have been
998
-			// set by ancient bugs
999
-			$row['expiration'] = null;
1000
-		}
1001
-	}
1002
-
1003
-	/**
1004
-	 * format result
1005
-	 * @param array $items result
1006
-	 * @param string $column is it a file share or a general share ('file_target' or 'item_target')
1007
-	 * @param \OCP\Share_Backend $backend sharing backend
1008
-	 * @param int $format
1009
-	 * @param array $parameters additional format parameters
1010
-	 * @return array format result
1011
-	 */
1012
-	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
1013
-		if ($format === self::FORMAT_NONE) {
1014
-			return $items;
1015
-		} elseif ($format === self::FORMAT_STATUSES) {
1016
-			$statuses = [];
1017
-			foreach ($items as $item) {
1018
-				if ($item['share_type'] === IShare::TYPE_LINK) {
1019
-					if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
1020
-						continue;
1021
-					}
1022
-					$statuses[$item[$column]]['link'] = true;
1023
-				} elseif (!isset($statuses[$item[$column]])) {
1024
-					$statuses[$item[$column]]['link'] = false;
1025
-				}
1026
-				if (!empty($item['file_target'])) {
1027
-					$statuses[$item[$column]]['path'] = $item['path'];
1028
-				}
1029
-			}
1030
-			return $statuses;
1031
-		} else {
1032
-			return $backend->formatItems($items, $format, $parameters);
1033
-		}
1034
-	}
1035
-
1036
-	/**
1037
-	 * remove protocol from URL
1038
-	 *
1039
-	 * @param string $url
1040
-	 * @return string
1041
-	 */
1042
-	public static function removeProtocolFromUrl($url) {
1043
-		if (strpos($url, 'https://') === 0) {
1044
-			return substr($url, strlen('https://'));
1045
-		} elseif (strpos($url, 'http://') === 0) {
1046
-			return substr($url, strlen('http://'));
1047
-		}
1048
-
1049
-		return $url;
1050
-	}
1051
-
1052
-	/**
1053
-	 * try http post first with https and then with http as a fallback
1054
-	 *
1055
-	 * @param string $remoteDomain
1056
-	 * @param string $urlSuffix
1057
-	 * @param array $fields post parameters
1058
-	 * @return array
1059
-	 */
1060
-	private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
1061
-		$protocol = 'https://';
1062
-		$result = [
1063
-			'success' => false,
1064
-			'result' => '',
1065
-		];
1066
-		$try = 0;
1067
-		$discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
1068
-		while ($result['success'] === false && $try < 2) {
1069
-			$federationEndpoints = $discoveryService->discover($protocol . $remoteDomain, 'FEDERATED_SHARING');
1070
-			$endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
1071
-			$client = \OC::$server->getHTTPClientService()->newClient();
1072
-
1073
-			try {
1074
-				$response = $client->post(
1075
-					$protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT,
1076
-					[
1077
-						'body' => $fields,
1078
-						'connect_timeout' => 10,
1079
-					]
1080
-				);
1081
-
1082
-				$result = ['success' => true, 'result' => $response->getBody()];
1083
-			} catch (\Exception $e) {
1084
-				$result = ['success' => false, 'result' => $e->getMessage()];
1085
-			}
1086
-
1087
-			$try++;
1088
-			$protocol = 'http://';
1089
-		}
1090
-
1091
-		return $result;
1092
-	}
1093
-
1094
-	/**
1095
-	 * send server-to-server unshare to remote server
1096
-	 *
1097
-	 * @param string $remote url
1098
-	 * @param int $id share id
1099
-	 * @param string $token
1100
-	 * @return bool
1101
-	 */
1102
-	private static function sendRemoteUnshare($remote, $id, $token) {
1103
-		$url = rtrim($remote, '/');
1104
-		$fields = ['token' => $token, 'format' => 'json'];
1105
-		$url = self::removeProtocolFromUrl($url);
1106
-		$result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
1107
-		$status = json_decode($result['result'], true);
1108
-
1109
-		return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
1110
-	}
1111
-
1112
-	/**
1113
-	 * @return int
1114
-	 */
1115
-	public static function getExpireInterval() {
1116
-		return (int)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1117
-	}
1118
-
1119
-	/**
1120
-	 * Checks whether the given path is reachable for the given owner
1121
-	 *
1122
-	 * @param string $path path relative to files
1123
-	 * @param string $ownerStorageId storage id of the owner
1124
-	 *
1125
-	 * @return boolean true if file is reachable, false otherwise
1126
-	 */
1127
-	private static function isFileReachable($path, $ownerStorageId) {
1128
-		// if outside the home storage, file is always considered reachable
1129
-		if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
1130
-			substr($ownerStorageId, 0, 13) === 'object::user:'
1131
-		)) {
1132
-			return true;
1133
-		}
1134
-
1135
-		// if inside the home storage, the file has to be under "/files/"
1136
-		$path = ltrim($path, '/');
1137
-		if (substr($path, 0, 6) === 'files/') {
1138
-			return true;
1139
-		}
1140
-
1141
-		return false;
1142
-	}
53
+    /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
54
+     * Construct permissions for share() and setPermissions with Or (|) e.g.
55
+     * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
56
+     *
57
+     * Check if permission is granted with And (&) e.g. Check if delete is
58
+     * granted: if ($permissions & PERMISSION_DELETE)
59
+     *
60
+     * Remove permissions with And (&) and Not (~) e.g. Remove the update
61
+     * permission: $permissions &= ~PERMISSION_UPDATE
62
+     *
63
+     * Apps are required to handle permissions on their own, this class only
64
+     * stores and manages the permissions of shares
65
+     * @see lib/public/constants.php
66
+     */
67
+
68
+    /**
69
+     * Register a sharing backend class that implements OCP\Share_Backend for an item type
70
+     * @param string $itemType Item type
71
+     * @param string $class Backend class
72
+     * @param string $collectionOf (optional) Depends on item type
73
+     * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
74
+     * @return boolean true if backend is registered or false if error
75
+     */
76
+    public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
77
+        if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') == 'yes') {
78
+            if (!isset(self::$backendTypes[$itemType])) {
79
+                self::$backendTypes[$itemType] = [
80
+                    'class' => $class,
81
+                    'collectionOf' => $collectionOf,
82
+                    'supportedFileExtensions' => $supportedFileExtensions
83
+                ];
84
+                return true;
85
+            }
86
+            \OCP\Util::writeLog('OCP\Share',
87
+                'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
88
+                .' is already registered for '.$itemType,
89
+                ILogger::WARN);
90
+        }
91
+        return false;
92
+    }
93
+
94
+    /**
95
+     * Get the items of item type shared with the current user
96
+     * @param string $itemType
97
+     * @param int $format (optional) Format type must be defined by the backend
98
+     * @param mixed $parameters (optional)
99
+     * @param int $limit Number of items to return (optional) Returns all by default
100
+     * @param boolean $includeCollections (optional)
101
+     * @return mixed Return depends on format
102
+     * @deprecated TESTS ONLY - this methods is only used by tests
103
+     * called like this:
104
+     * \OC\Share\Share::getItemsSharedWith('folder'); (apps/files_sharing/tests/UpdaterTest.php)
105
+     */
106
+    public static function getItemsSharedWith() {
107
+        return self::getItems('folder', null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, self::FORMAT_NONE,
108
+            null, -1, false);
109
+    }
110
+
111
+    /**
112
+     * Get the items of item type shared with a user
113
+     * @param string $itemType
114
+     * @param string $user id for which user we want the shares
115
+     * @param int $format (optional) Format type must be defined by the backend
116
+     * @param mixed $parameters (optional)
117
+     * @param int $limit Number of items to return (optional) Returns all by default
118
+     * @param boolean $includeCollections (optional)
119
+     * @return mixed Return depends on format
120
+     */
121
+    public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
122
+                                                    $parameters = null, $limit = -1, $includeCollections = false) {
123
+        return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
124
+            $parameters, $limit, $includeCollections);
125
+    }
126
+
127
+    /**
128
+     * Get the item of item type shared with a given user by source
129
+     * @param string $itemType
130
+     * @param string $itemSource
131
+     * @param string $user User to whom the item was shared
132
+     * @param string $owner Owner of the share
133
+     * @param int $shareType only look for a specific share type
134
+     * @return array Return list of items with file_target, permissions and expiration
135
+     */
136
+    public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
137
+        $shares = [];
138
+        $fileDependent = false;
139
+
140
+        $where = 'WHERE';
141
+        $fileDependentWhere = '';
142
+        if ($itemType === 'file' || $itemType === 'folder') {
143
+            $fileDependent = true;
144
+            $column = 'file_source';
145
+            $fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
146
+            $fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
147
+        } else {
148
+            $column = 'item_source';
149
+        }
150
+
151
+        $select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
152
+
153
+        $where .= ' `' . $column . '` = ? AND `item_type` = ? ';
154
+        $arguments = [$itemSource, $itemType];
155
+        // for link shares $user === null
156
+        if ($user !== null) {
157
+            $where .= ' AND `share_with` = ? ';
158
+            $arguments[] = $user;
159
+        }
160
+
161
+        if ($shareType !== null) {
162
+            $where .= ' AND `share_type` = ? ';
163
+            $arguments[] = $shareType;
164
+        }
165
+
166
+        if ($owner !== null) {
167
+            $where .= ' AND `uid_owner` = ? ';
168
+            $arguments[] = $owner;
169
+        }
170
+
171
+        $query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
172
+
173
+        $result = \OC_DB::executeAudited($query, $arguments);
174
+
175
+        while ($row = $result->fetchRow()) {
176
+            if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
177
+                continue;
178
+            }
179
+            if ($fileDependent && (int)$row['file_parent'] === -1) {
180
+                // if it is a mount point we need to get the path from the mount manager
181
+                $mountManager = \OC\Files\Filesystem::getMountManager();
182
+                $mountPoint = $mountManager->findByStorageId($row['storage_id']);
183
+                if (!empty($mountPoint)) {
184
+                    $path = $mountPoint[0]->getMountPoint();
185
+                    $path = trim($path, '/');
186
+                    $path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
187
+                    $row['path'] = $path;
188
+                } else {
189
+                    \OC::$server->getLogger()->warning(
190
+                        'Could not resolve mount point for ' . $row['storage_id'],
191
+                        ['app' => 'OCP\Share']
192
+                    );
193
+                }
194
+            }
195
+            $shares[] = $row;
196
+        }
197
+
198
+        //if didn't found a result than let's look for a group share.
199
+        if (empty($shares) && $user !== null) {
200
+            $userObject = \OC::$server->getUserManager()->get($user);
201
+            $groups = [];
202
+            if ($userObject) {
203
+                $groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
204
+            }
205
+
206
+            if (!empty($groups)) {
207
+                $where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
208
+                $arguments = [$itemSource, $itemType, $groups];
209
+                $types = [null, null, IQueryBuilder::PARAM_STR_ARRAY];
210
+
211
+                if ($owner !== null) {
212
+                    $where .= ' AND `uid_owner` = ?';
213
+                    $arguments[] = $owner;
214
+                    $types[] = null;
215
+                }
216
+
217
+                // TODO: inject connection, hopefully one day in the future when this
218
+                // class isn't static anymore...
219
+                $conn = \OC::$server->getDatabaseConnection();
220
+                $result = $conn->executeQuery(
221
+                    'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
222
+                    $arguments,
223
+                    $types
224
+                );
225
+
226
+                while ($row = $result->fetch()) {
227
+                    $shares[] = $row;
228
+                }
229
+            }
230
+        }
231
+
232
+        return $shares;
233
+    }
234
+
235
+    /**
236
+     * Get the item of item type shared with the current user by source
237
+     * @param string $itemType
238
+     * @param string $itemSource
239
+     * @param int $format (optional) Format type must be defined by the backend
240
+     * @param mixed $parameters
241
+     * @param boolean $includeCollections
242
+     * @param string $shareWith (optional) define against which user should be checked, default: current user
243
+     * @return array
244
+     */
245
+    public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
246
+                                                        $parameters = null, $includeCollections = false, $shareWith = null) {
247
+        $shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
248
+        return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
249
+            $parameters, 1, $includeCollections, true);
250
+    }
251
+
252
+    /**
253
+     * Get the shared item of item type owned by the current user
254
+     * @param string $itemType
255
+     * @param string $itemSource
256
+     * @param int $format (optional) Format type must be defined by the backend
257
+     * @param mixed $parameters
258
+     * @param boolean $includeCollections
259
+     * @return mixed Return depends on format
260
+     */
261
+    public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
262
+                                            $parameters = null, $includeCollections = false) {
263
+        return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
264
+            $parameters, -1, $includeCollections);
265
+    }
266
+
267
+    /**
268
+     * Unshare an item from a user, group, or delete a private link
269
+     * @param string $itemType
270
+     * @param string $itemSource
271
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
272
+     * @param string $shareWith User or group the item is being shared with
273
+     * @param string $owner owner of the share, if null the current user is used
274
+     * @return boolean true on success or false on failure
275
+     */
276
+    public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
277
+
278
+        // check if it is a valid itemType
279
+        self::getBackend($itemType);
280
+
281
+        $items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
282
+
283
+        $toDelete = [];
284
+        $newParent = null;
285
+        $currentUser = $owner ? $owner : \OC_User::getUser();
286
+        foreach ($items as $item) {
287
+            // delete the item with the expected share_type and owner
288
+            if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
289
+                $toDelete = $item;
290
+            // if there is more then one result we don't have to delete the children
291
+                // but update their parent. For group shares the new parent should always be
292
+                // the original group share and not the db entry with the unique name
293
+            } elseif ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
294
+                $newParent = $item['parent'];
295
+            } else {
296
+                $newParent = $item['id'];
297
+            }
298
+        }
299
+
300
+        if (!empty($toDelete)) {
301
+            self::unshareItem($toDelete, $newParent);
302
+            return true;
303
+        }
304
+        return false;
305
+    }
306
+
307
+    /**
308
+     * Checks whether a share has expired, calls unshareItem() if yes.
309
+     * @param array $item Share data (usually database row)
310
+     * @return boolean True if item was expired, false otherwise.
311
+     */
312
+    protected static function expireItem(array $item) {
313
+        $result = false;
314
+
315
+        // only use default expiration date for link shares
316
+        if ((int) $item['share_type'] === IShare::TYPE_LINK) {
317
+
318
+            // calculate expiration date
319
+            if (!empty($item['expiration'])) {
320
+                $userDefinedExpire = new \DateTime($item['expiration']);
321
+                $expires = $userDefinedExpire->getTimestamp();
322
+            } else {
323
+                $expires = null;
324
+            }
325
+
326
+
327
+            // get default expiration settings
328
+            $defaultSettings = Helper::getDefaultExpireSetting();
329
+            $expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
330
+
331
+
332
+            if (is_int($expires)) {
333
+                $now = time();
334
+                if ($now > $expires) {
335
+                    self::unshareItem($item);
336
+                    $result = true;
337
+                }
338
+            }
339
+        }
340
+        return $result;
341
+    }
342
+
343
+    /**
344
+     * Unshares a share given a share data array
345
+     * @param array $item Share data (usually database row)
346
+     * @param int $newParent parent ID
347
+     * @return null
348
+     */
349
+    protected static function unshareItem(array $item, $newParent = null) {
350
+        $shareType = (int)$item['share_type'];
351
+        $shareWith = null;
352
+        if ($shareType !== IShare::TYPE_LINK) {
353
+            $shareWith = $item['share_with'];
354
+        }
355
+
356
+        // Pass all the vars we have for now, they may be useful
357
+        $hookParams = [
358
+            'id'            => $item['id'],
359
+            'itemType'      => $item['item_type'],
360
+            'itemSource'    => $item['item_source'],
361
+            'shareType'     => $shareType,
362
+            'shareWith'     => $shareWith,
363
+            'itemParent'    => $item['parent'],
364
+            'uidOwner'      => $item['uid_owner'],
365
+        ];
366
+        if ($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
367
+            $hookParams['fileSource'] = $item['file_source'];
368
+            $hookParams['fileTarget'] = $item['file_target'];
369
+        }
370
+
371
+        \OC_Hook::emit(\OCP\Share::class, 'pre_unshare', $hookParams);
372
+        $deletedShares = Helper::delete($item['id'], false, null, $newParent);
373
+        $deletedShares[] = $hookParams;
374
+        $hookParams['deletedShares'] = $deletedShares;
375
+        \OC_Hook::emit(\OCP\Share::class, 'post_unshare', $hookParams);
376
+        if ((int)$item['share_type'] === IShare::TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
377
+            list(, $remote) = Helper::splitUserRemote($item['share_with']);
378
+            self::sendRemoteUnshare($remote, $item['id'], $item['token']);
379
+        }
380
+    }
381
+
382
+    /**
383
+     * Get the backend class for the specified item type
384
+     * @param string $itemType
385
+     * @throws \Exception
386
+     * @return \OCP\Share_Backend
387
+     */
388
+    public static function getBackend($itemType) {
389
+        $l = \OC::$server->getL10N('lib');
390
+        if (isset(self::$backends[$itemType])) {
391
+            return self::$backends[$itemType];
392
+        } elseif (isset(self::$backendTypes[$itemType]['class'])) {
393
+            $class = self::$backendTypes[$itemType]['class'];
394
+            if (class_exists($class)) {
395
+                self::$backends[$itemType] = new $class;
396
+                if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
397
+                    $message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
398
+                    $message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', [$class]);
399
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $class), ILogger::ERROR);
400
+                    throw new \Exception($message_t);
401
+                }
402
+                return self::$backends[$itemType];
403
+            } else {
404
+                $message = 'Sharing backend %s not found';
405
+                $message_t = $l->t('Sharing backend %s not found', [$class]);
406
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $class), ILogger::ERROR);
407
+                throw new \Exception($message_t);
408
+            }
409
+        }
410
+        $message = 'Sharing backend for %s not found';
411
+        $message_t = $l->t('Sharing backend for %s not found', [$itemType]);
412
+        \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), ILogger::ERROR);
413
+        throw new \Exception($message_t);
414
+    }
415
+
416
+    /**
417
+     * Check if resharing is allowed
418
+     * @return boolean true if allowed or false
419
+     *
420
+     * Resharing is allowed by default if not configured
421
+     */
422
+    public static function isResharingAllowed() {
423
+        if (!isset(self::$isResharingAllowed)) {
424
+            if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
425
+                self::$isResharingAllowed = true;
426
+            } else {
427
+                self::$isResharingAllowed = false;
428
+            }
429
+        }
430
+        return self::$isResharingAllowed;
431
+    }
432
+
433
+    /**
434
+     * Get a list of collection item types for the specified item type
435
+     * @param string $itemType
436
+     * @return array
437
+     */
438
+    private static function getCollectionItemTypes($itemType) {
439
+        $collectionTypes = [$itemType];
440
+        foreach (self::$backendTypes as $type => $backend) {
441
+            if (in_array($backend['collectionOf'], $collectionTypes)) {
442
+                $collectionTypes[] = $type;
443
+            }
444
+        }
445
+        // TODO Add option for collections to be collection of themselves, only 'folder' does it now...
446
+        if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
447
+            unset($collectionTypes[0]);
448
+        }
449
+        // Return array if collections were found or the item type is a
450
+        // collection itself - collections can be inside collections
451
+        if (count($collectionTypes) > 0) {
452
+            return $collectionTypes;
453
+        }
454
+        return false;
455
+    }
456
+
457
+    /**
458
+     * Get shared items from the database
459
+     * @param string $itemType
460
+     * @param string $item Item source or target (optional)
461
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
462
+     * @param string $shareWith User or group the item is being shared with
463
+     * @param string $uidOwner User that is the owner of shared items (optional)
464
+     * @param int $format Format to convert items to with formatItems() (optional)
465
+     * @param mixed $parameters to pass to formatItems() (optional)
466
+     * @param int $limit Number of items to return, -1 to return all matches (optional)
467
+     * @param boolean $includeCollections Include collection item types (optional)
468
+     * @param boolean $itemShareWithBySource (optional)
469
+     * @param boolean $checkExpireDate
470
+     * @return array
471
+     *
472
+     * See public functions getItem(s)... for parameter usage
473
+     *
474
+     */
475
+    public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
476
+                                    $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
477
+                                    $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
478
+        if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') != 'yes') {
479
+            return [];
480
+        }
481
+        $backend = self::getBackend($itemType);
482
+        $collectionTypes = false;
483
+        // Get filesystem root to add it to the file target and remove from the
484
+        // file source, match file_source with the file cache
485
+        if ($itemType == 'file' || $itemType == 'folder') {
486
+            if (!is_null($uidOwner)) {
487
+                $root = \OC\Files\Filesystem::getRoot();
488
+            } else {
489
+                $root = '';
490
+            }
491
+            $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
492
+            if (!isset($item)) {
493
+                $where .= ' AND `file_target` IS NOT NULL ';
494
+            }
495
+            $where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
496
+            $fileDependent = true;
497
+            $queryArgs = [];
498
+        } else {
499
+            $fileDependent = false;
500
+            $root = '';
501
+            $collectionTypes = self::getCollectionItemTypes($itemType);
502
+            if ($includeCollections && !isset($item) && $collectionTypes) {
503
+                // If includeCollections is true, find collections of this item type, e.g. a music album contains songs
504
+                if (!in_array($itemType, $collectionTypes)) {
505
+                    $itemTypes = array_merge([$itemType], $collectionTypes);
506
+                } else {
507
+                    $itemTypes = $collectionTypes;
508
+                }
509
+                $placeholders = implode(',', array_fill(0, count($itemTypes), '?'));
510
+                $where = ' WHERE `item_type` IN ('.$placeholders.'))';
511
+                $queryArgs = $itemTypes;
512
+            } else {
513
+                $where = ' WHERE `item_type` = ?';
514
+                $queryArgs = [$itemType];
515
+            }
516
+        }
517
+        if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
518
+            $where .= ' AND `share_type` != ?';
519
+            $queryArgs[] = IShare::TYPE_LINK;
520
+        }
521
+        if (isset($shareType)) {
522
+            // Include all user and group items
523
+            if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
524
+                $where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
525
+                $queryArgs[] = IShare::TYPE_USER;
526
+                $queryArgs[] = self::$shareTypeGroupUserUnique;
527
+                $queryArgs[] = $shareWith;
528
+
529
+                $user = \OC::$server->getUserManager()->get($shareWith);
530
+                $groups = [];
531
+                if ($user) {
532
+                    $groups = \OC::$server->getGroupManager()->getUserGroupIds($user);
533
+                }
534
+                if (!empty($groups)) {
535
+                    $placeholders = implode(',', array_fill(0, count($groups), '?'));
536
+                    $where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
537
+                    $queryArgs[] = IShare::TYPE_GROUP;
538
+                    $queryArgs = array_merge($queryArgs, $groups);
539
+                }
540
+                $where .= ')';
541
+                // Don't include own group shares
542
+                $where .= ' AND `uid_owner` != ?';
543
+                $queryArgs[] = $shareWith;
544
+            } else {
545
+                $where .= ' AND `share_type` = ?';
546
+                $queryArgs[] = $shareType;
547
+                if (isset($shareWith)) {
548
+                    $where .= ' AND `share_with` = ?';
549
+                    $queryArgs[] = $shareWith;
550
+                }
551
+            }
552
+        }
553
+        if (isset($uidOwner)) {
554
+            $where .= ' AND `uid_owner` = ?';
555
+            $queryArgs[] = $uidOwner;
556
+            if (!isset($shareType)) {
557
+                // Prevent unique user targets for group shares from being selected
558
+                $where .= ' AND `share_type` != ?';
559
+                $queryArgs[] = self::$shareTypeGroupUserUnique;
560
+            }
561
+            if ($fileDependent) {
562
+                $column = 'file_source';
563
+            } else {
564
+                $column = 'item_source';
565
+            }
566
+        } else {
567
+            if ($fileDependent) {
568
+                $column = 'file_target';
569
+            } else {
570
+                $column = 'item_target';
571
+            }
572
+        }
573
+        if (isset($item)) {
574
+            $collectionTypes = self::getCollectionItemTypes($itemType);
575
+            if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
576
+                $where .= ' AND (';
577
+            } else {
578
+                $where .= ' AND';
579
+            }
580
+            // If looking for own shared items, check item_source else check item_target
581
+            if (isset($uidOwner) || $itemShareWithBySource) {
582
+                // If item type is a file, file source needs to be checked in case the item was converted
583
+                if ($fileDependent) {
584
+                    $where .= ' `file_source` = ?';
585
+                    $column = 'file_source';
586
+                } else {
587
+                    $where .= ' `item_source` = ?';
588
+                    $column = 'item_source';
589
+                }
590
+            } else {
591
+                if ($fileDependent) {
592
+                    $where .= ' `file_target` = ?';
593
+                    $item = \OC\Files\Filesystem::normalizePath($item);
594
+                } else {
595
+                    $where .= ' `item_target` = ?';
596
+                }
597
+            }
598
+            $queryArgs[] = $item;
599
+            if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
600
+                $placeholders = implode(',', array_fill(0, count($collectionTypes), '?'));
601
+                $where .= ' OR `item_type` IN ('.$placeholders.'))';
602
+                $queryArgs = array_merge($queryArgs, $collectionTypes);
603
+            }
604
+        }
605
+
606
+        if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
607
+            // Make sure the unique user target is returned if it exists,
608
+            // unique targets should follow the group share in the database
609
+            // If the limit is not 1, the filtering can be done later
610
+            $where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
611
+        } else {
612
+            $where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
613
+        }
614
+
615
+        if ($limit != -1 && !$includeCollections) {
616
+            // The limit must be at least 3, because filtering needs to be done
617
+            if ($limit < 3) {
618
+                $queryLimit = 3;
619
+            } else {
620
+                $queryLimit = $limit;
621
+            }
622
+        } else {
623
+            $queryLimit = null;
624
+        }
625
+        $select = self::createSelectStatement($format, $fileDependent, $uidOwner);
626
+        $root = strlen($root);
627
+        $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
628
+        $result = $query->execute($queryArgs);
629
+        if ($result === false) {
630
+            \OCP\Util::writeLog('OCP\Share',
631
+                \OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
632
+                ILogger::ERROR);
633
+        }
634
+        $items = [];
635
+        $targets = [];
636
+        $switchedItems = [];
637
+        $mounts = [];
638
+        while ($row = $result->fetchRow()) {
639
+            self::transformDBResults($row);
640
+            // Filter out duplicate group shares for users with unique targets
641
+            if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
642
+                continue;
643
+            }
644
+            if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
645
+                $row['share_type'] = IShare::TYPE_GROUP;
646
+                $row['unique_name'] = true; // remember that we use a unique name for this user
647
+                $row['share_with'] = $items[$row['parent']]['share_with'];
648
+                // if the group share was unshared from the user we keep the permission, otherwise
649
+                // we take the permission from the parent because this is always the up-to-date
650
+                // permission for the group share
651
+                if ($row['permissions'] > 0) {
652
+                    $row['permissions'] = $items[$row['parent']]['permissions'];
653
+                }
654
+                // Remove the parent group share
655
+                unset($items[$row['parent']]);
656
+                if ($row['permissions'] == 0) {
657
+                    continue;
658
+                }
659
+            } elseif (!isset($uidOwner)) {
660
+                // Check if the same target already exists
661
+                if (isset($targets[$row['id']])) {
662
+                    // Check if the same owner shared with the user twice
663
+                    // through a group and user share - this is allowed
664
+                    $id = $targets[$row['id']];
665
+                    if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
666
+                        // Switch to group share type to ensure resharing conditions aren't bypassed
667
+                        if ($items[$id]['share_type'] != IShare::TYPE_GROUP) {
668
+                            $items[$id]['share_type'] = IShare::TYPE_GROUP;
669
+                            $items[$id]['share_with'] = $row['share_with'];
670
+                        }
671
+                        // Switch ids if sharing permission is granted on only
672
+                        // one share to ensure correct parent is used if resharing
673
+                        if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
674
+                            && (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
675
+                            $items[$row['id']] = $items[$id];
676
+                            $switchedItems[$id] = $row['id'];
677
+                            unset($items[$id]);
678
+                            $id = $row['id'];
679
+                        }
680
+                        $items[$id]['permissions'] |= (int)$row['permissions'];
681
+                    }
682
+                    continue;
683
+                } elseif (!empty($row['parent'])) {
684
+                    $targets[$row['parent']] = $row['id'];
685
+                }
686
+            }
687
+            // Remove root from file source paths if retrieving own shared items
688
+            if (isset($uidOwner) && isset($row['path'])) {
689
+                if (isset($row['parent'])) {
690
+                    $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
691
+                    $parentResult = $query->execute([$row['parent']]);
692
+                    if ($result === false) {
693
+                        \OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
694
+                            \OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
695
+                            ILogger::ERROR);
696
+                    } else {
697
+                        $parentRow = $parentResult->fetchRow();
698
+                        $tmpPath = $parentRow['file_target'];
699
+                        // find the right position where the row path continues from the target path
700
+                        $pos = strrpos($row['path'], $parentRow['file_target']);
701
+                        $subPath = substr($row['path'], $pos);
702
+                        $splitPath = explode('/', $subPath);
703
+                        foreach (array_slice($splitPath, 2) as $pathPart) {
704
+                            $tmpPath = $tmpPath . '/' . $pathPart;
705
+                        }
706
+                        $row['path'] = $tmpPath;
707
+                    }
708
+                } else {
709
+                    if (!isset($mounts[$row['storage']])) {
710
+                        $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
711
+                        if (is_array($mountPoints) && !empty($mountPoints)) {
712
+                            $mounts[$row['storage']] = current($mountPoints);
713
+                        }
714
+                    }
715
+                    if (!empty($mounts[$row['storage']])) {
716
+                        $path = $mounts[$row['storage']]->getMountPoint().$row['path'];
717
+                        $relPath = substr($path, $root); // path relative to data/user
718
+                        $row['path'] = rtrim($relPath, '/');
719
+                    }
720
+                }
721
+            }
722
+
723
+            if ($checkExpireDate) {
724
+                if (self::expireItem($row)) {
725
+                    continue;
726
+                }
727
+            }
728
+            // Check if resharing is allowed, if not remove share permission
729
+            if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) {
730
+                $row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
731
+            }
732
+            // Add display names to result
733
+            $row['share_with_displayname'] = $row['share_with'];
734
+            if (isset($row['share_with']) && $row['share_with'] != '' &&
735
+                $row['share_type'] === IShare::TYPE_USER) {
736
+                $shareWithUser = \OC::$server->getUserManager()->get($row['share_with']);
737
+                $row['share_with_displayname'] = $shareWithUser === null ? $row['share_with'] : $shareWithUser->getDisplayName();
738
+            } elseif (isset($row['share_with']) && $row['share_with'] != '' &&
739
+                $row['share_type'] === IShare::TYPE_REMOTE) {
740
+                $addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
741
+                foreach ($addressBookEntries as $entry) {
742
+                    foreach ($entry['CLOUD'] as $cloudID) {
743
+                        if ($cloudID === $row['share_with']) {
744
+                            $row['share_with_displayname'] = $entry['FN'];
745
+                        }
746
+                    }
747
+                }
748
+            }
749
+            if (isset($row['uid_owner']) && $row['uid_owner'] != '') {
750
+                $ownerUser = \OC::$server->getUserManager()->get($row['uid_owner']);
751
+                $row['displayname_owner'] = $ownerUser === null ? $row['uid_owner'] : $ownerUser->getDisplayName();
752
+            }
753
+
754
+            if ($row['permissions'] > 0) {
755
+                $items[$row['id']] = $row;
756
+            }
757
+        }
758
+
759
+        // group items if we are looking for items shared with the current user
760
+        if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
761
+            $items = self::groupItems($items, $itemType);
762
+        }
763
+
764
+        if (!empty($items)) {
765
+            $collectionItems = [];
766
+            foreach ($items as &$row) {
767
+                // Return only the item instead of a 2-dimensional array
768
+                if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
769
+                    if ($format == self::FORMAT_NONE) {
770
+                        return $row;
771
+                    } else {
772
+                        break;
773
+                    }
774
+                }
775
+                // Check if this is a collection of the requested item type
776
+                if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
777
+                    if (($collectionBackend = self::getBackend($row['item_type']))
778
+                        && $collectionBackend instanceof \OCP\Share_Backend_Collection) {
779
+                        // Collections can be inside collections, check if the item is a collection
780
+                        if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
781
+                            $collectionItems[] = $row;
782
+                        } else {
783
+                            $collection = [];
784
+                            $collection['item_type'] = $row['item_type'];
785
+                            if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
786
+                                $collection['path'] = basename($row['path']);
787
+                            }
788
+                            $row['collection'] = $collection;
789
+                            // Fetch all of the children sources
790
+                            $children = $collectionBackend->getChildren($row[$column]);
791
+                            foreach ($children as $child) {
792
+                                $childItem = $row;
793
+                                $childItem['item_type'] = $itemType;
794
+                                if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
795
+                                    $childItem['item_source'] = $child['source'];
796
+                                    $childItem['item_target'] = $child['target'];
797
+                                }
798
+                                if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
799
+                                    if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
800
+                                        $childItem['file_source'] = $child['source'];
801
+                                    } else { // TODO is this really needed if we already know that we use the file backend?
802
+                                        $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
803
+                                        $childItem['file_source'] = $meta['fileid'];
804
+                                    }
805
+                                    $childItem['file_target'] =
806
+                                        \OC\Files\Filesystem::normalizePath($child['file_path']);
807
+                                }
808
+                                if (isset($item)) {
809
+                                    if ($childItem[$column] == $item) {
810
+                                        // Return only the item instead of a 2-dimensional array
811
+                                        if ($limit == 1) {
812
+                                            if ($format == self::FORMAT_NONE) {
813
+                                                return $childItem;
814
+                                            } else {
815
+                                                // Unset the items array and break out of both loops
816
+                                                $items = [];
817
+                                                $items[] = $childItem;
818
+                                                break 2;
819
+                                            }
820
+                                        } else {
821
+                                            $collectionItems[] = $childItem;
822
+                                        }
823
+                                    }
824
+                                } else {
825
+                                    $collectionItems[] = $childItem;
826
+                                }
827
+                            }
828
+                        }
829
+                    }
830
+                    // Remove collection item
831
+                    $toRemove = $row['id'];
832
+                    if (array_key_exists($toRemove, $switchedItems)) {
833
+                        $toRemove = $switchedItems[$toRemove];
834
+                    }
835
+                    unset($items[$toRemove]);
836
+                } elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
837
+                    // FIXME: Thats a dirty hack to improve file sharing performance,
838
+                    // see github issue #10588 for more details
839
+                    // Need to find a solution which works for all back-ends
840
+                    $collectionBackend = self::getBackend($row['item_type']);
841
+                    $sharedParents = $collectionBackend->getParents($row['item_source']);
842
+                    foreach ($sharedParents as $parent) {
843
+                        $collectionItems[] = $parent;
844
+                    }
845
+                }
846
+            }
847
+            if (!empty($collectionItems)) {
848
+                $collectionItems = array_unique($collectionItems, SORT_REGULAR);
849
+                $items = array_merge($items, $collectionItems);
850
+            }
851
+
852
+            // filter out invalid items, these can appear when subshare entries exist
853
+            // for a group in which the requested user isn't a member any more
854
+            $items = array_filter($items, function ($item) {
855
+                return $item['share_type'] !== self::$shareTypeGroupUserUnique;
856
+            });
857
+
858
+            return self::formatResult($items, $column, $backend, $format, $parameters);
859
+        } elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
860
+            // FIXME: Thats a dirty hack to improve file sharing performance,
861
+            // see github issue #10588 for more details
862
+            // Need to find a solution which works for all back-ends
863
+            $collectionItems = [];
864
+            $collectionBackend = self::getBackend('folder');
865
+            $sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
866
+            foreach ($sharedParents as $parent) {
867
+                $collectionItems[] = $parent;
868
+            }
869
+            if ($limit === 1) {
870
+                return reset($collectionItems);
871
+            }
872
+            return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
873
+        }
874
+
875
+        return [];
876
+    }
877
+
878
+    /**
879
+     * group items with link to the same source
880
+     *
881
+     * @param array $items
882
+     * @param string $itemType
883
+     * @return array of grouped items
884
+     */
885
+    protected static function groupItems($items, $itemType) {
886
+        $fileSharing = $itemType === 'file' || $itemType === 'folder';
887
+
888
+        $result = [];
889
+
890
+        foreach ($items as $item) {
891
+            $grouped = false;
892
+            foreach ($result as $key => $r) {
893
+                // for file/folder shares we need to compare file_source, otherwise we compare item_source
894
+                // only group shares if they already point to the same target, otherwise the file where shared
895
+                // before grouping of shares was added. In this case we don't group them toi avoid confusions
896
+                if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
897
+                    (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
898
+                    // add the first item to the list of grouped shares
899
+                    if (!isset($result[$key]['grouped'])) {
900
+                        $result[$key]['grouped'][] = $result[$key];
901
+                    }
902
+                    $result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
903
+                    $result[$key]['grouped'][] = $item;
904
+                    $grouped = true;
905
+                    break;
906
+                }
907
+            }
908
+
909
+            if (!$grouped) {
910
+                $result[] = $item;
911
+            }
912
+        }
913
+
914
+        return $result;
915
+    }
916
+
917
+    /**
918
+     * construct select statement
919
+     * @param int $format
920
+     * @param boolean $fileDependent ist it a file/folder share or a generla share
921
+     * @param string $uidOwner
922
+     * @return string select statement
923
+     */
924
+    private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
925
+        $select = '*';
926
+        if ($format == self::FORMAT_STATUSES) {
927
+            if ($fileDependent) {
928
+                $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
929
+                    . '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
930
+                    . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
931
+                    . '`uid_initiator`';
932
+            } else {
933
+                $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
934
+            }
935
+        } else {
936
+            if (isset($uidOwner)) {
937
+                if ($fileDependent) {
938
+                    $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
939
+                        . ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
940
+                        . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
941
+                        . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
942
+                } else {
943
+                    $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
944
+                        . ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
945
+                }
946
+            } else {
947
+                if ($fileDependent) {
948
+                    if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
949
+                        $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
950
+                            . '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
951
+                            . '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
952
+                            . '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
953
+                    } else {
954
+                        $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
955
+                            . '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
956
+                            . '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
957
+                            . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
958
+                            . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
959
+                    }
960
+                }
961
+            }
962
+        }
963
+        return $select;
964
+    }
965
+
966
+
967
+    /**
968
+     * transform db results
969
+     * @param array $row result
970
+     */
971
+    private static function transformDBResults(&$row) {
972
+        if (isset($row['id'])) {
973
+            $row['id'] = (int) $row['id'];
974
+        }
975
+        if (isset($row['share_type'])) {
976
+            $row['share_type'] = (int) $row['share_type'];
977
+        }
978
+        if (isset($row['parent'])) {
979
+            $row['parent'] = (int) $row['parent'];
980
+        }
981
+        if (isset($row['file_parent'])) {
982
+            $row['file_parent'] = (int) $row['file_parent'];
983
+        }
984
+        if (isset($row['file_source'])) {
985
+            $row['file_source'] = (int) $row['file_source'];
986
+        }
987
+        if (isset($row['permissions'])) {
988
+            $row['permissions'] = (int) $row['permissions'];
989
+        }
990
+        if (isset($row['storage'])) {
991
+            $row['storage'] = (int) $row['storage'];
992
+        }
993
+        if (isset($row['stime'])) {
994
+            $row['stime'] = (int) $row['stime'];
995
+        }
996
+        if (isset($row['expiration']) && $row['share_type'] !== IShare::TYPE_LINK) {
997
+            // discard expiration date for non-link shares, which might have been
998
+            // set by ancient bugs
999
+            $row['expiration'] = null;
1000
+        }
1001
+    }
1002
+
1003
+    /**
1004
+     * format result
1005
+     * @param array $items result
1006
+     * @param string $column is it a file share or a general share ('file_target' or 'item_target')
1007
+     * @param \OCP\Share_Backend $backend sharing backend
1008
+     * @param int $format
1009
+     * @param array $parameters additional format parameters
1010
+     * @return array format result
1011
+     */
1012
+    private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
1013
+        if ($format === self::FORMAT_NONE) {
1014
+            return $items;
1015
+        } elseif ($format === self::FORMAT_STATUSES) {
1016
+            $statuses = [];
1017
+            foreach ($items as $item) {
1018
+                if ($item['share_type'] === IShare::TYPE_LINK) {
1019
+                    if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
1020
+                        continue;
1021
+                    }
1022
+                    $statuses[$item[$column]]['link'] = true;
1023
+                } elseif (!isset($statuses[$item[$column]])) {
1024
+                    $statuses[$item[$column]]['link'] = false;
1025
+                }
1026
+                if (!empty($item['file_target'])) {
1027
+                    $statuses[$item[$column]]['path'] = $item['path'];
1028
+                }
1029
+            }
1030
+            return $statuses;
1031
+        } else {
1032
+            return $backend->formatItems($items, $format, $parameters);
1033
+        }
1034
+    }
1035
+
1036
+    /**
1037
+     * remove protocol from URL
1038
+     *
1039
+     * @param string $url
1040
+     * @return string
1041
+     */
1042
+    public static function removeProtocolFromUrl($url) {
1043
+        if (strpos($url, 'https://') === 0) {
1044
+            return substr($url, strlen('https://'));
1045
+        } elseif (strpos($url, 'http://') === 0) {
1046
+            return substr($url, strlen('http://'));
1047
+        }
1048
+
1049
+        return $url;
1050
+    }
1051
+
1052
+    /**
1053
+     * try http post first with https and then with http as a fallback
1054
+     *
1055
+     * @param string $remoteDomain
1056
+     * @param string $urlSuffix
1057
+     * @param array $fields post parameters
1058
+     * @return array
1059
+     */
1060
+    private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
1061
+        $protocol = 'https://';
1062
+        $result = [
1063
+            'success' => false,
1064
+            'result' => '',
1065
+        ];
1066
+        $try = 0;
1067
+        $discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
1068
+        while ($result['success'] === false && $try < 2) {
1069
+            $federationEndpoints = $discoveryService->discover($protocol . $remoteDomain, 'FEDERATED_SHARING');
1070
+            $endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
1071
+            $client = \OC::$server->getHTTPClientService()->newClient();
1072
+
1073
+            try {
1074
+                $response = $client->post(
1075
+                    $protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT,
1076
+                    [
1077
+                        'body' => $fields,
1078
+                        'connect_timeout' => 10,
1079
+                    ]
1080
+                );
1081
+
1082
+                $result = ['success' => true, 'result' => $response->getBody()];
1083
+            } catch (\Exception $e) {
1084
+                $result = ['success' => false, 'result' => $e->getMessage()];
1085
+            }
1086
+
1087
+            $try++;
1088
+            $protocol = 'http://';
1089
+        }
1090
+
1091
+        return $result;
1092
+    }
1093
+
1094
+    /**
1095
+     * send server-to-server unshare to remote server
1096
+     *
1097
+     * @param string $remote url
1098
+     * @param int $id share id
1099
+     * @param string $token
1100
+     * @return bool
1101
+     */
1102
+    private static function sendRemoteUnshare($remote, $id, $token) {
1103
+        $url = rtrim($remote, '/');
1104
+        $fields = ['token' => $token, 'format' => 'json'];
1105
+        $url = self::removeProtocolFromUrl($url);
1106
+        $result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
1107
+        $status = json_decode($result['result'], true);
1108
+
1109
+        return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
1110
+    }
1111
+
1112
+    /**
1113
+     * @return int
1114
+     */
1115
+    public static function getExpireInterval() {
1116
+        return (int)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1117
+    }
1118
+
1119
+    /**
1120
+     * Checks whether the given path is reachable for the given owner
1121
+     *
1122
+     * @param string $path path relative to files
1123
+     * @param string $ownerStorageId storage id of the owner
1124
+     *
1125
+     * @return boolean true if file is reachable, false otherwise
1126
+     */
1127
+    private static function isFileReachable($path, $ownerStorageId) {
1128
+        // if outside the home storage, file is always considered reachable
1129
+        if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
1130
+            substr($ownerStorageId, 0, 13) === 'object::user:'
1131
+        )) {
1132
+            return true;
1133
+        }
1134
+
1135
+        // if inside the home storage, the file has to be under "/files/"
1136
+        $path = ltrim($path, '/');
1137
+        if (substr($path, 0, 6) === 'files/') {
1138
+            return true;
1139
+        }
1140
+
1141
+        return false;
1142
+    }
1143 1143
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -150,7 +150,7 @@  discard block
 block discarded – undo
150 150
 
151 151
 		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
152 152
 
153
-		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
153
+		$where .= ' `'.$column.'` = ? AND `item_type` = ? ';
154 154
 		$arguments = [$itemSource, $itemType];
155 155
 		// for link shares $user === null
156 156
 		if ($user !== null) {
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 			$arguments[] = $owner;
169 169
 		}
170 170
 
171
-		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
171
+		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$fileDependentWhere.$where);
172 172
 
173 173
 		$result = \OC_DB::executeAudited($query, $arguments);
174 174
 
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
 			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
177 177
 				continue;
178 178
 			}
179
-			if ($fileDependent && (int)$row['file_parent'] === -1) {
179
+			if ($fileDependent && (int) $row['file_parent'] === -1) {
180 180
 				// if it is a mount point we need to get the path from the mount manager
181 181
 				$mountManager = \OC\Files\Filesystem::getMountManager();
182 182
 				$mountPoint = $mountManager->findByStorageId($row['storage_id']);
@@ -187,7 +187,7 @@  discard block
 block discarded – undo
187 187
 					$row['path'] = $path;
188 188
 				} else {
189 189
 					\OC::$server->getLogger()->warning(
190
-						'Could not resolve mount point for ' . $row['storage_id'],
190
+						'Could not resolve mount point for '.$row['storage_id'],
191 191
 						['app' => 'OCP\Share']
192 192
 					);
193 193
 				}
@@ -204,7 +204,7 @@  discard block
 block discarded – undo
204 204
 			}
205 205
 
206 206
 			if (!empty($groups)) {
207
-				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
207
+				$where = $fileDependentWhere.' WHERE `'.$column.'` = ? AND `item_type` = ? AND `share_with` in (?)';
208 208
 				$arguments = [$itemSource, $itemType, $groups];
209 209
 				$types = [null, null, IQueryBuilder::PARAM_STR_ARRAY];
210 210
 
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
 				// class isn't static anymore...
219 219
 				$conn = \OC::$server->getDatabaseConnection();
220 220
 				$result = $conn->executeQuery(
221
-					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
221
+					'SELECT '.$select.' FROM `*PREFIX*share` '.$where,
222 222
 					$arguments,
223 223
 					$types
224 224
 				);
@@ -285,12 +285,12 @@  discard block
 block discarded – undo
285 285
 		$currentUser = $owner ? $owner : \OC_User::getUser();
286 286
 		foreach ($items as $item) {
287 287
 			// delete the item with the expected share_type and owner
288
-			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
288
+			if ((int) $item['share_type'] === (int) $shareType && $item['uid_owner'] === $currentUser) {
289 289
 				$toDelete = $item;
290 290
 			// if there is more then one result we don't have to delete the children
291 291
 				// but update their parent. For group shares the new parent should always be
292 292
 				// the original group share and not the db entry with the unique name
293
-			} elseif ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
293
+			} elseif ((int) $item['share_type'] === self::$shareTypeGroupUserUnique) {
294 294
 				$newParent = $item['parent'];
295 295
 			} else {
296 296
 				$newParent = $item['id'];
@@ -347,7 +347,7 @@  discard block
 block discarded – undo
347 347
 	 * @return null
348 348
 	 */
349 349
 	protected static function unshareItem(array $item, $newParent = null) {
350
-		$shareType = (int)$item['share_type'];
350
+		$shareType = (int) $item['share_type'];
351 351
 		$shareWith = null;
352 352
 		if ($shareType !== IShare::TYPE_LINK) {
353 353
 			$shareWith = $item['share_with'];
@@ -373,7 +373,7 @@  discard block
 block discarded – undo
373 373
 		$deletedShares[] = $hookParams;
374 374
 		$hookParams['deletedShares'] = $deletedShares;
375 375
 		\OC_Hook::emit(\OCP\Share::class, 'post_unshare', $hookParams);
376
-		if ((int)$item['share_type'] === IShare::TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
376
+		if ((int) $item['share_type'] === IShare::TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
377 377
 			list(, $remote) = Helper::splitUserRemote($item['share_with']);
378 378
 			self::sendRemoteUnshare($remote, $item['id'], $item['token']);
379 379
 		}
@@ -628,7 +628,7 @@  discard block
 block discarded – undo
628 628
 		$result = $query->execute($queryArgs);
629 629
 		if ($result === false) {
630 630
 			\OCP\Util::writeLog('OCP\Share',
631
-				\OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
631
+				\OC_DB::getErrorMessage().', select='.$select.' where=',
632 632
 				ILogger::ERROR);
633 633
 		}
634 634
 		$items = [];
@@ -670,14 +670,14 @@  discard block
 block discarded – undo
670 670
 						}
671 671
 						// Switch ids if sharing permission is granted on only
672 672
 						// one share to ensure correct parent is used if resharing
673
-						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
674
-							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
673
+						if (~(int) $items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
674
+							&& (int) $row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
675 675
 							$items[$row['id']] = $items[$id];
676 676
 							$switchedItems[$id] = $row['id'];
677 677
 							unset($items[$id]);
678 678
 							$id = $row['id'];
679 679
 						}
680
-						$items[$id]['permissions'] |= (int)$row['permissions'];
680
+						$items[$id]['permissions'] |= (int) $row['permissions'];
681 681
 					}
682 682
 					continue;
683 683
 				} elseif (!empty($row['parent'])) {
@@ -690,8 +690,8 @@  discard block
 block discarded – undo
690 690
 					$query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
691 691
 					$parentResult = $query->execute([$row['parent']]);
692 692
 					if ($result === false) {
693
-						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
694
-							\OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
693
+						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: '.
694
+							\OC_DB::getErrorMessage().', select='.$select.' where='.$where,
695 695
 							ILogger::ERROR);
696 696
 					} else {
697 697
 						$parentRow = $parentResult->fetchRow();
@@ -701,7 +701,7 @@  discard block
 block discarded – undo
701 701
 						$subPath = substr($row['path'], $pos);
702 702
 						$splitPath = explode('/', $subPath);
703 703
 						foreach (array_slice($splitPath, 2) as $pathPart) {
704
-							$tmpPath = $tmpPath . '/' . $pathPart;
704
+							$tmpPath = $tmpPath.'/'.$pathPart;
705 705
 						}
706 706
 						$row['path'] = $tmpPath;
707 707
 					}
@@ -851,7 +851,7 @@  discard block
 block discarded – undo
851 851
 
852 852
 			// filter out invalid items, these can appear when subshare entries exist
853 853
 			// for a group in which the requested user isn't a member any more
854
-			$items = array_filter($items, function ($item) {
854
+			$items = array_filter($items, function($item) {
855 855
 				return $item['share_type'] !== self::$shareTypeGroupUserUnique;
856 856
 			});
857 857
 
@@ -1009,7 +1009,7 @@  discard block
 block discarded – undo
1009 1009
 	 * @param array $parameters additional format parameters
1010 1010
 	 * @return array format result
1011 1011
 	 */
1012
-	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
1012
+	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE, $parameters = null) {
1013 1013
 		if ($format === self::FORMAT_NONE) {
1014 1014
 			return $items;
1015 1015
 		} elseif ($format === self::FORMAT_STATUSES) {
@@ -1066,13 +1066,13 @@  discard block
 block discarded – undo
1066 1066
 		$try = 0;
1067 1067
 		$discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
1068 1068
 		while ($result['success'] === false && $try < 2) {
1069
-			$federationEndpoints = $discoveryService->discover($protocol . $remoteDomain, 'FEDERATED_SHARING');
1069
+			$federationEndpoints = $discoveryService->discover($protocol.$remoteDomain, 'FEDERATED_SHARING');
1070 1070
 			$endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
1071 1071
 			$client = \OC::$server->getHTTPClientService()->newClient();
1072 1072
 
1073 1073
 			try {
1074 1074
 				$response = $client->post(
1075
-					$protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT,
1075
+					$protocol.$remoteDomain.$endpoint.$urlSuffix.'?format='.self::RESPONSE_FORMAT,
1076 1076
 					[
1077 1077
 						'body' => $fields,
1078 1078
 						'connect_timeout' => 10,
@@ -1113,7 +1113,7 @@  discard block
 block discarded – undo
1113 1113
 	 * @return int
1114 1114
 	 */
1115 1115
 	public static function getExpireInterval() {
1116
-		return (int)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1116
+		return (int) \OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1117 1117
 	}
1118 1118
 
1119 1119
 	/**
Please login to merge, or discard this patch.
lib/private/TagManager.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -41,53 +41,53 @@
 block discarded – undo
41 41
 
42 42
 class TagManager implements \OCP\ITagManager {
43 43
 
44
-	/**
45
-	 * User session
46
-	 *
47
-	 * @var \OCP\IUserSession
48
-	 */
49
-	private $userSession;
44
+    /**
45
+     * User session
46
+     *
47
+     * @var \OCP\IUserSession
48
+     */
49
+    private $userSession;
50 50
 
51
-	/**
52
-	 * TagMapper
53
-	 *
54
-	 * @var TagMapper
55
-	 */
56
-	private $mapper;
51
+    /**
52
+     * TagMapper
53
+     *
54
+     * @var TagMapper
55
+     */
56
+    private $mapper;
57 57
 
58
-	/**
59
-	 * Constructor.
60
-	 *
61
-	 * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
62
-	 * @param \OCP\IUserSession $userSession the user session
63
-	 */
64
-	public function __construct(TagMapper $mapper, \OCP\IUserSession $userSession) {
65
-		$this->mapper = $mapper;
66
-		$this->userSession = $userSession;
67
-	}
58
+    /**
59
+     * Constructor.
60
+     *
61
+     * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
62
+     * @param \OCP\IUserSession $userSession the user session
63
+     */
64
+    public function __construct(TagMapper $mapper, \OCP\IUserSession $userSession) {
65
+        $this->mapper = $mapper;
66
+        $this->userSession = $userSession;
67
+    }
68 68
 
69
-	/**
70
-	 * Create a new \OCP\ITags instance and load tags from db.
71
-	 *
72
-	 * @see \OCP\ITags
73
-	 * @param string $type The type identifier e.g. 'contact' or 'event'.
74
-	 * @param array $defaultTags An array of default tags to be used if none are stored.
75
-	 * @param boolean $includeShared Whether to include tags for items shared with this user by others.
76
-	 * @param string $userId user for which to retrieve the tags, defaults to the currently
77
-	 * logged in user
78
-	 * @return \OCP\ITags
79
-	 *
80
-	 * since 20.0.0 $includeShared isn't used anymore
81
-	 */
82
-	public function load($type, $defaultTags = [], $includeShared = false, $userId = null) {
83
-		if (is_null($userId)) {
84
-			$user = $this->userSession->getUser();
85
-			if ($user === null) {
86
-				// nothing we can do without a user
87
-				return null;
88
-			}
89
-			$userId = $this->userSession->getUser()->getUId();
90
-		}
91
-		return new Tags($this->mapper, $userId, $type, $defaultTags);
92
-	}
69
+    /**
70
+     * Create a new \OCP\ITags instance and load tags from db.
71
+     *
72
+     * @see \OCP\ITags
73
+     * @param string $type The type identifier e.g. 'contact' or 'event'.
74
+     * @param array $defaultTags An array of default tags to be used if none are stored.
75
+     * @param boolean $includeShared Whether to include tags for items shared with this user by others.
76
+     * @param string $userId user for which to retrieve the tags, defaults to the currently
77
+     * logged in user
78
+     * @return \OCP\ITags
79
+     *
80
+     * since 20.0.0 $includeShared isn't used anymore
81
+     */
82
+    public function load($type, $defaultTags = [], $includeShared = false, $userId = null) {
83
+        if (is_null($userId)) {
84
+            $user = $this->userSession->getUser();
85
+            if ($user === null) {
86
+                // nothing we can do without a user
87
+                return null;
88
+            }
89
+            $userId = $this->userSession->getUser()->getUId();
90
+        }
91
+        return new Tags($this->mapper, $userId, $type, $defaultTags);
92
+    }
93 93
 }
Please login to merge, or discard this patch.
lib/private/Tags.php 2 patches
Indentation   +785 added lines, -785 removed lines patch added patch discarded remove patch
@@ -51,789 +51,789 @@
 block discarded – undo
51 51
 
52 52
 class Tags implements ITags {
53 53
 
54
-	/**
55
-	 * Tags
56
-	 *
57
-	 * @var array
58
-	 */
59
-	private $tags = [];
60
-
61
-	/**
62
-	 * Used for storing objectid/categoryname pairs while rescanning.
63
-	 *
64
-	 * @var array
65
-	 */
66
-	private static $relations = [];
67
-
68
-	/**
69
-	 * Type
70
-	 *
71
-	 * @var string
72
-	 */
73
-	private $type;
74
-
75
-	/**
76
-	 * User
77
-	 *
78
-	 * @var string
79
-	 */
80
-	private $user;
81
-
82
-	/**
83
-	 * Are we including tags for shared items?
84
-	 *
85
-	 * @var bool
86
-	 */
87
-	private $includeShared = false;
88
-
89
-	/**
90
-	 * The current user, plus any owners of the items shared with the current
91
-	 * user, if $this->includeShared === true.
92
-	 *
93
-	 * @var array
94
-	 */
95
-	private $owners = [];
96
-
97
-	/**
98
-	 * The Mapper we're using to communicate our Tag objects to the database.
99
-	 *
100
-	 * @var TagMapper
101
-	 */
102
-	private $mapper;
103
-
104
-	/**
105
-	 * The sharing backend for objects of $this->type. Required if
106
-	 * $this->includeShared === true to determine ownership of items.
107
-	 *
108
-	 * @var \OCP\Share_Backend
109
-	 */
110
-	private $backend;
111
-
112
-	public const TAG_TABLE = '*PREFIX*vcategory';
113
-	public const RELATION_TABLE = '*PREFIX*vcategory_to_object';
114
-
115
-	/**
116
-	 * Constructor.
117
-	 *
118
-	 * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
119
-	 * @param string $user The user whose data the object will operate on.
120
-	 * @param string $type The type of items for which tags will be loaded.
121
-	 * @param array $defaultTags Tags that should be created at construction.
122
-	 *
123
-	 * since 20.0.0 $includeShared isn't used anymore
124
-	 */
125
-	public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) {
126
-		$this->mapper = $mapper;
127
-		$this->user = $user;
128
-		$this->type = $type;
129
-		$this->owners = [$this->user];
130
-		$this->tags = $this->mapper->loadTags($this->owners, $this->type);
131
-
132
-		if (count($defaultTags) > 0 && count($this->tags) === 0) {
133
-			$this->addMultiple($defaultTags, true);
134
-		}
135
-	}
136
-
137
-	/**
138
-	 * Check if any tags are saved for this type and user.
139
-	 *
140
-	 * @return boolean
141
-	 */
142
-	public function isEmpty() {
143
-		return count($this->tags) === 0;
144
-	}
145
-
146
-	/**
147
-	 * Returns an array mapping a given tag's properties to its values:
148
-	 * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
149
-	 *
150
-	 * @param string $id The ID of the tag that is going to be mapped
151
-	 * @return array|false
152
-	 */
153
-	public function getTag($id) {
154
-		$key = $this->getTagById($id);
155
-		if ($key !== false) {
156
-			return $this->tagMap($this->tags[$key]);
157
-		}
158
-		return false;
159
-	}
160
-
161
-	/**
162
-	 * Get the tags for a specific user.
163
-	 *
164
-	 * This returns an array with maps containing each tag's properties:
165
-	 * [
166
-	 * 	['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'],
167
-	 * 	['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'],
168
-	 * ]
169
-	 *
170
-	 * @return array
171
-	 */
172
-	public function getTags() {
173
-		if (!count($this->tags)) {
174
-			return [];
175
-		}
176
-
177
-		usort($this->tags, function ($a, $b) {
178
-			return strnatcasecmp($a->getName(), $b->getName());
179
-		});
180
-		$tagMap = [];
181
-
182
-		foreach ($this->tags as $tag) {
183
-			if ($tag->getName() !== ITags::TAG_FAVORITE) {
184
-				$tagMap[] = $this->tagMap($tag);
185
-			}
186
-		}
187
-		return $tagMap;
188
-	}
189
-
190
-	/**
191
-	 * Return only the tags owned by the given user, omitting any tags shared
192
-	 * by other users.
193
-	 *
194
-	 * @param string $user The user whose tags are to be checked.
195
-	 * @return array An array of Tag objects.
196
-	 */
197
-	public function getTagsForUser($user) {
198
-		return array_filter($this->tags,
199
-			function ($tag) use ($user) {
200
-				return $tag->getOwner() === $user;
201
-			}
202
-		);
203
-	}
204
-
205
-	/**
206
-	 * Get the list of tags for the given ids.
207
-	 *
208
-	 * @param array $objIds array of object ids
209
-	 * @return array|boolean of tags id as key to array of tag names
210
-	 * or false if an error occurred
211
-	 */
212
-	public function getTagsForObjects(array $objIds) {
213
-		$entries = [];
214
-
215
-		try {
216
-			$conn = \OC::$server->getDatabaseConnection();
217
-			$chunks = array_chunk($objIds, 900, false);
218
-			foreach ($chunks as $chunk) {
219
-				$result = $conn->executeQuery(
220
-					'SELECT `category`, `categoryid`, `objid` ' .
221
-					'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
222
-					'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
223
-					[$this->user, $this->type, $chunk],
224
-					[null, null, IQueryBuilder::PARAM_INT_ARRAY]
225
-				);
226
-				while ($row = $result->fetch()) {
227
-					$objId = (int)$row['objid'];
228
-					if (!isset($entries[$objId])) {
229
-						$entries[$objId] = [];
230
-					}
231
-					$entries[$objId][] = $row['category'];
232
-				}
233
-				if ($result === null) {
234
-					\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
235
-					return false;
236
-				}
237
-			}
238
-		} catch (\Exception $e) {
239
-			\OC::$server->getLogger()->logException($e, [
240
-				'message' => __METHOD__,
241
-				'level' => ILogger::ERROR,
242
-				'app' => 'core',
243
-			]);
244
-			return false;
245
-		}
246
-
247
-		return $entries;
248
-	}
249
-
250
-	/**
251
-	 * Get the a list if items tagged with $tag.
252
-	 *
253
-	 * Throws an exception if the tag could not be found.
254
-	 *
255
-	 * @param string $tag Tag id or name.
256
-	 * @return array|false An array of object ids or false on error.
257
-	 * @throws \Exception
258
-	 */
259
-	public function getIdsForTag($tag) {
260
-		$result = null;
261
-		$tagId = false;
262
-		if (is_numeric($tag)) {
263
-			$tagId = $tag;
264
-		} elseif (is_string($tag)) {
265
-			$tag = trim($tag);
266
-			if ($tag === '') {
267
-				\OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
268
-				return false;
269
-			}
270
-			$tagId = $this->getTagId($tag);
271
-		}
272
-
273
-		if ($tagId === false) {
274
-			$l10n = \OC::$server->getL10N('core');
275
-			throw new \Exception(
276
-				$l10n->t('Could not find category "%s"', [$tag])
277
-			);
278
-		}
279
-
280
-		$ids = [];
281
-		$sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
282
-			. '` WHERE `categoryid` = ?';
283
-
284
-		try {
285
-			$stmt = \OC_DB::prepare($sql);
286
-			$result = $stmt->execute([$tagId]);
287
-			if ($result === null) {
288
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
289
-				return false;
290
-			}
291
-		} catch (\Exception $e) {
292
-			\OC::$server->getLogger()->logException($e, [
293
-				'message' => __METHOD__,
294
-				'level' => ILogger::ERROR,
295
-				'app' => 'core',
296
-			]);
297
-			return false;
298
-		}
299
-
300
-		if (!is_null($result)) {
301
-			while ($row = $result->fetchRow()) {
302
-				$ids[] = (int)$row['objid'];
303
-			}
304
-		}
305
-
306
-		return $ids;
307
-	}
308
-
309
-	/**
310
-	 * Checks whether a tag is saved for the given user,
311
-	 * disregarding the ones shared with him or her.
312
-	 *
313
-	 * @param string $name The tag name to check for.
314
-	 * @param string $user The user whose tags are to be checked.
315
-	 * @return bool
316
-	 */
317
-	public function userHasTag($name, $user) {
318
-		$key = $this->array_searchi($name, $this->getTagsForUser($user));
319
-		return ($key !== false) ? $this->tags[$key]->getId() : false;
320
-	}
321
-
322
-	/**
323
-	 * Checks whether a tag is saved for or shared with the current user.
324
-	 *
325
-	 * @param string $name The tag name to check for.
326
-	 * @return bool
327
-	 */
328
-	public function hasTag($name) {
329
-		return $this->getTagId($name) !== false;
330
-	}
331
-
332
-	/**
333
-	 * Add a new tag.
334
-	 *
335
-	 * @param string $name A string with a name of the tag
336
-	 * @return false|int the id of the added tag or false on error.
337
-	 */
338
-	public function add($name) {
339
-		$name = trim($name);
340
-
341
-		if ($name === '') {
342
-			\OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
343
-			return false;
344
-		}
345
-		if ($this->userHasTag($name, $this->user)) {
346
-			\OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
347
-			return false;
348
-		}
349
-		try {
350
-			$tag = new Tag($this->user, $this->type, $name);
351
-			$tag = $this->mapper->insert($tag);
352
-			$this->tags[] = $tag;
353
-		} catch (\Exception $e) {
354
-			\OC::$server->getLogger()->logException($e, [
355
-				'message' => __METHOD__,
356
-				'level' => ILogger::ERROR,
357
-				'app' => 'core',
358
-			]);
359
-			return false;
360
-		}
361
-		\OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
362
-		return $tag->getId();
363
-	}
364
-
365
-	/**
366
-	 * Rename tag.
367
-	 *
368
-	 * @param string|integer $from The name or ID of the existing tag
369
-	 * @param string $to The new name of the tag.
370
-	 * @return bool
371
-	 */
372
-	public function rename($from, $to) {
373
-		$from = trim($from);
374
-		$to = trim($to);
375
-
376
-		if ($to === '' || $from === '') {
377
-			\OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
378
-			return false;
379
-		}
380
-
381
-		if (is_numeric($from)) {
382
-			$key = $this->getTagById($from);
383
-		} else {
384
-			$key = $this->getTagByName($from);
385
-		}
386
-		if ($key === false) {
387
-			\OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
388
-			return false;
389
-		}
390
-		$tag = $this->tags[$key];
391
-
392
-		if ($this->userHasTag($to, $tag->getOwner())) {
393
-			\OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
394
-			return false;
395
-		}
396
-
397
-		try {
398
-			$tag->setName($to);
399
-			$this->tags[$key] = $this->mapper->update($tag);
400
-		} catch (\Exception $e) {
401
-			\OC::$server->getLogger()->logException($e, [
402
-				'message' => __METHOD__,
403
-				'level' => ILogger::ERROR,
404
-				'app' => 'core',
405
-			]);
406
-			return false;
407
-		}
408
-		return true;
409
-	}
410
-
411
-	/**
412
-	 * Add a list of new tags.
413
-	 *
414
-	 * @param string[] $names A string with a name or an array of strings containing
415
-	 * the name(s) of the tag(s) to add.
416
-	 * @param bool $sync When true, save the tags
417
-	 * @param int|null $id int Optional object id to add to this|these tag(s)
418
-	 * @return bool Returns false on error.
419
-	 */
420
-	public function addMultiple($names, $sync=false, $id = null) {
421
-		if (!is_array($names)) {
422
-			$names = [$names];
423
-		}
424
-		$names = array_map('trim', $names);
425
-		array_filter($names);
426
-
427
-		$newones = [];
428
-		foreach ($names as $name) {
429
-			if (!$this->hasTag($name) && $name !== '') {
430
-				$newones[] = new Tag($this->user, $this->type, $name);
431
-			}
432
-			if (!is_null($id)) {
433
-				// Insert $objectid, $categoryid  pairs if not exist.
434
-				self::$relations[] = ['objid' => $id, 'tag' => $name];
435
-			}
436
-		}
437
-		$this->tags = array_merge($this->tags, $newones);
438
-		if ($sync === true) {
439
-			$this->save();
440
-		}
441
-
442
-		return true;
443
-	}
444
-
445
-	/**
446
-	 * Save the list of tags and their object relations
447
-	 */
448
-	protected function save() {
449
-		if (is_array($this->tags)) {
450
-			foreach ($this->tags as $tag) {
451
-				try {
452
-					if (!$this->mapper->tagExists($tag)) {
453
-						$this->mapper->insert($tag);
454
-					}
455
-				} catch (\Exception $e) {
456
-					\OC::$server->getLogger()->logException($e, [
457
-						'message' => __METHOD__,
458
-						'level' => ILogger::ERROR,
459
-						'app' => 'core',
460
-					]);
461
-				}
462
-			}
463
-
464
-			// reload tags to get the proper ids.
465
-			$this->tags = $this->mapper->loadTags($this->owners, $this->type);
466
-			\OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
467
-				ILogger::DEBUG);
468
-			// Loop through temporarily cached objectid/tagname pairs
469
-			// and save relations.
470
-			$tags = $this->tags;
471
-			// For some reason this is needed or array_search(i) will return 0..?
472
-			ksort($tags);
473
-			$dbConnection = \OC::$server->getDatabaseConnection();
474
-			foreach (self::$relations as $relation) {
475
-				$tagId = $this->getTagId($relation['tag']);
476
-				\OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
477
-				if ($tagId) {
478
-					try {
479
-						$dbConnection->insertIfNotExist(self::RELATION_TABLE,
480
-							[
481
-								'objid' => $relation['objid'],
482
-								'categoryid' => $tagId,
483
-								'type' => $this->type,
484
-							]);
485
-					} catch (\Exception $e) {
486
-						\OC::$server->getLogger()->logException($e, [
487
-							'message' => __METHOD__,
488
-							'level' => ILogger::ERROR,
489
-							'app' => 'core',
490
-						]);
491
-					}
492
-				}
493
-			}
494
-			self::$relations = []; // reset
495
-		} else {
496
-			\OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! '
497
-				. print_r($this->tags, true), ILogger::ERROR);
498
-		}
499
-	}
500
-
501
-	/**
502
-	 * Delete tags and tag/object relations for a user.
503
-	 *
504
-	 * For hooking up on post_deleteUser
505
-	 *
506
-	 * @param array $arguments
507
-	 */
508
-	public static function post_deleteUser($arguments) {
509
-		// Find all objectid/tagId pairs.
510
-		$result = null;
511
-		try {
512
-			$stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
513
-				. 'WHERE `uid` = ?');
514
-			$result = $stmt->execute([$arguments['uid']]);
515
-			if ($result === null) {
516
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
517
-			}
518
-		} catch (\Exception $e) {
519
-			\OC::$server->getLogger()->logException($e, [
520
-				'message' => __METHOD__,
521
-				'level' => ILogger::ERROR,
522
-				'app' => 'core',
523
-			]);
524
-		}
525
-
526
-		if (!is_null($result)) {
527
-			try {
528
-				$stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
529
-					. 'WHERE `categoryid` = ?');
530
-				while ($row = $result->fetchRow()) {
531
-					try {
532
-						$stmt->execute([$row['id']]);
533
-					} catch (\Exception $e) {
534
-						\OC::$server->getLogger()->logException($e, [
535
-							'message' => __METHOD__,
536
-							'level' => ILogger::ERROR,
537
-							'app' => 'core',
538
-						]);
539
-					}
540
-				}
541
-			} catch (\Exception $e) {
542
-				\OC::$server->getLogger()->logException($e, [
543
-					'message' => __METHOD__,
544
-					'level' => ILogger::ERROR,
545
-					'app' => 'core',
546
-				]);
547
-			}
548
-		}
549
-		try {
550
-			$stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
551
-				. 'WHERE `uid` = ?');
552
-			$result = $stmt->execute([$arguments['uid']]);
553
-			if ($result === null) {
554
-				\OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
555
-			}
556
-		} catch (\Exception $e) {
557
-			\OC::$server->getLogger()->logException($e, [
558
-				'message' => __METHOD__,
559
-				'level' => ILogger::ERROR,
560
-				'app' => 'core',
561
-			]);
562
-		}
563
-	}
564
-
565
-	/**
566
-	 * Delete tag/object relations from the db
567
-	 *
568
-	 * @param array $ids The ids of the objects
569
-	 * @return boolean Returns false on error.
570
-	 */
571
-	public function purgeObjects(array $ids) {
572
-		if (count($ids) === 0) {
573
-			// job done ;)
574
-			return true;
575
-		}
576
-		$updates = $ids;
577
-		try {
578
-			$query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
579
-			$query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) ';
580
-			$query .= 'AND `type`= ?';
581
-			$updates[] = $this->type;
582
-			$stmt = \OC_DB::prepare($query);
583
-			$result = $stmt->execute($updates);
584
-			if ($result === null) {
585
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
586
-				return false;
587
-			}
588
-		} catch (\Exception $e) {
589
-			\OC::$server->getLogger()->logException($e, [
590
-				'message' => __METHOD__,
591
-				'level' => ILogger::ERROR,
592
-				'app' => 'core',
593
-			]);
594
-			return false;
595
-		}
596
-		return true;
597
-	}
598
-
599
-	/**
600
-	 * Get favorites for an object type
601
-	 *
602
-	 * @return array|false An array of object ids.
603
-	 */
604
-	public function getFavorites() {
605
-		if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
606
-			return [];
607
-		}
608
-
609
-		try {
610
-			return $this->getIdsForTag(ITags::TAG_FAVORITE);
611
-		} catch (\Exception $e) {
612
-			\OC::$server->getLogger()->logException($e, [
613
-				'message' => __METHOD__,
614
-				'level' => ILogger::ERROR,
615
-				'app' => 'core',
616
-			]);
617
-			return [];
618
-		}
619
-	}
620
-
621
-	/**
622
-	 * Add an object to favorites
623
-	 *
624
-	 * @param int $objid The id of the object
625
-	 * @return boolean
626
-	 */
627
-	public function addToFavorites($objid) {
628
-		if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
629
-			$this->add(ITags::TAG_FAVORITE);
630
-		}
631
-		return $this->tagAs($objid, ITags::TAG_FAVORITE);
632
-	}
633
-
634
-	/**
635
-	 * Remove an object from favorites
636
-	 *
637
-	 * @param int $objid The id of the object
638
-	 * @return boolean
639
-	 */
640
-	public function removeFromFavorites($objid) {
641
-		return $this->unTag($objid, ITags::TAG_FAVORITE);
642
-	}
643
-
644
-	/**
645
-	 * Creates a tag/object relation.
646
-	 *
647
-	 * @param int $objid The id of the object
648
-	 * @param string $tag The id or name of the tag
649
-	 * @return boolean Returns false on error.
650
-	 */
651
-	public function tagAs($objid, $tag) {
652
-		if (is_string($tag) && !is_numeric($tag)) {
653
-			$tag = trim($tag);
654
-			if ($tag === '') {
655
-				\OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
656
-				return false;
657
-			}
658
-			if (!$this->hasTag($tag)) {
659
-				$this->add($tag);
660
-			}
661
-			$tagId =  $this->getTagId($tag);
662
-		} else {
663
-			$tagId = $tag;
664
-		}
665
-		try {
666
-			\OC::$server->getDatabaseConnection()->insertIfNotExist(self::RELATION_TABLE,
667
-				[
668
-					'objid' => $objid,
669
-					'categoryid' => $tagId,
670
-					'type' => $this->type,
671
-				]);
672
-		} catch (\Exception $e) {
673
-			\OC::$server->getLogger()->logException($e, [
674
-				'message' => __METHOD__,
675
-				'level' => ILogger::ERROR,
676
-				'app' => 'core',
677
-			]);
678
-			return false;
679
-		}
680
-		return true;
681
-	}
682
-
683
-	/**
684
-	 * Delete single tag/object relation from the db
685
-	 *
686
-	 * @param int $objid The id of the object
687
-	 * @param string $tag The id or name of the tag
688
-	 * @return boolean
689
-	 */
690
-	public function unTag($objid, $tag) {
691
-		if (is_string($tag) && !is_numeric($tag)) {
692
-			$tag = trim($tag);
693
-			if ($tag === '') {
694
-				\OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
695
-				return false;
696
-			}
697
-			$tagId =  $this->getTagId($tag);
698
-		} else {
699
-			$tagId = $tag;
700
-		}
701
-
702
-		try {
703
-			$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
704
-					. 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
705
-			$stmt = \OC_DB::prepare($sql);
706
-			$stmt->execute([$objid, $tagId, $this->type]);
707
-		} catch (\Exception $e) {
708
-			\OC::$server->getLogger()->logException($e, [
709
-				'message' => __METHOD__,
710
-				'level' => ILogger::ERROR,
711
-				'app' => 'core',
712
-			]);
713
-			return false;
714
-		}
715
-		return true;
716
-	}
717
-
718
-	/**
719
-	 * Delete tags from the database.
720
-	 *
721
-	 * @param string[]|integer[] $names An array of tags (names or IDs) to delete
722
-	 * @return bool Returns false on error
723
-	 */
724
-	public function delete($names) {
725
-		if (!is_array($names)) {
726
-			$names = [$names];
727
-		}
728
-
729
-		$names = array_map('trim', $names);
730
-		array_filter($names);
731
-
732
-		\OCP\Util::writeLog('core', __METHOD__ . ', before: '
733
-			. print_r($this->tags, true), ILogger::DEBUG);
734
-		foreach ($names as $name) {
735
-			$id = null;
736
-
737
-			if (is_numeric($name)) {
738
-				$key = $this->getTagById($name);
739
-			} else {
740
-				$key = $this->getTagByName($name);
741
-			}
742
-			if ($key !== false) {
743
-				$tag = $this->tags[$key];
744
-				$id = $tag->getId();
745
-				unset($this->tags[$key]);
746
-				$this->mapper->delete($tag);
747
-			} else {
748
-				\OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
749
-					. ': not found.', ILogger::ERROR);
750
-			}
751
-			if (!is_null($id) && $id !== false) {
752
-				try {
753
-					$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
754
-							. 'WHERE `categoryid` = ?';
755
-					$stmt = \OC_DB::prepare($sql);
756
-					$result = $stmt->execute([$id]);
757
-					if ($result === null) {
758
-						\OCP\Util::writeLog('core',
759
-							__METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
760
-							ILogger::ERROR);
761
-						return false;
762
-					}
763
-				} catch (\Exception $e) {
764
-					\OC::$server->getLogger()->logException($e, [
765
-						'message' => __METHOD__,
766
-						'level' => ILogger::ERROR,
767
-						'app' => 'core',
768
-					]);
769
-					return false;
770
-				}
771
-			}
772
-		}
773
-		return true;
774
-	}
775
-
776
-	// case-insensitive array_search
777
-	protected function array_searchi($needle, $haystack, $mem='getName') {
778
-		if (!is_array($haystack)) {
779
-			return false;
780
-		}
781
-		return array_search(strtolower($needle), array_map(
782
-			function ($tag) use ($mem) {
783
-				return strtolower(call_user_func([$tag, $mem]));
784
-			}, $haystack)
785
-		);
786
-	}
787
-
788
-	/**
789
-	 * Get a tag's ID.
790
-	 *
791
-	 * @param string $name The tag name to look for.
792
-	 * @return string|bool The tag's id or false if no matching tag is found.
793
-	 */
794
-	private function getTagId($name) {
795
-		$key = $this->array_searchi($name, $this->tags);
796
-		if ($key !== false) {
797
-			return $this->tags[$key]->getId();
798
-		}
799
-		return false;
800
-	}
801
-
802
-	/**
803
-	 * Get a tag by its name.
804
-	 *
805
-	 * @param string $name The tag name.
806
-	 * @return integer|bool The tag object's offset within the $this->tags
807
-	 *                      array or false if it doesn't exist.
808
-	 */
809
-	private function getTagByName($name) {
810
-		return $this->array_searchi($name, $this->tags, 'getName');
811
-	}
812
-
813
-	/**
814
-	 * Get a tag by its ID.
815
-	 *
816
-	 * @param string $id The tag ID to look for.
817
-	 * @return integer|bool The tag object's offset within the $this->tags
818
-	 *                      array or false if it doesn't exist.
819
-	 */
820
-	private function getTagById($id) {
821
-		return $this->array_searchi($id, $this->tags, 'getId');
822
-	}
823
-
824
-	/**
825
-	 * Returns an array mapping a given tag's properties to its values:
826
-	 * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
827
-	 *
828
-	 * @param Tag $tag The tag that is going to be mapped
829
-	 * @return array
830
-	 */
831
-	private function tagMap(Tag $tag) {
832
-		return [
833
-			'id'    => $tag->getId(),
834
-			'name'  => $tag->getName(),
835
-			'owner' => $tag->getOwner(),
836
-			'type'  => $tag->getType()
837
-		];
838
-	}
54
+    /**
55
+     * Tags
56
+     *
57
+     * @var array
58
+     */
59
+    private $tags = [];
60
+
61
+    /**
62
+     * Used for storing objectid/categoryname pairs while rescanning.
63
+     *
64
+     * @var array
65
+     */
66
+    private static $relations = [];
67
+
68
+    /**
69
+     * Type
70
+     *
71
+     * @var string
72
+     */
73
+    private $type;
74
+
75
+    /**
76
+     * User
77
+     *
78
+     * @var string
79
+     */
80
+    private $user;
81
+
82
+    /**
83
+     * Are we including tags for shared items?
84
+     *
85
+     * @var bool
86
+     */
87
+    private $includeShared = false;
88
+
89
+    /**
90
+     * The current user, plus any owners of the items shared with the current
91
+     * user, if $this->includeShared === true.
92
+     *
93
+     * @var array
94
+     */
95
+    private $owners = [];
96
+
97
+    /**
98
+     * The Mapper we're using to communicate our Tag objects to the database.
99
+     *
100
+     * @var TagMapper
101
+     */
102
+    private $mapper;
103
+
104
+    /**
105
+     * The sharing backend for objects of $this->type. Required if
106
+     * $this->includeShared === true to determine ownership of items.
107
+     *
108
+     * @var \OCP\Share_Backend
109
+     */
110
+    private $backend;
111
+
112
+    public const TAG_TABLE = '*PREFIX*vcategory';
113
+    public const RELATION_TABLE = '*PREFIX*vcategory_to_object';
114
+
115
+    /**
116
+     * Constructor.
117
+     *
118
+     * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
119
+     * @param string $user The user whose data the object will operate on.
120
+     * @param string $type The type of items for which tags will be loaded.
121
+     * @param array $defaultTags Tags that should be created at construction.
122
+     *
123
+     * since 20.0.0 $includeShared isn't used anymore
124
+     */
125
+    public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) {
126
+        $this->mapper = $mapper;
127
+        $this->user = $user;
128
+        $this->type = $type;
129
+        $this->owners = [$this->user];
130
+        $this->tags = $this->mapper->loadTags($this->owners, $this->type);
131
+
132
+        if (count($defaultTags) > 0 && count($this->tags) === 0) {
133
+            $this->addMultiple($defaultTags, true);
134
+        }
135
+    }
136
+
137
+    /**
138
+     * Check if any tags are saved for this type and user.
139
+     *
140
+     * @return boolean
141
+     */
142
+    public function isEmpty() {
143
+        return count($this->tags) === 0;
144
+    }
145
+
146
+    /**
147
+     * Returns an array mapping a given tag's properties to its values:
148
+     * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
149
+     *
150
+     * @param string $id The ID of the tag that is going to be mapped
151
+     * @return array|false
152
+     */
153
+    public function getTag($id) {
154
+        $key = $this->getTagById($id);
155
+        if ($key !== false) {
156
+            return $this->tagMap($this->tags[$key]);
157
+        }
158
+        return false;
159
+    }
160
+
161
+    /**
162
+     * Get the tags for a specific user.
163
+     *
164
+     * This returns an array with maps containing each tag's properties:
165
+     * [
166
+     * 	['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'],
167
+     * 	['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'],
168
+     * ]
169
+     *
170
+     * @return array
171
+     */
172
+    public function getTags() {
173
+        if (!count($this->tags)) {
174
+            return [];
175
+        }
176
+
177
+        usort($this->tags, function ($a, $b) {
178
+            return strnatcasecmp($a->getName(), $b->getName());
179
+        });
180
+        $tagMap = [];
181
+
182
+        foreach ($this->tags as $tag) {
183
+            if ($tag->getName() !== ITags::TAG_FAVORITE) {
184
+                $tagMap[] = $this->tagMap($tag);
185
+            }
186
+        }
187
+        return $tagMap;
188
+    }
189
+
190
+    /**
191
+     * Return only the tags owned by the given user, omitting any tags shared
192
+     * by other users.
193
+     *
194
+     * @param string $user The user whose tags are to be checked.
195
+     * @return array An array of Tag objects.
196
+     */
197
+    public function getTagsForUser($user) {
198
+        return array_filter($this->tags,
199
+            function ($tag) use ($user) {
200
+                return $tag->getOwner() === $user;
201
+            }
202
+        );
203
+    }
204
+
205
+    /**
206
+     * Get the list of tags for the given ids.
207
+     *
208
+     * @param array $objIds array of object ids
209
+     * @return array|boolean of tags id as key to array of tag names
210
+     * or false if an error occurred
211
+     */
212
+    public function getTagsForObjects(array $objIds) {
213
+        $entries = [];
214
+
215
+        try {
216
+            $conn = \OC::$server->getDatabaseConnection();
217
+            $chunks = array_chunk($objIds, 900, false);
218
+            foreach ($chunks as $chunk) {
219
+                $result = $conn->executeQuery(
220
+                    'SELECT `category`, `categoryid`, `objid` ' .
221
+                    'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
222
+                    'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
223
+                    [$this->user, $this->type, $chunk],
224
+                    [null, null, IQueryBuilder::PARAM_INT_ARRAY]
225
+                );
226
+                while ($row = $result->fetch()) {
227
+                    $objId = (int)$row['objid'];
228
+                    if (!isset($entries[$objId])) {
229
+                        $entries[$objId] = [];
230
+                    }
231
+                    $entries[$objId][] = $row['category'];
232
+                }
233
+                if ($result === null) {
234
+                    \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
235
+                    return false;
236
+                }
237
+            }
238
+        } catch (\Exception $e) {
239
+            \OC::$server->getLogger()->logException($e, [
240
+                'message' => __METHOD__,
241
+                'level' => ILogger::ERROR,
242
+                'app' => 'core',
243
+            ]);
244
+            return false;
245
+        }
246
+
247
+        return $entries;
248
+    }
249
+
250
+    /**
251
+     * Get the a list if items tagged with $tag.
252
+     *
253
+     * Throws an exception if the tag could not be found.
254
+     *
255
+     * @param string $tag Tag id or name.
256
+     * @return array|false An array of object ids or false on error.
257
+     * @throws \Exception
258
+     */
259
+    public function getIdsForTag($tag) {
260
+        $result = null;
261
+        $tagId = false;
262
+        if (is_numeric($tag)) {
263
+            $tagId = $tag;
264
+        } elseif (is_string($tag)) {
265
+            $tag = trim($tag);
266
+            if ($tag === '') {
267
+                \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
268
+                return false;
269
+            }
270
+            $tagId = $this->getTagId($tag);
271
+        }
272
+
273
+        if ($tagId === false) {
274
+            $l10n = \OC::$server->getL10N('core');
275
+            throw new \Exception(
276
+                $l10n->t('Could not find category "%s"', [$tag])
277
+            );
278
+        }
279
+
280
+        $ids = [];
281
+        $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
282
+            . '` WHERE `categoryid` = ?';
283
+
284
+        try {
285
+            $stmt = \OC_DB::prepare($sql);
286
+            $result = $stmt->execute([$tagId]);
287
+            if ($result === null) {
288
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
289
+                return false;
290
+            }
291
+        } catch (\Exception $e) {
292
+            \OC::$server->getLogger()->logException($e, [
293
+                'message' => __METHOD__,
294
+                'level' => ILogger::ERROR,
295
+                'app' => 'core',
296
+            ]);
297
+            return false;
298
+        }
299
+
300
+        if (!is_null($result)) {
301
+            while ($row = $result->fetchRow()) {
302
+                $ids[] = (int)$row['objid'];
303
+            }
304
+        }
305
+
306
+        return $ids;
307
+    }
308
+
309
+    /**
310
+     * Checks whether a tag is saved for the given user,
311
+     * disregarding the ones shared with him or her.
312
+     *
313
+     * @param string $name The tag name to check for.
314
+     * @param string $user The user whose tags are to be checked.
315
+     * @return bool
316
+     */
317
+    public function userHasTag($name, $user) {
318
+        $key = $this->array_searchi($name, $this->getTagsForUser($user));
319
+        return ($key !== false) ? $this->tags[$key]->getId() : false;
320
+    }
321
+
322
+    /**
323
+     * Checks whether a tag is saved for or shared with the current user.
324
+     *
325
+     * @param string $name The tag name to check for.
326
+     * @return bool
327
+     */
328
+    public function hasTag($name) {
329
+        return $this->getTagId($name) !== false;
330
+    }
331
+
332
+    /**
333
+     * Add a new tag.
334
+     *
335
+     * @param string $name A string with a name of the tag
336
+     * @return false|int the id of the added tag or false on error.
337
+     */
338
+    public function add($name) {
339
+        $name = trim($name);
340
+
341
+        if ($name === '') {
342
+            \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
343
+            return false;
344
+        }
345
+        if ($this->userHasTag($name, $this->user)) {
346
+            \OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
347
+            return false;
348
+        }
349
+        try {
350
+            $tag = new Tag($this->user, $this->type, $name);
351
+            $tag = $this->mapper->insert($tag);
352
+            $this->tags[] = $tag;
353
+        } catch (\Exception $e) {
354
+            \OC::$server->getLogger()->logException($e, [
355
+                'message' => __METHOD__,
356
+                'level' => ILogger::ERROR,
357
+                'app' => 'core',
358
+            ]);
359
+            return false;
360
+        }
361
+        \OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
362
+        return $tag->getId();
363
+    }
364
+
365
+    /**
366
+     * Rename tag.
367
+     *
368
+     * @param string|integer $from The name or ID of the existing tag
369
+     * @param string $to The new name of the tag.
370
+     * @return bool
371
+     */
372
+    public function rename($from, $to) {
373
+        $from = trim($from);
374
+        $to = trim($to);
375
+
376
+        if ($to === '' || $from === '') {
377
+            \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
378
+            return false;
379
+        }
380
+
381
+        if (is_numeric($from)) {
382
+            $key = $this->getTagById($from);
383
+        } else {
384
+            $key = $this->getTagByName($from);
385
+        }
386
+        if ($key === false) {
387
+            \OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
388
+            return false;
389
+        }
390
+        $tag = $this->tags[$key];
391
+
392
+        if ($this->userHasTag($to, $tag->getOwner())) {
393
+            \OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
394
+            return false;
395
+        }
396
+
397
+        try {
398
+            $tag->setName($to);
399
+            $this->tags[$key] = $this->mapper->update($tag);
400
+        } catch (\Exception $e) {
401
+            \OC::$server->getLogger()->logException($e, [
402
+                'message' => __METHOD__,
403
+                'level' => ILogger::ERROR,
404
+                'app' => 'core',
405
+            ]);
406
+            return false;
407
+        }
408
+        return true;
409
+    }
410
+
411
+    /**
412
+     * Add a list of new tags.
413
+     *
414
+     * @param string[] $names A string with a name or an array of strings containing
415
+     * the name(s) of the tag(s) to add.
416
+     * @param bool $sync When true, save the tags
417
+     * @param int|null $id int Optional object id to add to this|these tag(s)
418
+     * @return bool Returns false on error.
419
+     */
420
+    public function addMultiple($names, $sync=false, $id = null) {
421
+        if (!is_array($names)) {
422
+            $names = [$names];
423
+        }
424
+        $names = array_map('trim', $names);
425
+        array_filter($names);
426
+
427
+        $newones = [];
428
+        foreach ($names as $name) {
429
+            if (!$this->hasTag($name) && $name !== '') {
430
+                $newones[] = new Tag($this->user, $this->type, $name);
431
+            }
432
+            if (!is_null($id)) {
433
+                // Insert $objectid, $categoryid  pairs if not exist.
434
+                self::$relations[] = ['objid' => $id, 'tag' => $name];
435
+            }
436
+        }
437
+        $this->tags = array_merge($this->tags, $newones);
438
+        if ($sync === true) {
439
+            $this->save();
440
+        }
441
+
442
+        return true;
443
+    }
444
+
445
+    /**
446
+     * Save the list of tags and their object relations
447
+     */
448
+    protected function save() {
449
+        if (is_array($this->tags)) {
450
+            foreach ($this->tags as $tag) {
451
+                try {
452
+                    if (!$this->mapper->tagExists($tag)) {
453
+                        $this->mapper->insert($tag);
454
+                    }
455
+                } catch (\Exception $e) {
456
+                    \OC::$server->getLogger()->logException($e, [
457
+                        'message' => __METHOD__,
458
+                        'level' => ILogger::ERROR,
459
+                        'app' => 'core',
460
+                    ]);
461
+                }
462
+            }
463
+
464
+            // reload tags to get the proper ids.
465
+            $this->tags = $this->mapper->loadTags($this->owners, $this->type);
466
+            \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
467
+                ILogger::DEBUG);
468
+            // Loop through temporarily cached objectid/tagname pairs
469
+            // and save relations.
470
+            $tags = $this->tags;
471
+            // For some reason this is needed or array_search(i) will return 0..?
472
+            ksort($tags);
473
+            $dbConnection = \OC::$server->getDatabaseConnection();
474
+            foreach (self::$relations as $relation) {
475
+                $tagId = $this->getTagId($relation['tag']);
476
+                \OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
477
+                if ($tagId) {
478
+                    try {
479
+                        $dbConnection->insertIfNotExist(self::RELATION_TABLE,
480
+                            [
481
+                                'objid' => $relation['objid'],
482
+                                'categoryid' => $tagId,
483
+                                'type' => $this->type,
484
+                            ]);
485
+                    } catch (\Exception $e) {
486
+                        \OC::$server->getLogger()->logException($e, [
487
+                            'message' => __METHOD__,
488
+                            'level' => ILogger::ERROR,
489
+                            'app' => 'core',
490
+                        ]);
491
+                    }
492
+                }
493
+            }
494
+            self::$relations = []; // reset
495
+        } else {
496
+            \OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! '
497
+                . print_r($this->tags, true), ILogger::ERROR);
498
+        }
499
+    }
500
+
501
+    /**
502
+     * Delete tags and tag/object relations for a user.
503
+     *
504
+     * For hooking up on post_deleteUser
505
+     *
506
+     * @param array $arguments
507
+     */
508
+    public static function post_deleteUser($arguments) {
509
+        // Find all objectid/tagId pairs.
510
+        $result = null;
511
+        try {
512
+            $stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
513
+                . 'WHERE `uid` = ?');
514
+            $result = $stmt->execute([$arguments['uid']]);
515
+            if ($result === null) {
516
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
517
+            }
518
+        } catch (\Exception $e) {
519
+            \OC::$server->getLogger()->logException($e, [
520
+                'message' => __METHOD__,
521
+                'level' => ILogger::ERROR,
522
+                'app' => 'core',
523
+            ]);
524
+        }
525
+
526
+        if (!is_null($result)) {
527
+            try {
528
+                $stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
529
+                    . 'WHERE `categoryid` = ?');
530
+                while ($row = $result->fetchRow()) {
531
+                    try {
532
+                        $stmt->execute([$row['id']]);
533
+                    } catch (\Exception $e) {
534
+                        \OC::$server->getLogger()->logException($e, [
535
+                            'message' => __METHOD__,
536
+                            'level' => ILogger::ERROR,
537
+                            'app' => 'core',
538
+                        ]);
539
+                    }
540
+                }
541
+            } catch (\Exception $e) {
542
+                \OC::$server->getLogger()->logException($e, [
543
+                    'message' => __METHOD__,
544
+                    'level' => ILogger::ERROR,
545
+                    'app' => 'core',
546
+                ]);
547
+            }
548
+        }
549
+        try {
550
+            $stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
551
+                . 'WHERE `uid` = ?');
552
+            $result = $stmt->execute([$arguments['uid']]);
553
+            if ($result === null) {
554
+                \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
555
+            }
556
+        } catch (\Exception $e) {
557
+            \OC::$server->getLogger()->logException($e, [
558
+                'message' => __METHOD__,
559
+                'level' => ILogger::ERROR,
560
+                'app' => 'core',
561
+            ]);
562
+        }
563
+    }
564
+
565
+    /**
566
+     * Delete tag/object relations from the db
567
+     *
568
+     * @param array $ids The ids of the objects
569
+     * @return boolean Returns false on error.
570
+     */
571
+    public function purgeObjects(array $ids) {
572
+        if (count($ids) === 0) {
573
+            // job done ;)
574
+            return true;
575
+        }
576
+        $updates = $ids;
577
+        try {
578
+            $query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
579
+            $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) ';
580
+            $query .= 'AND `type`= ?';
581
+            $updates[] = $this->type;
582
+            $stmt = \OC_DB::prepare($query);
583
+            $result = $stmt->execute($updates);
584
+            if ($result === null) {
585
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
586
+                return false;
587
+            }
588
+        } catch (\Exception $e) {
589
+            \OC::$server->getLogger()->logException($e, [
590
+                'message' => __METHOD__,
591
+                'level' => ILogger::ERROR,
592
+                'app' => 'core',
593
+            ]);
594
+            return false;
595
+        }
596
+        return true;
597
+    }
598
+
599
+    /**
600
+     * Get favorites for an object type
601
+     *
602
+     * @return array|false An array of object ids.
603
+     */
604
+    public function getFavorites() {
605
+        if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
606
+            return [];
607
+        }
608
+
609
+        try {
610
+            return $this->getIdsForTag(ITags::TAG_FAVORITE);
611
+        } catch (\Exception $e) {
612
+            \OC::$server->getLogger()->logException($e, [
613
+                'message' => __METHOD__,
614
+                'level' => ILogger::ERROR,
615
+                'app' => 'core',
616
+            ]);
617
+            return [];
618
+        }
619
+    }
620
+
621
+    /**
622
+     * Add an object to favorites
623
+     *
624
+     * @param int $objid The id of the object
625
+     * @return boolean
626
+     */
627
+    public function addToFavorites($objid) {
628
+        if (!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
629
+            $this->add(ITags::TAG_FAVORITE);
630
+        }
631
+        return $this->tagAs($objid, ITags::TAG_FAVORITE);
632
+    }
633
+
634
+    /**
635
+     * Remove an object from favorites
636
+     *
637
+     * @param int $objid The id of the object
638
+     * @return boolean
639
+     */
640
+    public function removeFromFavorites($objid) {
641
+        return $this->unTag($objid, ITags::TAG_FAVORITE);
642
+    }
643
+
644
+    /**
645
+     * Creates a tag/object relation.
646
+     *
647
+     * @param int $objid The id of the object
648
+     * @param string $tag The id or name of the tag
649
+     * @return boolean Returns false on error.
650
+     */
651
+    public function tagAs($objid, $tag) {
652
+        if (is_string($tag) && !is_numeric($tag)) {
653
+            $tag = trim($tag);
654
+            if ($tag === '') {
655
+                \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
656
+                return false;
657
+            }
658
+            if (!$this->hasTag($tag)) {
659
+                $this->add($tag);
660
+            }
661
+            $tagId =  $this->getTagId($tag);
662
+        } else {
663
+            $tagId = $tag;
664
+        }
665
+        try {
666
+            \OC::$server->getDatabaseConnection()->insertIfNotExist(self::RELATION_TABLE,
667
+                [
668
+                    'objid' => $objid,
669
+                    'categoryid' => $tagId,
670
+                    'type' => $this->type,
671
+                ]);
672
+        } catch (\Exception $e) {
673
+            \OC::$server->getLogger()->logException($e, [
674
+                'message' => __METHOD__,
675
+                'level' => ILogger::ERROR,
676
+                'app' => 'core',
677
+            ]);
678
+            return false;
679
+        }
680
+        return true;
681
+    }
682
+
683
+    /**
684
+     * Delete single tag/object relation from the db
685
+     *
686
+     * @param int $objid The id of the object
687
+     * @param string $tag The id or name of the tag
688
+     * @return boolean
689
+     */
690
+    public function unTag($objid, $tag) {
691
+        if (is_string($tag) && !is_numeric($tag)) {
692
+            $tag = trim($tag);
693
+            if ($tag === '') {
694
+                \OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
695
+                return false;
696
+            }
697
+            $tagId =  $this->getTagId($tag);
698
+        } else {
699
+            $tagId = $tag;
700
+        }
701
+
702
+        try {
703
+            $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
704
+                    . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
705
+            $stmt = \OC_DB::prepare($sql);
706
+            $stmt->execute([$objid, $tagId, $this->type]);
707
+        } catch (\Exception $e) {
708
+            \OC::$server->getLogger()->logException($e, [
709
+                'message' => __METHOD__,
710
+                'level' => ILogger::ERROR,
711
+                'app' => 'core',
712
+            ]);
713
+            return false;
714
+        }
715
+        return true;
716
+    }
717
+
718
+    /**
719
+     * Delete tags from the database.
720
+     *
721
+     * @param string[]|integer[] $names An array of tags (names or IDs) to delete
722
+     * @return bool Returns false on error
723
+     */
724
+    public function delete($names) {
725
+        if (!is_array($names)) {
726
+            $names = [$names];
727
+        }
728
+
729
+        $names = array_map('trim', $names);
730
+        array_filter($names);
731
+
732
+        \OCP\Util::writeLog('core', __METHOD__ . ', before: '
733
+            . print_r($this->tags, true), ILogger::DEBUG);
734
+        foreach ($names as $name) {
735
+            $id = null;
736
+
737
+            if (is_numeric($name)) {
738
+                $key = $this->getTagById($name);
739
+            } else {
740
+                $key = $this->getTagByName($name);
741
+            }
742
+            if ($key !== false) {
743
+                $tag = $this->tags[$key];
744
+                $id = $tag->getId();
745
+                unset($this->tags[$key]);
746
+                $this->mapper->delete($tag);
747
+            } else {
748
+                \OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
749
+                    . ': not found.', ILogger::ERROR);
750
+            }
751
+            if (!is_null($id) && $id !== false) {
752
+                try {
753
+                    $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
754
+                            . 'WHERE `categoryid` = ?';
755
+                    $stmt = \OC_DB::prepare($sql);
756
+                    $result = $stmt->execute([$id]);
757
+                    if ($result === null) {
758
+                        \OCP\Util::writeLog('core',
759
+                            __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
760
+                            ILogger::ERROR);
761
+                        return false;
762
+                    }
763
+                } catch (\Exception $e) {
764
+                    \OC::$server->getLogger()->logException($e, [
765
+                        'message' => __METHOD__,
766
+                        'level' => ILogger::ERROR,
767
+                        'app' => 'core',
768
+                    ]);
769
+                    return false;
770
+                }
771
+            }
772
+        }
773
+        return true;
774
+    }
775
+
776
+    // case-insensitive array_search
777
+    protected function array_searchi($needle, $haystack, $mem='getName') {
778
+        if (!is_array($haystack)) {
779
+            return false;
780
+        }
781
+        return array_search(strtolower($needle), array_map(
782
+            function ($tag) use ($mem) {
783
+                return strtolower(call_user_func([$tag, $mem]));
784
+            }, $haystack)
785
+        );
786
+    }
787
+
788
+    /**
789
+     * Get a tag's ID.
790
+     *
791
+     * @param string $name The tag name to look for.
792
+     * @return string|bool The tag's id or false if no matching tag is found.
793
+     */
794
+    private function getTagId($name) {
795
+        $key = $this->array_searchi($name, $this->tags);
796
+        if ($key !== false) {
797
+            return $this->tags[$key]->getId();
798
+        }
799
+        return false;
800
+    }
801
+
802
+    /**
803
+     * Get a tag by its name.
804
+     *
805
+     * @param string $name The tag name.
806
+     * @return integer|bool The tag object's offset within the $this->tags
807
+     *                      array or false if it doesn't exist.
808
+     */
809
+    private function getTagByName($name) {
810
+        return $this->array_searchi($name, $this->tags, 'getName');
811
+    }
812
+
813
+    /**
814
+     * Get a tag by its ID.
815
+     *
816
+     * @param string $id The tag ID to look for.
817
+     * @return integer|bool The tag object's offset within the $this->tags
818
+     *                      array or false if it doesn't exist.
819
+     */
820
+    private function getTagById($id) {
821
+        return $this->array_searchi($id, $this->tags, 'getId');
822
+    }
823
+
824
+    /**
825
+     * Returns an array mapping a given tag's properties to its values:
826
+     * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
827
+     *
828
+     * @param Tag $tag The tag that is going to be mapped
829
+     * @return array
830
+     */
831
+    private function tagMap(Tag $tag) {
832
+        return [
833
+            'id'    => $tag->getId(),
834
+            'name'  => $tag->getName(),
835
+            'owner' => $tag->getOwner(),
836
+            'type'  => $tag->getType()
837
+        ];
838
+    }
839 839
 }
Please login to merge, or discard this patch.
Spacing   +33 added lines, -33 removed lines patch added patch discarded remove patch
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
 			return [];
175 175
 		}
176 176
 
177
-		usort($this->tags, function ($a, $b) {
177
+		usort($this->tags, function($a, $b) {
178 178
 			return strnatcasecmp($a->getName(), $b->getName());
179 179
 		});
180 180
 		$tagMap = [];
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
 	 */
197 197
 	public function getTagsForUser($user) {
198 198
 		return array_filter($this->tags,
199
-			function ($tag) use ($user) {
199
+			function($tag) use ($user) {
200 200
 				return $tag->getOwner() === $user;
201 201
 			}
202 202
 		);
@@ -217,21 +217,21 @@  discard block
 block discarded – undo
217 217
 			$chunks = array_chunk($objIds, 900, false);
218 218
 			foreach ($chunks as $chunk) {
219 219
 				$result = $conn->executeQuery(
220
-					'SELECT `category`, `categoryid`, `objid` ' .
221
-					'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
220
+					'SELECT `category`, `categoryid`, `objid` '.
221
+					'FROM `'.self::RELATION_TABLE.'` r, `'.self::TAG_TABLE.'` '.
222 222
 					'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
223 223
 					[$this->user, $this->type, $chunk],
224 224
 					[null, null, IQueryBuilder::PARAM_INT_ARRAY]
225 225
 				);
226 226
 				while ($row = $result->fetch()) {
227
-					$objId = (int)$row['objid'];
227
+					$objId = (int) $row['objid'];
228 228
 					if (!isset($entries[$objId])) {
229 229
 						$entries[$objId] = [];
230 230
 					}
231 231
 					$entries[$objId][] = $row['category'];
232 232
 				}
233 233
 				if ($result === null) {
234
-					\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
234
+					\OCP\Util::writeLog('core', __METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
235 235
 					return false;
236 236
 				}
237 237
 			}
@@ -278,14 +278,14 @@  discard block
 block discarded – undo
278 278
 		}
279 279
 
280 280
 		$ids = [];
281
-		$sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
281
+		$sql = 'SELECT `objid` FROM `'.self::RELATION_TABLE
282 282
 			. '` WHERE `categoryid` = ?';
283 283
 
284 284
 		try {
285 285
 			$stmt = \OC_DB::prepare($sql);
286 286
 			$result = $stmt->execute([$tagId]);
287 287
 			if ($result === null) {
288
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
288
+				\OCP\Util::writeLog('core', __METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
289 289
 				return false;
290 290
 			}
291 291
 		} catch (\Exception $e) {
@@ -299,7 +299,7 @@  discard block
 block discarded – undo
299 299
 
300 300
 		if (!is_null($result)) {
301 301
 			while ($row = $result->fetchRow()) {
302
-				$ids[] = (int)$row['objid'];
302
+				$ids[] = (int) $row['objid'];
303 303
 			}
304 304
 		}
305 305
 
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
 			return false;
344 344
 		}
345 345
 		if ($this->userHasTag($name, $this->user)) {
346
-			\OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
346
+			\OCP\Util::writeLog('core', __METHOD__.', name: '.$name.' exists already', ILogger::DEBUG);
347 347
 			return false;
348 348
 		}
349 349
 		try {
@@ -358,7 +358,7 @@  discard block
 block discarded – undo
358 358
 			]);
359 359
 			return false;
360 360
 		}
361
-		\OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
361
+		\OCP\Util::writeLog('core', __METHOD__.', id: '.$tag->getId(), ILogger::DEBUG);
362 362
 		return $tag->getId();
363 363
 	}
364 364
 
@@ -384,13 +384,13 @@  discard block
 block discarded – undo
384 384
 			$key = $this->getTagByName($from);
385 385
 		}
386 386
 		if ($key === false) {
387
-			\OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
387
+			\OCP\Util::writeLog('core', __METHOD__.', tag: '.$from.' does not exist', ILogger::DEBUG);
388 388
 			return false;
389 389
 		}
390 390
 		$tag = $this->tags[$key];
391 391
 
392 392
 		if ($this->userHasTag($to, $tag->getOwner())) {
393
-			\OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
393
+			\OCP\Util::writeLog('core', __METHOD__.', A tag named '.$to.' already exists for user '.$tag->getOwner().'.', ILogger::DEBUG);
394 394
 			return false;
395 395
 		}
396 396
 
@@ -417,7 +417,7 @@  discard block
 block discarded – undo
417 417
 	 * @param int|null $id int Optional object id to add to this|these tag(s)
418 418
 	 * @return bool Returns false on error.
419 419
 	 */
420
-	public function addMultiple($names, $sync=false, $id = null) {
420
+	public function addMultiple($names, $sync = false, $id = null) {
421 421
 		if (!is_array($names)) {
422 422
 			$names = [$names];
423 423
 		}
@@ -463,7 +463,7 @@  discard block
 block discarded – undo
463 463
 
464 464
 			// reload tags to get the proper ids.
465 465
 			$this->tags = $this->mapper->loadTags($this->owners, $this->type);
466
-			\OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
466
+			\OCP\Util::writeLog('core', __METHOD__.', tags: '.print_r($this->tags, true),
467 467
 				ILogger::DEBUG);
468 468
 			// Loop through temporarily cached objectid/tagname pairs
469 469
 			// and save relations.
@@ -473,7 +473,7 @@  discard block
 block discarded – undo
473 473
 			$dbConnection = \OC::$server->getDatabaseConnection();
474 474
 			foreach (self::$relations as $relation) {
475 475
 				$tagId = $this->getTagId($relation['tag']);
476
-				\OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
476
+				\OCP\Util::writeLog('core', __METHOD__.'catid, '.$relation['tag'].' '.$tagId, ILogger::DEBUG);
477 477
 				if ($tagId) {
478 478
 					try {
479 479
 						$dbConnection->insertIfNotExist(self::RELATION_TABLE,
@@ -509,11 +509,11 @@  discard block
 block discarded – undo
509 509
 		// Find all objectid/tagId pairs.
510 510
 		$result = null;
511 511
 		try {
512
-			$stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
512
+			$stmt = \OC_DB::prepare('SELECT `id` FROM `'.self::TAG_TABLE.'` '
513 513
 				. 'WHERE `uid` = ?');
514 514
 			$result = $stmt->execute([$arguments['uid']]);
515 515
 			if ($result === null) {
516
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
516
+				\OCP\Util::writeLog('core', __METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
517 517
 			}
518 518
 		} catch (\Exception $e) {
519 519
 			\OC::$server->getLogger()->logException($e, [
@@ -525,7 +525,7 @@  discard block
 block discarded – undo
525 525
 
526 526
 		if (!is_null($result)) {
527 527
 			try {
528
-				$stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
528
+				$stmt = \OC_DB::prepare('DELETE FROM `'.self::RELATION_TABLE.'` '
529 529
 					. 'WHERE `categoryid` = ?');
530 530
 				while ($row = $result->fetchRow()) {
531 531
 					try {
@@ -547,11 +547,11 @@  discard block
 block discarded – undo
547 547
 			}
548 548
 		}
549 549
 		try {
550
-			$stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
550
+			$stmt = \OC_DB::prepare('DELETE FROM `'.self::TAG_TABLE.'` '
551 551
 				. 'WHERE `uid` = ?');
552 552
 			$result = $stmt->execute([$arguments['uid']]);
553 553
 			if ($result === null) {
554
-				\OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
554
+				\OCP\Util::writeLog('core', __METHOD__.', DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
555 555
 			}
556 556
 		} catch (\Exception $e) {
557 557
 			\OC::$server->getLogger()->logException($e, [
@@ -575,14 +575,14 @@  discard block
 block discarded – undo
575 575
 		}
576 576
 		$updates = $ids;
577 577
 		try {
578
-			$query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
579
-			$query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) ';
578
+			$query = 'DELETE FROM `'.self::RELATION_TABLE.'` ';
579
+			$query .= 'WHERE `objid` IN ('.str_repeat('?,', count($ids) - 1).'?) ';
580 580
 			$query .= 'AND `type`= ?';
581 581
 			$updates[] = $this->type;
582 582
 			$stmt = \OC_DB::prepare($query);
583 583
 			$result = $stmt->execute($updates);
584 584
 			if ($result === null) {
585
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
585
+				\OCP\Util::writeLog('core', __METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
586 586
 				return false;
587 587
 			}
588 588
 		} catch (\Exception $e) {
@@ -658,7 +658,7 @@  discard block
 block discarded – undo
658 658
 			if (!$this->hasTag($tag)) {
659 659
 				$this->add($tag);
660 660
 			}
661
-			$tagId =  $this->getTagId($tag);
661
+			$tagId = $this->getTagId($tag);
662 662
 		} else {
663 663
 			$tagId = $tag;
664 664
 		}
@@ -694,13 +694,13 @@  discard block
 block discarded – undo
694 694
 				\OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
695 695
 				return false;
696 696
 			}
697
-			$tagId =  $this->getTagId($tag);
697
+			$tagId = $this->getTagId($tag);
698 698
 		} else {
699 699
 			$tagId = $tag;
700 700
 		}
701 701
 
702 702
 		try {
703
-			$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
703
+			$sql = 'DELETE FROM `'.self::RELATION_TABLE.'` '
704 704
 					. 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
705 705
 			$stmt = \OC_DB::prepare($sql);
706 706
 			$stmt->execute([$objid, $tagId, $this->type]);
@@ -729,7 +729,7 @@  discard block
 block discarded – undo
729 729
 		$names = array_map('trim', $names);
730 730
 		array_filter($names);
731 731
 
732
-		\OCP\Util::writeLog('core', __METHOD__ . ', before: '
732
+		\OCP\Util::writeLog('core', __METHOD__.', before: '
733 733
 			. print_r($this->tags, true), ILogger::DEBUG);
734 734
 		foreach ($names as $name) {
735 735
 			$id = null;
@@ -745,18 +745,18 @@  discard block
 block discarded – undo
745 745
 				unset($this->tags[$key]);
746 746
 				$this->mapper->delete($tag);
747 747
 			} else {
748
-				\OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
748
+				\OCP\Util::writeLog('core', __METHOD__.'Cannot delete tag '.$name
749 749
 					. ': not found.', ILogger::ERROR);
750 750
 			}
751 751
 			if (!is_null($id) && $id !== false) {
752 752
 				try {
753
-					$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
753
+					$sql = 'DELETE FROM `'.self::RELATION_TABLE.'` '
754 754
 							. 'WHERE `categoryid` = ?';
755 755
 					$stmt = \OC_DB::prepare($sql);
756 756
 					$result = $stmt->execute([$id]);
757 757
 					if ($result === null) {
758 758
 						\OCP\Util::writeLog('core',
759
-							__METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
759
+							__METHOD__.'DB error: '.\OC::$server->getDatabaseConnection()->getError(),
760 760
 							ILogger::ERROR);
761 761
 						return false;
762 762
 					}
@@ -774,12 +774,12 @@  discard block
 block discarded – undo
774 774
 	}
775 775
 
776 776
 	// case-insensitive array_search
777
-	protected function array_searchi($needle, $haystack, $mem='getName') {
777
+	protected function array_searchi($needle, $haystack, $mem = 'getName') {
778 778
 		if (!is_array($haystack)) {
779 779
 			return false;
780 780
 		}
781 781
 		return array_search(strtolower($needle), array_map(
782
-			function ($tag) use ($mem) {
782
+			function($tag) use ($mem) {
783 783
 				return strtolower(call_user_func([$tag, $mem]));
784 784
 			}, $haystack)
785 785
 		);
Please login to merge, or discard this patch.
lib/public/ITagManager.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -48,17 +48,17 @@
 block discarded – undo
48 48
  */
49 49
 interface ITagManager {
50 50
 
51
-	/**
52
-	 * Create a new \OCP\ITags instance and load tags from db for the current user.
53
-	 *
54
-	 * @see \OCP\ITags
55
-	 * @param string $type The type identifier e.g. 'contact' or 'event'.
56
-	 * @param array $defaultTags An array of default tags to be used if none are stored.
57
-	 * @param boolean $includeShared Whether to include tags for items shared with this user by others. - always false since 20.0.0
58
-	 * @param string $userId user for which to retrieve the tags, defaults to the currently
59
-	 * logged in user
60
-	 * @return \OCP\ITags
61
-	 * @since 6.0.0 - parameter $includeShared and $userId were added in 8.0.0 - $includeShared is always false since 20.0.0
62
-	 */
63
-	public function load($type, $defaultTags = [], $includeShared = false, $userId = null);
51
+    /**
52
+     * Create a new \OCP\ITags instance and load tags from db for the current user.
53
+     *
54
+     * @see \OCP\ITags
55
+     * @param string $type The type identifier e.g. 'contact' or 'event'.
56
+     * @param array $defaultTags An array of default tags to be used if none are stored.
57
+     * @param boolean $includeShared Whether to include tags for items shared with this user by others. - always false since 20.0.0
58
+     * @param string $userId user for which to retrieve the tags, defaults to the currently
59
+     * logged in user
60
+     * @return \OCP\ITags
61
+     * @since 6.0.0 - parameter $includeShared and $userId were added in 8.0.0 - $includeShared is always false since 20.0.0
62
+     */
63
+    public function load($type, $defaultTags = [], $includeShared = false, $userId = null);
64 64
 }
Please login to merge, or discard this patch.