Completed
Push — some-scrutinizing ( c6cac2...cfec76 )
by Maxence
02:21
created

ShareByCircleProvider::groupDeleted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/**
3
 * Circles - Bring cloud-users closer together.
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2017
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
28
namespace OCA\Circles;
29
30
31
use OCA\Circles\AppInfo\Application;
32
use OCA\Circles\Db\CircleProviderRequestBuilder;
33
use OCA\Circles\Model\Circle;
34
use OCP\Files\Folder;
35
use OCP\Files\Node;
36
use OCP\Files\IRootFolder;
37
use OC\Files\Cache\Cache;
38
use OC\Share20\Share;
39
use OCP\Share\IShare;
40
use OCP\Share\Exceptions\ShareNotFound;
41
use OCP\Share\IShareProvider;
42
use OCP\IDBConnection;
43
use OCP\IL10N;
44
use OCP\ILogger;
45
use OCP\IURLGenerator;
46
use OCP\Security\ISecureRandom;
47
use OCP\IUserManager;
48
49
class ShareByCircleProvider extends CircleProviderRequestBuilder implements IShareProvider {
50
51
	private $misc;
52
53
54
	/** @var ILogger */
55
	private $logger;
56
57
	/** @var ISecureRandom */
58
	private $secureRandom;
59
60
	/** @var IUserManager */
61
	private $userManager;
62
63
	/** @var IRootFolder */
64
	private $rootFolder;
65
66
	/** @var IL10N */
67
	private $l;
68
69
	/** @var IURLGenerator */
70
	private $urlGenerator;
71
72
73
	/**
74
	 * DefaultShareProvider constructor.
75
	 *
76
	 * @param IDBConnection $connection
77
	 * @param ISecureRandom $secureRandom
78
	 * @param IUserManager $userManager
79
	 * @param IRootFolder $rootFolder
80
	 * @param IL10N $l
81
	 * @param ILogger $logger
82
	 * @param IURLGenerator $urlGenerator
83
	 */
84
	public function __construct(
85
		IDBConnection $connection, ISecureRandom $secureRandom, IUserManager $userManager,
86
		IRootFolder $rootFolder, IL10N $l, ILogger $logger, IURLGenerator $urlGenerator
87
	) {
88
		$this->dbConnection = $connection;
89
		$this->secureRandom = $secureRandom;
90
		$this->userManager = $userManager;
91
		$this->rootFolder = $rootFolder;
92
		$this->l = $l;
93
		$this->logger = $logger;
94
		$this->urlGenerator = $urlGenerator;
95
96
		$app = new Application();
97
		$this->misc = $app->getContainer()
98
						  ->query('MiscService');
99
100
	}
101
102
103
	/**
104
	 * Return the identifier of this provider.
105
	 *
106
	 * @return string
107
	 */
108
	public function identifier() {
109
		return 'ocCircleShare';
110
	}
111
112
113
	/**
114
	 * Create a share if it does not exist already.
115
	 *
116
	 * @param IShare $share
117
	 *
118
	 * @return IShare The share object
119
	 * @throws \Exception
120
	 */
121
	// TODO: check if user can create a share in this circle !
122
	public function create(IShare $share) {
123
		$nodeId = $share->getNode()
124
						->getId();
125
126
		$qb = $this->findShareParentSql($nodeId, $share->getSharedWith());
127
		$exists = $qb->execute();
128
		$data = $exists->fetch();
129
		$exists->closeCursor();
130
131
		if ($data !== false) {
132
			throw $this->errorShareAlreadyExist($share);
133
		}
134
135
		$shareId = $this->createShare($share);
136
137
		return $this->getShareById($shareId);
138
	}
139
140
141
	/**
142
	 * Update a share
143
	 * permissions right, owner and initiator
144
	 *
145
	 * @param IShare $share
146
	 *
147
	 * @return IShare The share object
148
	 */
149
	public function update(IShare $share) {
150
151
		$qb = $this->getBaseUpdateSql();
152
		$this->limitToShare($qb, $share->getId());
153
		$qb->set('permissions', $qb->createNamedParameter($share->getPermissions()))
154
		   ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
155
		   ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
156
		$qb->execute();
157
158
		return $share;
159
	}
160
161
162
	/**
163
	 * Delete a share, and it's children
164
	 *
165
	 * @param IShare $share
166
	 */
167
	public function delete(IShare $share) {
168
169
		$qb = $this->getBaseDeleteSql();
170
		$this->limitToShareAndChildren($qb, $share->getId());
171
172
		$qb->execute();
173
	}
174
175
176
	/**
177
	 * Unshare a file from self as recipient.
178
	 * Because every circles share are group shares, we will set permissions to 0
179
	 *
180
	 * @param IShare $share
181
	 * @param string $userId
182
	 */
183 View Code Duplication
	public function deleteFromSelf(IShare $share, $userId) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
		$childId = $this->getShareChildId($share, $userId);
185
186
		$qb = $this->getBaseUpdateSql();
187
		$qb->set('permissions', $qb->createNamedParameter(0));
188
		$this->limitToShare($qb, $childId);
189
190
		$qb->execute();
191
	}
192
193
194
	/**
195
	 * Move a share as a recipient.
196
	 *
197
	 * @param IShare $share
198
	 * @param string $userId
199
	 *
200
	 * @return IShare
201
	 *
202
	 */
203 View Code Duplication
	public function move(IShare $share, $userId) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
204
205
		$childId = $this->getShareChildId($share, $userId);
206
207
		$qb = $this->getBaseUpdateSql();
208
		$qb->set('file_target', $qb->createNamedParameter($share->getTarget()));
209
		$this->limitToShare($qb, $childId);
210
		$qb->execute();
211
212
		return $share;
213
	}
214
215
216
	/**
217
	 * return the child ID of a share
218
	 *
219
	 * @param IShare $share
220
	 * @param $userId
221
	 *
222
	 * @return bool
223
	 */
224
	private function getShareChildId(IShare $share, $userId) {
225
		$qb = $this->getBaseSelectSql($share->getId());
226
		$this->limitToShareChildren($qb, $userId, $share->getId());
227
228
		$child = $qb->execute();
229
		$data = $child->fetch();
230
		$child->closeCursor();
231
232
		if ($data === false) {
233
			return $this->createShareChild($userId, $share);
234
		}
235
236
		return $data['id'];
237
	}
238
239
240
	/**
241
	 * Create a child and returns its ID
242
	 *
243
	 * @param IShare $share
244
	 *
245
	 * @return int
246
	 */
247
	private function createShare($share) {
248
		$qb = $this->getBaseInsertSql($share);
249
		$qb->execute();
250
		$id = $qb->getLastInsertId();
251
252
		return (int)$id;
253
	}
254
255
256
	/**
257
	 * Create a child and returns its ID
258
	 *
259
	 * @param string $userId
260
	 * @param IShare $share
261
	 *
262
	 * @return int
263
	 */
264
	private function createShareChild($userId, $share) {
265
		$qb = $this->getBaseInsertSql($share);
266
267
		$qb->setValue('parent', $qb->createNamedParameter($share->getId()));
268
		$qb->setValue('share_with', $qb->createNamedParameter($userId));
269
		$qb->execute();
270
		$id = $qb->getLastInsertId();
271
272
		return (int)$id;
273
	}
274
275
276
	/**
277
	 * Get all shares by the given user in a folder
278
	 *
279
	 * @param string $userId
280
	 * @param Folder $node
281
	 * @param bool $reshares Also get the shares where $user is the owner instead of just the
282
	 *     shares where $user is the initiator
283
	 *
284
	 * @return IShare[]
285
	 */
286
	public function getSharesInFolder($userId, Folder $node, $reshares) {
287
		$this->misc->log("CircleProvider: getSharesInFolder");
288
289
		$qb = $this->getBaseSelectSql();
290
		$this->linkToMember($qb, $userId);
291
		$cursor = $qb->execute();
292
293
		$shares = [];
294
		while ($data = $cursor->fetch()) {
295
			$shares[$data['file_source']][] = $this->createShareObject($data);
296
		}
297
		$cursor->closeCursor();
298
299
		return $shares;
300
	}
301
302
303
	/**
304
	 * Get all shares by the given user
305
	 *
306
	 * @param string $userId
307
	 * @param int $shareType
308
	 * @param Node|null $node
309
	 * @param bool $reShares
310
	 * @param int $limit The maximum number of shares to be returned, -1 for all shares
311
	 * @param int $offset
312
	 *
313
	 * @return IShare[]
314
	 * @internal param bool $reshares Also get the shares where $user is the owner instead of just
315
	 *     the shares where $user is the initiator
316
	 */
317
	public function getSharesBy($userId, $shareType, $node, $reShares, $limit, $offset) {
318
		$qb = $this->getBaseSelectSql();
319
		$this->limitToOwner($qb, $userId, $reShares);
320
321
		if ($node !== null) {
322
			$this->limitToFile($qb, $node->getId());
323
		}
324
325
		$this->limitToPage($qb, $limit, $offset);
326
		$cursor = $qb->execute();
327
328
		$shares = [];
329
		while ($data = $cursor->fetch()) {
330
			$shares[] = $this->createShareObject($this->editShareEntry($data));
331
		}
332
		$cursor->closeCursor();
333
334
		return $shares;
335
	}
336
337
338
	/**
339
	 * returns a better formatted string to display more information about
340
	 * the circle to the Sharing UI
341
	 *
342
	 * @param $data
343
	 *
344
	 * @return mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
345
	 */
346
	private function editShareEntry($data) {
347
		$data['share_with'] =
348
			sprintf('%s (%s)', $data['circle_name'], Circle::TypeLongString($data['circle_type']));
349
350
		return $data;
351
	}
352
353
354
	/**
355
	 * Get share by its id
356
	 *
357
	 * @param int $shareId
358
	 * @param string|null $recipientId
359
	 *
360
	 * @return IShare
0 ignored issues
show
Documentation introduced by
Should the return type not be Share?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
361
	 * @throws ShareNotFound
362
	 */
363
	public function getShareById($shareId, $recipientId = null) {
364
		$qb = $this->getBaseSelectSql();
365
		$this->limitToShare($qb, $shareId);
366
367
		$cursor = $qb->execute();
368
		$data = $cursor->fetch();
369
		$cursor->closeCursor();
370
371
		if ($data === false) {
372
			throw new ShareNotFound();
373
		}
374
375
		return $this->createShareObject($data);
376
	}
377
378
379
	/**
380
	 * Get shares for a given path
381
	 *
382
	 * @param Node $path
383
	 *
384
	 * @return IShare[]
0 ignored issues
show
Documentation introduced by
Should the return type not be IShare[]|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
385
	 */
386
	public function getSharesByPath(Node $path) {
387
		return null;
388
	}
389
390
391
	/**
392
	 * Get shared with the given user
393
	 *
394
	 * @param string $userId get shares where this user is the recipient
395
	 * @param int $shareType
396
	 * @param Node|null $node
397
	 * @param int $limit The max number of entries returned, -1 for all
398
	 * @param int $offset
399
	 *
400
	 * @return IShare[]
401
	 */
402
	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
403
404
		$qb = $this->getCompleteSelectSql();
405
		$this->linkToMember($qb, $userId);
406
		$this->linkToFileCache($qb, $userId);
407
		$this->limitToPage($qb, $limit, $offset);
408
409
		$cursor = $qb->execute();
410
411
		$shares = [];
412
		while ($data = $cursor->fetch()) {
413
			self::editShareFromParentEntry($data);
414
			if (self::isAccessibleResult($data)) {
415
				$shares[] = $this->createShareObject($data);
416
			}
417
		}
418
		$cursor->closeCursor();
419
420
		return $shares;
421
	}
422
423
424
	private static function editShareFromParentEntry(& $data) {
425
		if ($data['parent_id'] > 0) {
426
			if ($data['parent_perms'] === '0') {
427
				return;
428
			}
429
430
			$data['file_target'] = $data['parent_target'];
431
		}
432
	}
433
434
435
	/**
436
	 * Get a share by token
437
	 *
438
	 * @param string $token
439
	 *
440
	 * @return IShare
0 ignored issues
show
Documentation introduced by
Should the return type not be IShare|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
441
	 * @throws ShareNotFound
442
	 */
443
	public function getShareByToken($token) {
444
		return null;
445
	}/** @noinspection PhpUnusedParameterInspection */
446
	/** @noinspection PhpUnusedParameterInspection */
447
448
449
	/**
450
	 * We don't return a thing about children.
451
	 * The call to this function is deprecated and should be removed in next release of NC.
452
	 * Also, we get the children in the delete() method.
453
	 *
454
	 * @param IShare $parent
455
	 *
456
	 * @return array
457
	 */
458
	public function getChildren(IShare $parent) {
459
		return [];
460
	}
461
462
463
	/**
464
	 * A user is deleted from the system
465
	 * So clean up the relevant shares.
466
	 *
467
	 * @param string $uid
468
	 * @param int $shareType
469
	 */
470
	public function userDeleted($uid, $shareType) {
471
		$this->misc->log("CircleProvider: userDeleted");
472
		// TODO: Implement userDeleted() method.
473
	}
474
475
476
	/**
477
	 * A group is deleted from the system.
478
	 * We handle our own groups.
479
	 *
480
	 * @param string $gid
481
	 */
482
	public function groupDeleted($gid) {
483
		return;
484
	}
485
486
487
	/**
488
	 * A user is deleted from a group.
489
	 * We handle our own groups.
490
	 *
491
	 * @param string $uid
492
	 * @param string $gid
493
	 */
494
	public function userDeletedFromGroup($uid, $gid) {
495
		return;
496
	}
497
498
499
	/**
500
	 * Create a share object
501
	 *
502
	 * @param array $data
503
	 *
504
	 * @return IShare
0 ignored issues
show
Documentation introduced by
Should the return type not be Share?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
505
	 */
506
	private function createShareObject($data) {
507
508
		$share = new Share($this->rootFolder, $this->userManager);
509
		$share->setId((int)$data['id'])
510
			  ->setShareType((int)$data['share_type'])
511
			  ->setPermissions((int)$data['permissions'])
512
			  ->setTarget($data['file_target'])
513
			  ->setMailSend((bool)$data['mail_send']);
514
515
		$shareTime = new \DateTime();
516
		$shareTime->setTimestamp((int)$data['stime']);
517
		$share->setShareTime($shareTime);
518
519
		$share->setSharedWith($data['share_with']);
520
		$share->setSharedBy($data['uid_initiator']);
521
		$share->setShareOwner($data['uid_owner']);
522
523
		$share->setNodeId((int)$data['file_source']);
524
		$share->setNodeType($data['item_type']);
525
526
		if (isset($data['f_permissions'])) {
527
			$entryData = $data;
528
			$entryData['permissions'] = $entryData['f_permissions'];
529
			$entryData['parent'] = $entryData['f_parent'];
530
			$share->setNodeCacheEntry(
531
				Cache::cacheEntryFromData(
532
					$entryData,
533
					\OC::$server->getMimeTypeLoader()
534
				)
535
			);
536
		}
537
538
		$share->setProviderId($this->identifier());
539
540
		return $share;
541
	}
542
543
544
	/**
545
	 * Returns whether the given database result can be interpreted as
546
	 * a share with accessible file (not trashed, not deleted)
547
	 *
548
	 * Complete copy/paste from others ShareProvider
549
	 *
550
	 * @param $data
551
	 *
552
	 * @return bool
553
	 */
554
	private static function isAccessibleResult($data) {
555
		if ($data['fileid'] === null) {
556
			return false;
557
		}
558
559
		$pathSections = explode('/', $data['path'], 2);
560
		if ($pathSections[0] !== 'files'
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !($pathSections[0...d'], 2)[0] === 'home');.
Loading history...
561
			&& explode(':', $data['storage_string_id'], 2)[0] === 'home'
562
		) {
563
			return false;
564
		}
565
566
		return true;
567
	}
568
569
570
	/**
571
	 * @param IShare $share
572
	 *
573
	 * @return \Exception
574
	 */
575
	private function errorShareAlreadyExist($share) {
576
		$share_src = $share->getNode()
577
						   ->getName();
578
579
		$message = 'Sharing %s failed, this item is already shared with this circle';
580
		$message_t = $this->l->t($message, array($share_src));
581
		$this->logger->debug(
582
			sprintf($message, $share_src, $share->getSharedWith()), ['app' => 'circles']
583
		);
584
585
		return new \Exception($message_t);
586
	}
587
588
}
589