Passed
Push — master ( 6d2c79...4d8b4e )
by Roeland
23:36 queued 11:49
created
lib/private/L10N/Factory.php 1 patch
Indentation   +629 added lines, -629 removed lines patch added patch discarded remove patch
@@ -48,633 +48,633 @@
 block discarded – undo
48 48
  */
49 49
 class Factory implements IFactory {
50 50
 
51
-	/** @var string */
52
-	protected $requestLanguage = '';
53
-
54
-	/**
55
-	 * cached instances
56
-	 * @var array Structure: Lang => App => \OCP\IL10N
57
-	 */
58
-	protected $instances = [];
59
-
60
-	/**
61
-	 * @var array Structure: App => string[]
62
-	 */
63
-	protected $availableLanguages = [];
64
-
65
-	/**
66
-	 * @var array
67
-	 */
68
-	protected $availableLocales = [];
69
-
70
-	/**
71
-	 * @var array Structure: string => callable
72
-	 */
73
-	protected $pluralFunctions = [];
74
-
75
-	public const COMMON_LANGUAGE_CODES = [
76
-		'en', 'es', 'fr', 'de', 'de_DE', 'ja', 'ar', 'ru', 'nl', 'it',
77
-		'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko'
78
-	];
79
-
80
-	/** @var IConfig */
81
-	protected $config;
82
-
83
-	/** @var IRequest */
84
-	protected $request;
85
-
86
-	/** @var IUserSession */
87
-	protected $userSession;
88
-
89
-	/** @var string */
90
-	protected $serverRoot;
91
-
92
-	/**
93
-	 * @param IConfig $config
94
-	 * @param IRequest $request
95
-	 * @param IUserSession $userSession
96
-	 * @param string $serverRoot
97
-	 */
98
-	public function __construct(IConfig $config,
99
-								IRequest $request,
100
-								IUserSession $userSession,
101
-								$serverRoot) {
102
-		$this->config = $config;
103
-		$this->request = $request;
104
-		$this->userSession = $userSession;
105
-		$this->serverRoot = $serverRoot;
106
-	}
107
-
108
-	/**
109
-	 * Get a language instance
110
-	 *
111
-	 * @param string $app
112
-	 * @param string|null $lang
113
-	 * @param string|null $locale
114
-	 * @return \OCP\IL10N
115
-	 */
116
-	public function get($app, $lang = null, $locale = null) {
117
-		return new LazyL10N(function () use ($app, $lang, $locale) {
118
-			$app = \OC_App::cleanAppId($app);
119
-			if ($lang !== null) {
120
-				$lang = str_replace(['\0', '/', '\\', '..'], '', (string)$lang);
121
-			}
122
-
123
-			$forceLang = $this->config->getSystemValue('force_language', false);
124
-			if (is_string($forceLang)) {
125
-				$lang = $forceLang;
126
-			}
127
-
128
-			$forceLocale = $this->config->getSystemValue('force_locale', false);
129
-			if (is_string($forceLocale)) {
130
-				$locale = $forceLocale;
131
-			}
132
-
133
-			if ($lang === null || !$this->languageExists($app, $lang)) {
134
-				$lang = $this->findLanguage($app);
135
-			}
136
-
137
-			if ($locale === null || !$this->localeExists($locale)) {
138
-				$locale = $this->findLocale($lang);
139
-			}
140
-
141
-			if (!isset($this->instances[$lang][$app])) {
142
-				$this->instances[$lang][$app] = new L10N(
143
-					$this, $app, $lang, $locale,
144
-					$this->getL10nFilesForApp($app, $lang)
145
-				);
146
-			}
147
-
148
-			return $this->instances[$lang][$app];
149
-		});
150
-	}
151
-
152
-	/**
153
-	 * Find the best language
154
-	 *
155
-	 * @param string|null $app App id or null for core
156
-	 * @return string language If nothing works it returns 'en'
157
-	 */
158
-	public function findLanguage($app = null) {
159
-		$forceLang = $this->config->getSystemValue('force_language', false);
160
-		if (is_string($forceLang)) {
161
-			$this->requestLanguage = $forceLang;
162
-		}
163
-
164
-		if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
165
-			return $this->requestLanguage;
166
-		}
167
-
168
-		/**
169
-		 * At this point Nextcloud might not yet be installed and thus the lookup
170
-		 * in the preferences table might fail. For this reason we need to check
171
-		 * whether the instance has already been installed
172
-		 *
173
-		 * @link https://github.com/owncloud/core/issues/21955
174
-		 */
175
-		if ($this->config->getSystemValue('installed', false)) {
176
-			$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
177
-			if (!is_null($userId)) {
178
-				$userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
179
-			} else {
180
-				$userLang = null;
181
-			}
182
-		} else {
183
-			$userId = null;
184
-			$userLang = null;
185
-		}
186
-
187
-		if ($userLang) {
188
-			$this->requestLanguage = $userLang;
189
-			if ($this->languageExists($app, $userLang)) {
190
-				return $userLang;
191
-			}
192
-		}
193
-
194
-		try {
195
-			// Try to get the language from the Request
196
-			$lang = $this->getLanguageFromRequest($app);
197
-			if ($userId !== null && $app === null && !$userLang) {
198
-				$this->config->setUserValue($userId, 'core', 'lang', $lang);
199
-			}
200
-			return $lang;
201
-		} catch (LanguageNotFoundException $e) {
202
-			// Finding language from request failed fall back to default language
203
-			$defaultLanguage = $this->config->getSystemValue('default_language', false);
204
-			if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
205
-				return $defaultLanguage;
206
-			}
207
-		}
208
-
209
-		// We could not find any language so fall back to english
210
-		return 'en';
211
-	}
212
-
213
-	/**
214
-	 * find the best locale
215
-	 *
216
-	 * @param string $lang
217
-	 * @return null|string
218
-	 */
219
-	public function findLocale($lang = null) {
220
-		$forceLocale = $this->config->getSystemValue('force_locale', false);
221
-		if (is_string($forceLocale) && $this->localeExists($forceLocale)) {
222
-			return $forceLocale;
223
-		}
224
-
225
-		if ($this->config->getSystemValue('installed', false)) {
226
-			$userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() :  null;
227
-			$userLocale = null;
228
-			if (null !== $userId) {
229
-				$userLocale = $this->config->getUserValue($userId, 'core', 'locale', null);
230
-			}
231
-		} else {
232
-			$userId = null;
233
-			$userLocale = null;
234
-		}
235
-
236
-		if ($userLocale && $this->localeExists($userLocale)) {
237
-			return $userLocale;
238
-		}
239
-
240
-		// Default : use system default locale
241
-		$defaultLocale = $this->config->getSystemValue('default_locale', false);
242
-		if ($defaultLocale !== false && $this->localeExists($defaultLocale)) {
243
-			return $defaultLocale;
244
-		}
245
-
246
-		// If no user locale set, use lang as locale
247
-		if (null !== $lang && $this->localeExists($lang)) {
248
-			return $lang;
249
-		}
250
-
251
-		// At last, return USA
252
-		return 'en_US';
253
-	}
254
-
255
-	/**
256
-	 * find the matching lang from the locale
257
-	 *
258
-	 * @param string $app
259
-	 * @param string $locale
260
-	 * @return null|string
261
-	 */
262
-	public function findLanguageFromLocale(string $app = 'core', string $locale = null) {
263
-		if ($this->languageExists($app, $locale)) {
264
-			return $locale;
265
-		}
266
-
267
-		// Try to split e.g: fr_FR => fr
268
-		$locale = explode('_', $locale)[0];
269
-		if ($this->languageExists($app, $locale)) {
270
-			return $locale;
271
-		}
272
-	}
273
-
274
-	/**
275
-	 * Find all available languages for an app
276
-	 *
277
-	 * @param string|null $app App id or null for core
278
-	 * @return array an array of available languages
279
-	 */
280
-	public function findAvailableLanguages($app = null) {
281
-		$key = $app;
282
-		if ($key === null) {
283
-			$key = 'null';
284
-		}
285
-
286
-		// also works with null as key
287
-		if (!empty($this->availableLanguages[$key])) {
288
-			return $this->availableLanguages[$key];
289
-		}
290
-
291
-		$available = ['en']; //english is always available
292
-		$dir = $this->findL10nDir($app);
293
-		if (is_dir($dir)) {
294
-			$files = scandir($dir);
295
-			if ($files !== false) {
296
-				foreach ($files as $file) {
297
-					if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
298
-						$available[] = substr($file, 0, -5);
299
-					}
300
-				}
301
-			}
302
-		}
303
-
304
-		// merge with translations from theme
305
-		$theme = $this->config->getSystemValue('theme');
306
-		if (!empty($theme)) {
307
-			$themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
308
-
309
-			if (is_dir($themeDir)) {
310
-				$files = scandir($themeDir);
311
-				if ($files !== false) {
312
-					foreach ($files as $file) {
313
-						if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
314
-							$available[] = substr($file, 0, -5);
315
-						}
316
-					}
317
-				}
318
-			}
319
-		}
320
-
321
-		$this->availableLanguages[$key] = $available;
322
-		return $available;
323
-	}
324
-
325
-	/**
326
-	 * @return array|mixed
327
-	 */
328
-	public function findAvailableLocales() {
329
-		if (!empty($this->availableLocales)) {
330
-			return $this->availableLocales;
331
-		}
332
-
333
-		$localeData = file_get_contents(\OC::$SERVERROOT . '/resources/locales.json');
334
-		$this->availableLocales = \json_decode($localeData, true);
335
-
336
-		return $this->availableLocales;
337
-	}
338
-
339
-	/**
340
-	 * @param string|null $app App id or null for core
341
-	 * @param string $lang
342
-	 * @return bool
343
-	 */
344
-	public function languageExists($app, $lang) {
345
-		if ($lang === 'en') {//english is always available
346
-			return true;
347
-		}
348
-
349
-		$languages = $this->findAvailableLanguages($app);
350
-		return array_search($lang, $languages) !== false;
351
-	}
352
-
353
-	public function getLanguageIterator(IUser $user = null): ILanguageIterator {
354
-		$user = $user ?? $this->userSession->getUser();
355
-		if ($user === null) {
356
-			throw new \RuntimeException('Failed to get an IUser instance');
357
-		}
358
-		return new LanguageIterator($user, $this->config);
359
-	}
360
-
361
-	/**
362
-	 * Return the language to use when sending something to a user
363
-	 *
364
-	 * @param IUser|null $user
365
-	 * @return string
366
-	 * @since 20.0.0
367
-	 */
368
-	public function getUserLanguage(IUser $user = null): string {
369
-		$language = $this->config->getSystemValue('force_language', false);
370
-		if ($language !== false) {
371
-			return $language;
372
-		}
373
-
374
-		if ($user instanceof IUser) {
375
-			$language = $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
376
-			if ($language !== null) {
377
-				return $language;
378
-			}
379
-		}
380
-
381
-		return $this->config->getSystemValue('default_language', 'en');
382
-	}
383
-
384
-	/**
385
-	 * @param string $locale
386
-	 * @return bool
387
-	 */
388
-	public function localeExists($locale) {
389
-		if ($locale === 'en') { //english is always available
390
-			return true;
391
-		}
392
-
393
-		$locales = $this->findAvailableLocales();
394
-		$userLocale = array_filter($locales, function ($value) use ($locale) {
395
-			return $locale === $value['code'];
396
-		});
397
-
398
-		return !empty($userLocale);
399
-	}
400
-
401
-	/**
402
-	 * @param string|null $app
403
-	 * @return string
404
-	 * @throws LanguageNotFoundException
405
-	 */
406
-	private function getLanguageFromRequest($app) {
407
-		$header = $this->request->getHeader('ACCEPT_LANGUAGE');
408
-		if ($header !== '') {
409
-			$available = $this->findAvailableLanguages($app);
410
-
411
-			// E.g. make sure that 'de' is before 'de_DE'.
412
-			sort($available);
413
-
414
-			$preferences = preg_split('/,\s*/', strtolower($header));
415
-			foreach ($preferences as $preference) {
416
-				list($preferred_language) = explode(';', $preference);
417
-				$preferred_language = str_replace('-', '_', $preferred_language);
418
-
419
-				foreach ($available as $available_language) {
420
-					if ($preferred_language === strtolower($available_language)) {
421
-						return $this->respectDefaultLanguage($app, $available_language);
422
-					}
423
-				}
424
-
425
-				// Fallback from de_De to de
426
-				foreach ($available as $available_language) {
427
-					if (substr($preferred_language, 0, 2) === $available_language) {
428
-						return $available_language;
429
-					}
430
-				}
431
-			}
432
-		}
433
-
434
-		throw new LanguageNotFoundException();
435
-	}
436
-
437
-	/**
438
-	 * if default language is set to de_DE (formal German) this should be
439
-	 * preferred to 'de' (non-formal German) if possible
440
-	 *
441
-	 * @param string|null $app
442
-	 * @param string $lang
443
-	 * @return string
444
-	 */
445
-	protected function respectDefaultLanguage($app, $lang) {
446
-		$result = $lang;
447
-		$defaultLanguage = $this->config->getSystemValue('default_language', false);
448
-
449
-		// use formal version of german ("Sie" instead of "Du") if the default
450
-		// language is set to 'de_DE' if possible
451
-		if (is_string($defaultLanguage) &&
452
-			strtolower($lang) === 'de' &&
453
-			strtolower($defaultLanguage) === 'de_de' &&
454
-			$this->languageExists($app, 'de_DE')
455
-		) {
456
-			$result = 'de_DE';
457
-		}
458
-
459
-		return $result;
460
-	}
461
-
462
-	/**
463
-	 * Checks if $sub is a subdirectory of $parent
464
-	 *
465
-	 * @param string $sub
466
-	 * @param string $parent
467
-	 * @return bool
468
-	 */
469
-	private function isSubDirectory($sub, $parent) {
470
-		// Check whether $sub contains no ".."
471
-		if (strpos($sub, '..') !== false) {
472
-			return false;
473
-		}
474
-
475
-		// Check whether $sub is a subdirectory of $parent
476
-		if (strpos($sub, $parent) === 0) {
477
-			return true;
478
-		}
479
-
480
-		return false;
481
-	}
482
-
483
-	/**
484
-	 * Get a list of language files that should be loaded
485
-	 *
486
-	 * @param string $app
487
-	 * @param string $lang
488
-	 * @return string[]
489
-	 */
490
-	// FIXME This method is only public, until OC_L10N does not need it anymore,
491
-	// FIXME This is also the reason, why it is not in the public interface
492
-	public function getL10nFilesForApp($app, $lang) {
493
-		$languageFiles = [];
494
-
495
-		$i18nDir = $this->findL10nDir($app);
496
-		$transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
497
-
498
-		if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
499
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
500
-				|| $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
501
-			)
502
-			&& file_exists($transFile)) {
503
-			// load the translations file
504
-			$languageFiles[] = $transFile;
505
-		}
506
-
507
-		// merge with translations from theme
508
-		$theme = $this->config->getSystemValue('theme');
509
-		if (!empty($theme)) {
510
-			$transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
511
-			if (file_exists($transFile)) {
512
-				$languageFiles[] = $transFile;
513
-			}
514
-		}
515
-
516
-		return $languageFiles;
517
-	}
518
-
519
-	/**
520
-	 * find the l10n directory
521
-	 *
522
-	 * @param string $app App id or empty string for core
523
-	 * @return string directory
524
-	 */
525
-	protected function findL10nDir($app = null) {
526
-		if (in_array($app, ['core', 'lib'])) {
527
-			if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
528
-				return $this->serverRoot . '/' . $app . '/l10n/';
529
-			}
530
-		} elseif ($app && \OC_App::getAppPath($app) !== false) {
531
-			// Check if the app is in the app folder
532
-			return \OC_App::getAppPath($app) . '/l10n/';
533
-		}
534
-		return $this->serverRoot . '/core/l10n/';
535
-	}
536
-
537
-
538
-	/**
539
-	 * Creates a function from the plural string
540
-	 *
541
-	 * Parts of the code is copied from Habari:
542
-	 * https://github.com/habari/system/blob/master/classes/locale.php
543
-	 * @param string $string
544
-	 * @return string
545
-	 */
546
-	public function createPluralFunction($string) {
547
-		if (isset($this->pluralFunctions[$string])) {
548
-			return $this->pluralFunctions[$string];
549
-		}
550
-
551
-		if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
552
-			// sanitize
553
-			$nplurals = preg_replace('/[^0-9]/', '', $matches[1]);
554
-			$plural = preg_replace('#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2]);
555
-
556
-			$body = str_replace(
557
-				[ 'plural', 'n', '$n$plurals', ],
558
-				[ '$plural', '$n', '$nplurals', ],
559
-				'nplurals='. $nplurals . '; plural=' . $plural
560
-			);
561
-
562
-			// add parents
563
-			// important since PHP's ternary evaluates from left to right
564
-			$body .= ';';
565
-			$res = '';
566
-			$p = 0;
567
-			$length = strlen($body);
568
-			for ($i = 0; $i < $length; $i++) {
569
-				$ch = $body[$i];
570
-				switch ($ch) {
571
-					case '?':
572
-						$res .= ' ? (';
573
-						$p++;
574
-						break;
575
-					case ':':
576
-						$res .= ') : (';
577
-						break;
578
-					case ';':
579
-						$res .= str_repeat(')', $p) . ';';
580
-						$p = 0;
581
-						break;
582
-					default:
583
-						$res .= $ch;
584
-				}
585
-			}
586
-
587
-			$body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
588
-			$function = create_function('$n', $body);
589
-			$this->pluralFunctions[$string] = $function;
590
-			return $function;
591
-		} else {
592
-			// default: one plural form for all cases but n==1 (english)
593
-			$function = create_function(
594
-				'$n',
595
-				'$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
596
-			);
597
-			$this->pluralFunctions[$string] = $function;
598
-			return $function;
599
-		}
600
-	}
601
-
602
-	/**
603
-	 * returns the common language and other languages in an
604
-	 * associative array
605
-	 *
606
-	 * @return array
607
-	 */
608
-	public function getLanguages() {
609
-		$forceLanguage = $this->config->getSystemValue('force_language', false);
610
-		if ($forceLanguage !== false) {
611
-			$l = $this->get('lib', $forceLanguage);
612
-			$potentialName = (string) $l->t('__language_name__');
613
-
614
-			return [
615
-				'commonlanguages' => [[
616
-					'code' => $forceLanguage,
617
-					'name' => $potentialName,
618
-				]],
619
-				'languages' => [],
620
-			];
621
-		}
622
-
623
-		$languageCodes = $this->findAvailableLanguages();
624
-
625
-		$commonLanguages = [];
626
-		$languages = [];
627
-
628
-		foreach ($languageCodes as $lang) {
629
-			$l = $this->get('lib', $lang);
630
-			// TRANSLATORS this is the language name for the language switcher in the personal settings and should be the localized version
631
-			$potentialName = (string) $l->t('__language_name__');
632
-			if ($l->getLanguageCode() === $lang && $potentialName[0] !== '_') {//first check if the language name is in the translation file
633
-				$ln = [
634
-					'code' => $lang,
635
-					'name' => $potentialName
636
-				];
637
-			} elseif ($lang === 'en') {
638
-				$ln = [
639
-					'code' => $lang,
640
-					'name' => 'English (US)'
641
-				];
642
-			} else {//fallback to language code
643
-				$ln = [
644
-					'code' => $lang,
645
-					'name' => $lang
646
-				];
647
-			}
648
-
649
-			// put appropriate languages into appropriate arrays, to print them sorted
650
-			// common languages -> divider -> other languages
651
-			if (in_array($lang, self::COMMON_LANGUAGE_CODES)) {
652
-				$commonLanguages[array_search($lang, self::COMMON_LANGUAGE_CODES)] = $ln;
653
-			} else {
654
-				$languages[] = $ln;
655
-			}
656
-		}
657
-
658
-		ksort($commonLanguages);
659
-
660
-		// sort now by displayed language not the iso-code
661
-		usort($languages, function ($a, $b) {
662
-			if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) {
663
-				// If a doesn't have a name, but b does, list b before a
664
-				return 1;
665
-			}
666
-			if ($a['code'] !== $a['name'] && $b['code'] === $b['name']) {
667
-				// If a does have a name, but b doesn't, list a before b
668
-				return -1;
669
-			}
670
-			// Otherwise compare the names
671
-			return strcmp($a['name'], $b['name']);
672
-		});
673
-
674
-		return [
675
-			// reset indexes
676
-			'commonlanguages' => array_values($commonLanguages),
677
-			'languages' => $languages
678
-		];
679
-	}
51
+    /** @var string */
52
+    protected $requestLanguage = '';
53
+
54
+    /**
55
+     * cached instances
56
+     * @var array Structure: Lang => App => \OCP\IL10N
57
+     */
58
+    protected $instances = [];
59
+
60
+    /**
61
+     * @var array Structure: App => string[]
62
+     */
63
+    protected $availableLanguages = [];
64
+
65
+    /**
66
+     * @var array
67
+     */
68
+    protected $availableLocales = [];
69
+
70
+    /**
71
+     * @var array Structure: string => callable
72
+     */
73
+    protected $pluralFunctions = [];
74
+
75
+    public const COMMON_LANGUAGE_CODES = [
76
+        'en', 'es', 'fr', 'de', 'de_DE', 'ja', 'ar', 'ru', 'nl', 'it',
77
+        'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko'
78
+    ];
79
+
80
+    /** @var IConfig */
81
+    protected $config;
82
+
83
+    /** @var IRequest */
84
+    protected $request;
85
+
86
+    /** @var IUserSession */
87
+    protected $userSession;
88
+
89
+    /** @var string */
90
+    protected $serverRoot;
91
+
92
+    /**
93
+     * @param IConfig $config
94
+     * @param IRequest $request
95
+     * @param IUserSession $userSession
96
+     * @param string $serverRoot
97
+     */
98
+    public function __construct(IConfig $config,
99
+                                IRequest $request,
100
+                                IUserSession $userSession,
101
+                                $serverRoot) {
102
+        $this->config = $config;
103
+        $this->request = $request;
104
+        $this->userSession = $userSession;
105
+        $this->serverRoot = $serverRoot;
106
+    }
107
+
108
+    /**
109
+     * Get a language instance
110
+     *
111
+     * @param string $app
112
+     * @param string|null $lang
113
+     * @param string|null $locale
114
+     * @return \OCP\IL10N
115
+     */
116
+    public function get($app, $lang = null, $locale = null) {
117
+        return new LazyL10N(function () use ($app, $lang, $locale) {
118
+            $app = \OC_App::cleanAppId($app);
119
+            if ($lang !== null) {
120
+                $lang = str_replace(['\0', '/', '\\', '..'], '', (string)$lang);
121
+            }
122
+
123
+            $forceLang = $this->config->getSystemValue('force_language', false);
124
+            if (is_string($forceLang)) {
125
+                $lang = $forceLang;
126
+            }
127
+
128
+            $forceLocale = $this->config->getSystemValue('force_locale', false);
129
+            if (is_string($forceLocale)) {
130
+                $locale = $forceLocale;
131
+            }
132
+
133
+            if ($lang === null || !$this->languageExists($app, $lang)) {
134
+                $lang = $this->findLanguage($app);
135
+            }
136
+
137
+            if ($locale === null || !$this->localeExists($locale)) {
138
+                $locale = $this->findLocale($lang);
139
+            }
140
+
141
+            if (!isset($this->instances[$lang][$app])) {
142
+                $this->instances[$lang][$app] = new L10N(
143
+                    $this, $app, $lang, $locale,
144
+                    $this->getL10nFilesForApp($app, $lang)
145
+                );
146
+            }
147
+
148
+            return $this->instances[$lang][$app];
149
+        });
150
+    }
151
+
152
+    /**
153
+     * Find the best language
154
+     *
155
+     * @param string|null $app App id or null for core
156
+     * @return string language If nothing works it returns 'en'
157
+     */
158
+    public function findLanguage($app = null) {
159
+        $forceLang = $this->config->getSystemValue('force_language', false);
160
+        if (is_string($forceLang)) {
161
+            $this->requestLanguage = $forceLang;
162
+        }
163
+
164
+        if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
165
+            return $this->requestLanguage;
166
+        }
167
+
168
+        /**
169
+         * At this point Nextcloud might not yet be installed and thus the lookup
170
+         * in the preferences table might fail. For this reason we need to check
171
+         * whether the instance has already been installed
172
+         *
173
+         * @link https://github.com/owncloud/core/issues/21955
174
+         */
175
+        if ($this->config->getSystemValue('installed', false)) {
176
+            $userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
177
+            if (!is_null($userId)) {
178
+                $userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
179
+            } else {
180
+                $userLang = null;
181
+            }
182
+        } else {
183
+            $userId = null;
184
+            $userLang = null;
185
+        }
186
+
187
+        if ($userLang) {
188
+            $this->requestLanguage = $userLang;
189
+            if ($this->languageExists($app, $userLang)) {
190
+                return $userLang;
191
+            }
192
+        }
193
+
194
+        try {
195
+            // Try to get the language from the Request
196
+            $lang = $this->getLanguageFromRequest($app);
197
+            if ($userId !== null && $app === null && !$userLang) {
198
+                $this->config->setUserValue($userId, 'core', 'lang', $lang);
199
+            }
200
+            return $lang;
201
+        } catch (LanguageNotFoundException $e) {
202
+            // Finding language from request failed fall back to default language
203
+            $defaultLanguage = $this->config->getSystemValue('default_language', false);
204
+            if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
205
+                return $defaultLanguage;
206
+            }
207
+        }
208
+
209
+        // We could not find any language so fall back to english
210
+        return 'en';
211
+    }
212
+
213
+    /**
214
+     * find the best locale
215
+     *
216
+     * @param string $lang
217
+     * @return null|string
218
+     */
219
+    public function findLocale($lang = null) {
220
+        $forceLocale = $this->config->getSystemValue('force_locale', false);
221
+        if (is_string($forceLocale) && $this->localeExists($forceLocale)) {
222
+            return $forceLocale;
223
+        }
224
+
225
+        if ($this->config->getSystemValue('installed', false)) {
226
+            $userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() :  null;
227
+            $userLocale = null;
228
+            if (null !== $userId) {
229
+                $userLocale = $this->config->getUserValue($userId, 'core', 'locale', null);
230
+            }
231
+        } else {
232
+            $userId = null;
233
+            $userLocale = null;
234
+        }
235
+
236
+        if ($userLocale && $this->localeExists($userLocale)) {
237
+            return $userLocale;
238
+        }
239
+
240
+        // Default : use system default locale
241
+        $defaultLocale = $this->config->getSystemValue('default_locale', false);
242
+        if ($defaultLocale !== false && $this->localeExists($defaultLocale)) {
243
+            return $defaultLocale;
244
+        }
245
+
246
+        // If no user locale set, use lang as locale
247
+        if (null !== $lang && $this->localeExists($lang)) {
248
+            return $lang;
249
+        }
250
+
251
+        // At last, return USA
252
+        return 'en_US';
253
+    }
254
+
255
+    /**
256
+     * find the matching lang from the locale
257
+     *
258
+     * @param string $app
259
+     * @param string $locale
260
+     * @return null|string
261
+     */
262
+    public function findLanguageFromLocale(string $app = 'core', string $locale = null) {
263
+        if ($this->languageExists($app, $locale)) {
264
+            return $locale;
265
+        }
266
+
267
+        // Try to split e.g: fr_FR => fr
268
+        $locale = explode('_', $locale)[0];
269
+        if ($this->languageExists($app, $locale)) {
270
+            return $locale;
271
+        }
272
+    }
273
+
274
+    /**
275
+     * Find all available languages for an app
276
+     *
277
+     * @param string|null $app App id or null for core
278
+     * @return array an array of available languages
279
+     */
280
+    public function findAvailableLanguages($app = null) {
281
+        $key = $app;
282
+        if ($key === null) {
283
+            $key = 'null';
284
+        }
285
+
286
+        // also works with null as key
287
+        if (!empty($this->availableLanguages[$key])) {
288
+            return $this->availableLanguages[$key];
289
+        }
290
+
291
+        $available = ['en']; //english is always available
292
+        $dir = $this->findL10nDir($app);
293
+        if (is_dir($dir)) {
294
+            $files = scandir($dir);
295
+            if ($files !== false) {
296
+                foreach ($files as $file) {
297
+                    if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
298
+                        $available[] = substr($file, 0, -5);
299
+                    }
300
+                }
301
+            }
302
+        }
303
+
304
+        // merge with translations from theme
305
+        $theme = $this->config->getSystemValue('theme');
306
+        if (!empty($theme)) {
307
+            $themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
308
+
309
+            if (is_dir($themeDir)) {
310
+                $files = scandir($themeDir);
311
+                if ($files !== false) {
312
+                    foreach ($files as $file) {
313
+                        if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
314
+                            $available[] = substr($file, 0, -5);
315
+                        }
316
+                    }
317
+                }
318
+            }
319
+        }
320
+
321
+        $this->availableLanguages[$key] = $available;
322
+        return $available;
323
+    }
324
+
325
+    /**
326
+     * @return array|mixed
327
+     */
328
+    public function findAvailableLocales() {
329
+        if (!empty($this->availableLocales)) {
330
+            return $this->availableLocales;
331
+        }
332
+
333
+        $localeData = file_get_contents(\OC::$SERVERROOT . '/resources/locales.json');
334
+        $this->availableLocales = \json_decode($localeData, true);
335
+
336
+        return $this->availableLocales;
337
+    }
338
+
339
+    /**
340
+     * @param string|null $app App id or null for core
341
+     * @param string $lang
342
+     * @return bool
343
+     */
344
+    public function languageExists($app, $lang) {
345
+        if ($lang === 'en') {//english is always available
346
+            return true;
347
+        }
348
+
349
+        $languages = $this->findAvailableLanguages($app);
350
+        return array_search($lang, $languages) !== false;
351
+    }
352
+
353
+    public function getLanguageIterator(IUser $user = null): ILanguageIterator {
354
+        $user = $user ?? $this->userSession->getUser();
355
+        if ($user === null) {
356
+            throw new \RuntimeException('Failed to get an IUser instance');
357
+        }
358
+        return new LanguageIterator($user, $this->config);
359
+    }
360
+
361
+    /**
362
+     * Return the language to use when sending something to a user
363
+     *
364
+     * @param IUser|null $user
365
+     * @return string
366
+     * @since 20.0.0
367
+     */
368
+    public function getUserLanguage(IUser $user = null): string {
369
+        $language = $this->config->getSystemValue('force_language', false);
370
+        if ($language !== false) {
371
+            return $language;
372
+        }
373
+
374
+        if ($user instanceof IUser) {
375
+            $language = $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
376
+            if ($language !== null) {
377
+                return $language;
378
+            }
379
+        }
380
+
381
+        return $this->config->getSystemValue('default_language', 'en');
382
+    }
383
+
384
+    /**
385
+     * @param string $locale
386
+     * @return bool
387
+     */
388
+    public function localeExists($locale) {
389
+        if ($locale === 'en') { //english is always available
390
+            return true;
391
+        }
392
+
393
+        $locales = $this->findAvailableLocales();
394
+        $userLocale = array_filter($locales, function ($value) use ($locale) {
395
+            return $locale === $value['code'];
396
+        });
397
+
398
+        return !empty($userLocale);
399
+    }
400
+
401
+    /**
402
+     * @param string|null $app
403
+     * @return string
404
+     * @throws LanguageNotFoundException
405
+     */
406
+    private function getLanguageFromRequest($app) {
407
+        $header = $this->request->getHeader('ACCEPT_LANGUAGE');
408
+        if ($header !== '') {
409
+            $available = $this->findAvailableLanguages($app);
410
+
411
+            // E.g. make sure that 'de' is before 'de_DE'.
412
+            sort($available);
413
+
414
+            $preferences = preg_split('/,\s*/', strtolower($header));
415
+            foreach ($preferences as $preference) {
416
+                list($preferred_language) = explode(';', $preference);
417
+                $preferred_language = str_replace('-', '_', $preferred_language);
418
+
419
+                foreach ($available as $available_language) {
420
+                    if ($preferred_language === strtolower($available_language)) {
421
+                        return $this->respectDefaultLanguage($app, $available_language);
422
+                    }
423
+                }
424
+
425
+                // Fallback from de_De to de
426
+                foreach ($available as $available_language) {
427
+                    if (substr($preferred_language, 0, 2) === $available_language) {
428
+                        return $available_language;
429
+                    }
430
+                }
431
+            }
432
+        }
433
+
434
+        throw new LanguageNotFoundException();
435
+    }
436
+
437
+    /**
438
+     * if default language is set to de_DE (formal German) this should be
439
+     * preferred to 'de' (non-formal German) if possible
440
+     *
441
+     * @param string|null $app
442
+     * @param string $lang
443
+     * @return string
444
+     */
445
+    protected function respectDefaultLanguage($app, $lang) {
446
+        $result = $lang;
447
+        $defaultLanguage = $this->config->getSystemValue('default_language', false);
448
+
449
+        // use formal version of german ("Sie" instead of "Du") if the default
450
+        // language is set to 'de_DE' if possible
451
+        if (is_string($defaultLanguage) &&
452
+            strtolower($lang) === 'de' &&
453
+            strtolower($defaultLanguage) === 'de_de' &&
454
+            $this->languageExists($app, 'de_DE')
455
+        ) {
456
+            $result = 'de_DE';
457
+        }
458
+
459
+        return $result;
460
+    }
461
+
462
+    /**
463
+     * Checks if $sub is a subdirectory of $parent
464
+     *
465
+     * @param string $sub
466
+     * @param string $parent
467
+     * @return bool
468
+     */
469
+    private function isSubDirectory($sub, $parent) {
470
+        // Check whether $sub contains no ".."
471
+        if (strpos($sub, '..') !== false) {
472
+            return false;
473
+        }
474
+
475
+        // Check whether $sub is a subdirectory of $parent
476
+        if (strpos($sub, $parent) === 0) {
477
+            return true;
478
+        }
479
+
480
+        return false;
481
+    }
482
+
483
+    /**
484
+     * Get a list of language files that should be loaded
485
+     *
486
+     * @param string $app
487
+     * @param string $lang
488
+     * @return string[]
489
+     */
490
+    // FIXME This method is only public, until OC_L10N does not need it anymore,
491
+    // FIXME This is also the reason, why it is not in the public interface
492
+    public function getL10nFilesForApp($app, $lang) {
493
+        $languageFiles = [];
494
+
495
+        $i18nDir = $this->findL10nDir($app);
496
+        $transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
497
+
498
+        if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
499
+                || $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
500
+                || $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
501
+            )
502
+            && file_exists($transFile)) {
503
+            // load the translations file
504
+            $languageFiles[] = $transFile;
505
+        }
506
+
507
+        // merge with translations from theme
508
+        $theme = $this->config->getSystemValue('theme');
509
+        if (!empty($theme)) {
510
+            $transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
511
+            if (file_exists($transFile)) {
512
+                $languageFiles[] = $transFile;
513
+            }
514
+        }
515
+
516
+        return $languageFiles;
517
+    }
518
+
519
+    /**
520
+     * find the l10n directory
521
+     *
522
+     * @param string $app App id or empty string for core
523
+     * @return string directory
524
+     */
525
+    protected function findL10nDir($app = null) {
526
+        if (in_array($app, ['core', 'lib'])) {
527
+            if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
528
+                return $this->serverRoot . '/' . $app . '/l10n/';
529
+            }
530
+        } elseif ($app && \OC_App::getAppPath($app) !== false) {
531
+            // Check if the app is in the app folder
532
+            return \OC_App::getAppPath($app) . '/l10n/';
533
+        }
534
+        return $this->serverRoot . '/core/l10n/';
535
+    }
536
+
537
+
538
+    /**
539
+     * Creates a function from the plural string
540
+     *
541
+     * Parts of the code is copied from Habari:
542
+     * https://github.com/habari/system/blob/master/classes/locale.php
543
+     * @param string $string
544
+     * @return string
545
+     */
546
+    public function createPluralFunction($string) {
547
+        if (isset($this->pluralFunctions[$string])) {
548
+            return $this->pluralFunctions[$string];
549
+        }
550
+
551
+        if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
552
+            // sanitize
553
+            $nplurals = preg_replace('/[^0-9]/', '', $matches[1]);
554
+            $plural = preg_replace('#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2]);
555
+
556
+            $body = str_replace(
557
+                [ 'plural', 'n', '$n$plurals', ],
558
+                [ '$plural', '$n', '$nplurals', ],
559
+                'nplurals='. $nplurals . '; plural=' . $plural
560
+            );
561
+
562
+            // add parents
563
+            // important since PHP's ternary evaluates from left to right
564
+            $body .= ';';
565
+            $res = '';
566
+            $p = 0;
567
+            $length = strlen($body);
568
+            for ($i = 0; $i < $length; $i++) {
569
+                $ch = $body[$i];
570
+                switch ($ch) {
571
+                    case '?':
572
+                        $res .= ' ? (';
573
+                        $p++;
574
+                        break;
575
+                    case ':':
576
+                        $res .= ') : (';
577
+                        break;
578
+                    case ';':
579
+                        $res .= str_repeat(')', $p) . ';';
580
+                        $p = 0;
581
+                        break;
582
+                    default:
583
+                        $res .= $ch;
584
+                }
585
+            }
586
+
587
+            $body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
588
+            $function = create_function('$n', $body);
589
+            $this->pluralFunctions[$string] = $function;
590
+            return $function;
591
+        } else {
592
+            // default: one plural form for all cases but n==1 (english)
593
+            $function = create_function(
594
+                '$n',
595
+                '$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
596
+            );
597
+            $this->pluralFunctions[$string] = $function;
598
+            return $function;
599
+        }
600
+    }
601
+
602
+    /**
603
+     * returns the common language and other languages in an
604
+     * associative array
605
+     *
606
+     * @return array
607
+     */
608
+    public function getLanguages() {
609
+        $forceLanguage = $this->config->getSystemValue('force_language', false);
610
+        if ($forceLanguage !== false) {
611
+            $l = $this->get('lib', $forceLanguage);
612
+            $potentialName = (string) $l->t('__language_name__');
613
+
614
+            return [
615
+                'commonlanguages' => [[
616
+                    'code' => $forceLanguage,
617
+                    'name' => $potentialName,
618
+                ]],
619
+                'languages' => [],
620
+            ];
621
+        }
622
+
623
+        $languageCodes = $this->findAvailableLanguages();
624
+
625
+        $commonLanguages = [];
626
+        $languages = [];
627
+
628
+        foreach ($languageCodes as $lang) {
629
+            $l = $this->get('lib', $lang);
630
+            // TRANSLATORS this is the language name for the language switcher in the personal settings and should be the localized version
631
+            $potentialName = (string) $l->t('__language_name__');
632
+            if ($l->getLanguageCode() === $lang && $potentialName[0] !== '_') {//first check if the language name is in the translation file
633
+                $ln = [
634
+                    'code' => $lang,
635
+                    'name' => $potentialName
636
+                ];
637
+            } elseif ($lang === 'en') {
638
+                $ln = [
639
+                    'code' => $lang,
640
+                    'name' => 'English (US)'
641
+                ];
642
+            } else {//fallback to language code
643
+                $ln = [
644
+                    'code' => $lang,
645
+                    'name' => $lang
646
+                ];
647
+            }
648
+
649
+            // put appropriate languages into appropriate arrays, to print them sorted
650
+            // common languages -> divider -> other languages
651
+            if (in_array($lang, self::COMMON_LANGUAGE_CODES)) {
652
+                $commonLanguages[array_search($lang, self::COMMON_LANGUAGE_CODES)] = $ln;
653
+            } else {
654
+                $languages[] = $ln;
655
+            }
656
+        }
657
+
658
+        ksort($commonLanguages);
659
+
660
+        // sort now by displayed language not the iso-code
661
+        usort($languages, function ($a, $b) {
662
+            if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) {
663
+                // If a doesn't have a name, but b does, list b before a
664
+                return 1;
665
+            }
666
+            if ($a['code'] !== $a['name'] && $b['code'] === $b['name']) {
667
+                // If a does have a name, but b doesn't, list a before b
668
+                return -1;
669
+            }
670
+            // Otherwise compare the names
671
+            return strcmp($a['name'], $b['name']);
672
+        });
673
+
674
+        return [
675
+            // reset indexes
676
+            'commonlanguages' => array_values($commonLanguages),
677
+            'languages' => $languages
678
+        ];
679
+    }
680 680
 }
Please login to merge, or discard this patch.
lib/private/Share20/DefaultShareProvider.php 1 patch
Indentation   +1396 added lines, -1396 removed lines patch added patch discarded remove patch
@@ -62,1436 +62,1436 @@
 block discarded – undo
62 62
  */
63 63
 class DefaultShareProvider implements IShareProvider {
64 64
 
65
-	// Special share type for user modified group shares
66
-	public const SHARE_TYPE_USERGROUP = 2;
67
-
68
-	/** @var IDBConnection */
69
-	private $dbConn;
70
-
71
-	/** @var IUserManager */
72
-	private $userManager;
73
-
74
-	/** @var IGroupManager */
75
-	private $groupManager;
76
-
77
-	/** @var IRootFolder */
78
-	private $rootFolder;
79
-
80
-	/** @var IMailer */
81
-	private $mailer;
82
-
83
-	/** @var Defaults */
84
-	private $defaults;
85
-
86
-	/** @var IFactory */
87
-	private $l10nFactory;
88
-
89
-	/** @var IURLGenerator */
90
-	private $urlGenerator;
91
-
92
-	/** @var IConfig */
93
-	private $config;
94
-
95
-	public function __construct(
96
-			IDBConnection $connection,
97
-			IUserManager $userManager,
98
-			IGroupManager $groupManager,
99
-			IRootFolder $rootFolder,
100
-			IMailer $mailer,
101
-			Defaults $defaults,
102
-			IFactory $l10nFactory,
103
-			IURLGenerator $urlGenerator,
104
-			IConfig $config) {
105
-		$this->dbConn = $connection;
106
-		$this->userManager = $userManager;
107
-		$this->groupManager = $groupManager;
108
-		$this->rootFolder = $rootFolder;
109
-		$this->mailer = $mailer;
110
-		$this->defaults = $defaults;
111
-		$this->l10nFactory = $l10nFactory;
112
-		$this->urlGenerator = $urlGenerator;
113
-		$this->config = $config;
114
-	}
115
-
116
-	/**
117
-	 * Return the identifier of this provider.
118
-	 *
119
-	 * @return string Containing only [a-zA-Z0-9]
120
-	 */
121
-	public function identifier() {
122
-		return 'ocinternal';
123
-	}
124
-
125
-	/**
126
-	 * Share a path
127
-	 *
128
-	 * @param \OCP\Share\IShare $share
129
-	 * @return \OCP\Share\IShare The share object
130
-	 * @throws ShareNotFound
131
-	 * @throws \Exception
132
-	 */
133
-	public function create(\OCP\Share\IShare $share) {
134
-		$qb = $this->dbConn->getQueryBuilder();
135
-
136
-		$qb->insert('share');
137
-		$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
138
-
139
-		if ($share->getShareType() === IShare::TYPE_USER) {
140
-			//Set the UID of the user we share with
141
-			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
142
-			$qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
143
-
144
-			//If an expiration date is set store it
145
-			if ($share->getExpirationDate() !== null) {
146
-				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
147
-			}
148
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
149
-			//Set the GID of the group we share with
150
-			$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
151
-
152
-			//If an expiration date is set store it
153
-			if ($share->getExpirationDate() !== null) {
154
-				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
155
-			}
156
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
157
-			//set label for public link
158
-			$qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
159
-			//Set the token of the share
160
-			$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
161
-
162
-			//If a password is set store it
163
-			if ($share->getPassword() !== null) {
164
-				$qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
165
-			}
166
-
167
-			$qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
168
-
169
-			//If an expiration date is set store it
170
-			if ($share->getExpirationDate() !== null) {
171
-				$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
172
-			}
173
-
174
-			if (method_exists($share, 'getParent')) {
175
-				$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
176
-			}
177
-		} else {
178
-			throw new \Exception('invalid share type!');
179
-		}
180
-
181
-		// Set what is shares
182
-		$qb->setValue('item_type', $qb->createParameter('itemType'));
183
-		if ($share->getNode() instanceof \OCP\Files\File) {
184
-			$qb->setParameter('itemType', 'file');
185
-		} else {
186
-			$qb->setParameter('itemType', 'folder');
187
-		}
188
-
189
-		// Set the file id
190
-		$qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
191
-		$qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
192
-
193
-		// set the permissions
194
-		$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
195
-
196
-		// Set who created this share
197
-		$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
198
-
199
-		// Set who is the owner of this file/folder (and this the owner of the share)
200
-		$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
201
-
202
-		// Set the file target
203
-		$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
204
-
205
-		// Set the time this share was created
206
-		$qb->setValue('stime', $qb->createNamedParameter(time()));
207
-
208
-		// insert the data and fetch the id of the share
209
-		$this->dbConn->beginTransaction();
210
-		$qb->execute();
211
-		$id = $this->dbConn->lastInsertId('*PREFIX*share');
212
-
213
-		// Now fetch the inserted share and create a complete share object
214
-		$qb = $this->dbConn->getQueryBuilder();
215
-		$qb->select('*')
216
-			->from('share')
217
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
218
-
219
-		$cursor = $qb->execute();
220
-		$data = $cursor->fetch();
221
-		$this->dbConn->commit();
222
-		$cursor->closeCursor();
223
-
224
-		if ($data === false) {
225
-			throw new ShareNotFound();
226
-		}
227
-
228
-		$mailSendValue = $share->getMailSend();
229
-		$data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue;
230
-
231
-		$share = $this->createShare($data);
232
-		return $share;
233
-	}
234
-
235
-	/**
236
-	 * Update a share
237
-	 *
238
-	 * @param \OCP\Share\IShare $share
239
-	 * @return \OCP\Share\IShare The share object
240
-	 * @throws ShareNotFound
241
-	 * @throws \OCP\Files\InvalidPathException
242
-	 * @throws \OCP\Files\NotFoundException
243
-	 */
244
-	public function update(\OCP\Share\IShare $share) {
245
-		$originalShare = $this->getShareById($share->getId());
246
-
247
-		if ($share->getShareType() === IShare::TYPE_USER) {
248
-			/*
65
+    // Special share type for user modified group shares
66
+    public const SHARE_TYPE_USERGROUP = 2;
67
+
68
+    /** @var IDBConnection */
69
+    private $dbConn;
70
+
71
+    /** @var IUserManager */
72
+    private $userManager;
73
+
74
+    /** @var IGroupManager */
75
+    private $groupManager;
76
+
77
+    /** @var IRootFolder */
78
+    private $rootFolder;
79
+
80
+    /** @var IMailer */
81
+    private $mailer;
82
+
83
+    /** @var Defaults */
84
+    private $defaults;
85
+
86
+    /** @var IFactory */
87
+    private $l10nFactory;
88
+
89
+    /** @var IURLGenerator */
90
+    private $urlGenerator;
91
+
92
+    /** @var IConfig */
93
+    private $config;
94
+
95
+    public function __construct(
96
+            IDBConnection $connection,
97
+            IUserManager $userManager,
98
+            IGroupManager $groupManager,
99
+            IRootFolder $rootFolder,
100
+            IMailer $mailer,
101
+            Defaults $defaults,
102
+            IFactory $l10nFactory,
103
+            IURLGenerator $urlGenerator,
104
+            IConfig $config) {
105
+        $this->dbConn = $connection;
106
+        $this->userManager = $userManager;
107
+        $this->groupManager = $groupManager;
108
+        $this->rootFolder = $rootFolder;
109
+        $this->mailer = $mailer;
110
+        $this->defaults = $defaults;
111
+        $this->l10nFactory = $l10nFactory;
112
+        $this->urlGenerator = $urlGenerator;
113
+        $this->config = $config;
114
+    }
115
+
116
+    /**
117
+     * Return the identifier of this provider.
118
+     *
119
+     * @return string Containing only [a-zA-Z0-9]
120
+     */
121
+    public function identifier() {
122
+        return 'ocinternal';
123
+    }
124
+
125
+    /**
126
+     * Share a path
127
+     *
128
+     * @param \OCP\Share\IShare $share
129
+     * @return \OCP\Share\IShare The share object
130
+     * @throws ShareNotFound
131
+     * @throws \Exception
132
+     */
133
+    public function create(\OCP\Share\IShare $share) {
134
+        $qb = $this->dbConn->getQueryBuilder();
135
+
136
+        $qb->insert('share');
137
+        $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
138
+
139
+        if ($share->getShareType() === IShare::TYPE_USER) {
140
+            //Set the UID of the user we share with
141
+            $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
142
+            $qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
143
+
144
+            //If an expiration date is set store it
145
+            if ($share->getExpirationDate() !== null) {
146
+                $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
147
+            }
148
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
149
+            //Set the GID of the group we share with
150
+            $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
151
+
152
+            //If an expiration date is set store it
153
+            if ($share->getExpirationDate() !== null) {
154
+                $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
155
+            }
156
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
157
+            //set label for public link
158
+            $qb->setValue('label', $qb->createNamedParameter($share->getLabel()));
159
+            //Set the token of the share
160
+            $qb->setValue('token', $qb->createNamedParameter($share->getToken()));
161
+
162
+            //If a password is set store it
163
+            if ($share->getPassword() !== null) {
164
+                $qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
165
+            }
166
+
167
+            $qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
168
+
169
+            //If an expiration date is set store it
170
+            if ($share->getExpirationDate() !== null) {
171
+                $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
172
+            }
173
+
174
+            if (method_exists($share, 'getParent')) {
175
+                $qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
176
+            }
177
+        } else {
178
+            throw new \Exception('invalid share type!');
179
+        }
180
+
181
+        // Set what is shares
182
+        $qb->setValue('item_type', $qb->createParameter('itemType'));
183
+        if ($share->getNode() instanceof \OCP\Files\File) {
184
+            $qb->setParameter('itemType', 'file');
185
+        } else {
186
+            $qb->setParameter('itemType', 'folder');
187
+        }
188
+
189
+        // Set the file id
190
+        $qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId()));
191
+        $qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId()));
192
+
193
+        // set the permissions
194
+        $qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
195
+
196
+        // Set who created this share
197
+        $qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
198
+
199
+        // Set who is the owner of this file/folder (and this the owner of the share)
200
+        $qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()));
201
+
202
+        // Set the file target
203
+        $qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
204
+
205
+        // Set the time this share was created
206
+        $qb->setValue('stime', $qb->createNamedParameter(time()));
207
+
208
+        // insert the data and fetch the id of the share
209
+        $this->dbConn->beginTransaction();
210
+        $qb->execute();
211
+        $id = $this->dbConn->lastInsertId('*PREFIX*share');
212
+
213
+        // Now fetch the inserted share and create a complete share object
214
+        $qb = $this->dbConn->getQueryBuilder();
215
+        $qb->select('*')
216
+            ->from('share')
217
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
218
+
219
+        $cursor = $qb->execute();
220
+        $data = $cursor->fetch();
221
+        $this->dbConn->commit();
222
+        $cursor->closeCursor();
223
+
224
+        if ($data === false) {
225
+            throw new ShareNotFound();
226
+        }
227
+
228
+        $mailSendValue = $share->getMailSend();
229
+        $data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue;
230
+
231
+        $share = $this->createShare($data);
232
+        return $share;
233
+    }
234
+
235
+    /**
236
+     * Update a share
237
+     *
238
+     * @param \OCP\Share\IShare $share
239
+     * @return \OCP\Share\IShare The share object
240
+     * @throws ShareNotFound
241
+     * @throws \OCP\Files\InvalidPathException
242
+     * @throws \OCP\Files\NotFoundException
243
+     */
244
+    public function update(\OCP\Share\IShare $share) {
245
+        $originalShare = $this->getShareById($share->getId());
246
+
247
+        if ($share->getShareType() === IShare::TYPE_USER) {
248
+            /*
249 249
 			 * We allow updating the recipient on user shares.
250 250
 			 */
251
-			$qb = $this->dbConn->getQueryBuilder();
252
-			$qb->update('share')
253
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
254
-				->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
255
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
256
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
257
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
258
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
259
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
260
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
261
-				->set('note', $qb->createNamedParameter($share->getNote()))
262
-				->set('accepted', $qb->createNamedParameter($share->getStatus()))
263
-				->execute();
264
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
265
-			$qb = $this->dbConn->getQueryBuilder();
266
-			$qb->update('share')
267
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
268
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
269
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
270
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
271
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
272
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
273
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
274
-				->set('note', $qb->createNamedParameter($share->getNote()))
275
-				->execute();
276
-
277
-			/*
251
+            $qb = $this->dbConn->getQueryBuilder();
252
+            $qb->update('share')
253
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
254
+                ->set('share_with', $qb->createNamedParameter($share->getSharedWith()))
255
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
256
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
257
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
258
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
259
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
260
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
261
+                ->set('note', $qb->createNamedParameter($share->getNote()))
262
+                ->set('accepted', $qb->createNamedParameter($share->getStatus()))
263
+                ->execute();
264
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
265
+            $qb = $this->dbConn->getQueryBuilder();
266
+            $qb->update('share')
267
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
268
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
269
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
270
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
271
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
272
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
273
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
274
+                ->set('note', $qb->createNamedParameter($share->getNote()))
275
+                ->execute();
276
+
277
+            /*
278 278
 			 * Update all user defined group shares
279 279
 			 */
280
-			$qb = $this->dbConn->getQueryBuilder();
281
-			$qb->update('share')
282
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
283
-				->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
284
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
285
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
286
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
287
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
288
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
289
-				->set('note', $qb->createNamedParameter($share->getNote()))
290
-				->execute();
291
-
292
-			/*
280
+            $qb = $this->dbConn->getQueryBuilder();
281
+            $qb->update('share')
282
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
283
+                ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
284
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
285
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
286
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
287
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
288
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
289
+                ->set('note', $qb->createNamedParameter($share->getNote()))
290
+                ->execute();
291
+
292
+            /*
293 293
 			 * Now update the permissions for all children that have not set it to 0
294 294
 			 */
295
-			$qb = $this->dbConn->getQueryBuilder();
296
-			$qb->update('share')
297
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
298
-				->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
299
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
300
-				->execute();
301
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
302
-			$qb = $this->dbConn->getQueryBuilder();
303
-			$qb->update('share')
304
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
305
-				->set('password', $qb->createNamedParameter($share->getPassword()))
306
-				->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
307
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
308
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
309
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
310
-				->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
311
-				->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
312
-				->set('token', $qb->createNamedParameter($share->getToken()))
313
-				->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
314
-				->set('note', $qb->createNamedParameter($share->getNote()))
315
-				->set('label', $qb->createNamedParameter($share->getLabel()))
316
-				->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT)
317
-				->execute();
318
-		}
319
-
320
-		if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
321
-			$this->propagateNote($share);
322
-		}
323
-
324
-
325
-		return $share;
326
-	}
327
-
328
-	/**
329
-	 * Accept a share.
330
-	 *
331
-	 * @param IShare $share
332
-	 * @param string $recipient
333
-	 * @return IShare The share object
334
-	 * @since 9.0.0
335
-	 */
336
-	public function acceptShare(IShare $share, string $recipient): IShare {
337
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
338
-			$group = $this->groupManager->get($share->getSharedWith());
339
-			$user = $this->userManager->get($recipient);
340
-
341
-			if (is_null($group)) {
342
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
343
-			}
344
-
345
-			if (!$group->inGroup($user)) {
346
-				throw new ProviderException('Recipient not in receiving group');
347
-			}
348
-
349
-			// Try to fetch user specific share
350
-			$qb = $this->dbConn->getQueryBuilder();
351
-			$stmt = $qb->select('*')
352
-				->from('share')
353
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
354
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
355
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
356
-				->andWhere($qb->expr()->orX(
357
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
358
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
359
-				))
360
-				->execute();
361
-
362
-			$data = $stmt->fetch();
363
-			$stmt->closeCursor();
364
-
365
-			/*
295
+            $qb = $this->dbConn->getQueryBuilder();
296
+            $qb->update('share')
297
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
298
+                ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
299
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
300
+                ->execute();
301
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
302
+            $qb = $this->dbConn->getQueryBuilder();
303
+            $qb->update('share')
304
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
305
+                ->set('password', $qb->createNamedParameter($share->getPassword()))
306
+                ->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
307
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
308
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
309
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
310
+                ->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
311
+                ->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
312
+                ->set('token', $qb->createNamedParameter($share->getToken()))
313
+                ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
314
+                ->set('note', $qb->createNamedParameter($share->getNote()))
315
+                ->set('label', $qb->createNamedParameter($share->getLabel()))
316
+                ->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT)
317
+                ->execute();
318
+        }
319
+
320
+        if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') {
321
+            $this->propagateNote($share);
322
+        }
323
+
324
+
325
+        return $share;
326
+    }
327
+
328
+    /**
329
+     * Accept a share.
330
+     *
331
+     * @param IShare $share
332
+     * @param string $recipient
333
+     * @return IShare The share object
334
+     * @since 9.0.0
335
+     */
336
+    public function acceptShare(IShare $share, string $recipient): IShare {
337
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
338
+            $group = $this->groupManager->get($share->getSharedWith());
339
+            $user = $this->userManager->get($recipient);
340
+
341
+            if (is_null($group)) {
342
+                throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
343
+            }
344
+
345
+            if (!$group->inGroup($user)) {
346
+                throw new ProviderException('Recipient not in receiving group');
347
+            }
348
+
349
+            // Try to fetch user specific share
350
+            $qb = $this->dbConn->getQueryBuilder();
351
+            $stmt = $qb->select('*')
352
+                ->from('share')
353
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
354
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
355
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
356
+                ->andWhere($qb->expr()->orX(
357
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
358
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
359
+                ))
360
+                ->execute();
361
+
362
+            $data = $stmt->fetch();
363
+            $stmt->closeCursor();
364
+
365
+            /*
366 366
 			 * Check if there already is a user specific group share.
367 367
 			 * If there is update it (if required).
368 368
 			 */
369
-			if ($data === false) {
370
-				$id = $this->createUserSpecificGroupShare($share, $recipient);
371
-			} else {
372
-				$id = $data['id'];
373
-			}
374
-		} elseif ($share->getShareType() === IShare::TYPE_USER) {
375
-			if ($share->getSharedWith() !== $recipient) {
376
-				throw new ProviderException('Recipient does not match');
377
-			}
378
-
379
-			$id = $share->getId();
380
-		} else {
381
-			throw new ProviderException('Invalid shareType');
382
-		}
383
-
384
-		$qb = $this->dbConn->getQueryBuilder();
385
-		$qb->update('share')
386
-			->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
387
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
388
-			->execute();
389
-
390
-		return $share;
391
-	}
392
-
393
-	/**
394
-	 * Get all children of this share
395
-	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
396
-	 *
397
-	 * @param \OCP\Share\IShare $parent
398
-	 * @return \OCP\Share\IShare[]
399
-	 */
400
-	public function getChildren(\OCP\Share\IShare $parent) {
401
-		$children = [];
402
-
403
-		$qb = $this->dbConn->getQueryBuilder();
404
-		$qb->select('*')
405
-			->from('share')
406
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
407
-			->andWhere(
408
-				$qb->expr()->in(
409
-					'share_type',
410
-					$qb->createNamedParameter([
411
-						IShare::TYPE_USER,
412
-						IShare::TYPE_GROUP,
413
-						IShare::TYPE_LINK,
414
-					], IQueryBuilder::PARAM_INT_ARRAY)
415
-				)
416
-			)
417
-			->andWhere($qb->expr()->orX(
418
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
419
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
420
-			))
421
-			->orderBy('id');
422
-
423
-		$cursor = $qb->execute();
424
-		while ($data = $cursor->fetch()) {
425
-			$children[] = $this->createShare($data);
426
-		}
427
-		$cursor->closeCursor();
428
-
429
-		return $children;
430
-	}
431
-
432
-	/**
433
-	 * Delete a share
434
-	 *
435
-	 * @param \OCP\Share\IShare $share
436
-	 */
437
-	public function delete(\OCP\Share\IShare $share) {
438
-		$qb = $this->dbConn->getQueryBuilder();
439
-		$qb->delete('share')
440
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
441
-
442
-		/*
369
+            if ($data === false) {
370
+                $id = $this->createUserSpecificGroupShare($share, $recipient);
371
+            } else {
372
+                $id = $data['id'];
373
+            }
374
+        } elseif ($share->getShareType() === IShare::TYPE_USER) {
375
+            if ($share->getSharedWith() !== $recipient) {
376
+                throw new ProviderException('Recipient does not match');
377
+            }
378
+
379
+            $id = $share->getId();
380
+        } else {
381
+            throw new ProviderException('Invalid shareType');
382
+        }
383
+
384
+        $qb = $this->dbConn->getQueryBuilder();
385
+        $qb->update('share')
386
+            ->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
387
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
388
+            ->execute();
389
+
390
+        return $share;
391
+    }
392
+
393
+    /**
394
+     * Get all children of this share
395
+     * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
396
+     *
397
+     * @param \OCP\Share\IShare $parent
398
+     * @return \OCP\Share\IShare[]
399
+     */
400
+    public function getChildren(\OCP\Share\IShare $parent) {
401
+        $children = [];
402
+
403
+        $qb = $this->dbConn->getQueryBuilder();
404
+        $qb->select('*')
405
+            ->from('share')
406
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
407
+            ->andWhere(
408
+                $qb->expr()->in(
409
+                    'share_type',
410
+                    $qb->createNamedParameter([
411
+                        IShare::TYPE_USER,
412
+                        IShare::TYPE_GROUP,
413
+                        IShare::TYPE_LINK,
414
+                    ], IQueryBuilder::PARAM_INT_ARRAY)
415
+                )
416
+            )
417
+            ->andWhere($qb->expr()->orX(
418
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
419
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
420
+            ))
421
+            ->orderBy('id');
422
+
423
+        $cursor = $qb->execute();
424
+        while ($data = $cursor->fetch()) {
425
+            $children[] = $this->createShare($data);
426
+        }
427
+        $cursor->closeCursor();
428
+
429
+        return $children;
430
+    }
431
+
432
+    /**
433
+     * Delete a share
434
+     *
435
+     * @param \OCP\Share\IShare $share
436
+     */
437
+    public function delete(\OCP\Share\IShare $share) {
438
+        $qb = $this->dbConn->getQueryBuilder();
439
+        $qb->delete('share')
440
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
441
+
442
+        /*
443 443
 		 * If the share is a group share delete all possible
444 444
 		 * user defined groups shares.
445 445
 		 */
446
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
447
-			$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
448
-		}
449
-
450
-		$qb->execute();
451
-	}
452
-
453
-	/**
454
-	 * Unshare a share from the recipient. If this is a group share
455
-	 * this means we need a special entry in the share db.
456
-	 *
457
-	 * @param IShare $share
458
-	 * @param string $recipient UserId of recipient
459
-	 * @throws BackendError
460
-	 * @throws ProviderException
461
-	 */
462
-	public function deleteFromSelf(IShare $share, $recipient) {
463
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
464
-			$group = $this->groupManager->get($share->getSharedWith());
465
-			$user = $this->userManager->get($recipient);
466
-
467
-			if (is_null($group)) {
468
-				throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
469
-			}
470
-
471
-			if (!$group->inGroup($user)) {
472
-				throw new ProviderException('Recipient not in receiving group');
473
-			}
474
-
475
-			// Try to fetch user specific share
476
-			$qb = $this->dbConn->getQueryBuilder();
477
-			$stmt = $qb->select('*')
478
-				->from('share')
479
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
480
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
481
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
482
-				->andWhere($qb->expr()->orX(
483
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
484
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
485
-				))
486
-				->execute();
487
-
488
-			$data = $stmt->fetch();
489
-
490
-			/*
446
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
447
+            $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
448
+        }
449
+
450
+        $qb->execute();
451
+    }
452
+
453
+    /**
454
+     * Unshare a share from the recipient. If this is a group share
455
+     * this means we need a special entry in the share db.
456
+     *
457
+     * @param IShare $share
458
+     * @param string $recipient UserId of recipient
459
+     * @throws BackendError
460
+     * @throws ProviderException
461
+     */
462
+    public function deleteFromSelf(IShare $share, $recipient) {
463
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
464
+            $group = $this->groupManager->get($share->getSharedWith());
465
+            $user = $this->userManager->get($recipient);
466
+
467
+            if (is_null($group)) {
468
+                throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
469
+            }
470
+
471
+            if (!$group->inGroup($user)) {
472
+                throw new ProviderException('Recipient not in receiving group');
473
+            }
474
+
475
+            // Try to fetch user specific share
476
+            $qb = $this->dbConn->getQueryBuilder();
477
+            $stmt = $qb->select('*')
478
+                ->from('share')
479
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
480
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
481
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
482
+                ->andWhere($qb->expr()->orX(
483
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
484
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
485
+                ))
486
+                ->execute();
487
+
488
+            $data = $stmt->fetch();
489
+
490
+            /*
491 491
 			 * Check if there already is a user specific group share.
492 492
 			 * If there is update it (if required).
493 493
 			 */
494
-			if ($data === false) {
495
-				$id = $this->createUserSpecificGroupShare($share, $recipient);
496
-				$permissions = $share->getPermissions();
497
-			} else {
498
-				$permissions = $data['permissions'];
499
-				$id = $data['id'];
500
-			}
501
-
502
-			if ($permissions !== 0) {
503
-				// Update existing usergroup share
504
-				$qb = $this->dbConn->getQueryBuilder();
505
-				$qb->update('share')
506
-					->set('permissions', $qb->createNamedParameter(0))
507
-					->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
508
-					->execute();
509
-			}
510
-		} elseif ($share->getShareType() === IShare::TYPE_USER) {
511
-			if ($share->getSharedWith() !== $recipient) {
512
-				throw new ProviderException('Recipient does not match');
513
-			}
514
-
515
-			// We can just delete user and link shares
516
-			$this->delete($share);
517
-		} else {
518
-			throw new ProviderException('Invalid shareType');
519
-		}
520
-	}
521
-
522
-	protected function createUserSpecificGroupShare(IShare $share, string $recipient): int {
523
-		$type = $share->getNodeType();
524
-
525
-		$qb = $this->dbConn->getQueryBuilder();
526
-		$qb->insert('share')
527
-			->values([
528
-				'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
529
-				'share_with' => $qb->createNamedParameter($recipient),
530
-				'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
531
-				'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
532
-				'parent' => $qb->createNamedParameter($share->getId()),
533
-				'item_type' => $qb->createNamedParameter($type),
534
-				'item_source' => $qb->createNamedParameter($share->getNodeId()),
535
-				'file_source' => $qb->createNamedParameter($share->getNodeId()),
536
-				'file_target' => $qb->createNamedParameter($share->getTarget()),
537
-				'permissions' => $qb->createNamedParameter($share->getPermissions()),
538
-				'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
539
-			])->execute();
540
-
541
-		return $qb->getLastInsertId();
542
-	}
543
-
544
-	/**
545
-	 * @inheritdoc
546
-	 *
547
-	 * For now this only works for group shares
548
-	 * If this gets implemented for normal shares we have to extend it
549
-	 */
550
-	public function restore(IShare $share, string $recipient): IShare {
551
-		$qb = $this->dbConn->getQueryBuilder();
552
-		$qb->select('permissions')
553
-			->from('share')
554
-			->where(
555
-				$qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
556
-			);
557
-		$cursor = $qb->execute();
558
-		$data = $cursor->fetch();
559
-		$cursor->closeCursor();
560
-
561
-		$originalPermission = $data['permissions'];
562
-
563
-		$qb = $this->dbConn->getQueryBuilder();
564
-		$qb->update('share')
565
-			->set('permissions', $qb->createNamedParameter($originalPermission))
566
-			->where(
567
-				$qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
568
-			)->andWhere(
569
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
570
-			)->andWhere(
571
-				$qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
572
-			);
573
-
574
-		$qb->execute();
575
-
576
-		return $this->getShareById($share->getId(), $recipient);
577
-	}
578
-
579
-	/**
580
-	 * @inheritdoc
581
-	 */
582
-	public function move(\OCP\Share\IShare $share, $recipient) {
583
-		if ($share->getShareType() === IShare::TYPE_USER) {
584
-			// Just update the target
585
-			$qb = $this->dbConn->getQueryBuilder();
586
-			$qb->update('share')
587
-				->set('file_target', $qb->createNamedParameter($share->getTarget()))
588
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
589
-				->execute();
590
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
591
-
592
-			// Check if there is a usergroup share
593
-			$qb = $this->dbConn->getQueryBuilder();
594
-			$stmt = $qb->select('id')
595
-				->from('share')
596
-				->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
597
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
598
-				->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
599
-				->andWhere($qb->expr()->orX(
600
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
601
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
602
-				))
603
-				->setMaxResults(1)
604
-				->execute();
605
-
606
-			$data = $stmt->fetch();
607
-			$stmt->closeCursor();
608
-
609
-			if ($data === false) {
610
-				// No usergroup share yet. Create one.
611
-				$qb = $this->dbConn->getQueryBuilder();
612
-				$qb->insert('share')
613
-					->values([
614
-						'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
615
-						'share_with' => $qb->createNamedParameter($recipient),
616
-						'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
617
-						'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
618
-						'parent' => $qb->createNamedParameter($share->getId()),
619
-						'item_type' => $qb->createNamedParameter($share->getNodeType()),
620
-						'item_source' => $qb->createNamedParameter($share->getNodeId()),
621
-						'file_source' => $qb->createNamedParameter($share->getNodeId()),
622
-						'file_target' => $qb->createNamedParameter($share->getTarget()),
623
-						'permissions' => $qb->createNamedParameter($share->getPermissions()),
624
-						'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
625
-					])->execute();
626
-			} else {
627
-				// Already a usergroup share. Update it.
628
-				$qb = $this->dbConn->getQueryBuilder();
629
-				$qb->update('share')
630
-					->set('file_target', $qb->createNamedParameter($share->getTarget()))
631
-					->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
632
-					->execute();
633
-			}
634
-		}
635
-
636
-		return $share;
637
-	}
638
-
639
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
640
-		$qb = $this->dbConn->getQueryBuilder();
641
-		$qb->select('*')
642
-			->from('share', 's')
643
-			->andWhere($qb->expr()->orX(
644
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
645
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
646
-			));
647
-
648
-		$qb->andWhere($qb->expr()->orX(
649
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
650
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
651
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
652
-		));
653
-
654
-		/**
655
-		 * Reshares for this user are shares where they are the owner.
656
-		 */
657
-		if ($reshares === false) {
658
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
659
-		} else {
660
-			$qb->andWhere(
661
-				$qb->expr()->orX(
662
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
663
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
664
-				)
665
-			);
666
-		}
667
-
668
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
669
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
670
-
671
-		$qb->orderBy('id');
672
-
673
-		$cursor = $qb->execute();
674
-		$shares = [];
675
-		while ($data = $cursor->fetch()) {
676
-			$shares[$data['fileid']][] = $this->createShare($data);
677
-		}
678
-		$cursor->closeCursor();
679
-
680
-		return $shares;
681
-	}
682
-
683
-	/**
684
-	 * @inheritdoc
685
-	 */
686
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
687
-		$qb = $this->dbConn->getQueryBuilder();
688
-		$qb->select('*')
689
-			->from('share')
690
-			->andWhere($qb->expr()->orX(
691
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
692
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
693
-			));
694
-
695
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
696
-
697
-		/**
698
-		 * Reshares for this user are shares where they are the owner.
699
-		 */
700
-		if ($reshares === false) {
701
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
702
-		} else {
703
-			if ($node === null) {
704
-				$qb->andWhere(
705
-					$qb->expr()->orX(
706
-						$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
707
-						$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
708
-					)
709
-				);
710
-			}
711
-		}
712
-
713
-		if ($node !== null) {
714
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
715
-		}
716
-
717
-		if ($limit !== -1) {
718
-			$qb->setMaxResults($limit);
719
-		}
720
-
721
-		$qb->setFirstResult($offset);
722
-		$qb->orderBy('id');
723
-
724
-		$cursor = $qb->execute();
725
-		$shares = [];
726
-		while ($data = $cursor->fetch()) {
727
-			$shares[] = $this->createShare($data);
728
-		}
729
-		$cursor->closeCursor();
730
-
731
-		return $shares;
732
-	}
733
-
734
-	/**
735
-	 * @inheritdoc
736
-	 */
737
-	public function getShareById($id, $recipientId = null) {
738
-		$qb = $this->dbConn->getQueryBuilder();
739
-
740
-		$qb->select('*')
741
-			->from('share')
742
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
743
-			->andWhere(
744
-				$qb->expr()->in(
745
-					'share_type',
746
-					$qb->createNamedParameter([
747
-						IShare::TYPE_USER,
748
-						IShare::TYPE_GROUP,
749
-						IShare::TYPE_LINK,
750
-					], IQueryBuilder::PARAM_INT_ARRAY)
751
-				)
752
-			)
753
-			->andWhere($qb->expr()->orX(
754
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
755
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
756
-			));
757
-
758
-		$cursor = $qb->execute();
759
-		$data = $cursor->fetch();
760
-		$cursor->closeCursor();
761
-
762
-		if ($data === false) {
763
-			throw new ShareNotFound();
764
-		}
765
-
766
-		try {
767
-			$share = $this->createShare($data);
768
-		} catch (InvalidShare $e) {
769
-			throw new ShareNotFound();
770
-		}
771
-
772
-		// If the recipient is set for a group share resolve to that user
773
-		if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
774
-			$share = $this->resolveGroupShares([$share], $recipientId)[0];
775
-		}
776
-
777
-		return $share;
778
-	}
779
-
780
-	/**
781
-	 * Get shares for a given path
782
-	 *
783
-	 * @param \OCP\Files\Node $path
784
-	 * @return \OCP\Share\IShare[]
785
-	 */
786
-	public function getSharesByPath(Node $path) {
787
-		$qb = $this->dbConn->getQueryBuilder();
788
-
789
-		$cursor = $qb->select('*')
790
-			->from('share')
791
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
792
-			->andWhere(
793
-				$qb->expr()->orX(
794
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
795
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))
796
-				)
797
-			)
798
-			->andWhere($qb->expr()->orX(
799
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
800
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
801
-			))
802
-			->execute();
803
-
804
-		$shares = [];
805
-		while ($data = $cursor->fetch()) {
806
-			$shares[] = $this->createShare($data);
807
-		}
808
-		$cursor->closeCursor();
809
-
810
-		return $shares;
811
-	}
812
-
813
-	/**
814
-	 * Returns whether the given database result can be interpreted as
815
-	 * a share with accessible file (not trashed, not deleted)
816
-	 */
817
-	private function isAccessibleResult($data) {
818
-		// exclude shares leading to deleted file entries
819
-		if ($data['fileid'] === null || $data['path'] === null) {
820
-			return false;
821
-		}
822
-
823
-		// exclude shares leading to trashbin on home storages
824
-		$pathSections = explode('/', $data['path'], 2);
825
-		// FIXME: would not detect rare md5'd home storage case properly
826
-		if ($pathSections[0] !== 'files'
827
-				&& in_array(explode(':', $data['storage_string_id'], 2)[0], ['home', 'object'])) {
828
-			return false;
829
-		}
830
-		return true;
831
-	}
832
-
833
-	/**
834
-	 * @inheritdoc
835
-	 */
836
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
837
-		/** @var Share[] $shares */
838
-		$shares = [];
839
-
840
-		if ($shareType === IShare::TYPE_USER) {
841
-			//Get shares directly with this user
842
-			$qb = $this->dbConn->getQueryBuilder();
843
-			$qb->select('s.*',
844
-				'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
845
-				'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
846
-				'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
847
-			)
848
-				->selectAlias('st.id', 'storage_string_id')
849
-				->from('share', 's')
850
-				->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
851
-				->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
852
-
853
-			// Order by id
854
-			$qb->orderBy('s.id');
855
-
856
-			// Set limit and offset
857
-			if ($limit !== -1) {
858
-				$qb->setMaxResults($limit);
859
-			}
860
-			$qb->setFirstResult($offset);
861
-
862
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)))
863
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
864
-				->andWhere($qb->expr()->orX(
865
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
866
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
867
-				));
868
-
869
-			// Filter by node if provided
870
-			if ($node !== null) {
871
-				$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
872
-			}
873
-
874
-			$cursor = $qb->execute();
875
-
876
-			while ($data = $cursor->fetch()) {
877
-				if ($this->isAccessibleResult($data)) {
878
-					$shares[] = $this->createShare($data);
879
-				}
880
-			}
881
-			$cursor->closeCursor();
882
-		} elseif ($shareType === IShare::TYPE_GROUP) {
883
-			$user = $this->userManager->get($userId);
884
-			$allGroups = $this->groupManager->getUserGroupIds($user);
885
-
886
-			/** @var Share[] $shares2 */
887
-			$shares2 = [];
888
-
889
-			$start = 0;
890
-			while (true) {
891
-				$groups = array_slice($allGroups, $start, 100);
892
-				$start += 100;
893
-
894
-				if ($groups === []) {
895
-					break;
896
-				}
897
-
898
-				$qb = $this->dbConn->getQueryBuilder();
899
-				$qb->select('s.*',
900
-					'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
901
-					'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
902
-					'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
903
-				)
904
-					->selectAlias('st.id', 'storage_string_id')
905
-					->from('share', 's')
906
-					->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
907
-					->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
908
-					->orderBy('s.id')
909
-					->setFirstResult(0);
910
-
911
-				if ($limit !== -1) {
912
-					$qb->setMaxResults($limit - count($shares));
913
-				}
914
-
915
-				// Filter by node if provided
916
-				if ($node !== null) {
917
-					$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
918
-				}
919
-
920
-
921
-				$groups = array_filter($groups);
922
-
923
-				$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
924
-					->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
925
-						$groups,
926
-						IQueryBuilder::PARAM_STR_ARRAY
927
-					)))
928
-					->andWhere($qb->expr()->orX(
929
-						$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
930
-						$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
931
-					));
932
-
933
-				$cursor = $qb->execute();
934
-				while ($data = $cursor->fetch()) {
935
-					if ($offset > 0) {
936
-						$offset--;
937
-						continue;
938
-					}
939
-
940
-					if ($this->isAccessibleResult($data)) {
941
-						$shares2[] = $this->createShare($data);
942
-					}
943
-				}
944
-				$cursor->closeCursor();
945
-			}
946
-
947
-			/*
494
+            if ($data === false) {
495
+                $id = $this->createUserSpecificGroupShare($share, $recipient);
496
+                $permissions = $share->getPermissions();
497
+            } else {
498
+                $permissions = $data['permissions'];
499
+                $id = $data['id'];
500
+            }
501
+
502
+            if ($permissions !== 0) {
503
+                // Update existing usergroup share
504
+                $qb = $this->dbConn->getQueryBuilder();
505
+                $qb->update('share')
506
+                    ->set('permissions', $qb->createNamedParameter(0))
507
+                    ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
508
+                    ->execute();
509
+            }
510
+        } elseif ($share->getShareType() === IShare::TYPE_USER) {
511
+            if ($share->getSharedWith() !== $recipient) {
512
+                throw new ProviderException('Recipient does not match');
513
+            }
514
+
515
+            // We can just delete user and link shares
516
+            $this->delete($share);
517
+        } else {
518
+            throw new ProviderException('Invalid shareType');
519
+        }
520
+    }
521
+
522
+    protected function createUserSpecificGroupShare(IShare $share, string $recipient): int {
523
+        $type = $share->getNodeType();
524
+
525
+        $qb = $this->dbConn->getQueryBuilder();
526
+        $qb->insert('share')
527
+            ->values([
528
+                'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
529
+                'share_with' => $qb->createNamedParameter($recipient),
530
+                'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
531
+                'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
532
+                'parent' => $qb->createNamedParameter($share->getId()),
533
+                'item_type' => $qb->createNamedParameter($type),
534
+                'item_source' => $qb->createNamedParameter($share->getNodeId()),
535
+                'file_source' => $qb->createNamedParameter($share->getNodeId()),
536
+                'file_target' => $qb->createNamedParameter($share->getTarget()),
537
+                'permissions' => $qb->createNamedParameter($share->getPermissions()),
538
+                'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
539
+            ])->execute();
540
+
541
+        return $qb->getLastInsertId();
542
+    }
543
+
544
+    /**
545
+     * @inheritdoc
546
+     *
547
+     * For now this only works for group shares
548
+     * If this gets implemented for normal shares we have to extend it
549
+     */
550
+    public function restore(IShare $share, string $recipient): IShare {
551
+        $qb = $this->dbConn->getQueryBuilder();
552
+        $qb->select('permissions')
553
+            ->from('share')
554
+            ->where(
555
+                $qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
556
+            );
557
+        $cursor = $qb->execute();
558
+        $data = $cursor->fetch();
559
+        $cursor->closeCursor();
560
+
561
+        $originalPermission = $data['permissions'];
562
+
563
+        $qb = $this->dbConn->getQueryBuilder();
564
+        $qb->update('share')
565
+            ->set('permissions', $qb->createNamedParameter($originalPermission))
566
+            ->where(
567
+                $qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent()))
568
+            )->andWhere(
569
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
570
+            )->andWhere(
571
+                $qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
572
+            );
573
+
574
+        $qb->execute();
575
+
576
+        return $this->getShareById($share->getId(), $recipient);
577
+    }
578
+
579
+    /**
580
+     * @inheritdoc
581
+     */
582
+    public function move(\OCP\Share\IShare $share, $recipient) {
583
+        if ($share->getShareType() === IShare::TYPE_USER) {
584
+            // Just update the target
585
+            $qb = $this->dbConn->getQueryBuilder();
586
+            $qb->update('share')
587
+                ->set('file_target', $qb->createNamedParameter($share->getTarget()))
588
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
589
+                ->execute();
590
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
591
+
592
+            // Check if there is a usergroup share
593
+            $qb = $this->dbConn->getQueryBuilder();
594
+            $stmt = $qb->select('id')
595
+                ->from('share')
596
+                ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
597
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
598
+                ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
599
+                ->andWhere($qb->expr()->orX(
600
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
601
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
602
+                ))
603
+                ->setMaxResults(1)
604
+                ->execute();
605
+
606
+            $data = $stmt->fetch();
607
+            $stmt->closeCursor();
608
+
609
+            if ($data === false) {
610
+                // No usergroup share yet. Create one.
611
+                $qb = $this->dbConn->getQueryBuilder();
612
+                $qb->insert('share')
613
+                    ->values([
614
+                        'share_type' => $qb->createNamedParameter(IShare::TYPE_USERGROUP),
615
+                        'share_with' => $qb->createNamedParameter($recipient),
616
+                        'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
617
+                        'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
618
+                        'parent' => $qb->createNamedParameter($share->getId()),
619
+                        'item_type' => $qb->createNamedParameter($share->getNodeType()),
620
+                        'item_source' => $qb->createNamedParameter($share->getNodeId()),
621
+                        'file_source' => $qb->createNamedParameter($share->getNodeId()),
622
+                        'file_target' => $qb->createNamedParameter($share->getTarget()),
623
+                        'permissions' => $qb->createNamedParameter($share->getPermissions()),
624
+                        'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
625
+                    ])->execute();
626
+            } else {
627
+                // Already a usergroup share. Update it.
628
+                $qb = $this->dbConn->getQueryBuilder();
629
+                $qb->update('share')
630
+                    ->set('file_target', $qb->createNamedParameter($share->getTarget()))
631
+                    ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
632
+                    ->execute();
633
+            }
634
+        }
635
+
636
+        return $share;
637
+    }
638
+
639
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
640
+        $qb = $this->dbConn->getQueryBuilder();
641
+        $qb->select('*')
642
+            ->from('share', 's')
643
+            ->andWhere($qb->expr()->orX(
644
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
645
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
646
+            ));
647
+
648
+        $qb->andWhere($qb->expr()->orX(
649
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
650
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
651
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
652
+        ));
653
+
654
+        /**
655
+         * Reshares for this user are shares where they are the owner.
656
+         */
657
+        if ($reshares === false) {
658
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
659
+        } else {
660
+            $qb->andWhere(
661
+                $qb->expr()->orX(
662
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
663
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
664
+                )
665
+            );
666
+        }
667
+
668
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
669
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
670
+
671
+        $qb->orderBy('id');
672
+
673
+        $cursor = $qb->execute();
674
+        $shares = [];
675
+        while ($data = $cursor->fetch()) {
676
+            $shares[$data['fileid']][] = $this->createShare($data);
677
+        }
678
+        $cursor->closeCursor();
679
+
680
+        return $shares;
681
+    }
682
+
683
+    /**
684
+     * @inheritdoc
685
+     */
686
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
687
+        $qb = $this->dbConn->getQueryBuilder();
688
+        $qb->select('*')
689
+            ->from('share')
690
+            ->andWhere($qb->expr()->orX(
691
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
692
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
693
+            ));
694
+
695
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
696
+
697
+        /**
698
+         * Reshares for this user are shares where they are the owner.
699
+         */
700
+        if ($reshares === false) {
701
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
702
+        } else {
703
+            if ($node === null) {
704
+                $qb->andWhere(
705
+                    $qb->expr()->orX(
706
+                        $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
707
+                        $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
708
+                    )
709
+                );
710
+            }
711
+        }
712
+
713
+        if ($node !== null) {
714
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
715
+        }
716
+
717
+        if ($limit !== -1) {
718
+            $qb->setMaxResults($limit);
719
+        }
720
+
721
+        $qb->setFirstResult($offset);
722
+        $qb->orderBy('id');
723
+
724
+        $cursor = $qb->execute();
725
+        $shares = [];
726
+        while ($data = $cursor->fetch()) {
727
+            $shares[] = $this->createShare($data);
728
+        }
729
+        $cursor->closeCursor();
730
+
731
+        return $shares;
732
+    }
733
+
734
+    /**
735
+     * @inheritdoc
736
+     */
737
+    public function getShareById($id, $recipientId = null) {
738
+        $qb = $this->dbConn->getQueryBuilder();
739
+
740
+        $qb->select('*')
741
+            ->from('share')
742
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
743
+            ->andWhere(
744
+                $qb->expr()->in(
745
+                    'share_type',
746
+                    $qb->createNamedParameter([
747
+                        IShare::TYPE_USER,
748
+                        IShare::TYPE_GROUP,
749
+                        IShare::TYPE_LINK,
750
+                    ], IQueryBuilder::PARAM_INT_ARRAY)
751
+                )
752
+            )
753
+            ->andWhere($qb->expr()->orX(
754
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
755
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
756
+            ));
757
+
758
+        $cursor = $qb->execute();
759
+        $data = $cursor->fetch();
760
+        $cursor->closeCursor();
761
+
762
+        if ($data === false) {
763
+            throw new ShareNotFound();
764
+        }
765
+
766
+        try {
767
+            $share = $this->createShare($data);
768
+        } catch (InvalidShare $e) {
769
+            throw new ShareNotFound();
770
+        }
771
+
772
+        // If the recipient is set for a group share resolve to that user
773
+        if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
774
+            $share = $this->resolveGroupShares([$share], $recipientId)[0];
775
+        }
776
+
777
+        return $share;
778
+    }
779
+
780
+    /**
781
+     * Get shares for a given path
782
+     *
783
+     * @param \OCP\Files\Node $path
784
+     * @return \OCP\Share\IShare[]
785
+     */
786
+    public function getSharesByPath(Node $path) {
787
+        $qb = $this->dbConn->getQueryBuilder();
788
+
789
+        $cursor = $qb->select('*')
790
+            ->from('share')
791
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
792
+            ->andWhere(
793
+                $qb->expr()->orX(
794
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
795
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))
796
+                )
797
+            )
798
+            ->andWhere($qb->expr()->orX(
799
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
800
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
801
+            ))
802
+            ->execute();
803
+
804
+        $shares = [];
805
+        while ($data = $cursor->fetch()) {
806
+            $shares[] = $this->createShare($data);
807
+        }
808
+        $cursor->closeCursor();
809
+
810
+        return $shares;
811
+    }
812
+
813
+    /**
814
+     * Returns whether the given database result can be interpreted as
815
+     * a share with accessible file (not trashed, not deleted)
816
+     */
817
+    private function isAccessibleResult($data) {
818
+        // exclude shares leading to deleted file entries
819
+        if ($data['fileid'] === null || $data['path'] === null) {
820
+            return false;
821
+        }
822
+
823
+        // exclude shares leading to trashbin on home storages
824
+        $pathSections = explode('/', $data['path'], 2);
825
+        // FIXME: would not detect rare md5'd home storage case properly
826
+        if ($pathSections[0] !== 'files'
827
+                && in_array(explode(':', $data['storage_string_id'], 2)[0], ['home', 'object'])) {
828
+            return false;
829
+        }
830
+        return true;
831
+    }
832
+
833
+    /**
834
+     * @inheritdoc
835
+     */
836
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
837
+        /** @var Share[] $shares */
838
+        $shares = [];
839
+
840
+        if ($shareType === IShare::TYPE_USER) {
841
+            //Get shares directly with this user
842
+            $qb = $this->dbConn->getQueryBuilder();
843
+            $qb->select('s.*',
844
+                'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
845
+                'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
846
+                'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
847
+            )
848
+                ->selectAlias('st.id', 'storage_string_id')
849
+                ->from('share', 's')
850
+                ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
851
+                ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
852
+
853
+            // Order by id
854
+            $qb->orderBy('s.id');
855
+
856
+            // Set limit and offset
857
+            if ($limit !== -1) {
858
+                $qb->setMaxResults($limit);
859
+            }
860
+            $qb->setFirstResult($offset);
861
+
862
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)))
863
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
864
+                ->andWhere($qb->expr()->orX(
865
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
866
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
867
+                ));
868
+
869
+            // Filter by node if provided
870
+            if ($node !== null) {
871
+                $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
872
+            }
873
+
874
+            $cursor = $qb->execute();
875
+
876
+            while ($data = $cursor->fetch()) {
877
+                if ($this->isAccessibleResult($data)) {
878
+                    $shares[] = $this->createShare($data);
879
+                }
880
+            }
881
+            $cursor->closeCursor();
882
+        } elseif ($shareType === IShare::TYPE_GROUP) {
883
+            $user = $this->userManager->get($userId);
884
+            $allGroups = $this->groupManager->getUserGroupIds($user);
885
+
886
+            /** @var Share[] $shares2 */
887
+            $shares2 = [];
888
+
889
+            $start = 0;
890
+            while (true) {
891
+                $groups = array_slice($allGroups, $start, 100);
892
+                $start += 100;
893
+
894
+                if ($groups === []) {
895
+                    break;
896
+                }
897
+
898
+                $qb = $this->dbConn->getQueryBuilder();
899
+                $qb->select('s.*',
900
+                    'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
901
+                    'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
902
+                    'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
903
+                )
904
+                    ->selectAlias('st.id', 'storage_string_id')
905
+                    ->from('share', 's')
906
+                    ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
907
+                    ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
908
+                    ->orderBy('s.id')
909
+                    ->setFirstResult(0);
910
+
911
+                if ($limit !== -1) {
912
+                    $qb->setMaxResults($limit - count($shares));
913
+                }
914
+
915
+                // Filter by node if provided
916
+                if ($node !== null) {
917
+                    $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
918
+                }
919
+
920
+
921
+                $groups = array_filter($groups);
922
+
923
+                $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
924
+                    ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
925
+                        $groups,
926
+                        IQueryBuilder::PARAM_STR_ARRAY
927
+                    )))
928
+                    ->andWhere($qb->expr()->orX(
929
+                        $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
930
+                        $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
931
+                    ));
932
+
933
+                $cursor = $qb->execute();
934
+                while ($data = $cursor->fetch()) {
935
+                    if ($offset > 0) {
936
+                        $offset--;
937
+                        continue;
938
+                    }
939
+
940
+                    if ($this->isAccessibleResult($data)) {
941
+                        $shares2[] = $this->createShare($data);
942
+                    }
943
+                }
944
+                $cursor->closeCursor();
945
+            }
946
+
947
+            /*
948 948
 			 * Resolve all group shares to user specific shares
949 949
 			 */
950
-			$shares = $this->resolveGroupShares($shares2, $userId);
951
-		} else {
952
-			throw new BackendError('Invalid backend');
953
-		}
954
-
955
-
956
-		return $shares;
957
-	}
958
-
959
-	/**
960
-	 * Get a share by token
961
-	 *
962
-	 * @param string $token
963
-	 * @return \OCP\Share\IShare
964
-	 * @throws ShareNotFound
965
-	 */
966
-	public function getShareByToken($token) {
967
-		$qb = $this->dbConn->getQueryBuilder();
968
-
969
-		$cursor = $qb->select('*')
970
-			->from('share')
971
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)))
972
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
973
-			->andWhere($qb->expr()->orX(
974
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
975
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
976
-			))
977
-			->execute();
978
-
979
-		$data = $cursor->fetch();
980
-
981
-		if ($data === false) {
982
-			throw new ShareNotFound();
983
-		}
984
-
985
-		try {
986
-			$share = $this->createShare($data);
987
-		} catch (InvalidShare $e) {
988
-			throw new ShareNotFound();
989
-		}
990
-
991
-		return $share;
992
-	}
993
-
994
-	/**
995
-	 * Create a share object from an database row
996
-	 *
997
-	 * @param mixed[] $data
998
-	 * @return \OCP\Share\IShare
999
-	 * @throws InvalidShare
1000
-	 */
1001
-	private function createShare($data) {
1002
-		$share = new Share($this->rootFolder, $this->userManager);
1003
-		$share->setId((int)$data['id'])
1004
-			->setShareType((int)$data['share_type'])
1005
-			->setPermissions((int)$data['permissions'])
1006
-			->setTarget($data['file_target'])
1007
-			->setNote($data['note'])
1008
-			->setMailSend((bool)$data['mail_send'])
1009
-			->setStatus((int)$data['accepted'])
1010
-			->setLabel($data['label']);
1011
-
1012
-		$shareTime = new \DateTime();
1013
-		$shareTime->setTimestamp((int)$data['stime']);
1014
-		$share->setShareTime($shareTime);
1015
-
1016
-		if ($share->getShareType() === IShare::TYPE_USER) {
1017
-			$share->setSharedWith($data['share_with']);
1018
-			$user = $this->userManager->get($data['share_with']);
1019
-			if ($user !== null) {
1020
-				$share->setSharedWithDisplayName($user->getDisplayName());
1021
-			}
1022
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1023
-			$share->setSharedWith($data['share_with']);
1024
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
1025
-			$share->setPassword($data['password']);
1026
-			$share->setSendPasswordByTalk((bool)$data['password_by_talk']);
1027
-			$share->setToken($data['token']);
1028
-		}
1029
-
1030
-		$share->setSharedBy($data['uid_initiator']);
1031
-		$share->setShareOwner($data['uid_owner']);
1032
-
1033
-		$share->setNodeId((int)$data['file_source']);
1034
-		$share->setNodeType($data['item_type']);
1035
-
1036
-		if ($data['expiration'] !== null) {
1037
-			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
1038
-			$share->setExpirationDate($expiration);
1039
-		}
1040
-
1041
-		if (isset($data['f_permissions'])) {
1042
-			$entryData = $data;
1043
-			$entryData['permissions'] = $entryData['f_permissions'];
1044
-			$entryData['parent'] = $entryData['f_parent'];
1045
-			$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
1046
-				\OC::$server->getMimeTypeLoader()));
1047
-		}
1048
-
1049
-		$share->setProviderId($this->identifier());
1050
-		$share->setHideDownload((int)$data['hide_download'] === 1);
1051
-
1052
-		return $share;
1053
-	}
1054
-
1055
-	/**
1056
-	 * @param Share[] $shares
1057
-	 * @param $userId
1058
-	 * @return Share[] The updates shares if no update is found for a share return the original
1059
-	 */
1060
-	private function resolveGroupShares($shares, $userId) {
1061
-		$result = [];
1062
-
1063
-		$start = 0;
1064
-		while (true) {
1065
-			/** @var Share[] $shareSlice */
1066
-			$shareSlice = array_slice($shares, $start, 100);
1067
-			$start += 100;
1068
-
1069
-			if ($shareSlice === []) {
1070
-				break;
1071
-			}
1072
-
1073
-			/** @var int[] $ids */
1074
-			$ids = [];
1075
-			/** @var Share[] $shareMap */
1076
-			$shareMap = [];
1077
-
1078
-			foreach ($shareSlice as $share) {
1079
-				$ids[] = (int)$share->getId();
1080
-				$shareMap[$share->getId()] = $share;
1081
-			}
1082
-
1083
-			$qb = $this->dbConn->getQueryBuilder();
1084
-
1085
-			$query = $qb->select('*')
1086
-				->from('share')
1087
-				->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1088
-				->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
1089
-				->andWhere($qb->expr()->orX(
1090
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1091
-					$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1092
-				));
1093
-
1094
-			$stmt = $query->execute();
1095
-
1096
-			while ($data = $stmt->fetch()) {
1097
-				$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1098
-				$shareMap[$data['parent']]->setStatus((int)$data['accepted']);
1099
-				$shareMap[$data['parent']]->setTarget($data['file_target']);
1100
-				$shareMap[$data['parent']]->setParent($data['parent']);
1101
-			}
1102
-
1103
-			$stmt->closeCursor();
1104
-
1105
-			foreach ($shareMap as $share) {
1106
-				$result[] = $share;
1107
-			}
1108
-		}
1109
-
1110
-		return $result;
1111
-	}
1112
-
1113
-	/**
1114
-	 * A user is deleted from the system
1115
-	 * So clean up the relevant shares.
1116
-	 *
1117
-	 * @param string $uid
1118
-	 * @param int $shareType
1119
-	 */
1120
-	public function userDeleted($uid, $shareType) {
1121
-		$qb = $this->dbConn->getQueryBuilder();
1122
-
1123
-		$qb->delete('share');
1124
-
1125
-		if ($shareType === IShare::TYPE_USER) {
1126
-			/*
950
+            $shares = $this->resolveGroupShares($shares2, $userId);
951
+        } else {
952
+            throw new BackendError('Invalid backend');
953
+        }
954
+
955
+
956
+        return $shares;
957
+    }
958
+
959
+    /**
960
+     * Get a share by token
961
+     *
962
+     * @param string $token
963
+     * @return \OCP\Share\IShare
964
+     * @throws ShareNotFound
965
+     */
966
+    public function getShareByToken($token) {
967
+        $qb = $this->dbConn->getQueryBuilder();
968
+
969
+        $cursor = $qb->select('*')
970
+            ->from('share')
971
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)))
972
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
973
+            ->andWhere($qb->expr()->orX(
974
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
975
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
976
+            ))
977
+            ->execute();
978
+
979
+        $data = $cursor->fetch();
980
+
981
+        if ($data === false) {
982
+            throw new ShareNotFound();
983
+        }
984
+
985
+        try {
986
+            $share = $this->createShare($data);
987
+        } catch (InvalidShare $e) {
988
+            throw new ShareNotFound();
989
+        }
990
+
991
+        return $share;
992
+    }
993
+
994
+    /**
995
+     * Create a share object from an database row
996
+     *
997
+     * @param mixed[] $data
998
+     * @return \OCP\Share\IShare
999
+     * @throws InvalidShare
1000
+     */
1001
+    private function createShare($data) {
1002
+        $share = new Share($this->rootFolder, $this->userManager);
1003
+        $share->setId((int)$data['id'])
1004
+            ->setShareType((int)$data['share_type'])
1005
+            ->setPermissions((int)$data['permissions'])
1006
+            ->setTarget($data['file_target'])
1007
+            ->setNote($data['note'])
1008
+            ->setMailSend((bool)$data['mail_send'])
1009
+            ->setStatus((int)$data['accepted'])
1010
+            ->setLabel($data['label']);
1011
+
1012
+        $shareTime = new \DateTime();
1013
+        $shareTime->setTimestamp((int)$data['stime']);
1014
+        $share->setShareTime($shareTime);
1015
+
1016
+        if ($share->getShareType() === IShare::TYPE_USER) {
1017
+            $share->setSharedWith($data['share_with']);
1018
+            $user = $this->userManager->get($data['share_with']);
1019
+            if ($user !== null) {
1020
+                $share->setSharedWithDisplayName($user->getDisplayName());
1021
+            }
1022
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1023
+            $share->setSharedWith($data['share_with']);
1024
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
1025
+            $share->setPassword($data['password']);
1026
+            $share->setSendPasswordByTalk((bool)$data['password_by_talk']);
1027
+            $share->setToken($data['token']);
1028
+        }
1029
+
1030
+        $share->setSharedBy($data['uid_initiator']);
1031
+        $share->setShareOwner($data['uid_owner']);
1032
+
1033
+        $share->setNodeId((int)$data['file_source']);
1034
+        $share->setNodeType($data['item_type']);
1035
+
1036
+        if ($data['expiration'] !== null) {
1037
+            $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
1038
+            $share->setExpirationDate($expiration);
1039
+        }
1040
+
1041
+        if (isset($data['f_permissions'])) {
1042
+            $entryData = $data;
1043
+            $entryData['permissions'] = $entryData['f_permissions'];
1044
+            $entryData['parent'] = $entryData['f_parent'];
1045
+            $share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
1046
+                \OC::$server->getMimeTypeLoader()));
1047
+        }
1048
+
1049
+        $share->setProviderId($this->identifier());
1050
+        $share->setHideDownload((int)$data['hide_download'] === 1);
1051
+
1052
+        return $share;
1053
+    }
1054
+
1055
+    /**
1056
+     * @param Share[] $shares
1057
+     * @param $userId
1058
+     * @return Share[] The updates shares if no update is found for a share return the original
1059
+     */
1060
+    private function resolveGroupShares($shares, $userId) {
1061
+        $result = [];
1062
+
1063
+        $start = 0;
1064
+        while (true) {
1065
+            /** @var Share[] $shareSlice */
1066
+            $shareSlice = array_slice($shares, $start, 100);
1067
+            $start += 100;
1068
+
1069
+            if ($shareSlice === []) {
1070
+                break;
1071
+            }
1072
+
1073
+            /** @var int[] $ids */
1074
+            $ids = [];
1075
+            /** @var Share[] $shareMap */
1076
+            $shareMap = [];
1077
+
1078
+            foreach ($shareSlice as $share) {
1079
+                $ids[] = (int)$share->getId();
1080
+                $shareMap[$share->getId()] = $share;
1081
+            }
1082
+
1083
+            $qb = $this->dbConn->getQueryBuilder();
1084
+
1085
+            $query = $qb->select('*')
1086
+                ->from('share')
1087
+                ->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1088
+                ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
1089
+                ->andWhere($qb->expr()->orX(
1090
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1091
+                    $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1092
+                ));
1093
+
1094
+            $stmt = $query->execute();
1095
+
1096
+            while ($data = $stmt->fetch()) {
1097
+                $shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
1098
+                $shareMap[$data['parent']]->setStatus((int)$data['accepted']);
1099
+                $shareMap[$data['parent']]->setTarget($data['file_target']);
1100
+                $shareMap[$data['parent']]->setParent($data['parent']);
1101
+            }
1102
+
1103
+            $stmt->closeCursor();
1104
+
1105
+            foreach ($shareMap as $share) {
1106
+                $result[] = $share;
1107
+            }
1108
+        }
1109
+
1110
+        return $result;
1111
+    }
1112
+
1113
+    /**
1114
+     * A user is deleted from the system
1115
+     * So clean up the relevant shares.
1116
+     *
1117
+     * @param string $uid
1118
+     * @param int $shareType
1119
+     */
1120
+    public function userDeleted($uid, $shareType) {
1121
+        $qb = $this->dbConn->getQueryBuilder();
1122
+
1123
+        $qb->delete('share');
1124
+
1125
+        if ($shareType === IShare::TYPE_USER) {
1126
+            /*
1127 1127
 			 * Delete all user shares that are owned by this user
1128 1128
 			 * or that are received by this user
1129 1129
 			 */
1130 1130
 
1131
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)));
1131
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)));
1132 1132
 
1133
-			$qb->andWhere(
1134
-				$qb->expr()->orX(
1135
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1136
-					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1137
-				)
1138
-			);
1139
-		} elseif ($shareType === IShare::TYPE_GROUP) {
1140
-			/*
1133
+            $qb->andWhere(
1134
+                $qb->expr()->orX(
1135
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1136
+                    $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1137
+                )
1138
+            );
1139
+        } elseif ($shareType === IShare::TYPE_GROUP) {
1140
+            /*
1141 1141
 			 * Delete all group shares that are owned by this user
1142 1142
 			 * Or special user group shares that are received by this user
1143 1143
 			 */
1144
-			$qb->where(
1145
-				$qb->expr()->andX(
1146
-					$qb->expr()->orX(
1147
-						$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
1148
-						$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
1149
-					),
1150
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
1151
-				)
1152
-			);
1153
-
1154
-			$qb->orWhere(
1155
-				$qb->expr()->andX(
1156
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
1157
-					$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1158
-				)
1159
-			);
1160
-		} elseif ($shareType === IShare::TYPE_LINK) {
1161
-			/*
1144
+            $qb->where(
1145
+                $qb->expr()->andX(
1146
+                    $qb->expr()->orX(
1147
+                        $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
1148
+                        $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))
1149
+                    ),
1150
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
1151
+                )
1152
+            );
1153
+
1154
+            $qb->orWhere(
1155
+                $qb->expr()->andX(
1156
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)),
1157
+                    $qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
1158
+                )
1159
+            );
1160
+        } elseif ($shareType === IShare::TYPE_LINK) {
1161
+            /*
1162 1162
 			 * Delete all link shares owned by this user.
1163 1163
 			 * And all link shares initiated by this user (until #22327 is in)
1164 1164
 			 */
1165
-			$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)));
1166
-
1167
-			$qb->andWhere(
1168
-				$qb->expr()->orX(
1169
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1170
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
1171
-				)
1172
-			);
1173
-		} else {
1174
-			\OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType));
1175
-			return;
1176
-		}
1177
-
1178
-		$qb->execute();
1179
-	}
1180
-
1181
-	/**
1182
-	 * Delete all shares received by this group. As well as any custom group
1183
-	 * shares for group members.
1184
-	 *
1185
-	 * @param string $gid
1186
-	 */
1187
-	public function groupDeleted($gid) {
1188
-		/*
1165
+            $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)));
1166
+
1167
+            $qb->andWhere(
1168
+                $qb->expr()->orX(
1169
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
1170
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
1171
+                )
1172
+            );
1173
+        } else {
1174
+            \OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType));
1175
+            return;
1176
+        }
1177
+
1178
+        $qb->execute();
1179
+    }
1180
+
1181
+    /**
1182
+     * Delete all shares received by this group. As well as any custom group
1183
+     * shares for group members.
1184
+     *
1185
+     * @param string $gid
1186
+     */
1187
+    public function groupDeleted($gid) {
1188
+        /*
1189 1189
 		 * First delete all custom group shares for group members
1190 1190
 		 */
1191
-		$qb = $this->dbConn->getQueryBuilder();
1192
-		$qb->select('id')
1193
-			->from('share')
1194
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1195
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1196
-
1197
-		$cursor = $qb->execute();
1198
-		$ids = [];
1199
-		while ($row = $cursor->fetch()) {
1200
-			$ids[] = (int)$row['id'];
1201
-		}
1202
-		$cursor->closeCursor();
1203
-
1204
-		if (!empty($ids)) {
1205
-			$chunks = array_chunk($ids, 100);
1206
-			foreach ($chunks as $chunk) {
1207
-				$qb->delete('share')
1208
-					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1209
-					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1210
-				$qb->execute();
1211
-			}
1212
-		}
1213
-
1214
-		/*
1191
+        $qb = $this->dbConn->getQueryBuilder();
1192
+        $qb->select('id')
1193
+            ->from('share')
1194
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1195
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1196
+
1197
+        $cursor = $qb->execute();
1198
+        $ids = [];
1199
+        while ($row = $cursor->fetch()) {
1200
+            $ids[] = (int)$row['id'];
1201
+        }
1202
+        $cursor->closeCursor();
1203
+
1204
+        if (!empty($ids)) {
1205
+            $chunks = array_chunk($ids, 100);
1206
+            foreach ($chunks as $chunk) {
1207
+                $qb->delete('share')
1208
+                    ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1209
+                    ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1210
+                $qb->execute();
1211
+            }
1212
+        }
1213
+
1214
+        /*
1215 1215
 		 * Now delete all the group shares
1216 1216
 		 */
1217
-		$qb = $this->dbConn->getQueryBuilder();
1218
-		$qb->delete('share')
1219
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1220
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1221
-		$qb->execute();
1222
-	}
1223
-
1224
-	/**
1225
-	 * Delete custom group shares to this group for this user
1226
-	 *
1227
-	 * @param string $uid
1228
-	 * @param string $gid
1229
-	 */
1230
-	public function userDeletedFromGroup($uid, $gid) {
1231
-		/*
1217
+        $qb = $this->dbConn->getQueryBuilder();
1218
+        $qb->delete('share')
1219
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1220
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1221
+        $qb->execute();
1222
+    }
1223
+
1224
+    /**
1225
+     * Delete custom group shares to this group for this user
1226
+     *
1227
+     * @param string $uid
1228
+     * @param string $gid
1229
+     */
1230
+    public function userDeletedFromGroup($uid, $gid) {
1231
+        /*
1232 1232
 		 * Get all group shares
1233 1233
 		 */
1234
-		$qb = $this->dbConn->getQueryBuilder();
1235
-		$qb->select('id')
1236
-			->from('share')
1237
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1238
-			->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1239
-
1240
-		$cursor = $qb->execute();
1241
-		$ids = [];
1242
-		while ($row = $cursor->fetch()) {
1243
-			$ids[] = (int)$row['id'];
1244
-		}
1245
-		$cursor->closeCursor();
1246
-
1247
-		if (!empty($ids)) {
1248
-			$chunks = array_chunk($ids, 100);
1249
-			foreach ($chunks as $chunk) {
1250
-				/*
1234
+        $qb = $this->dbConn->getQueryBuilder();
1235
+        $qb->select('id')
1236
+            ->from('share')
1237
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)))
1238
+            ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid)));
1239
+
1240
+        $cursor = $qb->execute();
1241
+        $ids = [];
1242
+        while ($row = $cursor->fetch()) {
1243
+            $ids[] = (int)$row['id'];
1244
+        }
1245
+        $cursor->closeCursor();
1246
+
1247
+        if (!empty($ids)) {
1248
+            $chunks = array_chunk($ids, 100);
1249
+            foreach ($chunks as $chunk) {
1250
+                /*
1251 1251
 				 * Delete all special shares wit this users for the found group shares
1252 1252
 				 */
1253
-				$qb->delete('share')
1254
-					->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1255
-					->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1256
-					->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1257
-				$qb->execute();
1258
-			}
1259
-		}
1260
-	}
1261
-
1262
-	/**
1263
-	 * @inheritdoc
1264
-	 */
1265
-	public function getAccessList($nodes, $currentAccess) {
1266
-		$ids = [];
1267
-		foreach ($nodes as $node) {
1268
-			$ids[] = $node->getId();
1269
-		}
1270
-
1271
-		$qb = $this->dbConn->getQueryBuilder();
1272
-
1273
-		$or = $qb->expr()->orX(
1274
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
1275
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
1276
-			$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
1277
-		);
1278
-
1279
-		if ($currentAccess) {
1280
-			$or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)));
1281
-		}
1282
-
1283
-		$qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1284
-			->from('share')
1285
-			->where(
1286
-				$or
1287
-			)
1288
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1289
-			->andWhere($qb->expr()->orX(
1290
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1291
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1292
-			));
1293
-		$cursor = $qb->execute();
1294
-
1295
-		$users = [];
1296
-		$link = false;
1297
-		while ($row = $cursor->fetch()) {
1298
-			$type = (int)$row['share_type'];
1299
-			if ($type === IShare::TYPE_USER) {
1300
-				$uid = $row['share_with'];
1301
-				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1302
-				$users[$uid][$row['id']] = $row;
1303
-			} elseif ($type === IShare::TYPE_GROUP) {
1304
-				$gid = $row['share_with'];
1305
-				$group = $this->groupManager->get($gid);
1306
-
1307
-				if ($group === null) {
1308
-					continue;
1309
-				}
1310
-
1311
-				$userList = $group->getUsers();
1312
-				foreach ($userList as $user) {
1313
-					$uid = $user->getUID();
1314
-					$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1315
-					$users[$uid][$row['id']] = $row;
1316
-				}
1317
-			} elseif ($type === IShare::TYPE_LINK) {
1318
-				$link = true;
1319
-			} elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) {
1320
-				$uid = $row['share_with'];
1321
-				$users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1322
-				$users[$uid][$row['id']] = $row;
1323
-			}
1324
-		}
1325
-		$cursor->closeCursor();
1326
-
1327
-		if ($currentAccess === true) {
1328
-			$users = array_map([$this, 'filterSharesOfUser'], $users);
1329
-			$users = array_filter($users);
1330
-		} else {
1331
-			$users = array_keys($users);
1332
-		}
1333
-
1334
-		return ['users' => $users, 'public' => $link];
1335
-	}
1336
-
1337
-	/**
1338
-	 * For each user the path with the fewest slashes is returned
1339
-	 * @param array $shares
1340
-	 * @return array
1341
-	 */
1342
-	protected function filterSharesOfUser(array $shares) {
1343
-		// Group shares when the user has a share exception
1344
-		foreach ($shares as $id => $share) {
1345
-			$type = (int) $share['share_type'];
1346
-			$permissions = (int) $share['permissions'];
1347
-
1348
-			if ($type === IShare::TYPE_USERGROUP) {
1349
-				unset($shares[$share['parent']]);
1350
-
1351
-				if ($permissions === 0) {
1352
-					unset($shares[$id]);
1353
-				}
1354
-			}
1355
-		}
1356
-
1357
-		$best = [];
1358
-		$bestDepth = 0;
1359
-		foreach ($shares as $id => $share) {
1360
-			$depth = substr_count($share['file_target'], '/');
1361
-			if (empty($best) || $depth < $bestDepth) {
1362
-				$bestDepth = $depth;
1363
-				$best = [
1364
-					'node_id' => $share['file_source'],
1365
-					'node_path' => $share['file_target'],
1366
-				];
1367
-			}
1368
-		}
1369
-
1370
-		return $best;
1371
-	}
1372
-
1373
-	/**
1374
-	 * propagate notes to the recipients
1375
-	 *
1376
-	 * @param IShare $share
1377
-	 * @throws \OCP\Files\NotFoundException
1378
-	 */
1379
-	private function propagateNote(IShare $share) {
1380
-		if ($share->getShareType() === IShare::TYPE_USER) {
1381
-			$user = $this->userManager->get($share->getSharedWith());
1382
-			$this->sendNote([$user], $share);
1383
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1384
-			$group = $this->groupManager->get($share->getSharedWith());
1385
-			$groupMembers = $group->getUsers();
1386
-			$this->sendNote($groupMembers, $share);
1387
-		}
1388
-	}
1389
-
1390
-	/**
1391
-	 * send note by mail
1392
-	 *
1393
-	 * @param array $recipients
1394
-	 * @param IShare $share
1395
-	 * @throws \OCP\Files\NotFoundException
1396
-	 */
1397
-	private function sendNote(array $recipients, IShare $share) {
1398
-		$toListByLanguage = [];
1399
-
1400
-		foreach ($recipients as $recipient) {
1401
-			/** @var IUser $recipient */
1402
-			$email = $recipient->getEMailAddress();
1403
-			if ($email) {
1404
-				$language = $this->l10nFactory->getUserLanguage($recipient);
1405
-				if (!isset($toListByLanguage[$language])) {
1406
-					$toListByLanguage[$language] = [];
1407
-				}
1408
-				$toListByLanguage[$language][$email] = $recipient->getDisplayName();
1409
-			}
1410
-		}
1411
-
1412
-		if (empty($toListByLanguage)) {
1413
-			return;
1414
-		}
1415
-
1416
-		foreach ($toListByLanguage as $l10n => $toList) {
1417
-			$filename = $share->getNode()->getName();
1418
-			$initiator = $share->getSharedBy();
1419
-			$note = $share->getNote();
1420
-
1421
-			$l = $this->l10nFactory->get('lib', $l10n);
1422
-
1423
-			$initiatorUser = $this->userManager->get($initiator);
1424
-			$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1425
-			$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
1426
-			$plainHeading = $l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]);
1427
-			$htmlHeading = $l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]);
1428
-			$message = $this->mailer->createMessage();
1429
-
1430
-			$emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
1431
-
1432
-			$emailTemplate->setSubject($l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName]));
1433
-			$emailTemplate->addHeader();
1434
-			$emailTemplate->addHeading($htmlHeading, $plainHeading);
1435
-			$emailTemplate->addBodyText(htmlspecialchars($note), $note);
1436
-
1437
-			$link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
1438
-			$emailTemplate->addBodyButton(
1439
-				$l->t('Open »%s«', [$filename]),
1440
-				$link
1441
-			);
1442
-
1443
-
1444
-			// The "From" contains the sharers name
1445
-			$instanceName = $this->defaults->getName();
1446
-			$senderName = $l->t(
1447
-				'%1$s via %2$s',
1448
-				[
1449
-					$initiatorDisplayName,
1450
-					$instanceName
1451
-				]
1452
-			);
1453
-			$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1454
-			if ($initiatorEmailAddress !== null) {
1455
-				$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1456
-				$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1457
-			} else {
1458
-				$emailTemplate->addFooter();
1459
-			}
1460
-
1461
-			if (count($toList) === 1) {
1462
-				$message->setTo($toList);
1463
-			} else {
1464
-				$message->setTo([]);
1465
-				$message->setBcc($toList);
1466
-			}
1467
-			$message->useTemplate($emailTemplate);
1468
-			$this->mailer->send($message);
1469
-		}
1470
-	}
1471
-
1472
-	public function getAllShares(): iterable {
1473
-		$qb = $this->dbConn->getQueryBuilder();
1474
-
1475
-		$qb->select('*')
1476
-			->from('share')
1477
-			->where(
1478
-				$qb->expr()->orX(
1479
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_USER)),
1480
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_GROUP)),
1481
-					$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_LINK))
1482
-				)
1483
-			);
1484
-
1485
-		$cursor = $qb->execute();
1486
-		while ($data = $cursor->fetch()) {
1487
-			try {
1488
-				$share = $this->createShare($data);
1489
-			} catch (InvalidShare $e) {
1490
-				continue;
1491
-			}
1492
-
1493
-			yield $share;
1494
-		}
1495
-		$cursor->closeCursor();
1496
-	}
1253
+                $qb->delete('share')
1254
+                    ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
1255
+                    ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid)))
1256
+                    ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)));
1257
+                $qb->execute();
1258
+            }
1259
+        }
1260
+    }
1261
+
1262
+    /**
1263
+     * @inheritdoc
1264
+     */
1265
+    public function getAccessList($nodes, $currentAccess) {
1266
+        $ids = [];
1267
+        foreach ($nodes as $node) {
1268
+            $ids[] = $node->getId();
1269
+        }
1270
+
1271
+        $qb = $this->dbConn->getQueryBuilder();
1272
+
1273
+        $or = $qb->expr()->orX(
1274
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
1275
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
1276
+            $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))
1277
+        );
1278
+
1279
+        if ($currentAccess) {
1280
+            $or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)));
1281
+        }
1282
+
1283
+        $qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions')
1284
+            ->from('share')
1285
+            ->where(
1286
+                $or
1287
+            )
1288
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1289
+            ->andWhere($qb->expr()->orX(
1290
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1291
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1292
+            ));
1293
+        $cursor = $qb->execute();
1294
+
1295
+        $users = [];
1296
+        $link = false;
1297
+        while ($row = $cursor->fetch()) {
1298
+            $type = (int)$row['share_type'];
1299
+            if ($type === IShare::TYPE_USER) {
1300
+                $uid = $row['share_with'];
1301
+                $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1302
+                $users[$uid][$row['id']] = $row;
1303
+            } elseif ($type === IShare::TYPE_GROUP) {
1304
+                $gid = $row['share_with'];
1305
+                $group = $this->groupManager->get($gid);
1306
+
1307
+                if ($group === null) {
1308
+                    continue;
1309
+                }
1310
+
1311
+                $userList = $group->getUsers();
1312
+                foreach ($userList as $user) {
1313
+                    $uid = $user->getUID();
1314
+                    $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1315
+                    $users[$uid][$row['id']] = $row;
1316
+                }
1317
+            } elseif ($type === IShare::TYPE_LINK) {
1318
+                $link = true;
1319
+            } elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) {
1320
+                $uid = $row['share_with'];
1321
+                $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
1322
+                $users[$uid][$row['id']] = $row;
1323
+            }
1324
+        }
1325
+        $cursor->closeCursor();
1326
+
1327
+        if ($currentAccess === true) {
1328
+            $users = array_map([$this, 'filterSharesOfUser'], $users);
1329
+            $users = array_filter($users);
1330
+        } else {
1331
+            $users = array_keys($users);
1332
+        }
1333
+
1334
+        return ['users' => $users, 'public' => $link];
1335
+    }
1336
+
1337
+    /**
1338
+     * For each user the path with the fewest slashes is returned
1339
+     * @param array $shares
1340
+     * @return array
1341
+     */
1342
+    protected function filterSharesOfUser(array $shares) {
1343
+        // Group shares when the user has a share exception
1344
+        foreach ($shares as $id => $share) {
1345
+            $type = (int) $share['share_type'];
1346
+            $permissions = (int) $share['permissions'];
1347
+
1348
+            if ($type === IShare::TYPE_USERGROUP) {
1349
+                unset($shares[$share['parent']]);
1350
+
1351
+                if ($permissions === 0) {
1352
+                    unset($shares[$id]);
1353
+                }
1354
+            }
1355
+        }
1356
+
1357
+        $best = [];
1358
+        $bestDepth = 0;
1359
+        foreach ($shares as $id => $share) {
1360
+            $depth = substr_count($share['file_target'], '/');
1361
+            if (empty($best) || $depth < $bestDepth) {
1362
+                $bestDepth = $depth;
1363
+                $best = [
1364
+                    'node_id' => $share['file_source'],
1365
+                    'node_path' => $share['file_target'],
1366
+                ];
1367
+            }
1368
+        }
1369
+
1370
+        return $best;
1371
+    }
1372
+
1373
+    /**
1374
+     * propagate notes to the recipients
1375
+     *
1376
+     * @param IShare $share
1377
+     * @throws \OCP\Files\NotFoundException
1378
+     */
1379
+    private function propagateNote(IShare $share) {
1380
+        if ($share->getShareType() === IShare::TYPE_USER) {
1381
+            $user = $this->userManager->get($share->getSharedWith());
1382
+            $this->sendNote([$user], $share);
1383
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1384
+            $group = $this->groupManager->get($share->getSharedWith());
1385
+            $groupMembers = $group->getUsers();
1386
+            $this->sendNote($groupMembers, $share);
1387
+        }
1388
+    }
1389
+
1390
+    /**
1391
+     * send note by mail
1392
+     *
1393
+     * @param array $recipients
1394
+     * @param IShare $share
1395
+     * @throws \OCP\Files\NotFoundException
1396
+     */
1397
+    private function sendNote(array $recipients, IShare $share) {
1398
+        $toListByLanguage = [];
1399
+
1400
+        foreach ($recipients as $recipient) {
1401
+            /** @var IUser $recipient */
1402
+            $email = $recipient->getEMailAddress();
1403
+            if ($email) {
1404
+                $language = $this->l10nFactory->getUserLanguage($recipient);
1405
+                if (!isset($toListByLanguage[$language])) {
1406
+                    $toListByLanguage[$language] = [];
1407
+                }
1408
+                $toListByLanguage[$language][$email] = $recipient->getDisplayName();
1409
+            }
1410
+        }
1411
+
1412
+        if (empty($toListByLanguage)) {
1413
+            return;
1414
+        }
1415
+
1416
+        foreach ($toListByLanguage as $l10n => $toList) {
1417
+            $filename = $share->getNode()->getName();
1418
+            $initiator = $share->getSharedBy();
1419
+            $note = $share->getNote();
1420
+
1421
+            $l = $this->l10nFactory->get('lib', $l10n);
1422
+
1423
+            $initiatorUser = $this->userManager->get($initiator);
1424
+            $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
1425
+            $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
1426
+            $plainHeading = $l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]);
1427
+            $htmlHeading = $l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]);
1428
+            $message = $this->mailer->createMessage();
1429
+
1430
+            $emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote');
1431
+
1432
+            $emailTemplate->setSubject($l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName]));
1433
+            $emailTemplate->addHeader();
1434
+            $emailTemplate->addHeading($htmlHeading, $plainHeading);
1435
+            $emailTemplate->addBodyText(htmlspecialchars($note), $note);
1436
+
1437
+            $link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]);
1438
+            $emailTemplate->addBodyButton(
1439
+                $l->t('Open »%s«', [$filename]),
1440
+                $link
1441
+            );
1442
+
1443
+
1444
+            // The "From" contains the sharers name
1445
+            $instanceName = $this->defaults->getName();
1446
+            $senderName = $l->t(
1447
+                '%1$s via %2$s',
1448
+                [
1449
+                    $initiatorDisplayName,
1450
+                    $instanceName
1451
+                ]
1452
+            );
1453
+            $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
1454
+            if ($initiatorEmailAddress !== null) {
1455
+                $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
1456
+                $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
1457
+            } else {
1458
+                $emailTemplate->addFooter();
1459
+            }
1460
+
1461
+            if (count($toList) === 1) {
1462
+                $message->setTo($toList);
1463
+            } else {
1464
+                $message->setTo([]);
1465
+                $message->setBcc($toList);
1466
+            }
1467
+            $message->useTemplate($emailTemplate);
1468
+            $this->mailer->send($message);
1469
+        }
1470
+    }
1471
+
1472
+    public function getAllShares(): iterable {
1473
+        $qb = $this->dbConn->getQueryBuilder();
1474
+
1475
+        $qb->select('*')
1476
+            ->from('share')
1477
+            ->where(
1478
+                $qb->expr()->orX(
1479
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_USER)),
1480
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_GROUP)),
1481
+                    $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_LINK))
1482
+                )
1483
+            );
1484
+
1485
+        $cursor = $qb->execute();
1486
+        while ($data = $cursor->fetch()) {
1487
+            try {
1488
+                $share = $this->createShare($data);
1489
+            } catch (InvalidShare $e) {
1490
+                continue;
1491
+            }
1492
+
1493
+            yield $share;
1494
+        }
1495
+        $cursor->closeCursor();
1496
+    }
1497 1497
 }
Please login to merge, or discard this patch.
lib/private/Share20/Manager.php 1 patch
Indentation   +1780 added lines, -1780 removed lines patch added patch discarded remove patch
@@ -76,1807 +76,1807 @@
 block discarded – undo
76 76
  */
77 77
 class Manager implements IManager {
78 78
 
79
-	/** @var IProviderFactory */
80
-	private $factory;
81
-	/** @var ILogger */
82
-	private $logger;
83
-	/** @var IConfig */
84
-	private $config;
85
-	/** @var ISecureRandom */
86
-	private $secureRandom;
87
-	/** @var IHasher */
88
-	private $hasher;
89
-	/** @var IMountManager */
90
-	private $mountManager;
91
-	/** @var IGroupManager */
92
-	private $groupManager;
93
-	/** @var IL10N */
94
-	private $l;
95
-	/** @var IFactory */
96
-	private $l10nFactory;
97
-	/** @var IUserManager */
98
-	private $userManager;
99
-	/** @var IRootFolder */
100
-	private $rootFolder;
101
-	/** @var CappedMemoryCache */
102
-	private $sharingDisabledForUsersCache;
103
-	/** @var EventDispatcherInterface */
104
-	private $legacyDispatcher;
105
-	/** @var LegacyHooks */
106
-	private $legacyHooks;
107
-	/** @var IMailer */
108
-	private $mailer;
109
-	/** @var IURLGenerator */
110
-	private $urlGenerator;
111
-	/** @var \OC_Defaults */
112
-	private $defaults;
113
-	/** @var IEventDispatcher */
114
-	private $dispatcher;
115
-
116
-
117
-	/**
118
-	 * Manager constructor.
119
-	 *
120
-	 * @param ILogger $logger
121
-	 * @param IConfig $config
122
-	 * @param ISecureRandom $secureRandom
123
-	 * @param IHasher $hasher
124
-	 * @param IMountManager $mountManager
125
-	 * @param IGroupManager $groupManager
126
-	 * @param IL10N $l
127
-	 * @param IFactory $l10nFactory
128
-	 * @param IProviderFactory $factory
129
-	 * @param IUserManager $userManager
130
-	 * @param IRootFolder $rootFolder
131
-	 * @param EventDispatcherInterface $eventDispatcher
132
-	 * @param IMailer $mailer
133
-	 * @param IURLGenerator $urlGenerator
134
-	 * @param \OC_Defaults $defaults
135
-	 */
136
-	public function __construct(
137
-			ILogger $logger,
138
-			IConfig $config,
139
-			ISecureRandom $secureRandom,
140
-			IHasher $hasher,
141
-			IMountManager $mountManager,
142
-			IGroupManager $groupManager,
143
-			IL10N $l,
144
-			IFactory $l10nFactory,
145
-			IProviderFactory $factory,
146
-			IUserManager $userManager,
147
-			IRootFolder $rootFolder,
148
-			EventDispatcherInterface $legacyDispatcher,
149
-			IMailer $mailer,
150
-			IURLGenerator $urlGenerator,
151
-			\OC_Defaults $defaults,
152
-			IEventDispatcher $dispatcher
153
-	) {
154
-		$this->logger = $logger;
155
-		$this->config = $config;
156
-		$this->secureRandom = $secureRandom;
157
-		$this->hasher = $hasher;
158
-		$this->mountManager = $mountManager;
159
-		$this->groupManager = $groupManager;
160
-		$this->l = $l;
161
-		$this->l10nFactory = $l10nFactory;
162
-		$this->factory = $factory;
163
-		$this->userManager = $userManager;
164
-		$this->rootFolder = $rootFolder;
165
-		$this->legacyDispatcher = $legacyDispatcher;
166
-		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
167
-		$this->legacyHooks = new LegacyHooks($this->legacyDispatcher);
168
-		$this->mailer = $mailer;
169
-		$this->urlGenerator = $urlGenerator;
170
-		$this->defaults = $defaults;
171
-		$this->dispatcher = $dispatcher;
172
-	}
173
-
174
-	/**
175
-	 * Convert from a full share id to a tuple (providerId, shareId)
176
-	 *
177
-	 * @param string $id
178
-	 * @return string[]
179
-	 */
180
-	private function splitFullId($id) {
181
-		return explode(':', $id, 2);
182
-	}
183
-
184
-	/**
185
-	 * Verify if a password meets all requirements
186
-	 *
187
-	 * @param string $password
188
-	 * @throws \Exception
189
-	 */
190
-	protected function verifyPassword($password) {
191
-		if ($password === null) {
192
-			// No password is set, check if this is allowed.
193
-			if ($this->shareApiLinkEnforcePassword()) {
194
-				throw new \InvalidArgumentException('Passwords are enforced for link shares');
195
-			}
196
-
197
-			return;
198
-		}
199
-
200
-		// Let others verify the password
201
-		try {
202
-			$this->legacyDispatcher->dispatch(new ValidatePasswordPolicyEvent($password));
203
-		} catch (HintException $e) {
204
-			throw new \Exception($e->getHint());
205
-		}
206
-	}
207
-
208
-	/**
209
-	 * Check for generic requirements before creating a share
210
-	 *
211
-	 * @param IShare $share
212
-	 * @throws \InvalidArgumentException
213
-	 * @throws GenericShareException
214
-	 *
215
-	 * @suppress PhanUndeclaredClassMethod
216
-	 */
217
-	protected function generalCreateChecks(IShare $share) {
218
-		if ($share->getShareType() === IShare::TYPE_USER) {
219
-			// We expect a valid user as sharedWith for user shares
220
-			if (!$this->userManager->userExists($share->getSharedWith())) {
221
-				throw new \InvalidArgumentException('SharedWith is not a valid user');
222
-			}
223
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
224
-			// We expect a valid group as sharedWith for group shares
225
-			if (!$this->groupManager->groupExists($share->getSharedWith())) {
226
-				throw new \InvalidArgumentException('SharedWith is not a valid group');
227
-			}
228
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
229
-			if ($share->getSharedWith() !== null) {
230
-				throw new \InvalidArgumentException('SharedWith should be empty');
231
-			}
232
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
233
-			if ($share->getSharedWith() === null) {
234
-				throw new \InvalidArgumentException('SharedWith should not be empty');
235
-			}
236
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
237
-			if ($share->getSharedWith() === null) {
238
-				throw new \InvalidArgumentException('SharedWith should not be empty');
239
-			}
240
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
241
-			if ($share->getSharedWith() === null) {
242
-				throw new \InvalidArgumentException('SharedWith should not be empty');
243
-			}
244
-		} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
245
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
246
-			if ($circle === null) {
247
-				throw new \InvalidArgumentException('SharedWith is not a valid circle');
248
-			}
249
-		} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
250
-		} else {
251
-			// We can't handle other types yet
252
-			throw new \InvalidArgumentException('unknown share type');
253
-		}
254
-
255
-		// Verify the initiator of the share is set
256
-		if ($share->getSharedBy() === null) {
257
-			throw new \InvalidArgumentException('SharedBy should be set');
258
-		}
259
-
260
-		// Cannot share with yourself
261
-		if ($share->getShareType() === IShare::TYPE_USER &&
262
-			$share->getSharedWith() === $share->getSharedBy()) {
263
-			throw new \InvalidArgumentException('Can’t share with yourself');
264
-		}
265
-
266
-		// The path should be set
267
-		if ($share->getNode() === null) {
268
-			throw new \InvalidArgumentException('Path should be set');
269
-		}
270
-
271
-		// And it should be a file or a folder
272
-		if (!($share->getNode() instanceof \OCP\Files\File) &&
273
-				!($share->getNode() instanceof \OCP\Files\Folder)) {
274
-			throw new \InvalidArgumentException('Path should be either a file or a folder');
275
-		}
276
-
277
-		// And you can't share your rootfolder
278
-		if ($this->userManager->userExists($share->getSharedBy())) {
279
-			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
280
-		} else {
281
-			$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
282
-		}
283
-		if ($userFolder->getId() === $share->getNode()->getId()) {
284
-			throw new \InvalidArgumentException('You can’t share your root folder');
285
-		}
286
-
287
-		// Check if we actually have share permissions
288
-		if (!$share->getNode()->isShareable()) {
289
-			$path = $userFolder->getRelativePath($share->getNode()->getPath());
290
-			$message_t = $this->l->t('You are not allowed to share %s', [$path]);
291
-			throw new GenericShareException($message_t, $message_t, 404);
292
-		}
293
-
294
-		// Permissions should be set
295
-		if ($share->getPermissions() === null) {
296
-			throw new \InvalidArgumentException('A share requires permissions');
297
-		}
298
-
299
-		$isFederatedShare = $share->getNode()->getStorage()->instanceOfStorage('\OCA\Files_Sharing\External\Storage');
300
-		$permissions = 0;
301
-		$mount = $share->getNode()->getMountPoint();
302
-		if (!$isFederatedShare && $share->getNode()->getOwner() && $share->getNode()->getOwner()->getUID() !== $share->getSharedBy()) {
303
-			// When it's a reshare use the parent share permissions as maximum
304
-			$userMountPointId = $mount->getStorageRootId();
305
-			$userMountPoints = $userFolder->getById($userMountPointId);
306
-			$userMountPoint = array_shift($userMountPoints);
307
-
308
-			/* Check if this is an incoming share */
309
-			$incomingShares = $this->getSharedWith($share->getSharedBy(), IShare::TYPE_USER, $userMountPoint, -1, 0);
310
-			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_GROUP, $userMountPoint, -1, 0));
311
-			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_CIRCLE, $userMountPoint, -1, 0));
312
-			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_ROOM, $userMountPoint, -1, 0));
313
-
314
-			/** @var IShare[] $incomingShares */
315
-			if (!empty($incomingShares)) {
316
-				foreach ($incomingShares as $incomingShare) {
317
-					$permissions |= $incomingShare->getPermissions();
318
-				}
319
-			}
320
-		} else {
321
-			/*
79
+    /** @var IProviderFactory */
80
+    private $factory;
81
+    /** @var ILogger */
82
+    private $logger;
83
+    /** @var IConfig */
84
+    private $config;
85
+    /** @var ISecureRandom */
86
+    private $secureRandom;
87
+    /** @var IHasher */
88
+    private $hasher;
89
+    /** @var IMountManager */
90
+    private $mountManager;
91
+    /** @var IGroupManager */
92
+    private $groupManager;
93
+    /** @var IL10N */
94
+    private $l;
95
+    /** @var IFactory */
96
+    private $l10nFactory;
97
+    /** @var IUserManager */
98
+    private $userManager;
99
+    /** @var IRootFolder */
100
+    private $rootFolder;
101
+    /** @var CappedMemoryCache */
102
+    private $sharingDisabledForUsersCache;
103
+    /** @var EventDispatcherInterface */
104
+    private $legacyDispatcher;
105
+    /** @var LegacyHooks */
106
+    private $legacyHooks;
107
+    /** @var IMailer */
108
+    private $mailer;
109
+    /** @var IURLGenerator */
110
+    private $urlGenerator;
111
+    /** @var \OC_Defaults */
112
+    private $defaults;
113
+    /** @var IEventDispatcher */
114
+    private $dispatcher;
115
+
116
+
117
+    /**
118
+     * Manager constructor.
119
+     *
120
+     * @param ILogger $logger
121
+     * @param IConfig $config
122
+     * @param ISecureRandom $secureRandom
123
+     * @param IHasher $hasher
124
+     * @param IMountManager $mountManager
125
+     * @param IGroupManager $groupManager
126
+     * @param IL10N $l
127
+     * @param IFactory $l10nFactory
128
+     * @param IProviderFactory $factory
129
+     * @param IUserManager $userManager
130
+     * @param IRootFolder $rootFolder
131
+     * @param EventDispatcherInterface $eventDispatcher
132
+     * @param IMailer $mailer
133
+     * @param IURLGenerator $urlGenerator
134
+     * @param \OC_Defaults $defaults
135
+     */
136
+    public function __construct(
137
+            ILogger $logger,
138
+            IConfig $config,
139
+            ISecureRandom $secureRandom,
140
+            IHasher $hasher,
141
+            IMountManager $mountManager,
142
+            IGroupManager $groupManager,
143
+            IL10N $l,
144
+            IFactory $l10nFactory,
145
+            IProviderFactory $factory,
146
+            IUserManager $userManager,
147
+            IRootFolder $rootFolder,
148
+            EventDispatcherInterface $legacyDispatcher,
149
+            IMailer $mailer,
150
+            IURLGenerator $urlGenerator,
151
+            \OC_Defaults $defaults,
152
+            IEventDispatcher $dispatcher
153
+    ) {
154
+        $this->logger = $logger;
155
+        $this->config = $config;
156
+        $this->secureRandom = $secureRandom;
157
+        $this->hasher = $hasher;
158
+        $this->mountManager = $mountManager;
159
+        $this->groupManager = $groupManager;
160
+        $this->l = $l;
161
+        $this->l10nFactory = $l10nFactory;
162
+        $this->factory = $factory;
163
+        $this->userManager = $userManager;
164
+        $this->rootFolder = $rootFolder;
165
+        $this->legacyDispatcher = $legacyDispatcher;
166
+        $this->sharingDisabledForUsersCache = new CappedMemoryCache();
167
+        $this->legacyHooks = new LegacyHooks($this->legacyDispatcher);
168
+        $this->mailer = $mailer;
169
+        $this->urlGenerator = $urlGenerator;
170
+        $this->defaults = $defaults;
171
+        $this->dispatcher = $dispatcher;
172
+    }
173
+
174
+    /**
175
+     * Convert from a full share id to a tuple (providerId, shareId)
176
+     *
177
+     * @param string $id
178
+     * @return string[]
179
+     */
180
+    private function splitFullId($id) {
181
+        return explode(':', $id, 2);
182
+    }
183
+
184
+    /**
185
+     * Verify if a password meets all requirements
186
+     *
187
+     * @param string $password
188
+     * @throws \Exception
189
+     */
190
+    protected function verifyPassword($password) {
191
+        if ($password === null) {
192
+            // No password is set, check if this is allowed.
193
+            if ($this->shareApiLinkEnforcePassword()) {
194
+                throw new \InvalidArgumentException('Passwords are enforced for link shares');
195
+            }
196
+
197
+            return;
198
+        }
199
+
200
+        // Let others verify the password
201
+        try {
202
+            $this->legacyDispatcher->dispatch(new ValidatePasswordPolicyEvent($password));
203
+        } catch (HintException $e) {
204
+            throw new \Exception($e->getHint());
205
+        }
206
+    }
207
+
208
+    /**
209
+     * Check for generic requirements before creating a share
210
+     *
211
+     * @param IShare $share
212
+     * @throws \InvalidArgumentException
213
+     * @throws GenericShareException
214
+     *
215
+     * @suppress PhanUndeclaredClassMethod
216
+     */
217
+    protected function generalCreateChecks(IShare $share) {
218
+        if ($share->getShareType() === IShare::TYPE_USER) {
219
+            // We expect a valid user as sharedWith for user shares
220
+            if (!$this->userManager->userExists($share->getSharedWith())) {
221
+                throw new \InvalidArgumentException('SharedWith is not a valid user');
222
+            }
223
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
224
+            // We expect a valid group as sharedWith for group shares
225
+            if (!$this->groupManager->groupExists($share->getSharedWith())) {
226
+                throw new \InvalidArgumentException('SharedWith is not a valid group');
227
+            }
228
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
229
+            if ($share->getSharedWith() !== null) {
230
+                throw new \InvalidArgumentException('SharedWith should be empty');
231
+            }
232
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
233
+            if ($share->getSharedWith() === null) {
234
+                throw new \InvalidArgumentException('SharedWith should not be empty');
235
+            }
236
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
237
+            if ($share->getSharedWith() === null) {
238
+                throw new \InvalidArgumentException('SharedWith should not be empty');
239
+            }
240
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
241
+            if ($share->getSharedWith() === null) {
242
+                throw new \InvalidArgumentException('SharedWith should not be empty');
243
+            }
244
+        } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
245
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
246
+            if ($circle === null) {
247
+                throw new \InvalidArgumentException('SharedWith is not a valid circle');
248
+            }
249
+        } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
250
+        } else {
251
+            // We can't handle other types yet
252
+            throw new \InvalidArgumentException('unknown share type');
253
+        }
254
+
255
+        // Verify the initiator of the share is set
256
+        if ($share->getSharedBy() === null) {
257
+            throw new \InvalidArgumentException('SharedBy should be set');
258
+        }
259
+
260
+        // Cannot share with yourself
261
+        if ($share->getShareType() === IShare::TYPE_USER &&
262
+            $share->getSharedWith() === $share->getSharedBy()) {
263
+            throw new \InvalidArgumentException('Can’t share with yourself');
264
+        }
265
+
266
+        // The path should be set
267
+        if ($share->getNode() === null) {
268
+            throw new \InvalidArgumentException('Path should be set');
269
+        }
270
+
271
+        // And it should be a file or a folder
272
+        if (!($share->getNode() instanceof \OCP\Files\File) &&
273
+                !($share->getNode() instanceof \OCP\Files\Folder)) {
274
+            throw new \InvalidArgumentException('Path should be either a file or a folder');
275
+        }
276
+
277
+        // And you can't share your rootfolder
278
+        if ($this->userManager->userExists($share->getSharedBy())) {
279
+            $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
280
+        } else {
281
+            $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
282
+        }
283
+        if ($userFolder->getId() === $share->getNode()->getId()) {
284
+            throw new \InvalidArgumentException('You can’t share your root folder');
285
+        }
286
+
287
+        // Check if we actually have share permissions
288
+        if (!$share->getNode()->isShareable()) {
289
+            $path = $userFolder->getRelativePath($share->getNode()->getPath());
290
+            $message_t = $this->l->t('You are not allowed to share %s', [$path]);
291
+            throw new GenericShareException($message_t, $message_t, 404);
292
+        }
293
+
294
+        // Permissions should be set
295
+        if ($share->getPermissions() === null) {
296
+            throw new \InvalidArgumentException('A share requires permissions');
297
+        }
298
+
299
+        $isFederatedShare = $share->getNode()->getStorage()->instanceOfStorage('\OCA\Files_Sharing\External\Storage');
300
+        $permissions = 0;
301
+        $mount = $share->getNode()->getMountPoint();
302
+        if (!$isFederatedShare && $share->getNode()->getOwner() && $share->getNode()->getOwner()->getUID() !== $share->getSharedBy()) {
303
+            // When it's a reshare use the parent share permissions as maximum
304
+            $userMountPointId = $mount->getStorageRootId();
305
+            $userMountPoints = $userFolder->getById($userMountPointId);
306
+            $userMountPoint = array_shift($userMountPoints);
307
+
308
+            /* Check if this is an incoming share */
309
+            $incomingShares = $this->getSharedWith($share->getSharedBy(), IShare::TYPE_USER, $userMountPoint, -1, 0);
310
+            $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_GROUP, $userMountPoint, -1, 0));
311
+            $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_CIRCLE, $userMountPoint, -1, 0));
312
+            $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_ROOM, $userMountPoint, -1, 0));
313
+
314
+            /** @var IShare[] $incomingShares */
315
+            if (!empty($incomingShares)) {
316
+                foreach ($incomingShares as $incomingShare) {
317
+                    $permissions |= $incomingShare->getPermissions();
318
+                }
319
+            }
320
+        } else {
321
+            /*
322 322
 			 * Quick fix for #23536
323 323
 			 * Non moveable mount points do not have update and delete permissions
324 324
 			 * while we 'most likely' do have that on the storage.
325 325
 			 */
326
-			$permissions = $share->getNode()->getPermissions();
327
-			if (!($mount instanceof MoveableMount)) {
328
-				$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
329
-			}
330
-		}
331
-
332
-		// Check that we do not share with more permissions than we have
333
-		if ($share->getPermissions() & ~$permissions) {
334
-			$path = $userFolder->getRelativePath($share->getNode()->getPath());
335
-			$message_t = $this->l->t('Can’t increase permissions of %s', [$path]);
336
-			throw new GenericShareException($message_t, $message_t, 404);
337
-		}
338
-
339
-
340
-		// Check that read permissions are always set
341
-		// Link shares are allowed to have no read permissions to allow upload to hidden folders
342
-		$noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
343
-			|| $share->getShareType() === IShare::TYPE_EMAIL;
344
-		if (!$noReadPermissionRequired &&
345
-			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
346
-			throw new \InvalidArgumentException('Shares need at least read permissions');
347
-		}
348
-
349
-		if ($share->getNode() instanceof \OCP\Files\File) {
350
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
351
-				$message_t = $this->l->t('Files can’t be shared with delete permissions');
352
-				throw new GenericShareException($message_t);
353
-			}
354
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
355
-				$message_t = $this->l->t('Files can’t be shared with create permissions');
356
-				throw new GenericShareException($message_t);
357
-			}
358
-		}
359
-	}
360
-
361
-	/**
362
-	 * Validate if the expiration date fits the system settings
363
-	 *
364
-	 * @param IShare $share The share to validate the expiration date of
365
-	 * @return IShare The modified share object
366
-	 * @throws GenericShareException
367
-	 * @throws \InvalidArgumentException
368
-	 * @throws \Exception
369
-	 */
370
-	protected function validateExpirationDateInternal(IShare $share) {
371
-		$expirationDate = $share->getExpirationDate();
372
-
373
-		if ($expirationDate !== null) {
374
-			//Make sure the expiration date is a date
375
-			$expirationDate->setTime(0, 0, 0);
376
-
377
-			$date = new \DateTime();
378
-			$date->setTime(0, 0, 0);
379
-			if ($date >= $expirationDate) {
380
-				$message = $this->l->t('Expiration date is in the past');
381
-				throw new GenericShareException($message, $message, 404);
382
-			}
383
-		}
384
-
385
-		// If expiredate is empty set a default one if there is a default
386
-		$fullId = null;
387
-		try {
388
-			$fullId = $share->getFullId();
389
-		} catch (\UnexpectedValueException $e) {
390
-			// This is a new share
391
-		}
392
-
393
-		if ($fullId === null && $expirationDate === null && $this->shareApiInternalDefaultExpireDate()) {
394
-			$expirationDate = new \DateTime();
395
-			$expirationDate->setTime(0,0,0);
396
-
397
-			$days = (int)$this->config->getAppValue('core', 'internal_defaultExpDays', $this->shareApiLinkDefaultExpireDays());
398
-			if ($days > $this->shareApiLinkDefaultExpireDays()) {
399
-				$days = $this->shareApiLinkDefaultExpireDays();
400
-			}
401
-			$expirationDate->add(new \DateInterval('P'.$days.'D'));
402
-		}
403
-
404
-		// If we enforce the expiration date check that is does not exceed
405
-		if ($this->shareApiInternalDefaultExpireDateEnforced()) {
406
-			if ($expirationDate === null) {
407
-				throw new \InvalidArgumentException('Expiration date is enforced');
408
-			}
409
-
410
-			$date = new \DateTime();
411
-			$date->setTime(0, 0, 0);
412
-			$date->add(new \DateInterval('P' . $this->shareApiInternalDefaultExpireDays() . 'D'));
413
-			if ($date < $expirationDate) {
414
-				$message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiInternalDefaultExpireDays()]);
415
-				throw new GenericShareException($message, $message, 404);
416
-			}
417
-		}
418
-
419
-		$accepted = true;
420
-		$message = '';
421
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
422
-			'expirationDate' => &$expirationDate,
423
-			'accepted' => &$accepted,
424
-			'message' => &$message,
425
-			'passwordSet' => $share->getPassword() !== null,
426
-		]);
427
-
428
-		if (!$accepted) {
429
-			throw new \Exception($message);
430
-		}
431
-
432
-		$share->setExpirationDate($expirationDate);
433
-
434
-		return $share;
435
-	}
436
-
437
-	/**
438
-	 * Validate if the expiration date fits the system settings
439
-	 *
440
-	 * @param IShare $share The share to validate the expiration date of
441
-	 * @return IShare The modified share object
442
-	 * @throws GenericShareException
443
-	 * @throws \InvalidArgumentException
444
-	 * @throws \Exception
445
-	 */
446
-	protected function validateExpirationDate(IShare $share) {
447
-		$expirationDate = $share->getExpirationDate();
448
-
449
-		if ($expirationDate !== null) {
450
-			//Make sure the expiration date is a date
451
-			$expirationDate->setTime(0, 0, 0);
452
-
453
-			$date = new \DateTime();
454
-			$date->setTime(0, 0, 0);
455
-			if ($date >= $expirationDate) {
456
-				$message = $this->l->t('Expiration date is in the past');
457
-				throw new GenericShareException($message, $message, 404);
458
-			}
459
-		}
460
-
461
-		// If expiredate is empty set a default one if there is a default
462
-		$fullId = null;
463
-		try {
464
-			$fullId = $share->getFullId();
465
-		} catch (\UnexpectedValueException $e) {
466
-			// This is a new share
467
-		}
468
-
469
-		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
470
-			$expirationDate = new \DateTime();
471
-			$expirationDate->setTime(0,0,0);
472
-
473
-			$days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', $this->shareApiLinkDefaultExpireDays());
474
-			if ($days > $this->shareApiLinkDefaultExpireDays()) {
475
-				$days = $this->shareApiLinkDefaultExpireDays();
476
-			}
477
-			$expirationDate->add(new \DateInterval('P'.$days.'D'));
478
-		}
479
-
480
-		// If we enforce the expiration date check that is does not exceed
481
-		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
482
-			if ($expirationDate === null) {
483
-				throw new \InvalidArgumentException('Expiration date is enforced');
484
-			}
485
-
486
-			$date = new \DateTime();
487
-			$date->setTime(0, 0, 0);
488
-			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
489
-			if ($date < $expirationDate) {
490
-				$message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
491
-				throw new GenericShareException($message, $message, 404);
492
-			}
493
-		}
494
-
495
-		$accepted = true;
496
-		$message = '';
497
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
498
-			'expirationDate' => &$expirationDate,
499
-			'accepted' => &$accepted,
500
-			'message' => &$message,
501
-			'passwordSet' => $share->getPassword() !== null,
502
-		]);
503
-
504
-		if (!$accepted) {
505
-			throw new \Exception($message);
506
-		}
507
-
508
-		$share->setExpirationDate($expirationDate);
509
-
510
-		return $share;
511
-	}
512
-
513
-	/**
514
-	 * Check for pre share requirements for user shares
515
-	 *
516
-	 * @param IShare $share
517
-	 * @throws \Exception
518
-	 */
519
-	protected function userCreateChecks(IShare $share) {
520
-		// Check if we can share with group members only
521
-		if ($this->shareWithGroupMembersOnly()) {
522
-			$sharedBy = $this->userManager->get($share->getSharedBy());
523
-			$sharedWith = $this->userManager->get($share->getSharedWith());
524
-			// Verify we can share with this user
525
-			$groups = array_intersect(
526
-					$this->groupManager->getUserGroupIds($sharedBy),
527
-					$this->groupManager->getUserGroupIds($sharedWith)
528
-			);
529
-			if (empty($groups)) {
530
-				throw new \Exception('Sharing is only allowed with group members');
531
-			}
532
-		}
533
-
534
-		/*
326
+            $permissions = $share->getNode()->getPermissions();
327
+            if (!($mount instanceof MoveableMount)) {
328
+                $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
329
+            }
330
+        }
331
+
332
+        // Check that we do not share with more permissions than we have
333
+        if ($share->getPermissions() & ~$permissions) {
334
+            $path = $userFolder->getRelativePath($share->getNode()->getPath());
335
+            $message_t = $this->l->t('Can’t increase permissions of %s', [$path]);
336
+            throw new GenericShareException($message_t, $message_t, 404);
337
+        }
338
+
339
+
340
+        // Check that read permissions are always set
341
+        // Link shares are allowed to have no read permissions to allow upload to hidden folders
342
+        $noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
343
+            || $share->getShareType() === IShare::TYPE_EMAIL;
344
+        if (!$noReadPermissionRequired &&
345
+            ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
346
+            throw new \InvalidArgumentException('Shares need at least read permissions');
347
+        }
348
+
349
+        if ($share->getNode() instanceof \OCP\Files\File) {
350
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
351
+                $message_t = $this->l->t('Files can’t be shared with delete permissions');
352
+                throw new GenericShareException($message_t);
353
+            }
354
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
355
+                $message_t = $this->l->t('Files can’t be shared with create permissions');
356
+                throw new GenericShareException($message_t);
357
+            }
358
+        }
359
+    }
360
+
361
+    /**
362
+     * Validate if the expiration date fits the system settings
363
+     *
364
+     * @param IShare $share The share to validate the expiration date of
365
+     * @return IShare The modified share object
366
+     * @throws GenericShareException
367
+     * @throws \InvalidArgumentException
368
+     * @throws \Exception
369
+     */
370
+    protected function validateExpirationDateInternal(IShare $share) {
371
+        $expirationDate = $share->getExpirationDate();
372
+
373
+        if ($expirationDate !== null) {
374
+            //Make sure the expiration date is a date
375
+            $expirationDate->setTime(0, 0, 0);
376
+
377
+            $date = new \DateTime();
378
+            $date->setTime(0, 0, 0);
379
+            if ($date >= $expirationDate) {
380
+                $message = $this->l->t('Expiration date is in the past');
381
+                throw new GenericShareException($message, $message, 404);
382
+            }
383
+        }
384
+
385
+        // If expiredate is empty set a default one if there is a default
386
+        $fullId = null;
387
+        try {
388
+            $fullId = $share->getFullId();
389
+        } catch (\UnexpectedValueException $e) {
390
+            // This is a new share
391
+        }
392
+
393
+        if ($fullId === null && $expirationDate === null && $this->shareApiInternalDefaultExpireDate()) {
394
+            $expirationDate = new \DateTime();
395
+            $expirationDate->setTime(0,0,0);
396
+
397
+            $days = (int)$this->config->getAppValue('core', 'internal_defaultExpDays', $this->shareApiLinkDefaultExpireDays());
398
+            if ($days > $this->shareApiLinkDefaultExpireDays()) {
399
+                $days = $this->shareApiLinkDefaultExpireDays();
400
+            }
401
+            $expirationDate->add(new \DateInterval('P'.$days.'D'));
402
+        }
403
+
404
+        // If we enforce the expiration date check that is does not exceed
405
+        if ($this->shareApiInternalDefaultExpireDateEnforced()) {
406
+            if ($expirationDate === null) {
407
+                throw new \InvalidArgumentException('Expiration date is enforced');
408
+            }
409
+
410
+            $date = new \DateTime();
411
+            $date->setTime(0, 0, 0);
412
+            $date->add(new \DateInterval('P' . $this->shareApiInternalDefaultExpireDays() . 'D'));
413
+            if ($date < $expirationDate) {
414
+                $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiInternalDefaultExpireDays()]);
415
+                throw new GenericShareException($message, $message, 404);
416
+            }
417
+        }
418
+
419
+        $accepted = true;
420
+        $message = '';
421
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
422
+            'expirationDate' => &$expirationDate,
423
+            'accepted' => &$accepted,
424
+            'message' => &$message,
425
+            'passwordSet' => $share->getPassword() !== null,
426
+        ]);
427
+
428
+        if (!$accepted) {
429
+            throw new \Exception($message);
430
+        }
431
+
432
+        $share->setExpirationDate($expirationDate);
433
+
434
+        return $share;
435
+    }
436
+
437
+    /**
438
+     * Validate if the expiration date fits the system settings
439
+     *
440
+     * @param IShare $share The share to validate the expiration date of
441
+     * @return IShare The modified share object
442
+     * @throws GenericShareException
443
+     * @throws \InvalidArgumentException
444
+     * @throws \Exception
445
+     */
446
+    protected function validateExpirationDate(IShare $share) {
447
+        $expirationDate = $share->getExpirationDate();
448
+
449
+        if ($expirationDate !== null) {
450
+            //Make sure the expiration date is a date
451
+            $expirationDate->setTime(0, 0, 0);
452
+
453
+            $date = new \DateTime();
454
+            $date->setTime(0, 0, 0);
455
+            if ($date >= $expirationDate) {
456
+                $message = $this->l->t('Expiration date is in the past');
457
+                throw new GenericShareException($message, $message, 404);
458
+            }
459
+        }
460
+
461
+        // If expiredate is empty set a default one if there is a default
462
+        $fullId = null;
463
+        try {
464
+            $fullId = $share->getFullId();
465
+        } catch (\UnexpectedValueException $e) {
466
+            // This is a new share
467
+        }
468
+
469
+        if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
470
+            $expirationDate = new \DateTime();
471
+            $expirationDate->setTime(0,0,0);
472
+
473
+            $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', $this->shareApiLinkDefaultExpireDays());
474
+            if ($days > $this->shareApiLinkDefaultExpireDays()) {
475
+                $days = $this->shareApiLinkDefaultExpireDays();
476
+            }
477
+            $expirationDate->add(new \DateInterval('P'.$days.'D'));
478
+        }
479
+
480
+        // If we enforce the expiration date check that is does not exceed
481
+        if ($this->shareApiLinkDefaultExpireDateEnforced()) {
482
+            if ($expirationDate === null) {
483
+                throw new \InvalidArgumentException('Expiration date is enforced');
484
+            }
485
+
486
+            $date = new \DateTime();
487
+            $date->setTime(0, 0, 0);
488
+            $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
489
+            if ($date < $expirationDate) {
490
+                $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
491
+                throw new GenericShareException($message, $message, 404);
492
+            }
493
+        }
494
+
495
+        $accepted = true;
496
+        $message = '';
497
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
498
+            'expirationDate' => &$expirationDate,
499
+            'accepted' => &$accepted,
500
+            'message' => &$message,
501
+            'passwordSet' => $share->getPassword() !== null,
502
+        ]);
503
+
504
+        if (!$accepted) {
505
+            throw new \Exception($message);
506
+        }
507
+
508
+        $share->setExpirationDate($expirationDate);
509
+
510
+        return $share;
511
+    }
512
+
513
+    /**
514
+     * Check for pre share requirements for user shares
515
+     *
516
+     * @param IShare $share
517
+     * @throws \Exception
518
+     */
519
+    protected function userCreateChecks(IShare $share) {
520
+        // Check if we can share with group members only
521
+        if ($this->shareWithGroupMembersOnly()) {
522
+            $sharedBy = $this->userManager->get($share->getSharedBy());
523
+            $sharedWith = $this->userManager->get($share->getSharedWith());
524
+            // Verify we can share with this user
525
+            $groups = array_intersect(
526
+                    $this->groupManager->getUserGroupIds($sharedBy),
527
+                    $this->groupManager->getUserGroupIds($sharedWith)
528
+            );
529
+            if (empty($groups)) {
530
+                throw new \Exception('Sharing is only allowed with group members');
531
+            }
532
+        }
533
+
534
+        /*
535 535
 		 * TODO: Could be costly, fix
536 536
 		 *
537 537
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
538 538
 		 */
539
-		$provider = $this->factory->getProviderForType(IShare::TYPE_USER);
540
-		$existingShares = $provider->getSharesByPath($share->getNode());
541
-		foreach ($existingShares as $existingShare) {
542
-			// Ignore if it is the same share
543
-			try {
544
-				if ($existingShare->getFullId() === $share->getFullId()) {
545
-					continue;
546
-				}
547
-			} catch (\UnexpectedValueException $e) {
548
-				//Shares are not identical
549
-			}
550
-
551
-			// Identical share already existst
552
-			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
553
-				throw new \Exception('Path is already shared with this user');
554
-			}
555
-
556
-			// The share is already shared with this user via a group share
557
-			if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
558
-				$group = $this->groupManager->get($existingShare->getSharedWith());
559
-				if (!is_null($group)) {
560
-					$user = $this->userManager->get($share->getSharedWith());
561
-
562
-					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
563
-						throw new \Exception('Path is already shared with this user');
564
-					}
565
-				}
566
-			}
567
-		}
568
-	}
569
-
570
-	/**
571
-	 * Check for pre share requirements for group shares
572
-	 *
573
-	 * @param IShare $share
574
-	 * @throws \Exception
575
-	 */
576
-	protected function groupCreateChecks(IShare $share) {
577
-		// Verify group shares are allowed
578
-		if (!$this->allowGroupSharing()) {
579
-			throw new \Exception('Group sharing is now allowed');
580
-		}
581
-
582
-		// Verify if the user can share with this group
583
-		if ($this->shareWithGroupMembersOnly()) {
584
-			$sharedBy = $this->userManager->get($share->getSharedBy());
585
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
586
-			if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
587
-				throw new \Exception('Sharing is only allowed within your own groups');
588
-			}
589
-		}
590
-
591
-		/*
539
+        $provider = $this->factory->getProviderForType(IShare::TYPE_USER);
540
+        $existingShares = $provider->getSharesByPath($share->getNode());
541
+        foreach ($existingShares as $existingShare) {
542
+            // Ignore if it is the same share
543
+            try {
544
+                if ($existingShare->getFullId() === $share->getFullId()) {
545
+                    continue;
546
+                }
547
+            } catch (\UnexpectedValueException $e) {
548
+                //Shares are not identical
549
+            }
550
+
551
+            // Identical share already existst
552
+            if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
553
+                throw new \Exception('Path is already shared with this user');
554
+            }
555
+
556
+            // The share is already shared with this user via a group share
557
+            if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
558
+                $group = $this->groupManager->get($existingShare->getSharedWith());
559
+                if (!is_null($group)) {
560
+                    $user = $this->userManager->get($share->getSharedWith());
561
+
562
+                    if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
563
+                        throw new \Exception('Path is already shared with this user');
564
+                    }
565
+                }
566
+            }
567
+        }
568
+    }
569
+
570
+    /**
571
+     * Check for pre share requirements for group shares
572
+     *
573
+     * @param IShare $share
574
+     * @throws \Exception
575
+     */
576
+    protected function groupCreateChecks(IShare $share) {
577
+        // Verify group shares are allowed
578
+        if (!$this->allowGroupSharing()) {
579
+            throw new \Exception('Group sharing is now allowed');
580
+        }
581
+
582
+        // Verify if the user can share with this group
583
+        if ($this->shareWithGroupMembersOnly()) {
584
+            $sharedBy = $this->userManager->get($share->getSharedBy());
585
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
586
+            if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
587
+                throw new \Exception('Sharing is only allowed within your own groups');
588
+            }
589
+        }
590
+
591
+        /*
592 592
 		 * TODO: Could be costly, fix
593 593
 		 *
594 594
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
595 595
 		 */
596
-		$provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
597
-		$existingShares = $provider->getSharesByPath($share->getNode());
598
-		foreach ($existingShares as $existingShare) {
599
-			try {
600
-				if ($existingShare->getFullId() === $share->getFullId()) {
601
-					continue;
602
-				}
603
-			} catch (\UnexpectedValueException $e) {
604
-				//It is a new share so just continue
605
-			}
606
-
607
-			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
608
-				throw new \Exception('Path is already shared with this group');
609
-			}
610
-		}
611
-	}
612
-
613
-	/**
614
-	 * Check for pre share requirements for link shares
615
-	 *
616
-	 * @param IShare $share
617
-	 * @throws \Exception
618
-	 */
619
-	protected function linkCreateChecks(IShare $share) {
620
-		// Are link shares allowed?
621
-		if (!$this->shareApiAllowLinks()) {
622
-			throw new \Exception('Link sharing is not allowed');
623
-		}
624
-
625
-		// Check if public upload is allowed
626
-		if (!$this->shareApiLinkAllowPublicUpload() &&
627
-			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
628
-			throw new \InvalidArgumentException('Public upload is not allowed');
629
-		}
630
-	}
631
-
632
-	/**
633
-	 * To make sure we don't get invisible link shares we set the parent
634
-	 * of a link if it is a reshare. This is a quick word around
635
-	 * until we can properly display multiple link shares in the UI
636
-	 *
637
-	 * See: https://github.com/owncloud/core/issues/22295
638
-	 *
639
-	 * FIXME: Remove once multiple link shares can be properly displayed
640
-	 *
641
-	 * @param IShare $share
642
-	 */
643
-	protected function setLinkParent(IShare $share) {
644
-
645
-		// No sense in checking if the method is not there.
646
-		if (method_exists($share, 'setParent')) {
647
-			$storage = $share->getNode()->getStorage();
648
-			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
649
-				/** @var \OCA\Files_Sharing\SharedStorage $storage */
650
-				$share->setParent($storage->getShareId());
651
-			}
652
-		}
653
-	}
654
-
655
-	/**
656
-	 * @param File|Folder $path
657
-	 */
658
-	protected function pathCreateChecks($path) {
659
-		// Make sure that we do not share a path that contains a shared mountpoint
660
-		if ($path instanceof \OCP\Files\Folder) {
661
-			$mounts = $this->mountManager->findIn($path->getPath());
662
-			foreach ($mounts as $mount) {
663
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
664
-					throw new \InvalidArgumentException('Path contains files shared with you');
665
-				}
666
-			}
667
-		}
668
-	}
669
-
670
-	/**
671
-	 * Check if the user that is sharing can actually share
672
-	 *
673
-	 * @param IShare $share
674
-	 * @throws \Exception
675
-	 */
676
-	protected function canShare(IShare $share) {
677
-		if (!$this->shareApiEnabled()) {
678
-			throw new \Exception('Sharing is disabled');
679
-		}
680
-
681
-		if ($this->sharingDisabledForUser($share->getSharedBy())) {
682
-			throw new \Exception('Sharing is disabled for you');
683
-		}
684
-	}
685
-
686
-	/**
687
-	 * Share a path
688
-	 *
689
-	 * @param IShare $share
690
-	 * @return IShare The share object
691
-	 * @throws \Exception
692
-	 *
693
-	 * TODO: handle link share permissions or check them
694
-	 */
695
-	public function createShare(IShare $share) {
696
-		$this->canShare($share);
697
-
698
-		$this->generalCreateChecks($share);
699
-
700
-		// Verify if there are any issues with the path
701
-		$this->pathCreateChecks($share->getNode());
702
-
703
-		/*
596
+        $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
597
+        $existingShares = $provider->getSharesByPath($share->getNode());
598
+        foreach ($existingShares as $existingShare) {
599
+            try {
600
+                if ($existingShare->getFullId() === $share->getFullId()) {
601
+                    continue;
602
+                }
603
+            } catch (\UnexpectedValueException $e) {
604
+                //It is a new share so just continue
605
+            }
606
+
607
+            if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
608
+                throw new \Exception('Path is already shared with this group');
609
+            }
610
+        }
611
+    }
612
+
613
+    /**
614
+     * Check for pre share requirements for link shares
615
+     *
616
+     * @param IShare $share
617
+     * @throws \Exception
618
+     */
619
+    protected function linkCreateChecks(IShare $share) {
620
+        // Are link shares allowed?
621
+        if (!$this->shareApiAllowLinks()) {
622
+            throw new \Exception('Link sharing is not allowed');
623
+        }
624
+
625
+        // Check if public upload is allowed
626
+        if (!$this->shareApiLinkAllowPublicUpload() &&
627
+            ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
628
+            throw new \InvalidArgumentException('Public upload is not allowed');
629
+        }
630
+    }
631
+
632
+    /**
633
+     * To make sure we don't get invisible link shares we set the parent
634
+     * of a link if it is a reshare. This is a quick word around
635
+     * until we can properly display multiple link shares in the UI
636
+     *
637
+     * See: https://github.com/owncloud/core/issues/22295
638
+     *
639
+     * FIXME: Remove once multiple link shares can be properly displayed
640
+     *
641
+     * @param IShare $share
642
+     */
643
+    protected function setLinkParent(IShare $share) {
644
+
645
+        // No sense in checking if the method is not there.
646
+        if (method_exists($share, 'setParent')) {
647
+            $storage = $share->getNode()->getStorage();
648
+            if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
649
+                /** @var \OCA\Files_Sharing\SharedStorage $storage */
650
+                $share->setParent($storage->getShareId());
651
+            }
652
+        }
653
+    }
654
+
655
+    /**
656
+     * @param File|Folder $path
657
+     */
658
+    protected function pathCreateChecks($path) {
659
+        // Make sure that we do not share a path that contains a shared mountpoint
660
+        if ($path instanceof \OCP\Files\Folder) {
661
+            $mounts = $this->mountManager->findIn($path->getPath());
662
+            foreach ($mounts as $mount) {
663
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
664
+                    throw new \InvalidArgumentException('Path contains files shared with you');
665
+                }
666
+            }
667
+        }
668
+    }
669
+
670
+    /**
671
+     * Check if the user that is sharing can actually share
672
+     *
673
+     * @param IShare $share
674
+     * @throws \Exception
675
+     */
676
+    protected function canShare(IShare $share) {
677
+        if (!$this->shareApiEnabled()) {
678
+            throw new \Exception('Sharing is disabled');
679
+        }
680
+
681
+        if ($this->sharingDisabledForUser($share->getSharedBy())) {
682
+            throw new \Exception('Sharing is disabled for you');
683
+        }
684
+    }
685
+
686
+    /**
687
+     * Share a path
688
+     *
689
+     * @param IShare $share
690
+     * @return IShare The share object
691
+     * @throws \Exception
692
+     *
693
+     * TODO: handle link share permissions or check them
694
+     */
695
+    public function createShare(IShare $share) {
696
+        $this->canShare($share);
697
+
698
+        $this->generalCreateChecks($share);
699
+
700
+        // Verify if there are any issues with the path
701
+        $this->pathCreateChecks($share->getNode());
702
+
703
+        /*
704 704
 		 * On creation of a share the owner is always the owner of the path
705 705
 		 * Except for mounted federated shares.
706 706
 		 */
707
-		$storage = $share->getNode()->getStorage();
708
-		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
709
-			$parent = $share->getNode()->getParent();
710
-			while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
711
-				$parent = $parent->getParent();
712
-			}
713
-			$share->setShareOwner($parent->getOwner()->getUID());
714
-		} else {
715
-			if ($share->getNode()->getOwner()) {
716
-				$share->setShareOwner($share->getNode()->getOwner()->getUID());
717
-			} else {
718
-				$share->setShareOwner($share->getSharedBy());
719
-			}
720
-		}
721
-
722
-		//Verify share type
723
-		if ($share->getShareType() === IShare::TYPE_USER) {
724
-			$this->userCreateChecks($share);
725
-
726
-			//Verify the expiration date
727
-			$share = $this->validateExpirationDateInternal($share);
728
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
729
-			$this->groupCreateChecks($share);
730
-
731
-			//Verify the expiration date
732
-			$share = $this->validateExpirationDateInternal($share);
733
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
734
-			$this->linkCreateChecks($share);
735
-			$this->setLinkParent($share);
736
-
737
-			/*
707
+        $storage = $share->getNode()->getStorage();
708
+        if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
709
+            $parent = $share->getNode()->getParent();
710
+            while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
711
+                $parent = $parent->getParent();
712
+            }
713
+            $share->setShareOwner($parent->getOwner()->getUID());
714
+        } else {
715
+            if ($share->getNode()->getOwner()) {
716
+                $share->setShareOwner($share->getNode()->getOwner()->getUID());
717
+            } else {
718
+                $share->setShareOwner($share->getSharedBy());
719
+            }
720
+        }
721
+
722
+        //Verify share type
723
+        if ($share->getShareType() === IShare::TYPE_USER) {
724
+            $this->userCreateChecks($share);
725
+
726
+            //Verify the expiration date
727
+            $share = $this->validateExpirationDateInternal($share);
728
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
729
+            $this->groupCreateChecks($share);
730
+
731
+            //Verify the expiration date
732
+            $share = $this->validateExpirationDateInternal($share);
733
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
734
+            $this->linkCreateChecks($share);
735
+            $this->setLinkParent($share);
736
+
737
+            /*
738 738
 			 * For now ignore a set token.
739 739
 			 */
740
-			$share->setToken(
741
-				$this->secureRandom->generate(
742
-					\OC\Share\Constants::TOKEN_LENGTH,
743
-					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
744
-				)
745
-			);
746
-
747
-			//Verify the expiration date
748
-			$share = $this->validateExpirationDate($share);
749
-
750
-			//Verify the password
751
-			$this->verifyPassword($share->getPassword());
752
-
753
-			// If a password is set. Hash it!
754
-			if ($share->getPassword() !== null) {
755
-				$share->setPassword($this->hasher->hash($share->getPassword()));
756
-			}
757
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
758
-			$share->setToken(
759
-				$this->secureRandom->generate(
760
-					\OC\Share\Constants::TOKEN_LENGTH,
761
-					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
762
-				)
763
-			);
764
-		}
765
-
766
-		// Cannot share with the owner
767
-		if ($share->getShareType() === IShare::TYPE_USER &&
768
-			$share->getSharedWith() === $share->getShareOwner()) {
769
-			throw new \InvalidArgumentException('Can’t share with the share owner');
770
-		}
771
-
772
-		// Generate the target
773
-		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
774
-		$target = \OC\Files\Filesystem::normalizePath($target);
775
-		$share->setTarget($target);
776
-
777
-		// Pre share event
778
-		$event = new GenericEvent($share);
779
-		$this->legacyDispatcher->dispatch('OCP\Share::preShare', $event);
780
-		if ($event->isPropagationStopped() && $event->hasArgument('error')) {
781
-			throw new \Exception($event->getArgument('error'));
782
-		}
783
-
784
-		$oldShare = $share;
785
-		$provider = $this->factory->getProviderForType($share->getShareType());
786
-		$share = $provider->create($share);
787
-		//reuse the node we already have
788
-		$share->setNode($oldShare->getNode());
789
-
790
-		// Reset the target if it is null for the new share
791
-		if ($share->getTarget() === '') {
792
-			$share->setTarget($target);
793
-		}
794
-
795
-		// Post share event
796
-		$event = new GenericEvent($share);
797
-		$this->legacyDispatcher->dispatch('OCP\Share::postShare', $event);
798
-
799
-		$this->dispatcher->dispatchTyped(new Share\Events\ShareCreatedEvent($share));
800
-
801
-		if ($share->getShareType() === IShare::TYPE_USER) {
802
-			$mailSend = $share->getMailSend();
803
-			if ($mailSend === true) {
804
-				$user = $this->userManager->get($share->getSharedWith());
805
-				if ($user !== null) {
806
-					$emailAddress = $user->getEMailAddress();
807
-					if ($emailAddress !== null && $emailAddress !== '') {
808
-						$userLang = $this->l10nFactory->getUserLanguage($user);
809
-						$l = $this->l10nFactory->get('lib', $userLang);
810
-						$this->sendMailNotification(
811
-							$l,
812
-							$share->getNode()->getName(),
813
-							$this->urlGenerator->linkToRouteAbsolute('files_sharing.Accept.accept', ['shareId' => $share->getFullId()]),
814
-							$share->getSharedBy(),
815
-							$emailAddress,
816
-							$share->getExpirationDate()
817
-						);
818
-						$this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId(), ['app' => 'share']);
819
-					} else {
820
-						$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
821
-					}
822
-				} else {
823
-					$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
824
-				}
825
-			} else {
826
-				$this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
827
-			}
828
-		}
829
-
830
-		return $share;
831
-	}
832
-
833
-	/**
834
-	 * Send mail notifications
835
-	 *
836
-	 * This method will catch and log mail transmission errors
837
-	 *
838
-	 * @param IL10N $l Language of the recipient
839
-	 * @param string $filename file/folder name
840
-	 * @param string $link link to the file/folder
841
-	 * @param string $initiator user ID of share sender
842
-	 * @param string $shareWith email address of share receiver
843
-	 * @param \DateTime|null $expiration
844
-	 */
845
-	protected function sendMailNotification(IL10N $l,
846
-											$filename,
847
-											$link,
848
-											$initiator,
849
-											$shareWith,
850
-											\DateTime $expiration = null) {
851
-		$initiatorUser = $this->userManager->get($initiator);
852
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
853
-
854
-		$message = $this->mailer->createMessage();
855
-
856
-		$emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
857
-			'filename' => $filename,
858
-			'link' => $link,
859
-			'initiator' => $initiatorDisplayName,
860
-			'expiration' => $expiration,
861
-			'shareWith' => $shareWith,
862
-		]);
863
-
864
-		$emailTemplate->setSubject($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]));
865
-		$emailTemplate->addHeader();
866
-		$emailTemplate->addHeading($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]), false);
867
-		$text = $l->t('%1$s shared »%2$s« with you.', [$initiatorDisplayName, $filename]);
868
-
869
-		$emailTemplate->addBodyText(
870
-			htmlspecialchars($text . ' ' . $l->t('Click the button below to open it.')),
871
-			$text
872
-		);
873
-		$emailTemplate->addBodyButton(
874
-			$l->t('Open »%s«', [$filename]),
875
-			$link
876
-		);
877
-
878
-		$message->setTo([$shareWith]);
879
-
880
-		// The "From" contains the sharers name
881
-		$instanceName = $this->defaults->getName();
882
-		$senderName = $l->t(
883
-			'%1$s via %2$s',
884
-			[
885
-				$initiatorDisplayName,
886
-				$instanceName
887
-			]
888
-		);
889
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
890
-
891
-		// The "Reply-To" is set to the sharer if an mail address is configured
892
-		// also the default footer contains a "Do not reply" which needs to be adjusted.
893
-		$initiatorEmail = $initiatorUser->getEMailAddress();
894
-		if ($initiatorEmail !== null) {
895
-			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
896
-			$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan($l->getLanguageCode()) !== '' ? ' - ' . $this->defaults->getSlogan($l->getLanguageCode()) : ''));
897
-		} else {
898
-			$emailTemplate->addFooter('', $l->getLanguageCode());
899
-		}
900
-
901
-		$message->useTemplate($emailTemplate);
902
-		try {
903
-			$failedRecipients = $this->mailer->send($message);
904
-			if (!empty($failedRecipients)) {
905
-				$this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients));
906
-				return;
907
-			}
908
-		} catch (\Exception $e) {
909
-			$this->logger->logException($e, ['message' => 'Share notification mail could not be sent']);
910
-		}
911
-	}
912
-
913
-	/**
914
-	 * Update a share
915
-	 *
916
-	 * @param IShare $share
917
-	 * @return IShare The share object
918
-	 * @throws \InvalidArgumentException
919
-	 */
920
-	public function updateShare(IShare $share) {
921
-		$expirationDateUpdated = false;
922
-
923
-		$this->canShare($share);
924
-
925
-		try {
926
-			$originalShare = $this->getShareById($share->getFullId());
927
-		} catch (\UnexpectedValueException $e) {
928
-			throw new \InvalidArgumentException('Share does not have a full id');
929
-		}
930
-
931
-		// We can't change the share type!
932
-		if ($share->getShareType() !== $originalShare->getShareType()) {
933
-			throw new \InvalidArgumentException('Can’t change share type');
934
-		}
935
-
936
-		// We can only change the recipient on user shares
937
-		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
938
-			$share->getShareType() !== IShare::TYPE_USER) {
939
-			throw new \InvalidArgumentException('Can only update recipient on user shares');
940
-		}
941
-
942
-		// Cannot share with the owner
943
-		if ($share->getShareType() === IShare::TYPE_USER &&
944
-			$share->getSharedWith() === $share->getShareOwner()) {
945
-			throw new \InvalidArgumentException('Can’t share with the share owner');
946
-		}
947
-
948
-		$this->generalCreateChecks($share);
949
-
950
-		if ($share->getShareType() === IShare::TYPE_USER) {
951
-			$this->userCreateChecks($share);
952
-
953
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
954
-				//Verify the expiration date
955
-				$this->validateExpirationDate($share);
956
-				$expirationDateUpdated = true;
957
-			}
958
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
959
-			$this->groupCreateChecks($share);
960
-
961
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
962
-				//Verify the expiration date
963
-				$this->validateExpirationDate($share);
964
-				$expirationDateUpdated = true;
965
-			}
966
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
967
-			$this->linkCreateChecks($share);
968
-
969
-			$plainTextPassword = $share->getPassword();
970
-
971
-			$this->updateSharePasswordIfNeeded($share, $originalShare);
972
-
973
-			if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
974
-				throw new \InvalidArgumentException('Can’t enable sending the password by Talk with an empty password');
975
-			}
976
-
977
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
978
-				//Verify the expiration date
979
-				$this->validateExpirationDate($share);
980
-				$expirationDateUpdated = true;
981
-			}
982
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
983
-			// The new password is not set again if it is the same as the old
984
-			// one.
985
-			$plainTextPassword = $share->getPassword();
986
-			if (!empty($plainTextPassword) && !$this->updateSharePasswordIfNeeded($share, $originalShare)) {
987
-				$plainTextPassword = null;
988
-			}
989
-			if (empty($plainTextPassword) && !$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
990
-				// If the same password was already sent by mail the recipient
991
-				// would already have access to the share without having to call
992
-				// the sharer to verify her identity
993
-				throw new \InvalidArgumentException('Can’t enable sending the password by Talk without setting a new password');
994
-			} elseif (empty($plainTextPassword) && $originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
995
-				throw new \InvalidArgumentException('Can’t disable sending the password by Talk without setting a new password');
996
-			}
997
-		}
998
-
999
-		$this->pathCreateChecks($share->getNode());
1000
-
1001
-		// Now update the share!
1002
-		$provider = $this->factory->getProviderForType($share->getShareType());
1003
-		if ($share->getShareType() === IShare::TYPE_EMAIL) {
1004
-			$share = $provider->update($share, $plainTextPassword);
1005
-		} else {
1006
-			$share = $provider->update($share);
1007
-		}
1008
-
1009
-		if ($expirationDateUpdated === true) {
1010
-			\OC_Hook::emit(Share::class, 'post_set_expiration_date', [
1011
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1012
-				'itemSource' => $share->getNode()->getId(),
1013
-				'date' => $share->getExpirationDate(),
1014
-				'uidOwner' => $share->getSharedBy(),
1015
-			]);
1016
-		}
1017
-
1018
-		if ($share->getPassword() !== $originalShare->getPassword()) {
1019
-			\OC_Hook::emit(Share::class, 'post_update_password', [
1020
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1021
-				'itemSource' => $share->getNode()->getId(),
1022
-				'uidOwner' => $share->getSharedBy(),
1023
-				'token' => $share->getToken(),
1024
-				'disabled' => is_null($share->getPassword()),
1025
-			]);
1026
-		}
1027
-
1028
-		if ($share->getPermissions() !== $originalShare->getPermissions()) {
1029
-			if ($this->userManager->userExists($share->getShareOwner())) {
1030
-				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
1031
-			} else {
1032
-				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1033
-			}
1034
-			\OC_Hook::emit(Share::class, 'post_update_permissions', [
1035
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1036
-				'itemSource' => $share->getNode()->getId(),
1037
-				'shareType' => $share->getShareType(),
1038
-				'shareWith' => $share->getSharedWith(),
1039
-				'uidOwner' => $share->getSharedBy(),
1040
-				'permissions' => $share->getPermissions(),
1041
-				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
1042
-			]);
1043
-		}
1044
-
1045
-		return $share;
1046
-	}
1047
-
1048
-	/**
1049
-	 * Accept a share.
1050
-	 *
1051
-	 * @param IShare $share
1052
-	 * @param string $recipientId
1053
-	 * @return IShare The share object
1054
-	 * @throws \InvalidArgumentException
1055
-	 * @since 9.0.0
1056
-	 */
1057
-	public function acceptShare(IShare $share, string $recipientId): IShare {
1058
-		[$providerId, ] = $this->splitFullId($share->getFullId());
1059
-		$provider = $this->factory->getProvider($providerId);
1060
-
1061
-		if (!method_exists($provider, 'acceptShare')) {
1062
-			// TODO FIX ME
1063
-			throw new \InvalidArgumentException('Share provider does not support accepting');
1064
-		}
1065
-		$provider->acceptShare($share, $recipientId);
1066
-		$event = new GenericEvent($share);
1067
-		$this->legacyDispatcher->dispatch('OCP\Share::postAcceptShare', $event);
1068
-
1069
-		return $share;
1070
-	}
1071
-
1072
-	/**
1073
-	 * Updates the password of the given share if it is not the same as the
1074
-	 * password of the original share.
1075
-	 *
1076
-	 * @param IShare $share the share to update its password.
1077
-	 * @param IShare $originalShare the original share to compare its
1078
-	 *        password with.
1079
-	 * @return boolean whether the password was updated or not.
1080
-	 */
1081
-	private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
1082
-		$passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword()) &&
1083
-									(($share->getPassword() !== null && $originalShare->getPassword() === null) ||
1084
-									 ($share->getPassword() === null && $originalShare->getPassword() !== null) ||
1085
-									 ($share->getPassword() !== null && $originalShare->getPassword() !== null &&
1086
-										!$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
1087
-
1088
-		// Password updated.
1089
-		if ($passwordsAreDifferent) {
1090
-			//Verify the password
1091
-			$this->verifyPassword($share->getPassword());
1092
-
1093
-			// If a password is set. Hash it!
1094
-			if ($share->getPassword() !== null) {
1095
-				$share->setPassword($this->hasher->hash($share->getPassword()));
1096
-
1097
-				return true;
1098
-			}
1099
-		} else {
1100
-			// Reset the password to the original one, as it is either the same
1101
-			// as the "new" password or a hashed version of it.
1102
-			$share->setPassword($originalShare->getPassword());
1103
-		}
1104
-
1105
-		return false;
1106
-	}
1107
-
1108
-	/**
1109
-	 * Delete all the children of this share
1110
-	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
1111
-	 *
1112
-	 * @param IShare $share
1113
-	 * @return IShare[] List of deleted shares
1114
-	 */
1115
-	protected function deleteChildren(IShare $share) {
1116
-		$deletedShares = [];
1117
-
1118
-		$provider = $this->factory->getProviderForType($share->getShareType());
1119
-
1120
-		foreach ($provider->getChildren($share) as $child) {
1121
-			$deletedChildren = $this->deleteChildren($child);
1122
-			$deletedShares = array_merge($deletedShares, $deletedChildren);
1123
-
1124
-			$provider->delete($child);
1125
-			$deletedShares[] = $child;
1126
-		}
1127
-
1128
-		return $deletedShares;
1129
-	}
1130
-
1131
-	/**
1132
-	 * Delete a share
1133
-	 *
1134
-	 * @param IShare $share
1135
-	 * @throws ShareNotFound
1136
-	 * @throws \InvalidArgumentException
1137
-	 */
1138
-	public function deleteShare(IShare $share) {
1139
-		try {
1140
-			$share->getFullId();
1141
-		} catch (\UnexpectedValueException $e) {
1142
-			throw new \InvalidArgumentException('Share does not have a full id');
1143
-		}
1144
-
1145
-		$event = new GenericEvent($share);
1146
-		$this->legacyDispatcher->dispatch('OCP\Share::preUnshare', $event);
1147
-
1148
-		// Get all children and delete them as well
1149
-		$deletedShares = $this->deleteChildren($share);
1150
-
1151
-		// Do the actual delete
1152
-		$provider = $this->factory->getProviderForType($share->getShareType());
1153
-		$provider->delete($share);
1154
-
1155
-		// All the deleted shares caused by this delete
1156
-		$deletedShares[] = $share;
1157
-
1158
-		// Emit post hook
1159
-		$event->setArgument('deletedShares', $deletedShares);
1160
-		$this->legacyDispatcher->dispatch('OCP\Share::postUnshare', $event);
1161
-	}
1162
-
1163
-
1164
-	/**
1165
-	 * Unshare a file as the recipient.
1166
-	 * This can be different from a regular delete for example when one of
1167
-	 * the users in a groups deletes that share. But the provider should
1168
-	 * handle this.
1169
-	 *
1170
-	 * @param IShare $share
1171
-	 * @param string $recipientId
1172
-	 */
1173
-	public function deleteFromSelf(IShare $share, $recipientId) {
1174
-		list($providerId, ) = $this->splitFullId($share->getFullId());
1175
-		$provider = $this->factory->getProvider($providerId);
1176
-
1177
-		$provider->deleteFromSelf($share, $recipientId);
1178
-		$event = new GenericEvent($share);
1179
-		$this->legacyDispatcher->dispatch('OCP\Share::postUnshareFromSelf', $event);
1180
-	}
1181
-
1182
-	public function restoreShare(IShare $share, string $recipientId): IShare {
1183
-		list($providerId, ) = $this->splitFullId($share->getFullId());
1184
-		$provider = $this->factory->getProvider($providerId);
1185
-
1186
-		return $provider->restore($share, $recipientId);
1187
-	}
1188
-
1189
-	/**
1190
-	 * @inheritdoc
1191
-	 */
1192
-	public function moveShare(IShare $share, $recipientId) {
1193
-		if ($share->getShareType() === IShare::TYPE_LINK) {
1194
-			throw new \InvalidArgumentException('Can’t change target of link share');
1195
-		}
1196
-
1197
-		if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
1198
-			throw new \InvalidArgumentException('Invalid recipient');
1199
-		}
1200
-
1201
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
1202
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1203
-			if (is_null($sharedWith)) {
1204
-				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
1205
-			}
1206
-			$recipient = $this->userManager->get($recipientId);
1207
-			if (!$sharedWith->inGroup($recipient)) {
1208
-				throw new \InvalidArgumentException('Invalid recipient');
1209
-			}
1210
-		}
1211
-
1212
-		list($providerId, ) = $this->splitFullId($share->getFullId());
1213
-		$provider = $this->factory->getProvider($providerId);
1214
-
1215
-		return $provider->move($share, $recipientId);
1216
-	}
1217
-
1218
-	public function getSharesInFolder($userId, Folder $node, $reshares = false) {
1219
-		$providers = $this->factory->getAllProviders();
1220
-
1221
-		return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
1222
-			$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
1223
-			foreach ($newShares as $fid => $data) {
1224
-				if (!isset($shares[$fid])) {
1225
-					$shares[$fid] = [];
1226
-				}
1227
-
1228
-				$shares[$fid] = array_merge($shares[$fid], $data);
1229
-			}
1230
-			return $shares;
1231
-		}, []);
1232
-	}
1233
-
1234
-	/**
1235
-	 * @inheritdoc
1236
-	 */
1237
-	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
1238
-		if ($path !== null &&
1239
-				!($path instanceof \OCP\Files\File) &&
1240
-				!($path instanceof \OCP\Files\Folder)) {
1241
-			throw new \InvalidArgumentException('invalid path');
1242
-		}
1243
-
1244
-		try {
1245
-			$provider = $this->factory->getProviderForType($shareType);
1246
-		} catch (ProviderException $e) {
1247
-			return [];
1248
-		}
1249
-
1250
-		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1251
-
1252
-		/*
740
+            $share->setToken(
741
+                $this->secureRandom->generate(
742
+                    \OC\Share\Constants::TOKEN_LENGTH,
743
+                    \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
744
+                )
745
+            );
746
+
747
+            //Verify the expiration date
748
+            $share = $this->validateExpirationDate($share);
749
+
750
+            //Verify the password
751
+            $this->verifyPassword($share->getPassword());
752
+
753
+            // If a password is set. Hash it!
754
+            if ($share->getPassword() !== null) {
755
+                $share->setPassword($this->hasher->hash($share->getPassword()));
756
+            }
757
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
758
+            $share->setToken(
759
+                $this->secureRandom->generate(
760
+                    \OC\Share\Constants::TOKEN_LENGTH,
761
+                    \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
762
+                )
763
+            );
764
+        }
765
+
766
+        // Cannot share with the owner
767
+        if ($share->getShareType() === IShare::TYPE_USER &&
768
+            $share->getSharedWith() === $share->getShareOwner()) {
769
+            throw new \InvalidArgumentException('Can’t share with the share owner');
770
+        }
771
+
772
+        // Generate the target
773
+        $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
774
+        $target = \OC\Files\Filesystem::normalizePath($target);
775
+        $share->setTarget($target);
776
+
777
+        // Pre share event
778
+        $event = new GenericEvent($share);
779
+        $this->legacyDispatcher->dispatch('OCP\Share::preShare', $event);
780
+        if ($event->isPropagationStopped() && $event->hasArgument('error')) {
781
+            throw new \Exception($event->getArgument('error'));
782
+        }
783
+
784
+        $oldShare = $share;
785
+        $provider = $this->factory->getProviderForType($share->getShareType());
786
+        $share = $provider->create($share);
787
+        //reuse the node we already have
788
+        $share->setNode($oldShare->getNode());
789
+
790
+        // Reset the target if it is null for the new share
791
+        if ($share->getTarget() === '') {
792
+            $share->setTarget($target);
793
+        }
794
+
795
+        // Post share event
796
+        $event = new GenericEvent($share);
797
+        $this->legacyDispatcher->dispatch('OCP\Share::postShare', $event);
798
+
799
+        $this->dispatcher->dispatchTyped(new Share\Events\ShareCreatedEvent($share));
800
+
801
+        if ($share->getShareType() === IShare::TYPE_USER) {
802
+            $mailSend = $share->getMailSend();
803
+            if ($mailSend === true) {
804
+                $user = $this->userManager->get($share->getSharedWith());
805
+                if ($user !== null) {
806
+                    $emailAddress = $user->getEMailAddress();
807
+                    if ($emailAddress !== null && $emailAddress !== '') {
808
+                        $userLang = $this->l10nFactory->getUserLanguage($user);
809
+                        $l = $this->l10nFactory->get('lib', $userLang);
810
+                        $this->sendMailNotification(
811
+                            $l,
812
+                            $share->getNode()->getName(),
813
+                            $this->urlGenerator->linkToRouteAbsolute('files_sharing.Accept.accept', ['shareId' => $share->getFullId()]),
814
+                            $share->getSharedBy(),
815
+                            $emailAddress,
816
+                            $share->getExpirationDate()
817
+                        );
818
+                        $this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId(), ['app' => 'share']);
819
+                    } else {
820
+                        $this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
821
+                    }
822
+                } else {
823
+                    $this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
824
+                }
825
+            } else {
826
+                $this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
827
+            }
828
+        }
829
+
830
+        return $share;
831
+    }
832
+
833
+    /**
834
+     * Send mail notifications
835
+     *
836
+     * This method will catch and log mail transmission errors
837
+     *
838
+     * @param IL10N $l Language of the recipient
839
+     * @param string $filename file/folder name
840
+     * @param string $link link to the file/folder
841
+     * @param string $initiator user ID of share sender
842
+     * @param string $shareWith email address of share receiver
843
+     * @param \DateTime|null $expiration
844
+     */
845
+    protected function sendMailNotification(IL10N $l,
846
+                                            $filename,
847
+                                            $link,
848
+                                            $initiator,
849
+                                            $shareWith,
850
+                                            \DateTime $expiration = null) {
851
+        $initiatorUser = $this->userManager->get($initiator);
852
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
853
+
854
+        $message = $this->mailer->createMessage();
855
+
856
+        $emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
857
+            'filename' => $filename,
858
+            'link' => $link,
859
+            'initiator' => $initiatorDisplayName,
860
+            'expiration' => $expiration,
861
+            'shareWith' => $shareWith,
862
+        ]);
863
+
864
+        $emailTemplate->setSubject($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]));
865
+        $emailTemplate->addHeader();
866
+        $emailTemplate->addHeading($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]), false);
867
+        $text = $l->t('%1$s shared »%2$s« with you.', [$initiatorDisplayName, $filename]);
868
+
869
+        $emailTemplate->addBodyText(
870
+            htmlspecialchars($text . ' ' . $l->t('Click the button below to open it.')),
871
+            $text
872
+        );
873
+        $emailTemplate->addBodyButton(
874
+            $l->t('Open »%s«', [$filename]),
875
+            $link
876
+        );
877
+
878
+        $message->setTo([$shareWith]);
879
+
880
+        // The "From" contains the sharers name
881
+        $instanceName = $this->defaults->getName();
882
+        $senderName = $l->t(
883
+            '%1$s via %2$s',
884
+            [
885
+                $initiatorDisplayName,
886
+                $instanceName
887
+            ]
888
+        );
889
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
890
+
891
+        // The "Reply-To" is set to the sharer if an mail address is configured
892
+        // also the default footer contains a "Do not reply" which needs to be adjusted.
893
+        $initiatorEmail = $initiatorUser->getEMailAddress();
894
+        if ($initiatorEmail !== null) {
895
+            $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
896
+            $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan($l->getLanguageCode()) !== '' ? ' - ' . $this->defaults->getSlogan($l->getLanguageCode()) : ''));
897
+        } else {
898
+            $emailTemplate->addFooter('', $l->getLanguageCode());
899
+        }
900
+
901
+        $message->useTemplate($emailTemplate);
902
+        try {
903
+            $failedRecipients = $this->mailer->send($message);
904
+            if (!empty($failedRecipients)) {
905
+                $this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients));
906
+                return;
907
+            }
908
+        } catch (\Exception $e) {
909
+            $this->logger->logException($e, ['message' => 'Share notification mail could not be sent']);
910
+        }
911
+    }
912
+
913
+    /**
914
+     * Update a share
915
+     *
916
+     * @param IShare $share
917
+     * @return IShare The share object
918
+     * @throws \InvalidArgumentException
919
+     */
920
+    public function updateShare(IShare $share) {
921
+        $expirationDateUpdated = false;
922
+
923
+        $this->canShare($share);
924
+
925
+        try {
926
+            $originalShare = $this->getShareById($share->getFullId());
927
+        } catch (\UnexpectedValueException $e) {
928
+            throw new \InvalidArgumentException('Share does not have a full id');
929
+        }
930
+
931
+        // We can't change the share type!
932
+        if ($share->getShareType() !== $originalShare->getShareType()) {
933
+            throw new \InvalidArgumentException('Can’t change share type');
934
+        }
935
+
936
+        // We can only change the recipient on user shares
937
+        if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
938
+            $share->getShareType() !== IShare::TYPE_USER) {
939
+            throw new \InvalidArgumentException('Can only update recipient on user shares');
940
+        }
941
+
942
+        // Cannot share with the owner
943
+        if ($share->getShareType() === IShare::TYPE_USER &&
944
+            $share->getSharedWith() === $share->getShareOwner()) {
945
+            throw new \InvalidArgumentException('Can’t share with the share owner');
946
+        }
947
+
948
+        $this->generalCreateChecks($share);
949
+
950
+        if ($share->getShareType() === IShare::TYPE_USER) {
951
+            $this->userCreateChecks($share);
952
+
953
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
954
+                //Verify the expiration date
955
+                $this->validateExpirationDate($share);
956
+                $expirationDateUpdated = true;
957
+            }
958
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
959
+            $this->groupCreateChecks($share);
960
+
961
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
962
+                //Verify the expiration date
963
+                $this->validateExpirationDate($share);
964
+                $expirationDateUpdated = true;
965
+            }
966
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
967
+            $this->linkCreateChecks($share);
968
+
969
+            $plainTextPassword = $share->getPassword();
970
+
971
+            $this->updateSharePasswordIfNeeded($share, $originalShare);
972
+
973
+            if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
974
+                throw new \InvalidArgumentException('Can’t enable sending the password by Talk with an empty password');
975
+            }
976
+
977
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
978
+                //Verify the expiration date
979
+                $this->validateExpirationDate($share);
980
+                $expirationDateUpdated = true;
981
+            }
982
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
983
+            // The new password is not set again if it is the same as the old
984
+            // one.
985
+            $plainTextPassword = $share->getPassword();
986
+            if (!empty($plainTextPassword) && !$this->updateSharePasswordIfNeeded($share, $originalShare)) {
987
+                $plainTextPassword = null;
988
+            }
989
+            if (empty($plainTextPassword) && !$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
990
+                // If the same password was already sent by mail the recipient
991
+                // would already have access to the share without having to call
992
+                // the sharer to verify her identity
993
+                throw new \InvalidArgumentException('Can’t enable sending the password by Talk without setting a new password');
994
+            } elseif (empty($plainTextPassword) && $originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
995
+                throw new \InvalidArgumentException('Can’t disable sending the password by Talk without setting a new password');
996
+            }
997
+        }
998
+
999
+        $this->pathCreateChecks($share->getNode());
1000
+
1001
+        // Now update the share!
1002
+        $provider = $this->factory->getProviderForType($share->getShareType());
1003
+        if ($share->getShareType() === IShare::TYPE_EMAIL) {
1004
+            $share = $provider->update($share, $plainTextPassword);
1005
+        } else {
1006
+            $share = $provider->update($share);
1007
+        }
1008
+
1009
+        if ($expirationDateUpdated === true) {
1010
+            \OC_Hook::emit(Share::class, 'post_set_expiration_date', [
1011
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1012
+                'itemSource' => $share->getNode()->getId(),
1013
+                'date' => $share->getExpirationDate(),
1014
+                'uidOwner' => $share->getSharedBy(),
1015
+            ]);
1016
+        }
1017
+
1018
+        if ($share->getPassword() !== $originalShare->getPassword()) {
1019
+            \OC_Hook::emit(Share::class, 'post_update_password', [
1020
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1021
+                'itemSource' => $share->getNode()->getId(),
1022
+                'uidOwner' => $share->getSharedBy(),
1023
+                'token' => $share->getToken(),
1024
+                'disabled' => is_null($share->getPassword()),
1025
+            ]);
1026
+        }
1027
+
1028
+        if ($share->getPermissions() !== $originalShare->getPermissions()) {
1029
+            if ($this->userManager->userExists($share->getShareOwner())) {
1030
+                $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
1031
+            } else {
1032
+                $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1033
+            }
1034
+            \OC_Hook::emit(Share::class, 'post_update_permissions', [
1035
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1036
+                'itemSource' => $share->getNode()->getId(),
1037
+                'shareType' => $share->getShareType(),
1038
+                'shareWith' => $share->getSharedWith(),
1039
+                'uidOwner' => $share->getSharedBy(),
1040
+                'permissions' => $share->getPermissions(),
1041
+                'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
1042
+            ]);
1043
+        }
1044
+
1045
+        return $share;
1046
+    }
1047
+
1048
+    /**
1049
+     * Accept a share.
1050
+     *
1051
+     * @param IShare $share
1052
+     * @param string $recipientId
1053
+     * @return IShare The share object
1054
+     * @throws \InvalidArgumentException
1055
+     * @since 9.0.0
1056
+     */
1057
+    public function acceptShare(IShare $share, string $recipientId): IShare {
1058
+        [$providerId, ] = $this->splitFullId($share->getFullId());
1059
+        $provider = $this->factory->getProvider($providerId);
1060
+
1061
+        if (!method_exists($provider, 'acceptShare')) {
1062
+            // TODO FIX ME
1063
+            throw new \InvalidArgumentException('Share provider does not support accepting');
1064
+        }
1065
+        $provider->acceptShare($share, $recipientId);
1066
+        $event = new GenericEvent($share);
1067
+        $this->legacyDispatcher->dispatch('OCP\Share::postAcceptShare', $event);
1068
+
1069
+        return $share;
1070
+    }
1071
+
1072
+    /**
1073
+     * Updates the password of the given share if it is not the same as the
1074
+     * password of the original share.
1075
+     *
1076
+     * @param IShare $share the share to update its password.
1077
+     * @param IShare $originalShare the original share to compare its
1078
+     *        password with.
1079
+     * @return boolean whether the password was updated or not.
1080
+     */
1081
+    private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
1082
+        $passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword()) &&
1083
+                                    (($share->getPassword() !== null && $originalShare->getPassword() === null) ||
1084
+                                     ($share->getPassword() === null && $originalShare->getPassword() !== null) ||
1085
+                                     ($share->getPassword() !== null && $originalShare->getPassword() !== null &&
1086
+                                        !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
1087
+
1088
+        // Password updated.
1089
+        if ($passwordsAreDifferent) {
1090
+            //Verify the password
1091
+            $this->verifyPassword($share->getPassword());
1092
+
1093
+            // If a password is set. Hash it!
1094
+            if ($share->getPassword() !== null) {
1095
+                $share->setPassword($this->hasher->hash($share->getPassword()));
1096
+
1097
+                return true;
1098
+            }
1099
+        } else {
1100
+            // Reset the password to the original one, as it is either the same
1101
+            // as the "new" password or a hashed version of it.
1102
+            $share->setPassword($originalShare->getPassword());
1103
+        }
1104
+
1105
+        return false;
1106
+    }
1107
+
1108
+    /**
1109
+     * Delete all the children of this share
1110
+     * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
1111
+     *
1112
+     * @param IShare $share
1113
+     * @return IShare[] List of deleted shares
1114
+     */
1115
+    protected function deleteChildren(IShare $share) {
1116
+        $deletedShares = [];
1117
+
1118
+        $provider = $this->factory->getProviderForType($share->getShareType());
1119
+
1120
+        foreach ($provider->getChildren($share) as $child) {
1121
+            $deletedChildren = $this->deleteChildren($child);
1122
+            $deletedShares = array_merge($deletedShares, $deletedChildren);
1123
+
1124
+            $provider->delete($child);
1125
+            $deletedShares[] = $child;
1126
+        }
1127
+
1128
+        return $deletedShares;
1129
+    }
1130
+
1131
+    /**
1132
+     * Delete a share
1133
+     *
1134
+     * @param IShare $share
1135
+     * @throws ShareNotFound
1136
+     * @throws \InvalidArgumentException
1137
+     */
1138
+    public function deleteShare(IShare $share) {
1139
+        try {
1140
+            $share->getFullId();
1141
+        } catch (\UnexpectedValueException $e) {
1142
+            throw new \InvalidArgumentException('Share does not have a full id');
1143
+        }
1144
+
1145
+        $event = new GenericEvent($share);
1146
+        $this->legacyDispatcher->dispatch('OCP\Share::preUnshare', $event);
1147
+
1148
+        // Get all children and delete them as well
1149
+        $deletedShares = $this->deleteChildren($share);
1150
+
1151
+        // Do the actual delete
1152
+        $provider = $this->factory->getProviderForType($share->getShareType());
1153
+        $provider->delete($share);
1154
+
1155
+        // All the deleted shares caused by this delete
1156
+        $deletedShares[] = $share;
1157
+
1158
+        // Emit post hook
1159
+        $event->setArgument('deletedShares', $deletedShares);
1160
+        $this->legacyDispatcher->dispatch('OCP\Share::postUnshare', $event);
1161
+    }
1162
+
1163
+
1164
+    /**
1165
+     * Unshare a file as the recipient.
1166
+     * This can be different from a regular delete for example when one of
1167
+     * the users in a groups deletes that share. But the provider should
1168
+     * handle this.
1169
+     *
1170
+     * @param IShare $share
1171
+     * @param string $recipientId
1172
+     */
1173
+    public function deleteFromSelf(IShare $share, $recipientId) {
1174
+        list($providerId, ) = $this->splitFullId($share->getFullId());
1175
+        $provider = $this->factory->getProvider($providerId);
1176
+
1177
+        $provider->deleteFromSelf($share, $recipientId);
1178
+        $event = new GenericEvent($share);
1179
+        $this->legacyDispatcher->dispatch('OCP\Share::postUnshareFromSelf', $event);
1180
+    }
1181
+
1182
+    public function restoreShare(IShare $share, string $recipientId): IShare {
1183
+        list($providerId, ) = $this->splitFullId($share->getFullId());
1184
+        $provider = $this->factory->getProvider($providerId);
1185
+
1186
+        return $provider->restore($share, $recipientId);
1187
+    }
1188
+
1189
+    /**
1190
+     * @inheritdoc
1191
+     */
1192
+    public function moveShare(IShare $share, $recipientId) {
1193
+        if ($share->getShareType() === IShare::TYPE_LINK) {
1194
+            throw new \InvalidArgumentException('Can’t change target of link share');
1195
+        }
1196
+
1197
+        if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
1198
+            throw new \InvalidArgumentException('Invalid recipient');
1199
+        }
1200
+
1201
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
1202
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1203
+            if (is_null($sharedWith)) {
1204
+                throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
1205
+            }
1206
+            $recipient = $this->userManager->get($recipientId);
1207
+            if (!$sharedWith->inGroup($recipient)) {
1208
+                throw new \InvalidArgumentException('Invalid recipient');
1209
+            }
1210
+        }
1211
+
1212
+        list($providerId, ) = $this->splitFullId($share->getFullId());
1213
+        $provider = $this->factory->getProvider($providerId);
1214
+
1215
+        return $provider->move($share, $recipientId);
1216
+    }
1217
+
1218
+    public function getSharesInFolder($userId, Folder $node, $reshares = false) {
1219
+        $providers = $this->factory->getAllProviders();
1220
+
1221
+        return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
1222
+            $newShares = $provider->getSharesInFolder($userId, $node, $reshares);
1223
+            foreach ($newShares as $fid => $data) {
1224
+                if (!isset($shares[$fid])) {
1225
+                    $shares[$fid] = [];
1226
+                }
1227
+
1228
+                $shares[$fid] = array_merge($shares[$fid], $data);
1229
+            }
1230
+            return $shares;
1231
+        }, []);
1232
+    }
1233
+
1234
+    /**
1235
+     * @inheritdoc
1236
+     */
1237
+    public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
1238
+        if ($path !== null &&
1239
+                !($path instanceof \OCP\Files\File) &&
1240
+                !($path instanceof \OCP\Files\Folder)) {
1241
+            throw new \InvalidArgumentException('invalid path');
1242
+        }
1243
+
1244
+        try {
1245
+            $provider = $this->factory->getProviderForType($shareType);
1246
+        } catch (ProviderException $e) {
1247
+            return [];
1248
+        }
1249
+
1250
+        $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1251
+
1252
+        /*
1253 1253
 		 * Work around so we don't return expired shares but still follow
1254 1254
 		 * proper pagination.
1255 1255
 		 */
1256 1256
 
1257
-		$shares2 = [];
1258
-
1259
-		while (true) {
1260
-			$added = 0;
1261
-			foreach ($shares as $share) {
1262
-				try {
1263
-					$this->checkExpireDate($share);
1264
-				} catch (ShareNotFound $e) {
1265
-					//Ignore since this basically means the share is deleted
1266
-					continue;
1267
-				}
1268
-
1269
-				$added++;
1270
-				$shares2[] = $share;
1271
-
1272
-				if (count($shares2) === $limit) {
1273
-					break;
1274
-				}
1275
-			}
1276
-
1277
-			// If we did not fetch more shares than the limit then there are no more shares
1278
-			if (count($shares) < $limit) {
1279
-				break;
1280
-			}
1281
-
1282
-			if (count($shares2) === $limit) {
1283
-				break;
1284
-			}
1285
-
1286
-			// If there was no limit on the select we are done
1287
-			if ($limit === -1) {
1288
-				break;
1289
-			}
1290
-
1291
-			$offset += $added;
1292
-
1293
-			// Fetch again $limit shares
1294
-			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1295
-
1296
-			// No more shares means we are done
1297
-			if (empty($shares)) {
1298
-				break;
1299
-			}
1300
-		}
1301
-
1302
-		$shares = $shares2;
1303
-
1304
-		return $shares;
1305
-	}
1306
-
1307
-	/**
1308
-	 * @inheritdoc
1309
-	 */
1310
-	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1311
-		try {
1312
-			$provider = $this->factory->getProviderForType($shareType);
1313
-		} catch (ProviderException $e) {
1314
-			return [];
1315
-		}
1316
-
1317
-		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1318
-
1319
-		// remove all shares which are already expired
1320
-		foreach ($shares as $key => $share) {
1321
-			try {
1322
-				$this->checkExpireDate($share);
1323
-			} catch (ShareNotFound $e) {
1324
-				unset($shares[$key]);
1325
-			}
1326
-		}
1327
-
1328
-		return $shares;
1329
-	}
1330
-
1331
-	/**
1332
-	 * @inheritdoc
1333
-	 */
1334
-	public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1335
-		$shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1336
-
1337
-		// Only get deleted shares
1338
-		$shares = array_filter($shares, function (IShare $share) {
1339
-			return $share->getPermissions() === 0;
1340
-		});
1341
-
1342
-		// Only get shares where the owner still exists
1343
-		$shares = array_filter($shares, function (IShare $share) {
1344
-			return $this->userManager->userExists($share->getShareOwner());
1345
-		});
1346
-
1347
-		return $shares;
1348
-	}
1349
-
1350
-	/**
1351
-	 * @inheritdoc
1352
-	 */
1353
-	public function getShareById($id, $recipient = null) {
1354
-		if ($id === null) {
1355
-			throw new ShareNotFound();
1356
-		}
1357
-
1358
-		list($providerId, $id) = $this->splitFullId($id);
1359
-
1360
-		try {
1361
-			$provider = $this->factory->getProvider($providerId);
1362
-		} catch (ProviderException $e) {
1363
-			throw new ShareNotFound();
1364
-		}
1365
-
1366
-		$share = $provider->getShareById($id, $recipient);
1367
-
1368
-		$this->checkExpireDate($share);
1369
-
1370
-		return $share;
1371
-	}
1372
-
1373
-	/**
1374
-	 * Get all the shares for a given path
1375
-	 *
1376
-	 * @param \OCP\Files\Node $path
1377
-	 * @param int $page
1378
-	 * @param int $perPage
1379
-	 *
1380
-	 * @return Share[]
1381
-	 */
1382
-	public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
1383
-		return [];
1384
-	}
1385
-
1386
-	/**
1387
-	 * Get the share by token possible with password
1388
-	 *
1389
-	 * @param string $token
1390
-	 * @return IShare
1391
-	 *
1392
-	 * @throws ShareNotFound
1393
-	 */
1394
-	public function getShareByToken($token) {
1395
-		// tokens can't be valid local user names
1396
-		if ($this->userManager->userExists($token)) {
1397
-			throw new ShareNotFound();
1398
-		}
1399
-		$share = null;
1400
-		try {
1401
-			if ($this->shareApiAllowLinks()) {
1402
-				$provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
1403
-				$share = $provider->getShareByToken($token);
1404
-			}
1405
-		} catch (ProviderException $e) {
1406
-		} catch (ShareNotFound $e) {
1407
-		}
1408
-
1409
-
1410
-		// If it is not a link share try to fetch a federated share by token
1411
-		if ($share === null) {
1412
-			try {
1413
-				$provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
1414
-				$share = $provider->getShareByToken($token);
1415
-			} catch (ProviderException $e) {
1416
-			} catch (ShareNotFound $e) {
1417
-			}
1418
-		}
1419
-
1420
-		// If it is not a link share try to fetch a mail share by token
1421
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
1422
-			try {
1423
-				$provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
1424
-				$share = $provider->getShareByToken($token);
1425
-			} catch (ProviderException $e) {
1426
-			} catch (ShareNotFound $e) {
1427
-			}
1428
-		}
1429
-
1430
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
1431
-			try {
1432
-				$provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
1433
-				$share = $provider->getShareByToken($token);
1434
-			} catch (ProviderException $e) {
1435
-			} catch (ShareNotFound $e) {
1436
-			}
1437
-		}
1438
-
1439
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
1440
-			try {
1441
-				$provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
1442
-				$share = $provider->getShareByToken($token);
1443
-			} catch (ProviderException $e) {
1444
-			} catch (ShareNotFound $e) {
1445
-			}
1446
-		}
1447
-
1448
-		if ($share === null) {
1449
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1450
-		}
1451
-
1452
-		$this->checkExpireDate($share);
1453
-
1454
-		/*
1257
+        $shares2 = [];
1258
+
1259
+        while (true) {
1260
+            $added = 0;
1261
+            foreach ($shares as $share) {
1262
+                try {
1263
+                    $this->checkExpireDate($share);
1264
+                } catch (ShareNotFound $e) {
1265
+                    //Ignore since this basically means the share is deleted
1266
+                    continue;
1267
+                }
1268
+
1269
+                $added++;
1270
+                $shares2[] = $share;
1271
+
1272
+                if (count($shares2) === $limit) {
1273
+                    break;
1274
+                }
1275
+            }
1276
+
1277
+            // If we did not fetch more shares than the limit then there are no more shares
1278
+            if (count($shares) < $limit) {
1279
+                break;
1280
+            }
1281
+
1282
+            if (count($shares2) === $limit) {
1283
+                break;
1284
+            }
1285
+
1286
+            // If there was no limit on the select we are done
1287
+            if ($limit === -1) {
1288
+                break;
1289
+            }
1290
+
1291
+            $offset += $added;
1292
+
1293
+            // Fetch again $limit shares
1294
+            $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1295
+
1296
+            // No more shares means we are done
1297
+            if (empty($shares)) {
1298
+                break;
1299
+            }
1300
+        }
1301
+
1302
+        $shares = $shares2;
1303
+
1304
+        return $shares;
1305
+    }
1306
+
1307
+    /**
1308
+     * @inheritdoc
1309
+     */
1310
+    public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1311
+        try {
1312
+            $provider = $this->factory->getProviderForType($shareType);
1313
+        } catch (ProviderException $e) {
1314
+            return [];
1315
+        }
1316
+
1317
+        $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1318
+
1319
+        // remove all shares which are already expired
1320
+        foreach ($shares as $key => $share) {
1321
+            try {
1322
+                $this->checkExpireDate($share);
1323
+            } catch (ShareNotFound $e) {
1324
+                unset($shares[$key]);
1325
+            }
1326
+        }
1327
+
1328
+        return $shares;
1329
+    }
1330
+
1331
+    /**
1332
+     * @inheritdoc
1333
+     */
1334
+    public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1335
+        $shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1336
+
1337
+        // Only get deleted shares
1338
+        $shares = array_filter($shares, function (IShare $share) {
1339
+            return $share->getPermissions() === 0;
1340
+        });
1341
+
1342
+        // Only get shares where the owner still exists
1343
+        $shares = array_filter($shares, function (IShare $share) {
1344
+            return $this->userManager->userExists($share->getShareOwner());
1345
+        });
1346
+
1347
+        return $shares;
1348
+    }
1349
+
1350
+    /**
1351
+     * @inheritdoc
1352
+     */
1353
+    public function getShareById($id, $recipient = null) {
1354
+        if ($id === null) {
1355
+            throw new ShareNotFound();
1356
+        }
1357
+
1358
+        list($providerId, $id) = $this->splitFullId($id);
1359
+
1360
+        try {
1361
+            $provider = $this->factory->getProvider($providerId);
1362
+        } catch (ProviderException $e) {
1363
+            throw new ShareNotFound();
1364
+        }
1365
+
1366
+        $share = $provider->getShareById($id, $recipient);
1367
+
1368
+        $this->checkExpireDate($share);
1369
+
1370
+        return $share;
1371
+    }
1372
+
1373
+    /**
1374
+     * Get all the shares for a given path
1375
+     *
1376
+     * @param \OCP\Files\Node $path
1377
+     * @param int $page
1378
+     * @param int $perPage
1379
+     *
1380
+     * @return Share[]
1381
+     */
1382
+    public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
1383
+        return [];
1384
+    }
1385
+
1386
+    /**
1387
+     * Get the share by token possible with password
1388
+     *
1389
+     * @param string $token
1390
+     * @return IShare
1391
+     *
1392
+     * @throws ShareNotFound
1393
+     */
1394
+    public function getShareByToken($token) {
1395
+        // tokens can't be valid local user names
1396
+        if ($this->userManager->userExists($token)) {
1397
+            throw new ShareNotFound();
1398
+        }
1399
+        $share = null;
1400
+        try {
1401
+            if ($this->shareApiAllowLinks()) {
1402
+                $provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
1403
+                $share = $provider->getShareByToken($token);
1404
+            }
1405
+        } catch (ProviderException $e) {
1406
+        } catch (ShareNotFound $e) {
1407
+        }
1408
+
1409
+
1410
+        // If it is not a link share try to fetch a federated share by token
1411
+        if ($share === null) {
1412
+            try {
1413
+                $provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
1414
+                $share = $provider->getShareByToken($token);
1415
+            } catch (ProviderException $e) {
1416
+            } catch (ShareNotFound $e) {
1417
+            }
1418
+        }
1419
+
1420
+        // If it is not a link share try to fetch a mail share by token
1421
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
1422
+            try {
1423
+                $provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
1424
+                $share = $provider->getShareByToken($token);
1425
+            } catch (ProviderException $e) {
1426
+            } catch (ShareNotFound $e) {
1427
+            }
1428
+        }
1429
+
1430
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
1431
+            try {
1432
+                $provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
1433
+                $share = $provider->getShareByToken($token);
1434
+            } catch (ProviderException $e) {
1435
+            } catch (ShareNotFound $e) {
1436
+            }
1437
+        }
1438
+
1439
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
1440
+            try {
1441
+                $provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
1442
+                $share = $provider->getShareByToken($token);
1443
+            } catch (ProviderException $e) {
1444
+            } catch (ShareNotFound $e) {
1445
+            }
1446
+        }
1447
+
1448
+        if ($share === null) {
1449
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1450
+        }
1451
+
1452
+        $this->checkExpireDate($share);
1453
+
1454
+        /*
1455 1455
 		 * Reduce the permissions for link shares if public upload is not enabled
1456 1456
 		 */
1457
-		if ($share->getShareType() === IShare::TYPE_LINK &&
1458
-			!$this->shareApiLinkAllowPublicUpload()) {
1459
-			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1460
-		}
1461
-
1462
-		return $share;
1463
-	}
1464
-
1465
-	protected function checkExpireDate($share) {
1466
-		if ($share->isExpired()) {
1467
-			$this->deleteShare($share);
1468
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1469
-		}
1470
-	}
1471
-
1472
-	/**
1473
-	 * Verify the password of a public share
1474
-	 *
1475
-	 * @param IShare $share
1476
-	 * @param string $password
1477
-	 * @return bool
1478
-	 */
1479
-	public function checkPassword(IShare $share, $password) {
1480
-		$passwordProtected = $share->getShareType() !== IShare::TYPE_LINK
1481
-							 || $share->getShareType() !== IShare::TYPE_EMAIL
1482
-							 || $share->getShareType() !== IShare::TYPE_CIRCLE;
1483
-		if (!$passwordProtected) {
1484
-			//TODO maybe exception?
1485
-			return false;
1486
-		}
1487
-
1488
-		if ($password === null || $share->getPassword() === null) {
1489
-			return false;
1490
-		}
1491
-
1492
-		$newHash = '';
1493
-		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1494
-			return false;
1495
-		}
1496
-
1497
-		if (!empty($newHash)) {
1498
-			$share->setPassword($newHash);
1499
-			$provider = $this->factory->getProviderForType($share->getShareType());
1500
-			$provider->update($share);
1501
-		}
1502
-
1503
-		return true;
1504
-	}
1505
-
1506
-	/**
1507
-	 * @inheritdoc
1508
-	 */
1509
-	public function userDeleted($uid) {
1510
-		$types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
1511
-
1512
-		foreach ($types as $type) {
1513
-			try {
1514
-				$provider = $this->factory->getProviderForType($type);
1515
-			} catch (ProviderException $e) {
1516
-				continue;
1517
-			}
1518
-			$provider->userDeleted($uid, $type);
1519
-		}
1520
-	}
1521
-
1522
-	/**
1523
-	 * @inheritdoc
1524
-	 */
1525
-	public function groupDeleted($gid) {
1526
-		$provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
1527
-		$provider->groupDeleted($gid);
1528
-
1529
-		$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1530
-		if ($excludedGroups === '') {
1531
-			return;
1532
-		}
1533
-
1534
-		$excludedGroups = json_decode($excludedGroups, true);
1535
-		if (json_last_error() !== JSON_ERROR_NONE) {
1536
-			return;
1537
-		}
1538
-
1539
-		$excludedGroups = array_diff($excludedGroups, [$gid]);
1540
-		$this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1541
-	}
1542
-
1543
-	/**
1544
-	 * @inheritdoc
1545
-	 */
1546
-	public function userDeletedFromGroup($uid, $gid) {
1547
-		$provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
1548
-		$provider->userDeletedFromGroup($uid, $gid);
1549
-	}
1550
-
1551
-	/**
1552
-	 * Get access list to a path. This means
1553
-	 * all the users that can access a given path.
1554
-	 *
1555
-	 * Consider:
1556
-	 * -root
1557
-	 * |-folder1 (23)
1558
-	 *  |-folder2 (32)
1559
-	 *   |-fileA (42)
1560
-	 *
1561
-	 * fileA is shared with user1 and user1@server1
1562
-	 * folder2 is shared with group2 (user4 is a member of group2)
1563
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1564
-	 *
1565
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1566
-	 * [
1567
-	 *  users  => [
1568
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1569
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1570
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1571
-	 *  ],
1572
-	 *  remote => [
1573
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1574
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1575
-	 *  ],
1576
-	 *  public => bool
1577
-	 *  mail => bool
1578
-	 * ]
1579
-	 *
1580
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1581
-	 * [
1582
-	 *  users  => ['user1', 'user2', 'user4'],
1583
-	 *  remote => bool,
1584
-	 *  public => bool
1585
-	 *  mail => bool
1586
-	 * ]
1587
-	 *
1588
-	 * This is required for encryption/activity
1589
-	 *
1590
-	 * @param \OCP\Files\Node $path
1591
-	 * @param bool $recursive Should we check all parent folders as well
1592
-	 * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1593
-	 * @return array
1594
-	 */
1595
-	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1596
-		$owner = $path->getOwner();
1597
-
1598
-		if ($owner === null) {
1599
-			return [];
1600
-		}
1601
-
1602
-		$owner = $owner->getUID();
1603
-
1604
-		if ($currentAccess) {
1605
-			$al = ['users' => [], 'remote' => [], 'public' => false];
1606
-		} else {
1607
-			$al = ['users' => [], 'remote' => false, 'public' => false];
1608
-		}
1609
-		if (!$this->userManager->userExists($owner)) {
1610
-			return $al;
1611
-		}
1612
-
1613
-		//Get node for the owner and correct the owner in case of external storages
1614
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1615
-		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1616
-			$nodes = $userFolder->getById($path->getId());
1617
-			$path = array_shift($nodes);
1618
-			if ($path->getOwner() === null) {
1619
-				return [];
1620
-			}
1621
-			$owner = $path->getOwner()->getUID();
1622
-		}
1623
-
1624
-		$providers = $this->factory->getAllProviders();
1625
-
1626
-		/** @var Node[] $nodes */
1627
-		$nodes = [];
1628
-
1629
-
1630
-		if ($currentAccess) {
1631
-			$ownerPath = $path->getPath();
1632
-			$ownerPath = explode('/', $ownerPath, 4);
1633
-			if (count($ownerPath) < 4) {
1634
-				$ownerPath = '';
1635
-			} else {
1636
-				$ownerPath = $ownerPath[3];
1637
-			}
1638
-			$al['users'][$owner] = [
1639
-				'node_id' => $path->getId(),
1640
-				'node_path' => '/' . $ownerPath,
1641
-			];
1642
-		} else {
1643
-			$al['users'][] = $owner;
1644
-		}
1645
-
1646
-		// Collect all the shares
1647
-		while ($path->getPath() !== $userFolder->getPath()) {
1648
-			$nodes[] = $path;
1649
-			if (!$recursive) {
1650
-				break;
1651
-			}
1652
-			$path = $path->getParent();
1653
-		}
1654
-
1655
-		foreach ($providers as $provider) {
1656
-			$tmp = $provider->getAccessList($nodes, $currentAccess);
1657
-
1658
-			foreach ($tmp as $k => $v) {
1659
-				if (isset($al[$k])) {
1660
-					if (is_array($al[$k])) {
1661
-						if ($currentAccess) {
1662
-							$al[$k] += $v;
1663
-						} else {
1664
-							$al[$k] = array_merge($al[$k], $v);
1665
-							$al[$k] = array_unique($al[$k]);
1666
-							$al[$k] = array_values($al[$k]);
1667
-						}
1668
-					} else {
1669
-						$al[$k] = $al[$k] || $v;
1670
-					}
1671
-				} else {
1672
-					$al[$k] = $v;
1673
-				}
1674
-			}
1675
-		}
1676
-
1677
-		return $al;
1678
-	}
1679
-
1680
-	/**
1681
-	 * Create a new share
1682
-	 *
1683
-	 * @return IShare
1684
-	 */
1685
-	public function newShare() {
1686
-		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1687
-	}
1688
-
1689
-	/**
1690
-	 * Is the share API enabled
1691
-	 *
1692
-	 * @return bool
1693
-	 */
1694
-	public function shareApiEnabled() {
1695
-		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1696
-	}
1697
-
1698
-	/**
1699
-	 * Is public link sharing enabled
1700
-	 *
1701
-	 * @return bool
1702
-	 */
1703
-	public function shareApiAllowLinks() {
1704
-		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1705
-	}
1706
-
1707
-	/**
1708
-	 * Is password on public link requires
1709
-	 *
1710
-	 * @return bool
1711
-	 */
1712
-	public function shareApiLinkEnforcePassword() {
1713
-		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1714
-	}
1715
-
1716
-	/**
1717
-	 * Is default link expire date enabled
1718
-	 *
1719
-	 * @return bool
1720
-	 */
1721
-	public function shareApiLinkDefaultExpireDate() {
1722
-		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1723
-	}
1724
-
1725
-	/**
1726
-	 * Is default link expire date enforced
1727
-	 *`
1728
-	 * @return bool
1729
-	 */
1730
-	public function shareApiLinkDefaultExpireDateEnforced() {
1731
-		return $this->shareApiLinkDefaultExpireDate() &&
1732
-			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1733
-	}
1734
-
1735
-
1736
-	/**
1737
-	 * Number of default link expire days
1738
-	 * @return int
1739
-	 */
1740
-	public function shareApiLinkDefaultExpireDays() {
1741
-		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1742
-	}
1743
-
1744
-	/**
1745
-	 * Is default internal expire date enabled
1746
-	 *
1747
-	 * @return bool
1748
-	 */
1749
-	public function shareApiInternalDefaultExpireDate(): bool {
1750
-		return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1751
-	}
1752
-
1753
-	/**
1754
-	 * Is default expire date enforced
1755
-	 *`
1756
-	 * @return bool
1757
-	 */
1758
-	public function shareApiInternalDefaultExpireDateEnforced(): bool {
1759
-		return $this->shareApiInternalDefaultExpireDate() &&
1760
-			$this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1761
-	}
1762
-
1763
-
1764
-	/**
1765
-	 * Number of default expire days
1766
-	 * @return int
1767
-	 */
1768
-	public function shareApiInternalDefaultExpireDays(): int {
1769
-		return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1770
-	}
1771
-
1772
-	/**
1773
-	 * Allow public upload on link shares
1774
-	 *
1775
-	 * @return bool
1776
-	 */
1777
-	public function shareApiLinkAllowPublicUpload() {
1778
-		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1779
-	}
1780
-
1781
-	/**
1782
-	 * check if user can only share with group members
1783
-	 * @return bool
1784
-	 */
1785
-	public function shareWithGroupMembersOnly() {
1786
-		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1787
-	}
1788
-
1789
-	/**
1790
-	 * Check if users can share with groups
1791
-	 * @return bool
1792
-	 */
1793
-	public function allowGroupSharing() {
1794
-		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1795
-	}
1796
-
1797
-	public function allowEnumeration(): bool {
1798
-		return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1799
-	}
1800
-
1801
-	public function limitEnumerationToGroups(): bool {
1802
-		return $this->allowEnumeration() &&
1803
-			$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1804
-	}
1805
-
1806
-	/**
1807
-	 * Copied from \OC_Util::isSharingDisabledForUser
1808
-	 *
1809
-	 * TODO: Deprecate fuction from OC_Util
1810
-	 *
1811
-	 * @param string $userId
1812
-	 * @return bool
1813
-	 */
1814
-	public function sharingDisabledForUser($userId) {
1815
-		if ($userId === null) {
1816
-			return false;
1817
-		}
1818
-
1819
-		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1820
-			return $this->sharingDisabledForUsersCache[$userId];
1821
-		}
1822
-
1823
-		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1824
-			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1825
-			$excludedGroups = json_decode($groupsList);
1826
-			if (is_null($excludedGroups)) {
1827
-				$excludedGroups = explode(',', $groupsList);
1828
-				$newValue = json_encode($excludedGroups);
1829
-				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1830
-			}
1831
-			$user = $this->userManager->get($userId);
1832
-			$usersGroups = $this->groupManager->getUserGroupIds($user);
1833
-			if (!empty($usersGroups)) {
1834
-				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1835
-				// if the user is only in groups which are disabled for sharing then
1836
-				// sharing is also disabled for the user
1837
-				if (empty($remainingGroups)) {
1838
-					$this->sharingDisabledForUsersCache[$userId] = true;
1839
-					return true;
1840
-				}
1841
-			}
1842
-		}
1843
-
1844
-		$this->sharingDisabledForUsersCache[$userId] = false;
1845
-		return false;
1846
-	}
1847
-
1848
-	/**
1849
-	 * @inheritdoc
1850
-	 */
1851
-	public function outgoingServer2ServerSharesAllowed() {
1852
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1853
-	}
1854
-
1855
-	/**
1856
-	 * @inheritdoc
1857
-	 */
1858
-	public function outgoingServer2ServerGroupSharesAllowed() {
1859
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
1860
-	}
1861
-
1862
-	/**
1863
-	 * @inheritdoc
1864
-	 */
1865
-	public function shareProviderExists($shareType) {
1866
-		try {
1867
-			$this->factory->getProviderForType($shareType);
1868
-		} catch (ProviderException $e) {
1869
-			return false;
1870
-		}
1871
-
1872
-		return true;
1873
-	}
1874
-
1875
-	public function getAllShares(): iterable {
1876
-		$providers = $this->factory->getAllProviders();
1877
-
1878
-		foreach ($providers as $provider) {
1879
-			yield from $provider->getAllShares();
1880
-		}
1881
-	}
1457
+        if ($share->getShareType() === IShare::TYPE_LINK &&
1458
+            !$this->shareApiLinkAllowPublicUpload()) {
1459
+            $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1460
+        }
1461
+
1462
+        return $share;
1463
+    }
1464
+
1465
+    protected function checkExpireDate($share) {
1466
+        if ($share->isExpired()) {
1467
+            $this->deleteShare($share);
1468
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1469
+        }
1470
+    }
1471
+
1472
+    /**
1473
+     * Verify the password of a public share
1474
+     *
1475
+     * @param IShare $share
1476
+     * @param string $password
1477
+     * @return bool
1478
+     */
1479
+    public function checkPassword(IShare $share, $password) {
1480
+        $passwordProtected = $share->getShareType() !== IShare::TYPE_LINK
1481
+                             || $share->getShareType() !== IShare::TYPE_EMAIL
1482
+                             || $share->getShareType() !== IShare::TYPE_CIRCLE;
1483
+        if (!$passwordProtected) {
1484
+            //TODO maybe exception?
1485
+            return false;
1486
+        }
1487
+
1488
+        if ($password === null || $share->getPassword() === null) {
1489
+            return false;
1490
+        }
1491
+
1492
+        $newHash = '';
1493
+        if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1494
+            return false;
1495
+        }
1496
+
1497
+        if (!empty($newHash)) {
1498
+            $share->setPassword($newHash);
1499
+            $provider = $this->factory->getProviderForType($share->getShareType());
1500
+            $provider->update($share);
1501
+        }
1502
+
1503
+        return true;
1504
+    }
1505
+
1506
+    /**
1507
+     * @inheritdoc
1508
+     */
1509
+    public function userDeleted($uid) {
1510
+        $types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
1511
+
1512
+        foreach ($types as $type) {
1513
+            try {
1514
+                $provider = $this->factory->getProviderForType($type);
1515
+            } catch (ProviderException $e) {
1516
+                continue;
1517
+            }
1518
+            $provider->userDeleted($uid, $type);
1519
+        }
1520
+    }
1521
+
1522
+    /**
1523
+     * @inheritdoc
1524
+     */
1525
+    public function groupDeleted($gid) {
1526
+        $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
1527
+        $provider->groupDeleted($gid);
1528
+
1529
+        $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1530
+        if ($excludedGroups === '') {
1531
+            return;
1532
+        }
1533
+
1534
+        $excludedGroups = json_decode($excludedGroups, true);
1535
+        if (json_last_error() !== JSON_ERROR_NONE) {
1536
+            return;
1537
+        }
1538
+
1539
+        $excludedGroups = array_diff($excludedGroups, [$gid]);
1540
+        $this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1541
+    }
1542
+
1543
+    /**
1544
+     * @inheritdoc
1545
+     */
1546
+    public function userDeletedFromGroup($uid, $gid) {
1547
+        $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
1548
+        $provider->userDeletedFromGroup($uid, $gid);
1549
+    }
1550
+
1551
+    /**
1552
+     * Get access list to a path. This means
1553
+     * all the users that can access a given path.
1554
+     *
1555
+     * Consider:
1556
+     * -root
1557
+     * |-folder1 (23)
1558
+     *  |-folder2 (32)
1559
+     *   |-fileA (42)
1560
+     *
1561
+     * fileA is shared with user1 and user1@server1
1562
+     * folder2 is shared with group2 (user4 is a member of group2)
1563
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1564
+     *
1565
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1566
+     * [
1567
+     *  users  => [
1568
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1569
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1570
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1571
+     *  ],
1572
+     *  remote => [
1573
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1574
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1575
+     *  ],
1576
+     *  public => bool
1577
+     *  mail => bool
1578
+     * ]
1579
+     *
1580
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1581
+     * [
1582
+     *  users  => ['user1', 'user2', 'user4'],
1583
+     *  remote => bool,
1584
+     *  public => bool
1585
+     *  mail => bool
1586
+     * ]
1587
+     *
1588
+     * This is required for encryption/activity
1589
+     *
1590
+     * @param \OCP\Files\Node $path
1591
+     * @param bool $recursive Should we check all parent folders as well
1592
+     * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1593
+     * @return array
1594
+     */
1595
+    public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1596
+        $owner = $path->getOwner();
1597
+
1598
+        if ($owner === null) {
1599
+            return [];
1600
+        }
1601
+
1602
+        $owner = $owner->getUID();
1603
+
1604
+        if ($currentAccess) {
1605
+            $al = ['users' => [], 'remote' => [], 'public' => false];
1606
+        } else {
1607
+            $al = ['users' => [], 'remote' => false, 'public' => false];
1608
+        }
1609
+        if (!$this->userManager->userExists($owner)) {
1610
+            return $al;
1611
+        }
1612
+
1613
+        //Get node for the owner and correct the owner in case of external storages
1614
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1615
+        if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1616
+            $nodes = $userFolder->getById($path->getId());
1617
+            $path = array_shift($nodes);
1618
+            if ($path->getOwner() === null) {
1619
+                return [];
1620
+            }
1621
+            $owner = $path->getOwner()->getUID();
1622
+        }
1623
+
1624
+        $providers = $this->factory->getAllProviders();
1625
+
1626
+        /** @var Node[] $nodes */
1627
+        $nodes = [];
1628
+
1629
+
1630
+        if ($currentAccess) {
1631
+            $ownerPath = $path->getPath();
1632
+            $ownerPath = explode('/', $ownerPath, 4);
1633
+            if (count($ownerPath) < 4) {
1634
+                $ownerPath = '';
1635
+            } else {
1636
+                $ownerPath = $ownerPath[3];
1637
+            }
1638
+            $al['users'][$owner] = [
1639
+                'node_id' => $path->getId(),
1640
+                'node_path' => '/' . $ownerPath,
1641
+            ];
1642
+        } else {
1643
+            $al['users'][] = $owner;
1644
+        }
1645
+
1646
+        // Collect all the shares
1647
+        while ($path->getPath() !== $userFolder->getPath()) {
1648
+            $nodes[] = $path;
1649
+            if (!$recursive) {
1650
+                break;
1651
+            }
1652
+            $path = $path->getParent();
1653
+        }
1654
+
1655
+        foreach ($providers as $provider) {
1656
+            $tmp = $provider->getAccessList($nodes, $currentAccess);
1657
+
1658
+            foreach ($tmp as $k => $v) {
1659
+                if (isset($al[$k])) {
1660
+                    if (is_array($al[$k])) {
1661
+                        if ($currentAccess) {
1662
+                            $al[$k] += $v;
1663
+                        } else {
1664
+                            $al[$k] = array_merge($al[$k], $v);
1665
+                            $al[$k] = array_unique($al[$k]);
1666
+                            $al[$k] = array_values($al[$k]);
1667
+                        }
1668
+                    } else {
1669
+                        $al[$k] = $al[$k] || $v;
1670
+                    }
1671
+                } else {
1672
+                    $al[$k] = $v;
1673
+                }
1674
+            }
1675
+        }
1676
+
1677
+        return $al;
1678
+    }
1679
+
1680
+    /**
1681
+     * Create a new share
1682
+     *
1683
+     * @return IShare
1684
+     */
1685
+    public function newShare() {
1686
+        return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1687
+    }
1688
+
1689
+    /**
1690
+     * Is the share API enabled
1691
+     *
1692
+     * @return bool
1693
+     */
1694
+    public function shareApiEnabled() {
1695
+        return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1696
+    }
1697
+
1698
+    /**
1699
+     * Is public link sharing enabled
1700
+     *
1701
+     * @return bool
1702
+     */
1703
+    public function shareApiAllowLinks() {
1704
+        return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1705
+    }
1706
+
1707
+    /**
1708
+     * Is password on public link requires
1709
+     *
1710
+     * @return bool
1711
+     */
1712
+    public function shareApiLinkEnforcePassword() {
1713
+        return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1714
+    }
1715
+
1716
+    /**
1717
+     * Is default link expire date enabled
1718
+     *
1719
+     * @return bool
1720
+     */
1721
+    public function shareApiLinkDefaultExpireDate() {
1722
+        return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1723
+    }
1724
+
1725
+    /**
1726
+     * Is default link expire date enforced
1727
+     *`
1728
+     * @return bool
1729
+     */
1730
+    public function shareApiLinkDefaultExpireDateEnforced() {
1731
+        return $this->shareApiLinkDefaultExpireDate() &&
1732
+            $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1733
+    }
1734
+
1735
+
1736
+    /**
1737
+     * Number of default link expire days
1738
+     * @return int
1739
+     */
1740
+    public function shareApiLinkDefaultExpireDays() {
1741
+        return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1742
+    }
1743
+
1744
+    /**
1745
+     * Is default internal expire date enabled
1746
+     *
1747
+     * @return bool
1748
+     */
1749
+    public function shareApiInternalDefaultExpireDate(): bool {
1750
+        return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1751
+    }
1752
+
1753
+    /**
1754
+     * Is default expire date enforced
1755
+     *`
1756
+     * @return bool
1757
+     */
1758
+    public function shareApiInternalDefaultExpireDateEnforced(): bool {
1759
+        return $this->shareApiInternalDefaultExpireDate() &&
1760
+            $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1761
+    }
1762
+
1763
+
1764
+    /**
1765
+     * Number of default expire days
1766
+     * @return int
1767
+     */
1768
+    public function shareApiInternalDefaultExpireDays(): int {
1769
+        return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1770
+    }
1771
+
1772
+    /**
1773
+     * Allow public upload on link shares
1774
+     *
1775
+     * @return bool
1776
+     */
1777
+    public function shareApiLinkAllowPublicUpload() {
1778
+        return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1779
+    }
1780
+
1781
+    /**
1782
+     * check if user can only share with group members
1783
+     * @return bool
1784
+     */
1785
+    public function shareWithGroupMembersOnly() {
1786
+        return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1787
+    }
1788
+
1789
+    /**
1790
+     * Check if users can share with groups
1791
+     * @return bool
1792
+     */
1793
+    public function allowGroupSharing() {
1794
+        return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1795
+    }
1796
+
1797
+    public function allowEnumeration(): bool {
1798
+        return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1799
+    }
1800
+
1801
+    public function limitEnumerationToGroups(): bool {
1802
+        return $this->allowEnumeration() &&
1803
+            $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1804
+    }
1805
+
1806
+    /**
1807
+     * Copied from \OC_Util::isSharingDisabledForUser
1808
+     *
1809
+     * TODO: Deprecate fuction from OC_Util
1810
+     *
1811
+     * @param string $userId
1812
+     * @return bool
1813
+     */
1814
+    public function sharingDisabledForUser($userId) {
1815
+        if ($userId === null) {
1816
+            return false;
1817
+        }
1818
+
1819
+        if (isset($this->sharingDisabledForUsersCache[$userId])) {
1820
+            return $this->sharingDisabledForUsersCache[$userId];
1821
+        }
1822
+
1823
+        if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1824
+            $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1825
+            $excludedGroups = json_decode($groupsList);
1826
+            if (is_null($excludedGroups)) {
1827
+                $excludedGroups = explode(',', $groupsList);
1828
+                $newValue = json_encode($excludedGroups);
1829
+                $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1830
+            }
1831
+            $user = $this->userManager->get($userId);
1832
+            $usersGroups = $this->groupManager->getUserGroupIds($user);
1833
+            if (!empty($usersGroups)) {
1834
+                $remainingGroups = array_diff($usersGroups, $excludedGroups);
1835
+                // if the user is only in groups which are disabled for sharing then
1836
+                // sharing is also disabled for the user
1837
+                if (empty($remainingGroups)) {
1838
+                    $this->sharingDisabledForUsersCache[$userId] = true;
1839
+                    return true;
1840
+                }
1841
+            }
1842
+        }
1843
+
1844
+        $this->sharingDisabledForUsersCache[$userId] = false;
1845
+        return false;
1846
+    }
1847
+
1848
+    /**
1849
+     * @inheritdoc
1850
+     */
1851
+    public function outgoingServer2ServerSharesAllowed() {
1852
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1853
+    }
1854
+
1855
+    /**
1856
+     * @inheritdoc
1857
+     */
1858
+    public function outgoingServer2ServerGroupSharesAllowed() {
1859
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
1860
+    }
1861
+
1862
+    /**
1863
+     * @inheritdoc
1864
+     */
1865
+    public function shareProviderExists($shareType) {
1866
+        try {
1867
+            $this->factory->getProviderForType($shareType);
1868
+        } catch (ProviderException $e) {
1869
+            return false;
1870
+        }
1871
+
1872
+        return true;
1873
+    }
1874
+
1875
+    public function getAllShares(): iterable {
1876
+        $providers = $this->factory->getAllProviders();
1877
+
1878
+        foreach ($providers as $provider) {
1879
+            yield from $provider->getAllShares();
1880
+        }
1881
+    }
1882 1882
 }
Please login to merge, or discard this patch.
lib/public/L10N/IFactory.php 1 patch
Indentation   +90 added lines, -90 removed lines patch added patch discarded remove patch
@@ -35,104 +35,104 @@
 block discarded – undo
35 35
  * @since 8.2.0
36 36
  */
37 37
 interface IFactory {
38
-	/**
39
-	 * Get a language instance
40
-	 *
41
-	 * @param string $app
42
-	 * @param string|null $lang
43
-	 * @param string|null $locale
44
-	 * @return \OCP\IL10N
45
-	 * @since 8.2.0
46
-	 */
47
-	public function get($app, $lang = null, $locale = null);
38
+    /**
39
+     * Get a language instance
40
+     *
41
+     * @param string $app
42
+     * @param string|null $lang
43
+     * @param string|null $locale
44
+     * @return \OCP\IL10N
45
+     * @since 8.2.0
46
+     */
47
+    public function get($app, $lang = null, $locale = null);
48 48
 
49
-	/**
50
-	 * Find the best language
51
-	 *
52
-	 * @param string|null $app App id or null for core
53
-	 * @return string language If nothing works it returns 'en'
54
-	 * @since 9.0.0
55
-	 */
56
-	public function findLanguage($app = null);
49
+    /**
50
+     * Find the best language
51
+     *
52
+     * @param string|null $app App id or null for core
53
+     * @return string language If nothing works it returns 'en'
54
+     * @since 9.0.0
55
+     */
56
+    public function findLanguage($app = null);
57 57
 
58
-	/**
59
-	 * @param string|null $lang user language as default locale
60
-	 * @return string locale If nothing works it returns 'en_US'
61
-	 * @since 14.0.0
62
-	 */
63
-	public function findLocale($lang = null);
58
+    /**
59
+     * @param string|null $lang user language as default locale
60
+     * @return string locale If nothing works it returns 'en_US'
61
+     * @since 14.0.0
62
+     */
63
+    public function findLocale($lang = null);
64 64
 
65
-	/**
66
-	 * find the matching lang from the locale
67
-	 *
68
-	 * @param string $app
69
-	 * @param string $locale
70
-	 * @return null|string
71
-	 * @since 14.0.1
72
-	 */
73
-	public function findLanguageFromLocale(string $app = 'core', string $locale = null);
65
+    /**
66
+     * find the matching lang from the locale
67
+     *
68
+     * @param string $app
69
+     * @param string $locale
70
+     * @return null|string
71
+     * @since 14.0.1
72
+     */
73
+    public function findLanguageFromLocale(string $app = 'core', string $locale = null);
74 74
 
75
-	/**
76
-	 * Find all available languages for an app
77
-	 *
78
-	 * @param string|null $app App id or null for core
79
-	 * @return string[] an array of available languages
80
-	 * @since 9.0.0
81
-	 */
82
-	public function findAvailableLanguages($app = null);
75
+    /**
76
+     * Find all available languages for an app
77
+     *
78
+     * @param string|null $app App id or null for core
79
+     * @return string[] an array of available languages
80
+     * @since 9.0.0
81
+     */
82
+    public function findAvailableLanguages($app = null);
83 83
 
84
-	/**
85
-	 * @return array an array of available
86
-	 * @since 14.0.0
87
-	 */
88
-	public function findAvailableLocales();
84
+    /**
85
+     * @return array an array of available
86
+     * @since 14.0.0
87
+     */
88
+    public function findAvailableLocales();
89 89
 
90
-	/**
91
-	 * @param string|null $app App id or null for core
92
-	 * @param string $lang
93
-	 * @return bool
94
-	 * @since 9.0.0
95
-	 */
96
-	public function languageExists($app, $lang);
90
+    /**
91
+     * @param string|null $app App id or null for core
92
+     * @param string $lang
93
+     * @return bool
94
+     * @since 9.0.0
95
+     */
96
+    public function languageExists($app, $lang);
97 97
 
98
-	/**
99
-	 * @param string $locale
100
-	 * @return bool
101
-	 * @since 14.0.0
102
-	 */
103
-	public function localeExists($locale);
98
+    /**
99
+     * @param string $locale
100
+     * @return bool
101
+     * @since 14.0.0
102
+     */
103
+    public function localeExists($locale);
104 104
 
105
-	/**
106
-	 * Creates a function from the plural string
107
-	 *
108
-	 * @param string $string
109
-	 * @return string Unique function name
110
-	 * @since 14.0.0
111
-	 */
112
-	public function createPluralFunction($string);
105
+    /**
106
+     * Creates a function from the plural string
107
+     *
108
+     * @param string $string
109
+     * @return string Unique function name
110
+     * @since 14.0.0
111
+     */
112
+    public function createPluralFunction($string);
113 113
 
114
-	/**
115
-	 * iterate through language settings (if provided) in this order:
116
-	 * 1. returns the forced language or:
117
-	 * 2. if applicable, the trunk of 1 (e.g. "fu" instead of "fu_BAR"
118
-	 * 3. returns the user language or:
119
-	 * 4. if applicable, the trunk of 3
120
-	 * 5. returns the system default language or:
121
-	 * 6. if applicable, the trunk of 5
122
-	 * 7+∞. returns 'en'
123
-	 *
124
-	 * Hint: in most cases findLanguage() suits you fine
125
-	 *
126
-	 * @since 14.0.0
127
-	 */
128
-	public function getLanguageIterator(IUser $user = null): ILanguageIterator;
114
+    /**
115
+     * iterate through language settings (if provided) in this order:
116
+     * 1. returns the forced language or:
117
+     * 2. if applicable, the trunk of 1 (e.g. "fu" instead of "fu_BAR"
118
+     * 3. returns the user language or:
119
+     * 4. if applicable, the trunk of 3
120
+     * 5. returns the system default language or:
121
+     * 6. if applicable, the trunk of 5
122
+     * 7+∞. returns 'en'
123
+     *
124
+     * Hint: in most cases findLanguage() suits you fine
125
+     *
126
+     * @since 14.0.0
127
+     */
128
+    public function getLanguageIterator(IUser $user = null): ILanguageIterator;
129 129
 
130
-	/**
131
-	 * Return the language to use when sending something to a user
132
-	 *
133
-	 * @param IUser|null $user
134
-	 * @return string
135
-	 * @since 20.0.0
136
-	 */
137
-	public function getUserLanguage(IUser $user = null): string;
130
+    /**
131
+     * Return the language to use when sending something to a user
132
+     *
133
+     * @param IUser|null $user
134
+     * @return string
135
+     * @since 20.0.0
136
+     */
137
+    public function getUserLanguage(IUser $user = null): string;
138 138
 }
Please login to merge, or discard this patch.
apps/settings/lib/Hooks.php 2 patches
Indentation   +243 added lines, -243 removed lines patch added patch discarded remove patch
@@ -43,247 +43,247 @@
 block discarded – undo
43 43
 
44 44
 class Hooks {
45 45
 
46
-	/** @var IActivityManager */
47
-	protected $activityManager;
48
-	/** @var IGroupManager|\OC\Group\Manager */
49
-	protected $groupManager;
50
-	/** @var IUserManager */
51
-	protected $userManager;
52
-	/** @var IUserSession */
53
-	protected $userSession;
54
-	/** @var IURLGenerator */
55
-	protected $urlGenerator;
56
-	/** @var IMailer */
57
-	protected $mailer;
58
-	/** @var IConfig */
59
-	protected $config;
60
-	/** @var IFactory */
61
-	protected $languageFactory;
62
-
63
-	public function __construct(IActivityManager $activityManager,
64
-								IGroupManager $groupManager,
65
-								IUserManager $userManager,
66
-								IUserSession $userSession,
67
-								IURLGenerator $urlGenerator,
68
-								IMailer $mailer,
69
-								IConfig $config,
70
-								IFactory $languageFactory) {
71
-		$this->activityManager = $activityManager;
72
-		$this->groupManager = $groupManager;
73
-		$this->userManager = $userManager;
74
-		$this->userSession = $userSession;
75
-		$this->urlGenerator = $urlGenerator;
76
-		$this->mailer = $mailer;
77
-		$this->config = $config;
78
-		$this->languageFactory = $languageFactory;
79
-	}
80
-
81
-	/**
82
-	 * @param string $uid
83
-	 * @throws \InvalidArgumentException
84
-	 * @throws \BadMethodCallException
85
-	 * @throws \Exception
86
-	 */
87
-	public function onChangePassword($uid) {
88
-		$user = $this->userManager->get($uid);
89
-
90
-		if (!$user instanceof IUser || $user->getLastLogin() === 0) {
91
-			// User didn't login, so don't create activities and emails.
92
-			return;
93
-		}
94
-
95
-		$event = $this->activityManager->generateEvent();
96
-		$event->setApp('settings')
97
-			->setType('personal_settings')
98
-			->setAffectedUser($user->getUID());
99
-
100
-		$instanceUrl = $this->urlGenerator->getAbsoluteURL('/');
101
-		$language = $this->languageFactory->getUserLanguage($user);
102
-		$l = $this->languageFactory->get('settings', $language);
103
-
104
-		$actor = $this->userSession->getUser();
105
-		if ($actor instanceof IUser) {
106
-			if ($actor->getUID() !== $user->getUID()) {
107
-				// Admin changed the password through the user panel
108
-				$text = $l->t('%1$s changed your password on %2$s.', [$actor->getDisplayName(), $instanceUrl]);
109
-				$event->setAuthor($actor->getUID())
110
-					->setSubject(Provider::PASSWORD_CHANGED_BY, [$actor->getUID()]);
111
-			} else {
112
-				// User changed their password themselves through settings
113
-				$text = $l->t('Your password on %s was changed.', [$instanceUrl]);
114
-				$event->setAuthor($actor->getUID())
115
-					->setSubject(Provider::PASSWORD_CHANGED_SELF);
116
-			}
117
-		} else {
118
-			if (\OC::$CLI) {
119
-				// Admin used occ to reset the password
120
-				$text = $l->t('Your password on %s was reset by an administrator.', [$instanceUrl]);
121
-				$event->setSubject(Provider::PASSWORD_RESET);
122
-			} else {
123
-				// User reset their password from Lost page
124
-				$text = $l->t('Your password on %s was reset.', [$instanceUrl]);
125
-				$event->setSubject(Provider::PASSWORD_RESET_SELF);
126
-			}
127
-		}
128
-
129
-		$this->activityManager->publish($event);
130
-
131
-		if ($user->getEMailAddress() !== null) {
132
-			$template = $this->mailer->createEMailTemplate('settings.PasswordChanged', [
133
-				'displayname' => $user->getDisplayName(),
134
-				'emailAddress' => $user->getEMailAddress(),
135
-				'instanceUrl' => $instanceUrl,
136
-			]);
137
-
138
-			$template->setSubject($l->t('Password for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
139
-			$template->addHeader();
140
-			$template->addHeading($l->t('Password changed for %s', [$user->getDisplayName()]), false);
141
-			$template->addBodyText($text . ' ' . $l->t('If you did not request this, please contact an administrator.'));
142
-			$template->addFooter();
143
-
144
-
145
-			$message = $this->mailer->createMessage();
146
-			$message->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
147
-			$message->useTemplate($template);
148
-			$this->mailer->send($message);
149
-		}
150
-	}
151
-
152
-	/**
153
-	 * @param IUser $user
154
-	 * @param string|null $oldMailAddress
155
-	 * @throws \InvalidArgumentException
156
-	 * @throws \BadMethodCallException
157
-	 */
158
-	public function onChangeEmail(IUser $user, $oldMailAddress) {
159
-		if ($oldMailAddress === $user->getEMailAddress() ||
160
-			$user->getLastLogin() === 0) {
161
-			// Email didn't really change or user didn't login,
162
-			// so don't create activities and emails.
163
-			return;
164
-		}
165
-
166
-		$event = $this->activityManager->generateEvent();
167
-		$event->setApp('settings')
168
-			->setType('personal_settings')
169
-			->setAffectedUser($user->getUID());
170
-
171
-		$instanceUrl = $this->urlGenerator->getAbsoluteURL('/');
172
-		$language = $this->languageFactory->getUserLanguage($user);
173
-		$l = $this->languageFactory->get('settings', $language);
174
-
175
-		$actor = $this->userSession->getUser();
176
-		if ($actor instanceof IUser) {
177
-			$subject = Provider::EMAIL_CHANGED_SELF;
178
-			if ($actor->getUID() !== $user->getUID()) {
179
-				$subject = Provider::EMAIL_CHANGED;
180
-			}
181
-			$text = $l->t('Your email address on %s was changed.', [$instanceUrl]);
182
-			$event->setAuthor($actor->getUID())
183
-				->setSubject($subject);
184
-		} else {
185
-			$text = $l->t('Your email address on %s was changed by an administrator.', [$instanceUrl]);
186
-			$event->setSubject(Provider::EMAIL_CHANGED);
187
-		}
188
-		$this->activityManager->publish($event);
189
-
190
-
191
-		if ($oldMailAddress !== null) {
192
-			$template = $this->mailer->createEMailTemplate('settings.EmailChanged', [
193
-				'displayname' => $user->getDisplayName(),
194
-				'newEMailAddress' => $user->getEMailAddress(),
195
-				'oldEMailAddress' => $oldMailAddress,
196
-				'instanceUrl' => $instanceUrl,
197
-			]);
198
-
199
-			$template->setSubject($l->t('Email address for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
200
-			$template->addHeader();
201
-			$template->addHeading($l->t('Email address changed for %s', [$user->getDisplayName()]), false);
202
-			$template->addBodyText($text . ' ' . $l->t('If you did not request this, please contact an administrator.'));
203
-			if ($user->getEMailAddress()) {
204
-				$template->addBodyText($l->t('The new email address is %s', [$user->getEMailAddress()]));
205
-			}
206
-			$template->addFooter();
207
-
208
-
209
-			$message = $this->mailer->createMessage();
210
-			$message->setTo([$oldMailAddress => $user->getDisplayName()]);
211
-			$message->useTemplate($template);
212
-			$this->mailer->send($message);
213
-		}
214
-	}
215
-
216
-	/**
217
-	 * @param IGroup $group
218
-	 * @param IUser $user
219
-	 * @throws \InvalidArgumentException
220
-	 * @throws \BadMethodCallException
221
-	 */
222
-	public function addUserToGroup(IGroup $group, IUser $user): void {
223
-		$subAdminManager = $this->groupManager->getSubAdmin();
224
-		$usersToNotify = $subAdminManager->getGroupsSubAdmins($group);
225
-		$usersToNotify[] = $user;
226
-
227
-
228
-		$event = $this->activityManager->generateEvent();
229
-		$event->setApp('settings')
230
-			->setType('group_settings');
231
-
232
-		$actor = $this->userSession->getUser();
233
-		if ($actor instanceof IUser) {
234
-			$event->setAuthor($actor->getUID())
235
-				->setSubject(GroupProvider::ADDED_TO_GROUP, [
236
-					'user' => $user->getUID(),
237
-					'group' => $group->getGID(),
238
-					'actor' => $actor->getUID(),
239
-				]);
240
-		} else {
241
-			$event->setSubject(GroupProvider::ADDED_TO_GROUP, [
242
-				'user' => $user->getUID(),
243
-				'group' => $group->getGID(),
244
-			]);
245
-		}
246
-
247
-		foreach ($usersToNotify as $userToNotify) {
248
-			$event->setAffectedUser($userToNotify->getUID());
249
-			$this->activityManager->publish($event);
250
-		}
251
-	}
252
-
253
-	/**
254
-	 * @param IGroup $group
255
-	 * @param IUser $user
256
-	 * @throws \InvalidArgumentException
257
-	 * @throws \BadMethodCallException
258
-	 */
259
-	public function removeUserFromGroup(IGroup $group, IUser $user): void {
260
-		$subAdminManager = $this->groupManager->getSubAdmin();
261
-		$usersToNotify = $subAdminManager->getGroupsSubAdmins($group);
262
-		$usersToNotify[] = $user;
263
-
264
-
265
-		$event = $this->activityManager->generateEvent();
266
-		$event->setApp('settings')
267
-			->setType('group_settings');
268
-
269
-		$actor = $this->userSession->getUser();
270
-		if ($actor instanceof IUser) {
271
-			$event->setAuthor($actor->getUID())
272
-				->setSubject(GroupProvider::REMOVED_FROM_GROUP, [
273
-					'user' => $user->getUID(),
274
-					'group' => $group->getGID(),
275
-					'actor' => $actor->getUID(),
276
-				]);
277
-		} else {
278
-			$event->setSubject(GroupProvider::REMOVED_FROM_GROUP, [
279
-				'user' => $user->getUID(),
280
-				'group' => $group->getGID(),
281
-			]);
282
-		}
283
-
284
-		foreach ($usersToNotify as $userToNotify) {
285
-			$event->setAffectedUser($userToNotify->getUID());
286
-			$this->activityManager->publish($event);
287
-		}
288
-	}
46
+    /** @var IActivityManager */
47
+    protected $activityManager;
48
+    /** @var IGroupManager|\OC\Group\Manager */
49
+    protected $groupManager;
50
+    /** @var IUserManager */
51
+    protected $userManager;
52
+    /** @var IUserSession */
53
+    protected $userSession;
54
+    /** @var IURLGenerator */
55
+    protected $urlGenerator;
56
+    /** @var IMailer */
57
+    protected $mailer;
58
+    /** @var IConfig */
59
+    protected $config;
60
+    /** @var IFactory */
61
+    protected $languageFactory;
62
+
63
+    public function __construct(IActivityManager $activityManager,
64
+                                IGroupManager $groupManager,
65
+                                IUserManager $userManager,
66
+                                IUserSession $userSession,
67
+                                IURLGenerator $urlGenerator,
68
+                                IMailer $mailer,
69
+                                IConfig $config,
70
+                                IFactory $languageFactory) {
71
+        $this->activityManager = $activityManager;
72
+        $this->groupManager = $groupManager;
73
+        $this->userManager = $userManager;
74
+        $this->userSession = $userSession;
75
+        $this->urlGenerator = $urlGenerator;
76
+        $this->mailer = $mailer;
77
+        $this->config = $config;
78
+        $this->languageFactory = $languageFactory;
79
+    }
80
+
81
+    /**
82
+     * @param string $uid
83
+     * @throws \InvalidArgumentException
84
+     * @throws \BadMethodCallException
85
+     * @throws \Exception
86
+     */
87
+    public function onChangePassword($uid) {
88
+        $user = $this->userManager->get($uid);
89
+
90
+        if (!$user instanceof IUser || $user->getLastLogin() === 0) {
91
+            // User didn't login, so don't create activities and emails.
92
+            return;
93
+        }
94
+
95
+        $event = $this->activityManager->generateEvent();
96
+        $event->setApp('settings')
97
+            ->setType('personal_settings')
98
+            ->setAffectedUser($user->getUID());
99
+
100
+        $instanceUrl = $this->urlGenerator->getAbsoluteURL('/');
101
+        $language = $this->languageFactory->getUserLanguage($user);
102
+        $l = $this->languageFactory->get('settings', $language);
103
+
104
+        $actor = $this->userSession->getUser();
105
+        if ($actor instanceof IUser) {
106
+            if ($actor->getUID() !== $user->getUID()) {
107
+                // Admin changed the password through the user panel
108
+                $text = $l->t('%1$s changed your password on %2$s.', [$actor->getDisplayName(), $instanceUrl]);
109
+                $event->setAuthor($actor->getUID())
110
+                    ->setSubject(Provider::PASSWORD_CHANGED_BY, [$actor->getUID()]);
111
+            } else {
112
+                // User changed their password themselves through settings
113
+                $text = $l->t('Your password on %s was changed.', [$instanceUrl]);
114
+                $event->setAuthor($actor->getUID())
115
+                    ->setSubject(Provider::PASSWORD_CHANGED_SELF);
116
+            }
117
+        } else {
118
+            if (\OC::$CLI) {
119
+                // Admin used occ to reset the password
120
+                $text = $l->t('Your password on %s was reset by an administrator.', [$instanceUrl]);
121
+                $event->setSubject(Provider::PASSWORD_RESET);
122
+            } else {
123
+                // User reset their password from Lost page
124
+                $text = $l->t('Your password on %s was reset.', [$instanceUrl]);
125
+                $event->setSubject(Provider::PASSWORD_RESET_SELF);
126
+            }
127
+        }
128
+
129
+        $this->activityManager->publish($event);
130
+
131
+        if ($user->getEMailAddress() !== null) {
132
+            $template = $this->mailer->createEMailTemplate('settings.PasswordChanged', [
133
+                'displayname' => $user->getDisplayName(),
134
+                'emailAddress' => $user->getEMailAddress(),
135
+                'instanceUrl' => $instanceUrl,
136
+            ]);
137
+
138
+            $template->setSubject($l->t('Password for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
139
+            $template->addHeader();
140
+            $template->addHeading($l->t('Password changed for %s', [$user->getDisplayName()]), false);
141
+            $template->addBodyText($text . ' ' . $l->t('If you did not request this, please contact an administrator.'));
142
+            $template->addFooter();
143
+
144
+
145
+            $message = $this->mailer->createMessage();
146
+            $message->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
147
+            $message->useTemplate($template);
148
+            $this->mailer->send($message);
149
+        }
150
+    }
151
+
152
+    /**
153
+     * @param IUser $user
154
+     * @param string|null $oldMailAddress
155
+     * @throws \InvalidArgumentException
156
+     * @throws \BadMethodCallException
157
+     */
158
+    public function onChangeEmail(IUser $user, $oldMailAddress) {
159
+        if ($oldMailAddress === $user->getEMailAddress() ||
160
+            $user->getLastLogin() === 0) {
161
+            // Email didn't really change or user didn't login,
162
+            // so don't create activities and emails.
163
+            return;
164
+        }
165
+
166
+        $event = $this->activityManager->generateEvent();
167
+        $event->setApp('settings')
168
+            ->setType('personal_settings')
169
+            ->setAffectedUser($user->getUID());
170
+
171
+        $instanceUrl = $this->urlGenerator->getAbsoluteURL('/');
172
+        $language = $this->languageFactory->getUserLanguage($user);
173
+        $l = $this->languageFactory->get('settings', $language);
174
+
175
+        $actor = $this->userSession->getUser();
176
+        if ($actor instanceof IUser) {
177
+            $subject = Provider::EMAIL_CHANGED_SELF;
178
+            if ($actor->getUID() !== $user->getUID()) {
179
+                $subject = Provider::EMAIL_CHANGED;
180
+            }
181
+            $text = $l->t('Your email address on %s was changed.', [$instanceUrl]);
182
+            $event->setAuthor($actor->getUID())
183
+                ->setSubject($subject);
184
+        } else {
185
+            $text = $l->t('Your email address on %s was changed by an administrator.', [$instanceUrl]);
186
+            $event->setSubject(Provider::EMAIL_CHANGED);
187
+        }
188
+        $this->activityManager->publish($event);
189
+
190
+
191
+        if ($oldMailAddress !== null) {
192
+            $template = $this->mailer->createEMailTemplate('settings.EmailChanged', [
193
+                'displayname' => $user->getDisplayName(),
194
+                'newEMailAddress' => $user->getEMailAddress(),
195
+                'oldEMailAddress' => $oldMailAddress,
196
+                'instanceUrl' => $instanceUrl,
197
+            ]);
198
+
199
+            $template->setSubject($l->t('Email address for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
200
+            $template->addHeader();
201
+            $template->addHeading($l->t('Email address changed for %s', [$user->getDisplayName()]), false);
202
+            $template->addBodyText($text . ' ' . $l->t('If you did not request this, please contact an administrator.'));
203
+            if ($user->getEMailAddress()) {
204
+                $template->addBodyText($l->t('The new email address is %s', [$user->getEMailAddress()]));
205
+            }
206
+            $template->addFooter();
207
+
208
+
209
+            $message = $this->mailer->createMessage();
210
+            $message->setTo([$oldMailAddress => $user->getDisplayName()]);
211
+            $message->useTemplate($template);
212
+            $this->mailer->send($message);
213
+        }
214
+    }
215
+
216
+    /**
217
+     * @param IGroup $group
218
+     * @param IUser $user
219
+     * @throws \InvalidArgumentException
220
+     * @throws \BadMethodCallException
221
+     */
222
+    public function addUserToGroup(IGroup $group, IUser $user): void {
223
+        $subAdminManager = $this->groupManager->getSubAdmin();
224
+        $usersToNotify = $subAdminManager->getGroupsSubAdmins($group);
225
+        $usersToNotify[] = $user;
226
+
227
+
228
+        $event = $this->activityManager->generateEvent();
229
+        $event->setApp('settings')
230
+            ->setType('group_settings');
231
+
232
+        $actor = $this->userSession->getUser();
233
+        if ($actor instanceof IUser) {
234
+            $event->setAuthor($actor->getUID())
235
+                ->setSubject(GroupProvider::ADDED_TO_GROUP, [
236
+                    'user' => $user->getUID(),
237
+                    'group' => $group->getGID(),
238
+                    'actor' => $actor->getUID(),
239
+                ]);
240
+        } else {
241
+            $event->setSubject(GroupProvider::ADDED_TO_GROUP, [
242
+                'user' => $user->getUID(),
243
+                'group' => $group->getGID(),
244
+            ]);
245
+        }
246
+
247
+        foreach ($usersToNotify as $userToNotify) {
248
+            $event->setAffectedUser($userToNotify->getUID());
249
+            $this->activityManager->publish($event);
250
+        }
251
+    }
252
+
253
+    /**
254
+     * @param IGroup $group
255
+     * @param IUser $user
256
+     * @throws \InvalidArgumentException
257
+     * @throws \BadMethodCallException
258
+     */
259
+    public function removeUserFromGroup(IGroup $group, IUser $user): void {
260
+        $subAdminManager = $this->groupManager->getSubAdmin();
261
+        $usersToNotify = $subAdminManager->getGroupsSubAdmins($group);
262
+        $usersToNotify[] = $user;
263
+
264
+
265
+        $event = $this->activityManager->generateEvent();
266
+        $event->setApp('settings')
267
+            ->setType('group_settings');
268
+
269
+        $actor = $this->userSession->getUser();
270
+        if ($actor instanceof IUser) {
271
+            $event->setAuthor($actor->getUID())
272
+                ->setSubject(GroupProvider::REMOVED_FROM_GROUP, [
273
+                    'user' => $user->getUID(),
274
+                    'group' => $group->getGID(),
275
+                    'actor' => $actor->getUID(),
276
+                ]);
277
+        } else {
278
+            $event->setSubject(GroupProvider::REMOVED_FROM_GROUP, [
279
+                'user' => $user->getUID(),
280
+                'group' => $group->getGID(),
281
+            ]);
282
+        }
283
+
284
+        foreach ($usersToNotify as $userToNotify) {
285
+            $event->setAffectedUser($userToNotify->getUID());
286
+            $this->activityManager->publish($event);
287
+        }
288
+    }
289 289
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
 			$template->setSubject($l->t('Password for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
139 139
 			$template->addHeader();
140 140
 			$template->addHeading($l->t('Password changed for %s', [$user->getDisplayName()]), false);
141
-			$template->addBodyText($text . ' ' . $l->t('If you did not request this, please contact an administrator.'));
141
+			$template->addBodyText($text.' '.$l->t('If you did not request this, please contact an administrator.'));
142 142
 			$template->addFooter();
143 143
 
144 144
 
@@ -199,7 +199,7 @@  discard block
 block discarded – undo
199 199
 			$template->setSubject($l->t('Email address for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
200 200
 			$template->addHeader();
201 201
 			$template->addHeading($l->t('Email address changed for %s', [$user->getDisplayName()]), false);
202
-			$template->addBodyText($text . ' ' . $l->t('If you did not request this, please contact an administrator.'));
202
+			$template->addBodyText($text.' '.$l->t('If you did not request this, please contact an administrator.'));
203 203
 			if ($user->getEMailAddress()) {
204 204
 				$template->addBodyText($l->t('The new email address is %s', [$user->getEMailAddress()]));
205 205
 			}
Please login to merge, or discard this patch.
apps/settings/lib/Mailer/NewUserMailHelper.php 1 patch
Indentation   +120 added lines, -120 removed lines patch added patch discarded remove patch
@@ -42,131 +42,131 @@
 block discarded – undo
42 42
 use OCP\Security\ISecureRandom;
43 43
 
44 44
 class NewUserMailHelper {
45
-	/** @var Defaults */
46
-	private $themingDefaults;
47
-	/** @var IURLGenerator */
48
-	private $urlGenerator;
49
-	/** @var IFactory */
50
-	private $l10nFactory;
51
-	/** @var IMailer */
52
-	private $mailer;
53
-	/** @var ISecureRandom */
54
-	private $secureRandom;
55
-	/** @var ITimeFactory */
56
-	private $timeFactory;
57
-	/** @var IConfig */
58
-	private $config;
59
-	/** @var ICrypto */
60
-	private $crypto;
61
-	/** @var string */
62
-	private $fromAddress;
45
+    /** @var Defaults */
46
+    private $themingDefaults;
47
+    /** @var IURLGenerator */
48
+    private $urlGenerator;
49
+    /** @var IFactory */
50
+    private $l10nFactory;
51
+    /** @var IMailer */
52
+    private $mailer;
53
+    /** @var ISecureRandom */
54
+    private $secureRandom;
55
+    /** @var ITimeFactory */
56
+    private $timeFactory;
57
+    /** @var IConfig */
58
+    private $config;
59
+    /** @var ICrypto */
60
+    private $crypto;
61
+    /** @var string */
62
+    private $fromAddress;
63 63
 
64
-	/**
65
-	 * @param Defaults $themingDefaults
66
-	 * @param IURLGenerator $urlGenerator
67
-	 * @param IFactory $l10nFactory
68
-	 * @param IMailer $mailer
69
-	 * @param ISecureRandom $secureRandom
70
-	 * @param ITimeFactory $timeFactory
71
-	 * @param IConfig $config
72
-	 * @param ICrypto $crypto
73
-	 * @param string $fromAddress
74
-	 */
75
-	public function __construct(Defaults $themingDefaults,
76
-								IURLGenerator $urlGenerator,
77
-								IFactory $l10nFactory,
78
-								IMailer $mailer,
79
-								ISecureRandom $secureRandom,
80
-								ITimeFactory $timeFactory,
81
-								IConfig $config,
82
-								ICrypto $crypto,
83
-								$fromAddress) {
84
-		$this->themingDefaults = $themingDefaults;
85
-		$this->urlGenerator = $urlGenerator;
86
-		$this->l10nFactory = $l10nFactory;
87
-		$this->mailer = $mailer;
88
-		$this->secureRandom = $secureRandom;
89
-		$this->timeFactory = $timeFactory;
90
-		$this->config = $config;
91
-		$this->crypto = $crypto;
92
-		$this->fromAddress = $fromAddress;
93
-	}
64
+    /**
65
+     * @param Defaults $themingDefaults
66
+     * @param IURLGenerator $urlGenerator
67
+     * @param IFactory $l10nFactory
68
+     * @param IMailer $mailer
69
+     * @param ISecureRandom $secureRandom
70
+     * @param ITimeFactory $timeFactory
71
+     * @param IConfig $config
72
+     * @param ICrypto $crypto
73
+     * @param string $fromAddress
74
+     */
75
+    public function __construct(Defaults $themingDefaults,
76
+                                IURLGenerator $urlGenerator,
77
+                                IFactory $l10nFactory,
78
+                                IMailer $mailer,
79
+                                ISecureRandom $secureRandom,
80
+                                ITimeFactory $timeFactory,
81
+                                IConfig $config,
82
+                                ICrypto $crypto,
83
+                                $fromAddress) {
84
+        $this->themingDefaults = $themingDefaults;
85
+        $this->urlGenerator = $urlGenerator;
86
+        $this->l10nFactory = $l10nFactory;
87
+        $this->mailer = $mailer;
88
+        $this->secureRandom = $secureRandom;
89
+        $this->timeFactory = $timeFactory;
90
+        $this->config = $config;
91
+        $this->crypto = $crypto;
92
+        $this->fromAddress = $fromAddress;
93
+    }
94 94
 
95
-	/**
96
-	 * @param IUser $user
97
-	 * @param bool $generatePasswordResetToken
98
-	 * @return IEMailTemplate
99
-	 */
100
-	public function generateTemplate(IUser $user, $generatePasswordResetToken = false) {
101
-		$userId = $user->getUID();
102
-		$lang = $this->l10nFactory->getUserLanguage($user);
103
-		$l10n = $this->l10nFactory->get('settings', $lang);
95
+    /**
96
+     * @param IUser $user
97
+     * @param bool $generatePasswordResetToken
98
+     * @return IEMailTemplate
99
+     */
100
+    public function generateTemplate(IUser $user, $generatePasswordResetToken = false) {
101
+        $userId = $user->getUID();
102
+        $lang = $this->l10nFactory->getUserLanguage($user);
103
+        $l10n = $this->l10nFactory->get('settings', $lang);
104 104
 
105
-		if ($generatePasswordResetToken) {
106
-			$token = $this->secureRandom->generate(
107
-				21,
108
-				ISecureRandom::CHAR_DIGITS .
109
-				ISecureRandom::CHAR_LOWER .
110
-				ISecureRandom::CHAR_UPPER
111
-			);
112
-			$tokenValue = $this->timeFactory->getTime() . ':' . $token;
113
-			$mailAddress = (null !== $user->getEMailAddress()) ? $user->getEMailAddress() : '';
114
-			$encryptedValue = $this->crypto->encrypt($tokenValue, $mailAddress . $this->config->getSystemValue('secret'));
115
-			$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
116
-			$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]);
117
-		} else {
118
-			$link = $this->urlGenerator->getAbsoluteURL('/');
119
-		}
120
-		$displayName = $user->getDisplayName();
105
+        if ($generatePasswordResetToken) {
106
+            $token = $this->secureRandom->generate(
107
+                21,
108
+                ISecureRandom::CHAR_DIGITS .
109
+                ISecureRandom::CHAR_LOWER .
110
+                ISecureRandom::CHAR_UPPER
111
+            );
112
+            $tokenValue = $this->timeFactory->getTime() . ':' . $token;
113
+            $mailAddress = (null !== $user->getEMailAddress()) ? $user->getEMailAddress() : '';
114
+            $encryptedValue = $this->crypto->encrypt($tokenValue, $mailAddress . $this->config->getSystemValue('secret'));
115
+            $this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
116
+            $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]);
117
+        } else {
118
+            $link = $this->urlGenerator->getAbsoluteURL('/');
119
+        }
120
+        $displayName = $user->getDisplayName();
121 121
 
122
-		$emailTemplate = $this->mailer->createEMailTemplate('settings.Welcome', [
123
-			'link' => $link,
124
-			'displayname' => $displayName,
125
-			'userid' => $userId,
126
-			'instancename' => $this->themingDefaults->getName(),
127
-			'resetTokenGenerated' => $generatePasswordResetToken,
128
-		]);
122
+        $emailTemplate = $this->mailer->createEMailTemplate('settings.Welcome', [
123
+            'link' => $link,
124
+            'displayname' => $displayName,
125
+            'userid' => $userId,
126
+            'instancename' => $this->themingDefaults->getName(),
127
+            'resetTokenGenerated' => $generatePasswordResetToken,
128
+        ]);
129 129
 
130
-		$emailTemplate->setSubject($l10n->t('Your %s account was created', [$this->themingDefaults->getName()]));
131
-		$emailTemplate->addHeader();
132
-		if ($displayName === $userId) {
133
-			$emailTemplate->addHeading($l10n->t('Welcome aboard'));
134
-		} else {
135
-			$emailTemplate->addHeading($l10n->t('Welcome aboard %s', [$displayName]));
136
-		}
137
-		$emailTemplate->addBodyText($l10n->t('Welcome to your %s account, you can add, protect, and share your data.', [$this->themingDefaults->getName()]));
138
-		if ($user->getBackendClassName() !== 'LDAP') {
139
-			$emailTemplate->addBodyText($l10n->t('Your username is: %s', [$userId]));
140
-		}
141
-		if ($generatePasswordResetToken) {
142
-			$leftButtonText = $l10n->t('Set your password');
143
-		} else {
144
-			$leftButtonText = $l10n->t('Go to %s', [$this->themingDefaults->getName()]);
145
-		}
146
-		$emailTemplate->addBodyButtonGroup(
147
-			$leftButtonText,
148
-			$link,
149
-			$l10n->t('Install Client'),
150
-			$this->config->getSystemValue('customclient_desktop', 'https://nextcloud.com/install/#install-clients')
151
-		);
152
-		$emailTemplate->addFooter();
130
+        $emailTemplate->setSubject($l10n->t('Your %s account was created', [$this->themingDefaults->getName()]));
131
+        $emailTemplate->addHeader();
132
+        if ($displayName === $userId) {
133
+            $emailTemplate->addHeading($l10n->t('Welcome aboard'));
134
+        } else {
135
+            $emailTemplate->addHeading($l10n->t('Welcome aboard %s', [$displayName]));
136
+        }
137
+        $emailTemplate->addBodyText($l10n->t('Welcome to your %s account, you can add, protect, and share your data.', [$this->themingDefaults->getName()]));
138
+        if ($user->getBackendClassName() !== 'LDAP') {
139
+            $emailTemplate->addBodyText($l10n->t('Your username is: %s', [$userId]));
140
+        }
141
+        if ($generatePasswordResetToken) {
142
+            $leftButtonText = $l10n->t('Set your password');
143
+        } else {
144
+            $leftButtonText = $l10n->t('Go to %s', [$this->themingDefaults->getName()]);
145
+        }
146
+        $emailTemplate->addBodyButtonGroup(
147
+            $leftButtonText,
148
+            $link,
149
+            $l10n->t('Install Client'),
150
+            $this->config->getSystemValue('customclient_desktop', 'https://nextcloud.com/install/#install-clients')
151
+        );
152
+        $emailTemplate->addFooter();
153 153
 
154
-		return $emailTemplate;
155
-	}
154
+        return $emailTemplate;
155
+    }
156 156
 
157
-	/**
158
-	 * Sends a welcome mail to $user
159
-	 *
160
-	 * @param IUser $user
161
-	 * @param IEmailTemplate $emailTemplate
162
-	 * @throws \Exception If mail could not be sent
163
-	 */
164
-	public function sendMail(IUser $user,
165
-							 IEMailTemplate $emailTemplate) {
166
-		$message = $this->mailer->createMessage();
167
-		$message->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
168
-		$message->setFrom([$this->fromAddress => $this->themingDefaults->getName()]);
169
-		$message->useTemplate($emailTemplate);
170
-		$this->mailer->send($message);
171
-	}
157
+    /**
158
+     * Sends a welcome mail to $user
159
+     *
160
+     * @param IUser $user
161
+     * @param IEmailTemplate $emailTemplate
162
+     * @throws \Exception If mail could not be sent
163
+     */
164
+    public function sendMail(IUser $user,
165
+                                IEMailTemplate $emailTemplate) {
166
+        $message = $this->mailer->createMessage();
167
+        $message->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
168
+        $message->setFrom([$this->fromAddress => $this->themingDefaults->getName()]);
169
+        $message->useTemplate($emailTemplate);
170
+        $this->mailer->send($message);
171
+    }
172 172
 }
Please login to merge, or discard this patch.
apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php 1 patch
Indentation   +142 added lines, -142 removed lines patch added patch discarded remove patch
@@ -47,146 +47,146 @@
 block discarded – undo
47 47
  */
48 48
 abstract class AbstractProvider implements INotificationProvider {
49 49
 
50
-	/** @var string */
51
-	public const NOTIFICATION_TYPE = '';
52
-
53
-	/** @var ILogger */
54
-	protected $logger;
55
-
56
-	/** @var L10NFactory */
57
-	protected $l10nFactory;
58
-
59
-	/** @var IL10N[] */
60
-	private $l10ns;
61
-
62
-	/** @var string */
63
-	private $fallbackLanguage;
64
-
65
-	/** @var IURLGenerator */
66
-	protected $urlGenerator;
67
-
68
-	/** @var IConfig */
69
-	protected $config;
70
-
71
-	/**
72
-	 * @param ILogger $logger
73
-	 * @param L10NFactory $l10nFactory
74
-	 * @param IConfig $config
75
-	 * @param IUrlGenerator $urlGenerator
76
-	 */
77
-	public function __construct(ILogger $logger,
78
-								L10NFactory $l10nFactory,
79
-								IURLGenerator $urlGenerator,
80
-								IConfig $config) {
81
-		$this->logger = $logger;
82
-		$this->l10nFactory = $l10nFactory;
83
-		$this->urlGenerator = $urlGenerator;
84
-		$this->config = $config;
85
-	}
86
-
87
-	/**
88
-	 * Send notification
89
-	 *
90
-	 * @param VEvent $vevent
91
-	 * @param string $calendarDisplayName
92
-	 * @param IUser[] $users
93
-	 * @return void
94
-	 */
95
-	abstract public function send(VEvent $vevent,
96
-						   string $calendarDisplayName,
97
-						   array $users=[]): void;
98
-
99
-	/**
100
-	 * @return string
101
-	 */
102
-	protected function getFallbackLanguage():string {
103
-		if ($this->fallbackLanguage) {
104
-			return $this->fallbackLanguage;
105
-		}
106
-
107
-		$fallbackLanguage = $this->l10nFactory->findLanguage();
108
-		$this->fallbackLanguage = $fallbackLanguage;
109
-
110
-		return $fallbackLanguage;
111
-	}
112
-
113
-	/**
114
-	 * @param string $lang
115
-	 * @return bool
116
-	 */
117
-	protected function hasL10NForLang(string $lang):bool {
118
-		return $this->l10nFactory->languageExists('dav', $lang);
119
-	}
120
-
121
-	/**
122
-	 * @param string $lang
123
-	 * @return IL10N
124
-	 */
125
-	protected function getL10NForLang(string $lang):IL10N {
126
-		if (isset($this->l10ns[$lang])) {
127
-			return $this->l10ns[$lang];
128
-		}
129
-
130
-		$l10n = $this->l10nFactory->get('dav', $lang);
131
-		$this->l10ns[$lang] = $l10n;
132
-
133
-		return $l10n;
134
-	}
135
-
136
-	/**
137
-	 * @param VEvent $vevent
138
-	 * @return string
139
-	 */
140
-	private function getStatusOfEvent(VEvent $vevent):string {
141
-		if ($vevent->STATUS) {
142
-			return (string) $vevent->STATUS;
143
-		}
144
-
145
-		// Doesn't say so in the standard,
146
-		// but we consider events without a status
147
-		// to be confirmed
148
-		return 'CONFIRMED';
149
-	}
150
-
151
-	/**
152
-	 * @param VEvent $vevent
153
-	 * @return bool
154
-	 */
155
-	protected function isEventTentative(VEvent $vevent):bool {
156
-		return $this->getStatusOfEvent($vevent) === 'TENTATIVE';
157
-	}
158
-
159
-	/**
160
-	 * @param VEvent $vevent
161
-	 * @return Property\ICalendar\DateTime
162
-	 */
163
-	protected function getDTEndFromEvent(VEvent $vevent):Property\ICalendar\DateTime {
164
-		if (isset($vevent->DTEND)) {
165
-			return $vevent->DTEND;
166
-		}
167
-
168
-		if (isset($vevent->DURATION)) {
169
-			$isFloating = $vevent->DTSTART->isFloating();
170
-			/** @var Property\ICalendar\DateTime $end */
171
-			$end = clone $vevent->DTSTART;
172
-			$endDateTime = $end->getDateTime();
173
-			$endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
174
-			$end->setDateTime($endDateTime, $isFloating);
175
-
176
-			return $end;
177
-		}
178
-
179
-		if (!$vevent->DTSTART->hasTime()) {
180
-			$isFloating = $vevent->DTSTART->isFloating();
181
-			/** @var Property\ICalendar\DateTime $end */
182
-			$end = clone $vevent->DTSTART;
183
-			$endDateTime = $end->getDateTime();
184
-			$endDateTime = $endDateTime->modify('+1 day');
185
-			$end->setDateTime($endDateTime, $isFloating);
186
-
187
-			return $end;
188
-		}
189
-
190
-		return clone $vevent->DTSTART;
191
-	}
50
+    /** @var string */
51
+    public const NOTIFICATION_TYPE = '';
52
+
53
+    /** @var ILogger */
54
+    protected $logger;
55
+
56
+    /** @var L10NFactory */
57
+    protected $l10nFactory;
58
+
59
+    /** @var IL10N[] */
60
+    private $l10ns;
61
+
62
+    /** @var string */
63
+    private $fallbackLanguage;
64
+
65
+    /** @var IURLGenerator */
66
+    protected $urlGenerator;
67
+
68
+    /** @var IConfig */
69
+    protected $config;
70
+
71
+    /**
72
+     * @param ILogger $logger
73
+     * @param L10NFactory $l10nFactory
74
+     * @param IConfig $config
75
+     * @param IUrlGenerator $urlGenerator
76
+     */
77
+    public function __construct(ILogger $logger,
78
+                                L10NFactory $l10nFactory,
79
+                                IURLGenerator $urlGenerator,
80
+                                IConfig $config) {
81
+        $this->logger = $logger;
82
+        $this->l10nFactory = $l10nFactory;
83
+        $this->urlGenerator = $urlGenerator;
84
+        $this->config = $config;
85
+    }
86
+
87
+    /**
88
+     * Send notification
89
+     *
90
+     * @param VEvent $vevent
91
+     * @param string $calendarDisplayName
92
+     * @param IUser[] $users
93
+     * @return void
94
+     */
95
+    abstract public function send(VEvent $vevent,
96
+                            string $calendarDisplayName,
97
+                            array $users=[]): void;
98
+
99
+    /**
100
+     * @return string
101
+     */
102
+    protected function getFallbackLanguage():string {
103
+        if ($this->fallbackLanguage) {
104
+            return $this->fallbackLanguage;
105
+        }
106
+
107
+        $fallbackLanguage = $this->l10nFactory->findLanguage();
108
+        $this->fallbackLanguage = $fallbackLanguage;
109
+
110
+        return $fallbackLanguage;
111
+    }
112
+
113
+    /**
114
+     * @param string $lang
115
+     * @return bool
116
+     */
117
+    protected function hasL10NForLang(string $lang):bool {
118
+        return $this->l10nFactory->languageExists('dav', $lang);
119
+    }
120
+
121
+    /**
122
+     * @param string $lang
123
+     * @return IL10N
124
+     */
125
+    protected function getL10NForLang(string $lang):IL10N {
126
+        if (isset($this->l10ns[$lang])) {
127
+            return $this->l10ns[$lang];
128
+        }
129
+
130
+        $l10n = $this->l10nFactory->get('dav', $lang);
131
+        $this->l10ns[$lang] = $l10n;
132
+
133
+        return $l10n;
134
+    }
135
+
136
+    /**
137
+     * @param VEvent $vevent
138
+     * @return string
139
+     */
140
+    private function getStatusOfEvent(VEvent $vevent):string {
141
+        if ($vevent->STATUS) {
142
+            return (string) $vevent->STATUS;
143
+        }
144
+
145
+        // Doesn't say so in the standard,
146
+        // but we consider events without a status
147
+        // to be confirmed
148
+        return 'CONFIRMED';
149
+    }
150
+
151
+    /**
152
+     * @param VEvent $vevent
153
+     * @return bool
154
+     */
155
+    protected function isEventTentative(VEvent $vevent):bool {
156
+        return $this->getStatusOfEvent($vevent) === 'TENTATIVE';
157
+    }
158
+
159
+    /**
160
+     * @param VEvent $vevent
161
+     * @return Property\ICalendar\DateTime
162
+     */
163
+    protected function getDTEndFromEvent(VEvent $vevent):Property\ICalendar\DateTime {
164
+        if (isset($vevent->DTEND)) {
165
+            return $vevent->DTEND;
166
+        }
167
+
168
+        if (isset($vevent->DURATION)) {
169
+            $isFloating = $vevent->DTSTART->isFloating();
170
+            /** @var Property\ICalendar\DateTime $end */
171
+            $end = clone $vevent->DTSTART;
172
+            $endDateTime = $end->getDateTime();
173
+            $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
174
+            $end->setDateTime($endDateTime, $isFloating);
175
+
176
+            return $end;
177
+        }
178
+
179
+        if (!$vevent->DTSTART->hasTime()) {
180
+            $isFloating = $vevent->DTSTART->isFloating();
181
+            /** @var Property\ICalendar\DateTime $end */
182
+            $end = clone $vevent->DTSTART;
183
+            $endDateTime = $end->getDateTime();
184
+            $endDateTime = $endDateTime->modify('+1 day');
185
+            $end->setDateTime($endDateTime, $isFloating);
186
+
187
+            return $end;
188
+        }
189
+
190
+        return clone $vevent->DTSTART;
191
+    }
192 192
 }
Please login to merge, or discard this patch.
apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php 1 patch
Indentation   +446 added lines, -446 removed lines patch added patch discarded remove patch
@@ -50,450 +50,450 @@
 block discarded – undo
50 50
  */
51 51
 class EmailProvider extends AbstractProvider {
52 52
 
53
-	/** @var string */
54
-	public const NOTIFICATION_TYPE = 'EMAIL';
55
-
56
-	/** @var IMailer */
57
-	private $mailer;
58
-
59
-	/**
60
-	 * @param IConfig $config
61
-	 * @param IMailer $mailer
62
-	 * @param ILogger $logger
63
-	 * @param L10NFactory $l10nFactory
64
-	 * @param IUrlGenerator $urlGenerator
65
-	 */
66
-	public function __construct(IConfig $config,
67
-								IMailer $mailer,
68
-								ILogger $logger,
69
-								L10NFactory $l10nFactory,
70
-								IURLGenerator $urlGenerator) {
71
-		parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
72
-		$this->mailer = $mailer;
73
-	}
74
-
75
-	/**
76
-	 * Send out notification via email
77
-	 *
78
-	 * @param VEvent $vevent
79
-	 * @param string $calendarDisplayName
80
-	 * @param array $users
81
-	 * @throws \Exception
82
-	 */
83
-	public function send(VEvent $vevent,
84
-						 string $calendarDisplayName,
85
-						 array $users=[]):void {
86
-		$fallbackLanguage = $this->getFallbackLanguage();
87
-
88
-		$emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users);
89
-		$emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent);
90
-
91
-		// Quote from php.net:
92
-		// If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.
93
-		// => if there are duplicate email addresses, it will always take the system value
94
-		$emailAddresses = array_merge(
95
-			$emailAddressesOfAttendees,
96
-			$emailAddressesOfSharees
97
-		);
98
-
99
-		$sortedByLanguage = $this->sortEMailAddressesByLanguage($emailAddresses, $fallbackLanguage);
100
-		$organizer = $this->getOrganizerEMailAndNameFromEvent($vevent);
101
-
102
-		foreach ($sortedByLanguage as $lang => $emailAddresses) {
103
-			if (!$this->hasL10NForLang($lang)) {
104
-				$lang = $fallbackLanguage;
105
-			}
106
-			$l10n = $this->getL10NForLang($lang);
107
-			$fromEMail = \OCP\Util::getDefaultEmailAddress('reminders-noreply');
108
-
109
-			$template = $this->mailer->createEMailTemplate('dav.calendarReminder');
110
-			$template->addHeader();
111
-			$this->addSubjectAndHeading($template, $l10n, $vevent);
112
-			$this->addBulletList($template, $l10n, $calendarDisplayName, $vevent);
113
-			$template->addFooter();
114
-
115
-			foreach ($emailAddresses as $emailAddress) {
116
-				$message = $this->mailer->createMessage();
117
-				$message->setFrom([$fromEMail]);
118
-				if ($organizer) {
119
-					$message->setReplyTo($organizer);
120
-				}
121
-				$message->setTo([$emailAddress]);
122
-				$message->useTemplate($template);
123
-
124
-				try {
125
-					$failed = $this->mailer->send($message);
126
-					if ($failed) {
127
-						$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
128
-					}
129
-				} catch (\Exception $ex) {
130
-					$this->logger->logException($ex, ['app' => 'dav']);
131
-				}
132
-			}
133
-		}
134
-	}
135
-
136
-	/**
137
-	 * @param IEMailTemplate $template
138
-	 * @param IL10N $l10n
139
-	 * @param VEvent $vevent
140
-	 */
141
-	private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n, VEvent $vevent):void {
142
-		$template->setSubject('Notification: ' . $this->getTitleFromVEvent($vevent, $l10n));
143
-		$template->addHeading($this->getTitleFromVEvent($vevent, $l10n));
144
-	}
145
-
146
-	/**
147
-	 * @param IEMailTemplate $template
148
-	 * @param IL10N $l10n
149
-	 * @param string $calendarDisplayName
150
-	 * @param array $eventData
151
-	 */
152
-	private function addBulletList(IEMailTemplate $template,
153
-								   IL10N $l10n,
154
-								   string $calendarDisplayName,
155
-								   VEvent $vevent):void {
156
-		$template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'),
157
-			$this->getAbsoluteImagePath('actions/info.svg'));
158
-
159
-		$template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'),
160
-			$this->getAbsoluteImagePath('places/calendar.svg'));
161
-
162
-		if (isset($vevent->LOCATION)) {
163
-			$template->addBodyListItem((string) $vevent->LOCATION, $l10n->t('Where:'),
164
-				$this->getAbsoluteImagePath('actions/address.svg'));
165
-		}
166
-		if (isset($vevent->DESCRIPTION)) {
167
-			$template->addBodyListItem((string) $vevent->DESCRIPTION, $l10n->t('Description:'),
168
-				$this->getAbsoluteImagePath('actions/more.svg'));
169
-		}
170
-	}
171
-
172
-	/**
173
-	 * @param string $path
174
-	 * @return string
175
-	 */
176
-	private function getAbsoluteImagePath(string $path):string {
177
-		return $this->urlGenerator->getAbsoluteURL(
178
-			$this->urlGenerator->imagePath('core', $path)
179
-		);
180
-	}
181
-
182
-	/**
183
-	 * @param VEvent $vevent
184
-	 * @return array|null
185
-	 */
186
-	private function getOrganizerEMailAndNameFromEvent(VEvent $vevent):?array {
187
-		if (!$vevent->ORGANIZER) {
188
-			return null;
189
-		}
190
-
191
-		$organizer = $vevent->ORGANIZER;
192
-		if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) {
193
-			return null;
194
-		}
195
-
196
-		$organizerEMail = substr($organizer->getValue(), 7);
197
-
198
-		$name = $organizer->offsetGet('CN');
199
-		if ($name instanceof Parameter) {
200
-			return [$organizerEMail => $name];
201
-		}
202
-
203
-		return [$organizerEMail];
204
-	}
205
-
206
-	/**
207
-	 * @param array $emails
208
-	 * @param string $defaultLanguage
209
-	 * @return array
210
-	 */
211
-	private function sortEMailAddressesByLanguage(array $emails,
212
-												  string $defaultLanguage):array {
213
-		$sortedByLanguage = [];
214
-
215
-		foreach ($emails as $emailAddress => $parameters) {
216
-			if (isset($parameters['LANG'])) {
217
-				$lang = $parameters['LANG'];
218
-			} else {
219
-				$lang = $defaultLanguage;
220
-			}
221
-
222
-			if (!isset($sortedByLanguage[$lang])) {
223
-				$sortedByLanguage[$lang] = [];
224
-			}
225
-
226
-			$sortedByLanguage[$lang][] = $emailAddress;
227
-		}
228
-
229
-		return $sortedByLanguage;
230
-	}
231
-
232
-	/**
233
-	 * @param VEvent $vevent
234
-	 * @return array
235
-	 */
236
-	private function getAllEMailAddressesFromEvent(VEvent $vevent):array {
237
-		$emailAddresses = [];
238
-
239
-		if (isset($vevent->ATTENDEE)) {
240
-			foreach ($vevent->ATTENDEE as $attendee) {
241
-				if (!($attendee instanceof VObject\Property)) {
242
-					continue;
243
-				}
244
-
245
-				$cuType = $this->getCUTypeOfAttendee($attendee);
246
-				if (\in_array($cuType, ['RESOURCE', 'ROOM', 'UNKNOWN'])) {
247
-					// Don't send emails to things
248
-					continue;
249
-				}
250
-
251
-				$partstat = $this->getPartstatOfAttendee($attendee);
252
-				if ($partstat === 'DECLINED') {
253
-					// Don't send out emails to people who declined
254
-					continue;
255
-				}
256
-				if ($partstat === 'DELEGATED') {
257
-					$delegates = $attendee->offsetGet('DELEGATED-TO');
258
-					if (!($delegates instanceof VObject\Parameter)) {
259
-						continue;
260
-					}
261
-
262
-					$emailAddressesOfDelegates = $delegates->getParts();
263
-					foreach ($emailAddressesOfDelegates as $addressesOfDelegate) {
264
-						if (strcasecmp($addressesOfDelegate, 'mailto:') === 0) {
265
-							$emailAddresses[substr($addressesOfDelegate, 7)] = [];
266
-						}
267
-					}
268
-
269
-					continue;
270
-				}
271
-
272
-				$emailAddressOfAttendee = $this->getEMailAddressOfAttendee($attendee);
273
-				if ($emailAddressOfAttendee !== null) {
274
-					$properties = [];
275
-
276
-					$langProp = $attendee->offsetGet('LANG');
277
-					if ($langProp instanceof VObject\Parameter) {
278
-						$properties['LANG'] = $langProp->getValue();
279
-					}
280
-
281
-					$emailAddresses[$emailAddressOfAttendee] = $properties;
282
-				}
283
-			}
284
-		}
285
-
286
-		if (isset($vevent->ORGANIZER) && $this->hasAttendeeMailURI($vevent->ORGANIZER)) {
287
-			$emailAddresses[$this->getEMailAddressOfAttendee($vevent->ORGANIZER)] = [];
288
-		}
289
-
290
-		return $emailAddresses;
291
-	}
292
-
293
-
294
-
295
-	/**
296
-	 * @param VObject\Property $attendee
297
-	 * @return string
298
-	 */
299
-	private function getCUTypeOfAttendee(VObject\Property $attendee):string {
300
-		$cuType = $attendee->offsetGet('CUTYPE');
301
-		if ($cuType instanceof VObject\Parameter) {
302
-			return strtoupper($cuType->getValue());
303
-		}
304
-
305
-		return 'INDIVIDUAL';
306
-	}
307
-
308
-	/**
309
-	 * @param VObject\Property $attendee
310
-	 * @return string
311
-	 */
312
-	private function getPartstatOfAttendee(VObject\Property $attendee):string {
313
-		$partstat = $attendee->offsetGet('PARTSTAT');
314
-		if ($partstat instanceof VObject\Parameter) {
315
-			return strtoupper($partstat->getValue());
316
-		}
317
-
318
-		return 'NEEDS-ACTION';
319
-	}
320
-
321
-	/**
322
-	 * @param VObject\Property $attendee
323
-	 * @return bool
324
-	 */
325
-	private function hasAttendeeMailURI(VObject\Property $attendee):bool {
326
-		return stripos($attendee->getValue(), 'mailto:') === 0;
327
-	}
328
-
329
-	/**
330
-	 * @param VObject\Property $attendee
331
-	 * @return string|null
332
-	 */
333
-	private function getEMailAddressOfAttendee(VObject\Property $attendee):?string {
334
-		if (!$this->hasAttendeeMailURI($attendee)) {
335
-			return null;
336
-		}
337
-
338
-		return substr($attendee->getValue(), 7);
339
-	}
340
-
341
-	/**
342
-	 * @param array $users
343
-	 * @return array
344
-	 */
345
-	private function getEMailAddressesOfAllUsersWithWriteAccessToCalendar(array $users):array {
346
-		$emailAddresses = [];
347
-
348
-		foreach ($users as $user) {
349
-			$emailAddress = $user->getEMailAddress();
350
-			if ($emailAddress) {
351
-				$lang = $this->l10nFactory->getUserLanguage($user);
352
-				if ($lang) {
353
-					$emailAddresses[$emailAddress] = [
354
-						'LANG' => $lang,
355
-					];
356
-				} else {
357
-					$emailAddresses[$emailAddress] = [];
358
-				}
359
-			}
360
-		}
361
-
362
-		return $emailAddresses;
363
-	}
364
-
365
-	/**
366
-	 * @param IL10N $l10n
367
-	 * @param VEvent $vevent
368
-	 * @return string
369
-	 * @throws \Exception
370
-	 */
371
-	private function generateDateString(IL10N $l10n, VEvent $vevent):string {
372
-		$isAllDay = $vevent->DTSTART instanceof Property\ICalendar\Date;
373
-
374
-		/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
375
-		/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
376
-		/** @var \DateTimeImmutable $dtstartDt */
377
-		$dtstartDt = $vevent->DTSTART->getDateTime();
378
-		/** @var \DateTimeImmutable $dtendDt */
379
-		$dtendDt = $this->getDTEndFromEvent($vevent)->getDateTime();
380
-
381
-		$diff = $dtstartDt->diff($dtendDt);
382
-
383
-		/** @phan-suppress-next-line PhanUndeclaredClassMethod */
384
-		$dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
385
-		/** @phan-suppress-next-line PhanUndeclaredClassMethod */
386
-		$dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
387
-
388
-		if ($isAllDay) {
389
-			// One day event
390
-			if ($diff->days === 1) {
391
-				return $this->getDateString($l10n, $dtstartDt);
392
-			}
393
-
394
-			return implode(' - ', [
395
-				$this->getDateString($l10n, $dtstartDt),
396
-				$this->getDateString($l10n, $dtendDt),
397
-			]);
398
-		}
399
-
400
-		$startTimezone = $endTimezone = null;
401
-		if (!$vevent->DTSTART->isFloating()) {
402
-			/** @phan-suppress-next-line PhanUndeclaredClassMethod */
403
-			$startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName();
404
-			/** @phan-suppress-next-line PhanUndeclaredClassMethod */
405
-			$endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName();
406
-		}
407
-
408
-		$localeStart = implode(', ', [
409
-			$this->getWeekDayName($l10n, $dtstartDt),
410
-			$this->getDateTimeString($l10n, $dtstartDt)
411
-		]);
412
-
413
-		// always show full date with timezone if timezones are different
414
-		if ($startTimezone !== $endTimezone) {
415
-			$localeEnd = implode(', ', [
416
-				$this->getWeekDayName($l10n, $dtendDt),
417
-				$this->getDateTimeString($l10n, $dtendDt)
418
-			]);
419
-
420
-			return $localeStart
421
-				. ' (' . $startTimezone . ') '
422
-				. ' - '
423
-				. $localeEnd
424
-				. ' (' . $endTimezone . ')';
425
-		}
426
-
427
-		// Show only the time if the day is the same
428
-		$localeEnd = $this->isDayEqual($dtstartDt, $dtendDt)
429
-			? $this->getTimeString($l10n, $dtendDt)
430
-			: implode(', ', [
431
-				$this->getWeekDayName($l10n, $dtendDt),
432
-				$this->getDateTimeString($l10n, $dtendDt)
433
-			]);
434
-
435
-		return $localeStart
436
-			. ' - '
437
-			. $localeEnd
438
-			. ' (' . $startTimezone . ')';
439
-	}
440
-
441
-	/**
442
-	 * @param DateTime $dtStart
443
-	 * @param DateTime $dtEnd
444
-	 * @return bool
445
-	 */
446
-	private function isDayEqual(DateTime $dtStart,
447
-								DateTime $dtEnd):bool {
448
-		return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
449
-	}
450
-
451
-	/**
452
-	 * @param IL10N $l10n
453
-	 * @param DateTime $dt
454
-	 * @return string
455
-	 */
456
-	private function getWeekDayName(IL10N $l10n, DateTime $dt):string {
457
-		return $l10n->l('weekdayName', $dt, ['width' => 'abbreviated']);
458
-	}
459
-
460
-	/**
461
-	 * @param IL10N $l10n
462
-	 * @param DateTime $dt
463
-	 * @return string
464
-	 */
465
-	private function getDateString(IL10N $l10n, DateTime $dt):string {
466
-		return $l10n->l('date', $dt, ['width' => 'medium']);
467
-	}
468
-
469
-	/**
470
-	 * @param IL10N $l10n
471
-	 * @param DateTime $dt
472
-	 * @return string
473
-	 */
474
-	private function getDateTimeString(IL10N $l10n, DateTime $dt):string {
475
-		return $l10n->l('datetime', $dt, ['width' => 'medium|short']);
476
-	}
477
-
478
-	/**
479
-	 * @param IL10N $l10n
480
-	 * @param DateTime $dt
481
-	 * @return string
482
-	 */
483
-	private function getTimeString(IL10N $l10n, DateTime $dt):string {
484
-		return $l10n->l('time', $dt, ['width' => 'short']);
485
-	}
486
-
487
-	/**
488
-	 * @param VEvent $vevent
489
-	 * @param IL10N $l10n
490
-	 * @return string
491
-	 */
492
-	private function getTitleFromVEvent(VEvent $vevent, IL10N $l10n):string {
493
-		if (isset($vevent->SUMMARY)) {
494
-			return (string)$vevent->SUMMARY;
495
-		}
496
-
497
-		return $l10n->t('Untitled event');
498
-	}
53
+    /** @var string */
54
+    public const NOTIFICATION_TYPE = 'EMAIL';
55
+
56
+    /** @var IMailer */
57
+    private $mailer;
58
+
59
+    /**
60
+     * @param IConfig $config
61
+     * @param IMailer $mailer
62
+     * @param ILogger $logger
63
+     * @param L10NFactory $l10nFactory
64
+     * @param IUrlGenerator $urlGenerator
65
+     */
66
+    public function __construct(IConfig $config,
67
+                                IMailer $mailer,
68
+                                ILogger $logger,
69
+                                L10NFactory $l10nFactory,
70
+                                IURLGenerator $urlGenerator) {
71
+        parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
72
+        $this->mailer = $mailer;
73
+    }
74
+
75
+    /**
76
+     * Send out notification via email
77
+     *
78
+     * @param VEvent $vevent
79
+     * @param string $calendarDisplayName
80
+     * @param array $users
81
+     * @throws \Exception
82
+     */
83
+    public function send(VEvent $vevent,
84
+                            string $calendarDisplayName,
85
+                            array $users=[]):void {
86
+        $fallbackLanguage = $this->getFallbackLanguage();
87
+
88
+        $emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users);
89
+        $emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent);
90
+
91
+        // Quote from php.net:
92
+        // If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.
93
+        // => if there are duplicate email addresses, it will always take the system value
94
+        $emailAddresses = array_merge(
95
+            $emailAddressesOfAttendees,
96
+            $emailAddressesOfSharees
97
+        );
98
+
99
+        $sortedByLanguage = $this->sortEMailAddressesByLanguage($emailAddresses, $fallbackLanguage);
100
+        $organizer = $this->getOrganizerEMailAndNameFromEvent($vevent);
101
+
102
+        foreach ($sortedByLanguage as $lang => $emailAddresses) {
103
+            if (!$this->hasL10NForLang($lang)) {
104
+                $lang = $fallbackLanguage;
105
+            }
106
+            $l10n = $this->getL10NForLang($lang);
107
+            $fromEMail = \OCP\Util::getDefaultEmailAddress('reminders-noreply');
108
+
109
+            $template = $this->mailer->createEMailTemplate('dav.calendarReminder');
110
+            $template->addHeader();
111
+            $this->addSubjectAndHeading($template, $l10n, $vevent);
112
+            $this->addBulletList($template, $l10n, $calendarDisplayName, $vevent);
113
+            $template->addFooter();
114
+
115
+            foreach ($emailAddresses as $emailAddress) {
116
+                $message = $this->mailer->createMessage();
117
+                $message->setFrom([$fromEMail]);
118
+                if ($organizer) {
119
+                    $message->setReplyTo($organizer);
120
+                }
121
+                $message->setTo([$emailAddress]);
122
+                $message->useTemplate($template);
123
+
124
+                try {
125
+                    $failed = $this->mailer->send($message);
126
+                    if ($failed) {
127
+                        $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
128
+                    }
129
+                } catch (\Exception $ex) {
130
+                    $this->logger->logException($ex, ['app' => 'dav']);
131
+                }
132
+            }
133
+        }
134
+    }
135
+
136
+    /**
137
+     * @param IEMailTemplate $template
138
+     * @param IL10N $l10n
139
+     * @param VEvent $vevent
140
+     */
141
+    private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n, VEvent $vevent):void {
142
+        $template->setSubject('Notification: ' . $this->getTitleFromVEvent($vevent, $l10n));
143
+        $template->addHeading($this->getTitleFromVEvent($vevent, $l10n));
144
+    }
145
+
146
+    /**
147
+     * @param IEMailTemplate $template
148
+     * @param IL10N $l10n
149
+     * @param string $calendarDisplayName
150
+     * @param array $eventData
151
+     */
152
+    private function addBulletList(IEMailTemplate $template,
153
+                                    IL10N $l10n,
154
+                                    string $calendarDisplayName,
155
+                                    VEvent $vevent):void {
156
+        $template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'),
157
+            $this->getAbsoluteImagePath('actions/info.svg'));
158
+
159
+        $template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'),
160
+            $this->getAbsoluteImagePath('places/calendar.svg'));
161
+
162
+        if (isset($vevent->LOCATION)) {
163
+            $template->addBodyListItem((string) $vevent->LOCATION, $l10n->t('Where:'),
164
+                $this->getAbsoluteImagePath('actions/address.svg'));
165
+        }
166
+        if (isset($vevent->DESCRIPTION)) {
167
+            $template->addBodyListItem((string) $vevent->DESCRIPTION, $l10n->t('Description:'),
168
+                $this->getAbsoluteImagePath('actions/more.svg'));
169
+        }
170
+    }
171
+
172
+    /**
173
+     * @param string $path
174
+     * @return string
175
+     */
176
+    private function getAbsoluteImagePath(string $path):string {
177
+        return $this->urlGenerator->getAbsoluteURL(
178
+            $this->urlGenerator->imagePath('core', $path)
179
+        );
180
+    }
181
+
182
+    /**
183
+     * @param VEvent $vevent
184
+     * @return array|null
185
+     */
186
+    private function getOrganizerEMailAndNameFromEvent(VEvent $vevent):?array {
187
+        if (!$vevent->ORGANIZER) {
188
+            return null;
189
+        }
190
+
191
+        $organizer = $vevent->ORGANIZER;
192
+        if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) {
193
+            return null;
194
+        }
195
+
196
+        $organizerEMail = substr($organizer->getValue(), 7);
197
+
198
+        $name = $organizer->offsetGet('CN');
199
+        if ($name instanceof Parameter) {
200
+            return [$organizerEMail => $name];
201
+        }
202
+
203
+        return [$organizerEMail];
204
+    }
205
+
206
+    /**
207
+     * @param array $emails
208
+     * @param string $defaultLanguage
209
+     * @return array
210
+     */
211
+    private function sortEMailAddressesByLanguage(array $emails,
212
+                                                    string $defaultLanguage):array {
213
+        $sortedByLanguage = [];
214
+
215
+        foreach ($emails as $emailAddress => $parameters) {
216
+            if (isset($parameters['LANG'])) {
217
+                $lang = $parameters['LANG'];
218
+            } else {
219
+                $lang = $defaultLanguage;
220
+            }
221
+
222
+            if (!isset($sortedByLanguage[$lang])) {
223
+                $sortedByLanguage[$lang] = [];
224
+            }
225
+
226
+            $sortedByLanguage[$lang][] = $emailAddress;
227
+        }
228
+
229
+        return $sortedByLanguage;
230
+    }
231
+
232
+    /**
233
+     * @param VEvent $vevent
234
+     * @return array
235
+     */
236
+    private function getAllEMailAddressesFromEvent(VEvent $vevent):array {
237
+        $emailAddresses = [];
238
+
239
+        if (isset($vevent->ATTENDEE)) {
240
+            foreach ($vevent->ATTENDEE as $attendee) {
241
+                if (!($attendee instanceof VObject\Property)) {
242
+                    continue;
243
+                }
244
+
245
+                $cuType = $this->getCUTypeOfAttendee($attendee);
246
+                if (\in_array($cuType, ['RESOURCE', 'ROOM', 'UNKNOWN'])) {
247
+                    // Don't send emails to things
248
+                    continue;
249
+                }
250
+
251
+                $partstat = $this->getPartstatOfAttendee($attendee);
252
+                if ($partstat === 'DECLINED') {
253
+                    // Don't send out emails to people who declined
254
+                    continue;
255
+                }
256
+                if ($partstat === 'DELEGATED') {
257
+                    $delegates = $attendee->offsetGet('DELEGATED-TO');
258
+                    if (!($delegates instanceof VObject\Parameter)) {
259
+                        continue;
260
+                    }
261
+
262
+                    $emailAddressesOfDelegates = $delegates->getParts();
263
+                    foreach ($emailAddressesOfDelegates as $addressesOfDelegate) {
264
+                        if (strcasecmp($addressesOfDelegate, 'mailto:') === 0) {
265
+                            $emailAddresses[substr($addressesOfDelegate, 7)] = [];
266
+                        }
267
+                    }
268
+
269
+                    continue;
270
+                }
271
+
272
+                $emailAddressOfAttendee = $this->getEMailAddressOfAttendee($attendee);
273
+                if ($emailAddressOfAttendee !== null) {
274
+                    $properties = [];
275
+
276
+                    $langProp = $attendee->offsetGet('LANG');
277
+                    if ($langProp instanceof VObject\Parameter) {
278
+                        $properties['LANG'] = $langProp->getValue();
279
+                    }
280
+
281
+                    $emailAddresses[$emailAddressOfAttendee] = $properties;
282
+                }
283
+            }
284
+        }
285
+
286
+        if (isset($vevent->ORGANIZER) && $this->hasAttendeeMailURI($vevent->ORGANIZER)) {
287
+            $emailAddresses[$this->getEMailAddressOfAttendee($vevent->ORGANIZER)] = [];
288
+        }
289
+
290
+        return $emailAddresses;
291
+    }
292
+
293
+
294
+
295
+    /**
296
+     * @param VObject\Property $attendee
297
+     * @return string
298
+     */
299
+    private function getCUTypeOfAttendee(VObject\Property $attendee):string {
300
+        $cuType = $attendee->offsetGet('CUTYPE');
301
+        if ($cuType instanceof VObject\Parameter) {
302
+            return strtoupper($cuType->getValue());
303
+        }
304
+
305
+        return 'INDIVIDUAL';
306
+    }
307
+
308
+    /**
309
+     * @param VObject\Property $attendee
310
+     * @return string
311
+     */
312
+    private function getPartstatOfAttendee(VObject\Property $attendee):string {
313
+        $partstat = $attendee->offsetGet('PARTSTAT');
314
+        if ($partstat instanceof VObject\Parameter) {
315
+            return strtoupper($partstat->getValue());
316
+        }
317
+
318
+        return 'NEEDS-ACTION';
319
+    }
320
+
321
+    /**
322
+     * @param VObject\Property $attendee
323
+     * @return bool
324
+     */
325
+    private function hasAttendeeMailURI(VObject\Property $attendee):bool {
326
+        return stripos($attendee->getValue(), 'mailto:') === 0;
327
+    }
328
+
329
+    /**
330
+     * @param VObject\Property $attendee
331
+     * @return string|null
332
+     */
333
+    private function getEMailAddressOfAttendee(VObject\Property $attendee):?string {
334
+        if (!$this->hasAttendeeMailURI($attendee)) {
335
+            return null;
336
+        }
337
+
338
+        return substr($attendee->getValue(), 7);
339
+    }
340
+
341
+    /**
342
+     * @param array $users
343
+     * @return array
344
+     */
345
+    private function getEMailAddressesOfAllUsersWithWriteAccessToCalendar(array $users):array {
346
+        $emailAddresses = [];
347
+
348
+        foreach ($users as $user) {
349
+            $emailAddress = $user->getEMailAddress();
350
+            if ($emailAddress) {
351
+                $lang = $this->l10nFactory->getUserLanguage($user);
352
+                if ($lang) {
353
+                    $emailAddresses[$emailAddress] = [
354
+                        'LANG' => $lang,
355
+                    ];
356
+                } else {
357
+                    $emailAddresses[$emailAddress] = [];
358
+                }
359
+            }
360
+        }
361
+
362
+        return $emailAddresses;
363
+    }
364
+
365
+    /**
366
+     * @param IL10N $l10n
367
+     * @param VEvent $vevent
368
+     * @return string
369
+     * @throws \Exception
370
+     */
371
+    private function generateDateString(IL10N $l10n, VEvent $vevent):string {
372
+        $isAllDay = $vevent->DTSTART instanceof Property\ICalendar\Date;
373
+
374
+        /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
375
+        /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
376
+        /** @var \DateTimeImmutable $dtstartDt */
377
+        $dtstartDt = $vevent->DTSTART->getDateTime();
378
+        /** @var \DateTimeImmutable $dtendDt */
379
+        $dtendDt = $this->getDTEndFromEvent($vevent)->getDateTime();
380
+
381
+        $diff = $dtstartDt->diff($dtendDt);
382
+
383
+        /** @phan-suppress-next-line PhanUndeclaredClassMethod */
384
+        $dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
385
+        /** @phan-suppress-next-line PhanUndeclaredClassMethod */
386
+        $dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
387
+
388
+        if ($isAllDay) {
389
+            // One day event
390
+            if ($diff->days === 1) {
391
+                return $this->getDateString($l10n, $dtstartDt);
392
+            }
393
+
394
+            return implode(' - ', [
395
+                $this->getDateString($l10n, $dtstartDt),
396
+                $this->getDateString($l10n, $dtendDt),
397
+            ]);
398
+        }
399
+
400
+        $startTimezone = $endTimezone = null;
401
+        if (!$vevent->DTSTART->isFloating()) {
402
+            /** @phan-suppress-next-line PhanUndeclaredClassMethod */
403
+            $startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName();
404
+            /** @phan-suppress-next-line PhanUndeclaredClassMethod */
405
+            $endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName();
406
+        }
407
+
408
+        $localeStart = implode(', ', [
409
+            $this->getWeekDayName($l10n, $dtstartDt),
410
+            $this->getDateTimeString($l10n, $dtstartDt)
411
+        ]);
412
+
413
+        // always show full date with timezone if timezones are different
414
+        if ($startTimezone !== $endTimezone) {
415
+            $localeEnd = implode(', ', [
416
+                $this->getWeekDayName($l10n, $dtendDt),
417
+                $this->getDateTimeString($l10n, $dtendDt)
418
+            ]);
419
+
420
+            return $localeStart
421
+                . ' (' . $startTimezone . ') '
422
+                . ' - '
423
+                . $localeEnd
424
+                . ' (' . $endTimezone . ')';
425
+        }
426
+
427
+        // Show only the time if the day is the same
428
+        $localeEnd = $this->isDayEqual($dtstartDt, $dtendDt)
429
+            ? $this->getTimeString($l10n, $dtendDt)
430
+            : implode(', ', [
431
+                $this->getWeekDayName($l10n, $dtendDt),
432
+                $this->getDateTimeString($l10n, $dtendDt)
433
+            ]);
434
+
435
+        return $localeStart
436
+            . ' - '
437
+            . $localeEnd
438
+            . ' (' . $startTimezone . ')';
439
+    }
440
+
441
+    /**
442
+     * @param DateTime $dtStart
443
+     * @param DateTime $dtEnd
444
+     * @return bool
445
+     */
446
+    private function isDayEqual(DateTime $dtStart,
447
+                                DateTime $dtEnd):bool {
448
+        return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
449
+    }
450
+
451
+    /**
452
+     * @param IL10N $l10n
453
+     * @param DateTime $dt
454
+     * @return string
455
+     */
456
+    private function getWeekDayName(IL10N $l10n, DateTime $dt):string {
457
+        return $l10n->l('weekdayName', $dt, ['width' => 'abbreviated']);
458
+    }
459
+
460
+    /**
461
+     * @param IL10N $l10n
462
+     * @param DateTime $dt
463
+     * @return string
464
+     */
465
+    private function getDateString(IL10N $l10n, DateTime $dt):string {
466
+        return $l10n->l('date', $dt, ['width' => 'medium']);
467
+    }
468
+
469
+    /**
470
+     * @param IL10N $l10n
471
+     * @param DateTime $dt
472
+     * @return string
473
+     */
474
+    private function getDateTimeString(IL10N $l10n, DateTime $dt):string {
475
+        return $l10n->l('datetime', $dt, ['width' => 'medium|short']);
476
+    }
477
+
478
+    /**
479
+     * @param IL10N $l10n
480
+     * @param DateTime $dt
481
+     * @return string
482
+     */
483
+    private function getTimeString(IL10N $l10n, DateTime $dt):string {
484
+        return $l10n->l('time', $dt, ['width' => 'short']);
485
+    }
486
+
487
+    /**
488
+     * @param VEvent $vevent
489
+     * @param IL10N $l10n
490
+     * @return string
491
+     */
492
+    private function getTitleFromVEvent(VEvent $vevent, IL10N $l10n):string {
493
+        if (isset($vevent->SUMMARY)) {
494
+            return (string)$vevent->SUMMARY;
495
+        }
496
+
497
+        return $l10n->t('Untitled event');
498
+    }
499 499
 }
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Controller/UsersController.php 1 patch
Indentation   +904 added lines, -904 removed lines patch added patch discarded remove patch
@@ -62,908 +62,908 @@
 block discarded – undo
62 62
 
63 63
 class UsersController extends AUserData {
64 64
 
65
-	/** @var IAppManager */
66
-	private $appManager;
67
-	/** @var ILogger */
68
-	private $logger;
69
-	/** @var IFactory */
70
-	protected $l10nFactory;
71
-	/** @var NewUserMailHelper */
72
-	private $newUserMailHelper;
73
-	/** @var FederatedFileSharingFactory */
74
-	private $federatedFileSharingFactory;
75
-	/** @var ISecureRandom */
76
-	private $secureRandom;
77
-	/** @var RemoteWipe */
78
-	private $remoteWipe;
79
-
80
-	public function __construct(string $appName,
81
-								IRequest $request,
82
-								IUserManager $userManager,
83
-								IConfig $config,
84
-								IAppManager $appManager,
85
-								IGroupManager $groupManager,
86
-								IUserSession $userSession,
87
-								AccountManager $accountManager,
88
-								ILogger $logger,
89
-								IFactory $l10nFactory,
90
-								NewUserMailHelper $newUserMailHelper,
91
-								FederatedFileSharingFactory $federatedFileSharingFactory,
92
-								ISecureRandom $secureRandom,
93
-								RemoteWipe $remoteWipe) {
94
-		parent::__construct($appName,
95
-							$request,
96
-							$userManager,
97
-							$config,
98
-							$groupManager,
99
-							$userSession,
100
-							$accountManager,
101
-							$l10nFactory);
102
-
103
-		$this->appManager = $appManager;
104
-		$this->logger = $logger;
105
-		$this->l10nFactory = $l10nFactory;
106
-		$this->newUserMailHelper = $newUserMailHelper;
107
-		$this->federatedFileSharingFactory = $federatedFileSharingFactory;
108
-		$this->secureRandom = $secureRandom;
109
-		$this->remoteWipe = $remoteWipe;
110
-	}
111
-
112
-	/**
113
-	 * @NoAdminRequired
114
-	 *
115
-	 * returns a list of users
116
-	 *
117
-	 * @param string $search
118
-	 * @param int $limit
119
-	 * @param int $offset
120
-	 * @return DataResponse
121
-	 */
122
-	public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
123
-		$user = $this->userSession->getUser();
124
-		$users = [];
125
-
126
-		// Admin? Or SubAdmin?
127
-		$uid = $user->getUID();
128
-		$subAdminManager = $this->groupManager->getSubAdmin();
129
-		if ($this->groupManager->isAdmin($uid)) {
130
-			$users = $this->userManager->search($search, $limit, $offset);
131
-		} elseif ($subAdminManager->isSubAdmin($user)) {
132
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
133
-			foreach ($subAdminOfGroups as $key => $group) {
134
-				$subAdminOfGroups[$key] = $group->getGID();
135
-			}
136
-
137
-			$users = [];
138
-			foreach ($subAdminOfGroups as $group) {
139
-				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
140
-			}
141
-		}
142
-
143
-		$users = array_keys($users);
144
-
145
-		return new DataResponse([
146
-			'users' => $users
147
-		]);
148
-	}
149
-
150
-	/**
151
-	 * @NoAdminRequired
152
-	 *
153
-	 * returns a list of users and their data
154
-	 */
155
-	public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
156
-		$currentUser = $this->userSession->getUser();
157
-		$users = [];
158
-
159
-		// Admin? Or SubAdmin?
160
-		$uid = $currentUser->getUID();
161
-		$subAdminManager = $this->groupManager->getSubAdmin();
162
-		if ($this->groupManager->isAdmin($uid)) {
163
-			$users = $this->userManager->search($search, $limit, $offset);
164
-			$users = array_keys($users);
165
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
166
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
167
-			foreach ($subAdminOfGroups as $key => $group) {
168
-				$subAdminOfGroups[$key] = $group->getGID();
169
-			}
170
-
171
-			$users = [];
172
-			foreach ($subAdminOfGroups as $group) {
173
-				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
174
-			}
175
-			$users = array_merge(...$users);
176
-		}
177
-
178
-		$usersDetails = [];
179
-		foreach ($users as $userId) {
180
-			$userId = (string) $userId;
181
-			$userData = $this->getUserData($userId);
182
-			// Do not insert empty entry
183
-			if (!empty($userData)) {
184
-				$usersDetails[$userId] = $userData;
185
-			} else {
186
-				// Logged user does not have permissions to see this user
187
-				// only showing its id
188
-				$usersDetails[$userId] = ['id' => $userId];
189
-			}
190
-		}
191
-
192
-		return new DataResponse([
193
-			'users' => $usersDetails
194
-		]);
195
-	}
196
-
197
-	/**
198
-	 * @throws OCSException
199
-	 */
200
-	private function createNewUserId(): string {
201
-		$attempts = 0;
202
-		do {
203
-			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
204
-			if (!$this->userManager->userExists($uidCandidate)) {
205
-				return $uidCandidate;
206
-			}
207
-			$attempts++;
208
-		} while ($attempts < 10);
209
-		throw new OCSException('Could not create non-existing user id', 111);
210
-	}
211
-
212
-	/**
213
-	 * @PasswordConfirmationRequired
214
-	 * @NoAdminRequired
215
-	 *
216
-	 * @param string $userid
217
-	 * @param string $password
218
-	 * @param string $displayName
219
-	 * @param string $email
220
-	 * @param array $groups
221
-	 * @param array $subadmin
222
-	 * @param string $quota
223
-	 * @param string $language
224
-	 * @return DataResponse
225
-	 * @throws OCSException
226
-	 */
227
-	public function addUser(string $userid,
228
-							string $password = '',
229
-							string $displayName = '',
230
-							string $email = '',
231
-							array $groups = [],
232
-							array $subadmin = [],
233
-							string $quota = '',
234
-							string $language = ''): DataResponse {
235
-		$user = $this->userSession->getUser();
236
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
237
-		$subAdminManager = $this->groupManager->getSubAdmin();
238
-
239
-		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
240
-			$userid = $this->createNewUserId();
241
-		}
242
-
243
-		if ($this->userManager->userExists($userid)) {
244
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
245
-			throw new OCSException('User already exists', 102);
246
-		}
247
-
248
-		if ($groups !== []) {
249
-			foreach ($groups as $group) {
250
-				if (!$this->groupManager->groupExists($group)) {
251
-					throw new OCSException('group '.$group.' does not exist', 104);
252
-				}
253
-				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
254
-					throw new OCSException('insufficient privileges for group '. $group, 105);
255
-				}
256
-			}
257
-		} else {
258
-			if (!$isAdmin) {
259
-				throw new OCSException('no group specified (required for subadmins)', 106);
260
-			}
261
-		}
262
-
263
-		$subadminGroups = [];
264
-		if ($subadmin !== []) {
265
-			foreach ($subadmin as $groupid) {
266
-				$group = $this->groupManager->get($groupid);
267
-				// Check if group exists
268
-				if ($group === null) {
269
-					throw new OCSException('Subadmin group does not exist',  102);
270
-				}
271
-				// Check if trying to make subadmin of admin group
272
-				if ($group->getGID() === 'admin') {
273
-					throw new OCSException('Cannot create subadmins for admin group', 103);
274
-				}
275
-				// Check if has permission to promote subadmins
276
-				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
277
-					throw new OCSForbiddenException('No permissions to promote subadmins');
278
-				}
279
-				$subadminGroups[] = $group;
280
-			}
281
-		}
282
-
283
-		$generatePasswordResetToken = false;
284
-		if ($password === '') {
285
-			if ($email === '') {
286
-				throw new OCSException('To send a password link to the user an email address is required.', 108);
287
-			}
288
-
289
-			$password = $this->secureRandom->generate(10);
290
-			// Make sure we pass the password_policy
291
-			$password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()');
292
-			$generatePasswordResetToken = true;
293
-		}
294
-
295
-		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
296
-			throw new OCSException('Required email address was not provided', 110);
297
-		}
298
-
299
-		try {
300
-			$newUser = $this->userManager->createUser($userid, $password);
301
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
302
-
303
-			foreach ($groups as $group) {
304
-				$this->groupManager->get($group)->addUser($newUser);
305
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
306
-			}
307
-			foreach ($subadminGroups as $group) {
308
-				$subAdminManager->createSubAdmin($newUser, $group);
309
-			}
310
-
311
-			if ($displayName !== '') {
312
-				$this->editUser($userid, 'display', $displayName);
313
-			}
314
-
315
-			if ($quota !== '') {
316
-				$this->editUser($userid, 'quota', $quota);
317
-			}
318
-
319
-			if ($language !== '') {
320
-				$this->editUser($userid, 'language', $language);
321
-			}
322
-
323
-			// Send new user mail only if a mail is set
324
-			if ($email !== '' && $this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
325
-				$newUser->setEMailAddress($email);
326
-				try {
327
-					$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
328
-					$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
329
-				} catch (\Exception $e) {
330
-					// Mail could be failing hard or just be plain not configured
331
-					// Logging error as it is the hardest of the two
332
-					$this->logger->logException($e, [
333
-						'message' => "Unable to send the invitation mail to $email",
334
-						'level' => ILogger::ERROR,
335
-						'app' => 'ocs_api',
336
-					]);
337
-				}
338
-			}
339
-
340
-			return new DataResponse(['id' => $userid]);
341
-		} catch (HintException $e) {
342
-			$this->logger->logException($e, [
343
-				'message' => 'Failed addUser attempt with hint exception.',
344
-				'level' => ILogger::WARN,
345
-				'app' => 'ocs_api',
346
-			]);
347
-			throw new OCSException($e->getHint(), 107);
348
-		} catch (OCSException $e) {
349
-			$this->logger->logException($e, [
350
-				'message' => 'Failed addUser attempt with ocs exeption.',
351
-				'level' => ILogger::ERROR,
352
-				'app' => 'ocs_api',
353
-			]);
354
-			throw $e;
355
-		} catch (\Exception $e) {
356
-			$this->logger->logException($e, [
357
-				'message' => 'Failed addUser attempt with exception.',
358
-				'level' => ILogger::ERROR,
359
-				'app' => 'ocs_api',
360
-			]);
361
-			throw new OCSException('Bad request', 101);
362
-		}
363
-	}
364
-
365
-	/**
366
-	 * @NoAdminRequired
367
-	 * @NoSubAdminRequired
368
-	 *
369
-	 * gets user info
370
-	 *
371
-	 * @param string $userId
372
-	 * @return DataResponse
373
-	 * @throws OCSException
374
-	 */
375
-	public function getUser(string $userId): DataResponse {
376
-		$data = $this->getUserData($userId);
377
-		// getUserData returns empty array if not enough permissions
378
-		if (empty($data)) {
379
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
380
-		}
381
-		return new DataResponse($data);
382
-	}
383
-
384
-	/**
385
-	 * @NoAdminRequired
386
-	 * @NoSubAdminRequired
387
-	 *
388
-	 * gets user info from the currently logged in user
389
-	 *
390
-	 * @return DataResponse
391
-	 * @throws OCSException
392
-	 */
393
-	public function getCurrentUser(): DataResponse {
394
-		$user = $this->userSession->getUser();
395
-		if ($user) {
396
-			$data =  $this->getUserData($user->getUID());
397
-			// rename "displayname" to "display-name" only for this call to keep
398
-			// the API stable.
399
-			$data['display-name'] = $data['displayname'];
400
-			unset($data['displayname']);
401
-			return new DataResponse($data);
402
-		}
403
-
404
-		throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
405
-	}
406
-
407
-	/**
408
-	 * @NoAdminRequired
409
-	 * @NoSubAdminRequired
410
-	 */
411
-	public function getEditableFields(): DataResponse {
412
-		$permittedFields = [];
413
-
414
-		// Editing self (display, email)
415
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
416
-			$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
417
-			$permittedFields[] = AccountManager::PROPERTY_EMAIL;
418
-		}
419
-
420
-		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
421
-			$federatedFileSharing = $this->federatedFileSharingFactory->get();
422
-			$shareProvider = $federatedFileSharing->getFederatedShareProvider();
423
-			if ($shareProvider->isLookupServerUploadEnabled()) {
424
-				$permittedFields[] = AccountManager::PROPERTY_PHONE;
425
-				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
426
-				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
427
-				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
428
-			}
429
-		}
430
-
431
-		return new DataResponse($permittedFields);
432
-	}
433
-
434
-	/**
435
-	 * @NoAdminRequired
436
-	 * @NoSubAdminRequired
437
-	 * @PasswordConfirmationRequired
438
-	 *
439
-	 * edit users
440
-	 *
441
-	 * @param string $userId
442
-	 * @param string $key
443
-	 * @param string $value
444
-	 * @return DataResponse
445
-	 * @throws OCSException
446
-	 */
447
-	public function editUser(string $userId, string $key, string $value): DataResponse {
448
-		$currentLoggedInUser = $this->userSession->getUser();
449
-
450
-		$targetUser = $this->userManager->get($userId);
451
-		if ($targetUser === null) {
452
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
453
-		}
454
-
455
-		$permittedFields = [];
456
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
457
-			// Editing self (display, email)
458
-			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
459
-				$permittedFields[] = 'display';
460
-				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
461
-				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
462
-			}
463
-
464
-			$permittedFields[] = 'password';
465
-			if ($this->config->getSystemValue('force_language', false) === false ||
466
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
467
-				$permittedFields[] = 'language';
468
-			}
469
-
470
-			if ($this->config->getSystemValue('force_locale', false) === false ||
471
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
472
-				$permittedFields[] = 'locale';
473
-			}
474
-
475
-			if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
476
-				$federatedFileSharing = \OC::$server->query(\OCA\FederatedFileSharing\AppInfo\Application::class);
477
-				$shareProvider = $federatedFileSharing->getFederatedShareProvider();
478
-				if ($shareProvider->isLookupServerUploadEnabled()) {
479
-					$permittedFields[] = AccountManager::PROPERTY_PHONE;
480
-					$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
481
-					$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
482
-					$permittedFields[] = AccountManager::PROPERTY_TWITTER;
483
-				}
484
-			}
485
-
486
-			// If admin they can edit their own quota
487
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
488
-				$permittedFields[] = 'quota';
489
-			}
490
-		} else {
491
-			// Check if admin / subadmin
492
-			$subAdminManager = $this->groupManager->getSubAdmin();
493
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
494
-			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
495
-				// They have permissions over the user
496
-				$permittedFields[] = 'display';
497
-				$permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
498
-				$permittedFields[] = AccountManager::PROPERTY_EMAIL;
499
-				$permittedFields[] = 'password';
500
-				$permittedFields[] = 'language';
501
-				$permittedFields[] = 'locale';
502
-				$permittedFields[] = AccountManager::PROPERTY_PHONE;
503
-				$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
504
-				$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
505
-				$permittedFields[] = AccountManager::PROPERTY_TWITTER;
506
-				$permittedFields[] = 'quota';
507
-			} else {
508
-				// No rights
509
-				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
510
-			}
511
-		}
512
-		// Check if permitted to edit this field
513
-		if (!in_array($key, $permittedFields)) {
514
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
515
-		}
516
-		// Process the edit
517
-		switch ($key) {
518
-			case 'display':
519
-			case AccountManager::PROPERTY_DISPLAYNAME:
520
-				$targetUser->setDisplayName($value);
521
-				break;
522
-			case 'quota':
523
-				$quota = $value;
524
-				if ($quota !== 'none' && $quota !== 'default') {
525
-					if (is_numeric($quota)) {
526
-						$quota = (float) $quota;
527
-					} else {
528
-						$quota = \OCP\Util::computerFileSize($quota);
529
-					}
530
-					if ($quota === false) {
531
-						throw new OCSException('Invalid quota value '.$value, 103);
532
-					}
533
-					if ($quota === -1) {
534
-						$quota = 'none';
535
-					} else {
536
-						$quota = \OCP\Util::humanFileSize($quota);
537
-					}
538
-				}
539
-				$targetUser->setQuota($quota);
540
-				break;
541
-			case 'password':
542
-				try {
543
-					if (!$targetUser->canChangePassword()) {
544
-						throw new OCSException('Setting the password is not supported by the users backend', 103);
545
-					}
546
-					$targetUser->setPassword($value);
547
-				} catch (HintException $e) { // password policy error
548
-					throw new OCSException($e->getMessage(), 103);
549
-				}
550
-				break;
551
-			case 'language':
552
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
553
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
554
-					throw new OCSException('Invalid language', 102);
555
-				}
556
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
557
-				break;
558
-			case 'locale':
559
-				if (!$this->l10nFactory->localeExists($value)) {
560
-					throw new OCSException('Invalid locale', 102);
561
-				}
562
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
563
-				break;
564
-			case AccountManager::PROPERTY_EMAIL:
565
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
566
-					$targetUser->setEMailAddress($value);
567
-				} else {
568
-					throw new OCSException('', 102);
569
-				}
570
-				break;
571
-			case AccountManager::PROPERTY_PHONE:
572
-			case AccountManager::PROPERTY_ADDRESS:
573
-			case AccountManager::PROPERTY_WEBSITE:
574
-			case AccountManager::PROPERTY_TWITTER:
575
-				$userAccount = $this->accountManager->getUser($targetUser);
576
-				if ($userAccount[$key]['value'] !== $value) {
577
-					$userAccount[$key]['value'] = $value;
578
-					$this->accountManager->updateUser($targetUser, $userAccount);
579
-				}
580
-				break;
581
-			default:
582
-				throw new OCSException('', 103);
583
-		}
584
-		return new DataResponse();
585
-	}
586
-
587
-	/**
588
-	 * @PasswordConfirmationRequired
589
-	 * @NoAdminRequired
590
-	 *
591
-	 * @param string $userId
592
-	 *
593
-	 * @return DataResponse
594
-	 *
595
-	 * @throws OCSException
596
-	 */
597
-	public function wipeUserDevices(string $userId): DataResponse {
598
-		/** @var IUser $currentLoggedInUser */
599
-		$currentLoggedInUser = $this->userSession->getUser();
600
-
601
-		$targetUser = $this->userManager->get($userId);
602
-
603
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
604
-			throw new OCSException('', 101);
605
-		}
606
-
607
-		// If not permitted
608
-		$subAdminManager = $this->groupManager->getSubAdmin();
609
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
610
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
611
-		}
612
-
613
-		$this->remoteWipe->markAllTokensForWipe($targetUser);
614
-
615
-		return new DataResponse();
616
-	}
617
-
618
-	/**
619
-	 * @PasswordConfirmationRequired
620
-	 * @NoAdminRequired
621
-	 *
622
-	 * @param string $userId
623
-	 * @return DataResponse
624
-	 * @throws OCSException
625
-	 */
626
-	public function deleteUser(string $userId): DataResponse {
627
-		$currentLoggedInUser = $this->userSession->getUser();
628
-
629
-		$targetUser = $this->userManager->get($userId);
630
-
631
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
632
-			throw new OCSException('', 101);
633
-		}
634
-
635
-		// If not permitted
636
-		$subAdminManager = $this->groupManager->getSubAdmin();
637
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
638
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
639
-		}
640
-
641
-		// Go ahead with the delete
642
-		if ($targetUser->delete()) {
643
-			return new DataResponse();
644
-		} else {
645
-			throw new OCSException('', 101);
646
-		}
647
-	}
648
-
649
-	/**
650
-	 * @PasswordConfirmationRequired
651
-	 * @NoAdminRequired
652
-	 *
653
-	 * @param string $userId
654
-	 * @return DataResponse
655
-	 * @throws OCSException
656
-	 * @throws OCSForbiddenException
657
-	 */
658
-	public function disableUser(string $userId): DataResponse {
659
-		return $this->setEnabled($userId, false);
660
-	}
661
-
662
-	/**
663
-	 * @PasswordConfirmationRequired
664
-	 * @NoAdminRequired
665
-	 *
666
-	 * @param string $userId
667
-	 * @return DataResponse
668
-	 * @throws OCSException
669
-	 * @throws OCSForbiddenException
670
-	 */
671
-	public function enableUser(string $userId): DataResponse {
672
-		return $this->setEnabled($userId, true);
673
-	}
674
-
675
-	/**
676
-	 * @param string $userId
677
-	 * @param bool $value
678
-	 * @return DataResponse
679
-	 * @throws OCSException
680
-	 */
681
-	private function setEnabled(string $userId, bool $value): DataResponse {
682
-		$currentLoggedInUser = $this->userSession->getUser();
683
-
684
-		$targetUser = $this->userManager->get($userId);
685
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
686
-			throw new OCSException('', 101);
687
-		}
688
-
689
-		// If not permitted
690
-		$subAdminManager = $this->groupManager->getSubAdmin();
691
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
692
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
693
-		}
694
-
695
-		// enable/disable the user now
696
-		$targetUser->setEnabled($value);
697
-		return new DataResponse();
698
-	}
699
-
700
-	/**
701
-	 * @NoAdminRequired
702
-	 * @NoSubAdminRequired
703
-	 *
704
-	 * @param string $userId
705
-	 * @return DataResponse
706
-	 * @throws OCSException
707
-	 */
708
-	public function getUsersGroups(string $userId): DataResponse {
709
-		$loggedInUser = $this->userSession->getUser();
710
-
711
-		$targetUser = $this->userManager->get($userId);
712
-		if ($targetUser === null) {
713
-			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
714
-		}
715
-
716
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
717
-			// Self lookup or admin lookup
718
-			return new DataResponse([
719
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
720
-			]);
721
-		} else {
722
-			$subAdminManager = $this->groupManager->getSubAdmin();
723
-
724
-			// Looking up someone else
725
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
726
-				// Return the group that the method caller is subadmin of for the user in question
727
-				/** @var IGroup[] $getSubAdminsGroups */
728
-				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
729
-				foreach ($getSubAdminsGroups as $key => $group) {
730
-					$getSubAdminsGroups[$key] = $group->getGID();
731
-				}
732
-				$groups = array_intersect(
733
-					$getSubAdminsGroups,
734
-					$this->groupManager->getUserGroupIds($targetUser)
735
-				);
736
-				return new DataResponse(['groups' => $groups]);
737
-			} else {
738
-				// Not permitted
739
-				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
740
-			}
741
-		}
742
-	}
743
-
744
-	/**
745
-	 * @PasswordConfirmationRequired
746
-	 * @NoAdminRequired
747
-	 *
748
-	 * @param string $userId
749
-	 * @param string $groupid
750
-	 * @return DataResponse
751
-	 * @throws OCSException
752
-	 */
753
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
754
-		if ($groupid === '') {
755
-			throw new OCSException('', 101);
756
-		}
757
-
758
-		$group = $this->groupManager->get($groupid);
759
-		$targetUser = $this->userManager->get($userId);
760
-		if ($group === null) {
761
-			throw new OCSException('', 102);
762
-		}
763
-		if ($targetUser === null) {
764
-			throw new OCSException('', 103);
765
-		}
766
-
767
-		// If they're not an admin, check they are a subadmin of the group in question
768
-		$loggedInUser = $this->userSession->getUser();
769
-		$subAdminManager = $this->groupManager->getSubAdmin();
770
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
771
-			throw new OCSException('', 104);
772
-		}
773
-
774
-		// Add user to group
775
-		$group->addUser($targetUser);
776
-		return new DataResponse();
777
-	}
778
-
779
-	/**
780
-	 * @PasswordConfirmationRequired
781
-	 * @NoAdminRequired
782
-	 *
783
-	 * @param string $userId
784
-	 * @param string $groupid
785
-	 * @return DataResponse
786
-	 * @throws OCSException
787
-	 */
788
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
789
-		$loggedInUser = $this->userSession->getUser();
790
-
791
-		if ($groupid === null || trim($groupid) === '') {
792
-			throw new OCSException('', 101);
793
-		}
794
-
795
-		$group = $this->groupManager->get($groupid);
796
-		if ($group === null) {
797
-			throw new OCSException('', 102);
798
-		}
799
-
800
-		$targetUser = $this->userManager->get($userId);
801
-		if ($targetUser === null) {
802
-			throw new OCSException('', 103);
803
-		}
804
-
805
-		// If they're not an admin, check they are a subadmin of the group in question
806
-		$subAdminManager = $this->groupManager->getSubAdmin();
807
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
808
-			throw new OCSException('', 104);
809
-		}
810
-
811
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
812
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
813
-			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
814
-				if ($group->getGID() === 'admin') {
815
-					throw new OCSException('Cannot remove yourself from the admin group', 105);
816
-				}
817
-			} else {
818
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
819
-				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
820
-			}
821
-		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
822
-			/** @var IGroup[] $subAdminGroups */
823
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
824
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
825
-				return $subAdminGroup->getGID();
826
-			}, $subAdminGroups);
827
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
828
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
829
-
830
-			if (count($userSubAdminGroups) <= 1) {
831
-				// Subadmin must not be able to remove a user from all their subadmin groups.
832
-				throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
833
-			}
834
-		}
835
-
836
-		// Remove user from group
837
-		$group->removeUser($targetUser);
838
-		return new DataResponse();
839
-	}
840
-
841
-	/**
842
-	 * Creates a subadmin
843
-	 *
844
-	 * @PasswordConfirmationRequired
845
-	 *
846
-	 * @param string $userId
847
-	 * @param string $groupid
848
-	 * @return DataResponse
849
-	 * @throws OCSException
850
-	 */
851
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
852
-		$group = $this->groupManager->get($groupid);
853
-		$user = $this->userManager->get($userId);
854
-
855
-		// Check if the user exists
856
-		if ($user === null) {
857
-			throw new OCSException('User does not exist', 101);
858
-		}
859
-		// Check if group exists
860
-		if ($group === null) {
861
-			throw new OCSException('Group does not exist',  102);
862
-		}
863
-		// Check if trying to make subadmin of admin group
864
-		if ($group->getGID() === 'admin') {
865
-			throw new OCSException('Cannot create subadmins for admin group', 103);
866
-		}
867
-
868
-		$subAdminManager = $this->groupManager->getSubAdmin();
869
-
870
-		// We cannot be subadmin twice
871
-		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
872
-			return new DataResponse();
873
-		}
874
-		// Go
875
-		$subAdminManager->createSubAdmin($user, $group);
876
-		return new DataResponse();
877
-	}
878
-
879
-	/**
880
-	 * Removes a subadmin from a group
881
-	 *
882
-	 * @PasswordConfirmationRequired
883
-	 *
884
-	 * @param string $userId
885
-	 * @param string $groupid
886
-	 * @return DataResponse
887
-	 * @throws OCSException
888
-	 */
889
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
890
-		$group = $this->groupManager->get($groupid);
891
-		$user = $this->userManager->get($userId);
892
-		$subAdminManager = $this->groupManager->getSubAdmin();
893
-
894
-		// Check if the user exists
895
-		if ($user === null) {
896
-			throw new OCSException('User does not exist', 101);
897
-		}
898
-		// Check if the group exists
899
-		if ($group === null) {
900
-			throw new OCSException('Group does not exist', 101);
901
-		}
902
-		// Check if they are a subadmin of this said group
903
-		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
904
-			throw new OCSException('User is not a subadmin of this group', 102);
905
-		}
906
-
907
-		// Go
908
-		$subAdminManager->deleteSubAdmin($user, $group);
909
-		return new DataResponse();
910
-	}
911
-
912
-	/**
913
-	 * Get the groups a user is a subadmin of
914
-	 *
915
-	 * @param string $userId
916
-	 * @return DataResponse
917
-	 * @throws OCSException
918
-	 */
919
-	public function getUserSubAdminGroups(string $userId): DataResponse {
920
-		$groups = $this->getUserSubAdminGroupsData($userId);
921
-		return new DataResponse($groups);
922
-	}
923
-
924
-	/**
925
-	 * @NoAdminRequired
926
-	 * @PasswordConfirmationRequired
927
-	 *
928
-	 * resend welcome message
929
-	 *
930
-	 * @param string $userId
931
-	 * @return DataResponse
932
-	 * @throws OCSException
933
-	 */
934
-	public function resendWelcomeMessage(string $userId): DataResponse {
935
-		$currentLoggedInUser = $this->userSession->getUser();
936
-
937
-		$targetUser = $this->userManager->get($userId);
938
-		if ($targetUser === null) {
939
-			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
940
-		}
941
-
942
-		// Check if admin / subadmin
943
-		$subAdminManager = $this->groupManager->getSubAdmin();
944
-		if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
945
-			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
946
-			// No rights
947
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
948
-		}
949
-
950
-		$email = $targetUser->getEMailAddress();
951
-		if ($email === '' || $email === null) {
952
-			throw new OCSException('Email address not available', 101);
953
-		}
954
-
955
-		try {
956
-			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
957
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
958
-		} catch (\Exception $e) {
959
-			$this->logger->logException($e, [
960
-				'message' => "Can't send new user mail to $email",
961
-				'level' => ILogger::ERROR,
962
-				'app' => 'settings',
963
-			]);
964
-			throw new OCSException('Sending email failed', 102);
965
-		}
966
-
967
-		return new DataResponse();
968
-	}
65
+    /** @var IAppManager */
66
+    private $appManager;
67
+    /** @var ILogger */
68
+    private $logger;
69
+    /** @var IFactory */
70
+    protected $l10nFactory;
71
+    /** @var NewUserMailHelper */
72
+    private $newUserMailHelper;
73
+    /** @var FederatedFileSharingFactory */
74
+    private $federatedFileSharingFactory;
75
+    /** @var ISecureRandom */
76
+    private $secureRandom;
77
+    /** @var RemoteWipe */
78
+    private $remoteWipe;
79
+
80
+    public function __construct(string $appName,
81
+                                IRequest $request,
82
+                                IUserManager $userManager,
83
+                                IConfig $config,
84
+                                IAppManager $appManager,
85
+                                IGroupManager $groupManager,
86
+                                IUserSession $userSession,
87
+                                AccountManager $accountManager,
88
+                                ILogger $logger,
89
+                                IFactory $l10nFactory,
90
+                                NewUserMailHelper $newUserMailHelper,
91
+                                FederatedFileSharingFactory $federatedFileSharingFactory,
92
+                                ISecureRandom $secureRandom,
93
+                                RemoteWipe $remoteWipe) {
94
+        parent::__construct($appName,
95
+                            $request,
96
+                            $userManager,
97
+                            $config,
98
+                            $groupManager,
99
+                            $userSession,
100
+                            $accountManager,
101
+                            $l10nFactory);
102
+
103
+        $this->appManager = $appManager;
104
+        $this->logger = $logger;
105
+        $this->l10nFactory = $l10nFactory;
106
+        $this->newUserMailHelper = $newUserMailHelper;
107
+        $this->federatedFileSharingFactory = $federatedFileSharingFactory;
108
+        $this->secureRandom = $secureRandom;
109
+        $this->remoteWipe = $remoteWipe;
110
+    }
111
+
112
+    /**
113
+     * @NoAdminRequired
114
+     *
115
+     * returns a list of users
116
+     *
117
+     * @param string $search
118
+     * @param int $limit
119
+     * @param int $offset
120
+     * @return DataResponse
121
+     */
122
+    public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
123
+        $user = $this->userSession->getUser();
124
+        $users = [];
125
+
126
+        // Admin? Or SubAdmin?
127
+        $uid = $user->getUID();
128
+        $subAdminManager = $this->groupManager->getSubAdmin();
129
+        if ($this->groupManager->isAdmin($uid)) {
130
+            $users = $this->userManager->search($search, $limit, $offset);
131
+        } elseif ($subAdminManager->isSubAdmin($user)) {
132
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
133
+            foreach ($subAdminOfGroups as $key => $group) {
134
+                $subAdminOfGroups[$key] = $group->getGID();
135
+            }
136
+
137
+            $users = [];
138
+            foreach ($subAdminOfGroups as $group) {
139
+                $users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
140
+            }
141
+        }
142
+
143
+        $users = array_keys($users);
144
+
145
+        return new DataResponse([
146
+            'users' => $users
147
+        ]);
148
+    }
149
+
150
+    /**
151
+     * @NoAdminRequired
152
+     *
153
+     * returns a list of users and their data
154
+     */
155
+    public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
156
+        $currentUser = $this->userSession->getUser();
157
+        $users = [];
158
+
159
+        // Admin? Or SubAdmin?
160
+        $uid = $currentUser->getUID();
161
+        $subAdminManager = $this->groupManager->getSubAdmin();
162
+        if ($this->groupManager->isAdmin($uid)) {
163
+            $users = $this->userManager->search($search, $limit, $offset);
164
+            $users = array_keys($users);
165
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
166
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
167
+            foreach ($subAdminOfGroups as $key => $group) {
168
+                $subAdminOfGroups[$key] = $group->getGID();
169
+            }
170
+
171
+            $users = [];
172
+            foreach ($subAdminOfGroups as $group) {
173
+                $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
174
+            }
175
+            $users = array_merge(...$users);
176
+        }
177
+
178
+        $usersDetails = [];
179
+        foreach ($users as $userId) {
180
+            $userId = (string) $userId;
181
+            $userData = $this->getUserData($userId);
182
+            // Do not insert empty entry
183
+            if (!empty($userData)) {
184
+                $usersDetails[$userId] = $userData;
185
+            } else {
186
+                // Logged user does not have permissions to see this user
187
+                // only showing its id
188
+                $usersDetails[$userId] = ['id' => $userId];
189
+            }
190
+        }
191
+
192
+        return new DataResponse([
193
+            'users' => $usersDetails
194
+        ]);
195
+    }
196
+
197
+    /**
198
+     * @throws OCSException
199
+     */
200
+    private function createNewUserId(): string {
201
+        $attempts = 0;
202
+        do {
203
+            $uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
204
+            if (!$this->userManager->userExists($uidCandidate)) {
205
+                return $uidCandidate;
206
+            }
207
+            $attempts++;
208
+        } while ($attempts < 10);
209
+        throw new OCSException('Could not create non-existing user id', 111);
210
+    }
211
+
212
+    /**
213
+     * @PasswordConfirmationRequired
214
+     * @NoAdminRequired
215
+     *
216
+     * @param string $userid
217
+     * @param string $password
218
+     * @param string $displayName
219
+     * @param string $email
220
+     * @param array $groups
221
+     * @param array $subadmin
222
+     * @param string $quota
223
+     * @param string $language
224
+     * @return DataResponse
225
+     * @throws OCSException
226
+     */
227
+    public function addUser(string $userid,
228
+                            string $password = '',
229
+                            string $displayName = '',
230
+                            string $email = '',
231
+                            array $groups = [],
232
+                            array $subadmin = [],
233
+                            string $quota = '',
234
+                            string $language = ''): DataResponse {
235
+        $user = $this->userSession->getUser();
236
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
237
+        $subAdminManager = $this->groupManager->getSubAdmin();
238
+
239
+        if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
240
+            $userid = $this->createNewUserId();
241
+        }
242
+
243
+        if ($this->userManager->userExists($userid)) {
244
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
245
+            throw new OCSException('User already exists', 102);
246
+        }
247
+
248
+        if ($groups !== []) {
249
+            foreach ($groups as $group) {
250
+                if (!$this->groupManager->groupExists($group)) {
251
+                    throw new OCSException('group '.$group.' does not exist', 104);
252
+                }
253
+                if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
254
+                    throw new OCSException('insufficient privileges for group '. $group, 105);
255
+                }
256
+            }
257
+        } else {
258
+            if (!$isAdmin) {
259
+                throw new OCSException('no group specified (required for subadmins)', 106);
260
+            }
261
+        }
262
+
263
+        $subadminGroups = [];
264
+        if ($subadmin !== []) {
265
+            foreach ($subadmin as $groupid) {
266
+                $group = $this->groupManager->get($groupid);
267
+                // Check if group exists
268
+                if ($group === null) {
269
+                    throw new OCSException('Subadmin group does not exist',  102);
270
+                }
271
+                // Check if trying to make subadmin of admin group
272
+                if ($group->getGID() === 'admin') {
273
+                    throw new OCSException('Cannot create subadmins for admin group', 103);
274
+                }
275
+                // Check if has permission to promote subadmins
276
+                if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
277
+                    throw new OCSForbiddenException('No permissions to promote subadmins');
278
+                }
279
+                $subadminGroups[] = $group;
280
+            }
281
+        }
282
+
283
+        $generatePasswordResetToken = false;
284
+        if ($password === '') {
285
+            if ($email === '') {
286
+                throw new OCSException('To send a password link to the user an email address is required.', 108);
287
+            }
288
+
289
+            $password = $this->secureRandom->generate(10);
290
+            // Make sure we pass the password_policy
291
+            $password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()');
292
+            $generatePasswordResetToken = true;
293
+        }
294
+
295
+        if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
296
+            throw new OCSException('Required email address was not provided', 110);
297
+        }
298
+
299
+        try {
300
+            $newUser = $this->userManager->createUser($userid, $password);
301
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
302
+
303
+            foreach ($groups as $group) {
304
+                $this->groupManager->get($group)->addUser($newUser);
305
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
306
+            }
307
+            foreach ($subadminGroups as $group) {
308
+                $subAdminManager->createSubAdmin($newUser, $group);
309
+            }
310
+
311
+            if ($displayName !== '') {
312
+                $this->editUser($userid, 'display', $displayName);
313
+            }
314
+
315
+            if ($quota !== '') {
316
+                $this->editUser($userid, 'quota', $quota);
317
+            }
318
+
319
+            if ($language !== '') {
320
+                $this->editUser($userid, 'language', $language);
321
+            }
322
+
323
+            // Send new user mail only if a mail is set
324
+            if ($email !== '' && $this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
325
+                $newUser->setEMailAddress($email);
326
+                try {
327
+                    $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
328
+                    $this->newUserMailHelper->sendMail($newUser, $emailTemplate);
329
+                } catch (\Exception $e) {
330
+                    // Mail could be failing hard or just be plain not configured
331
+                    // Logging error as it is the hardest of the two
332
+                    $this->logger->logException($e, [
333
+                        'message' => "Unable to send the invitation mail to $email",
334
+                        'level' => ILogger::ERROR,
335
+                        'app' => 'ocs_api',
336
+                    ]);
337
+                }
338
+            }
339
+
340
+            return new DataResponse(['id' => $userid]);
341
+        } catch (HintException $e) {
342
+            $this->logger->logException($e, [
343
+                'message' => 'Failed addUser attempt with hint exception.',
344
+                'level' => ILogger::WARN,
345
+                'app' => 'ocs_api',
346
+            ]);
347
+            throw new OCSException($e->getHint(), 107);
348
+        } catch (OCSException $e) {
349
+            $this->logger->logException($e, [
350
+                'message' => 'Failed addUser attempt with ocs exeption.',
351
+                'level' => ILogger::ERROR,
352
+                'app' => 'ocs_api',
353
+            ]);
354
+            throw $e;
355
+        } catch (\Exception $e) {
356
+            $this->logger->logException($e, [
357
+                'message' => 'Failed addUser attempt with exception.',
358
+                'level' => ILogger::ERROR,
359
+                'app' => 'ocs_api',
360
+            ]);
361
+            throw new OCSException('Bad request', 101);
362
+        }
363
+    }
364
+
365
+    /**
366
+     * @NoAdminRequired
367
+     * @NoSubAdminRequired
368
+     *
369
+     * gets user info
370
+     *
371
+     * @param string $userId
372
+     * @return DataResponse
373
+     * @throws OCSException
374
+     */
375
+    public function getUser(string $userId): DataResponse {
376
+        $data = $this->getUserData($userId);
377
+        // getUserData returns empty array if not enough permissions
378
+        if (empty($data)) {
379
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
380
+        }
381
+        return new DataResponse($data);
382
+    }
383
+
384
+    /**
385
+     * @NoAdminRequired
386
+     * @NoSubAdminRequired
387
+     *
388
+     * gets user info from the currently logged in user
389
+     *
390
+     * @return DataResponse
391
+     * @throws OCSException
392
+     */
393
+    public function getCurrentUser(): DataResponse {
394
+        $user = $this->userSession->getUser();
395
+        if ($user) {
396
+            $data =  $this->getUserData($user->getUID());
397
+            // rename "displayname" to "display-name" only for this call to keep
398
+            // the API stable.
399
+            $data['display-name'] = $data['displayname'];
400
+            unset($data['displayname']);
401
+            return new DataResponse($data);
402
+        }
403
+
404
+        throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
405
+    }
406
+
407
+    /**
408
+     * @NoAdminRequired
409
+     * @NoSubAdminRequired
410
+     */
411
+    public function getEditableFields(): DataResponse {
412
+        $permittedFields = [];
413
+
414
+        // Editing self (display, email)
415
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
416
+            $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
417
+            $permittedFields[] = AccountManager::PROPERTY_EMAIL;
418
+        }
419
+
420
+        if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
421
+            $federatedFileSharing = $this->federatedFileSharingFactory->get();
422
+            $shareProvider = $federatedFileSharing->getFederatedShareProvider();
423
+            if ($shareProvider->isLookupServerUploadEnabled()) {
424
+                $permittedFields[] = AccountManager::PROPERTY_PHONE;
425
+                $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
426
+                $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
427
+                $permittedFields[] = AccountManager::PROPERTY_TWITTER;
428
+            }
429
+        }
430
+
431
+        return new DataResponse($permittedFields);
432
+    }
433
+
434
+    /**
435
+     * @NoAdminRequired
436
+     * @NoSubAdminRequired
437
+     * @PasswordConfirmationRequired
438
+     *
439
+     * edit users
440
+     *
441
+     * @param string $userId
442
+     * @param string $key
443
+     * @param string $value
444
+     * @return DataResponse
445
+     * @throws OCSException
446
+     */
447
+    public function editUser(string $userId, string $key, string $value): DataResponse {
448
+        $currentLoggedInUser = $this->userSession->getUser();
449
+
450
+        $targetUser = $this->userManager->get($userId);
451
+        if ($targetUser === null) {
452
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
453
+        }
454
+
455
+        $permittedFields = [];
456
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
457
+            // Editing self (display, email)
458
+            if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
459
+                $permittedFields[] = 'display';
460
+                $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
461
+                $permittedFields[] = AccountManager::PROPERTY_EMAIL;
462
+            }
463
+
464
+            $permittedFields[] = 'password';
465
+            if ($this->config->getSystemValue('force_language', false) === false ||
466
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
467
+                $permittedFields[] = 'language';
468
+            }
469
+
470
+            if ($this->config->getSystemValue('force_locale', false) === false ||
471
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
472
+                $permittedFields[] = 'locale';
473
+            }
474
+
475
+            if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
476
+                $federatedFileSharing = \OC::$server->query(\OCA\FederatedFileSharing\AppInfo\Application::class);
477
+                $shareProvider = $federatedFileSharing->getFederatedShareProvider();
478
+                if ($shareProvider->isLookupServerUploadEnabled()) {
479
+                    $permittedFields[] = AccountManager::PROPERTY_PHONE;
480
+                    $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
481
+                    $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
482
+                    $permittedFields[] = AccountManager::PROPERTY_TWITTER;
483
+                }
484
+            }
485
+
486
+            // If admin they can edit their own quota
487
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
488
+                $permittedFields[] = 'quota';
489
+            }
490
+        } else {
491
+            // Check if admin / subadmin
492
+            $subAdminManager = $this->groupManager->getSubAdmin();
493
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
494
+            || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
495
+                // They have permissions over the user
496
+                $permittedFields[] = 'display';
497
+                $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
498
+                $permittedFields[] = AccountManager::PROPERTY_EMAIL;
499
+                $permittedFields[] = 'password';
500
+                $permittedFields[] = 'language';
501
+                $permittedFields[] = 'locale';
502
+                $permittedFields[] = AccountManager::PROPERTY_PHONE;
503
+                $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
504
+                $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
505
+                $permittedFields[] = AccountManager::PROPERTY_TWITTER;
506
+                $permittedFields[] = 'quota';
507
+            } else {
508
+                // No rights
509
+                throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
510
+            }
511
+        }
512
+        // Check if permitted to edit this field
513
+        if (!in_array($key, $permittedFields)) {
514
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
515
+        }
516
+        // Process the edit
517
+        switch ($key) {
518
+            case 'display':
519
+            case AccountManager::PROPERTY_DISPLAYNAME:
520
+                $targetUser->setDisplayName($value);
521
+                break;
522
+            case 'quota':
523
+                $quota = $value;
524
+                if ($quota !== 'none' && $quota !== 'default') {
525
+                    if (is_numeric($quota)) {
526
+                        $quota = (float) $quota;
527
+                    } else {
528
+                        $quota = \OCP\Util::computerFileSize($quota);
529
+                    }
530
+                    if ($quota === false) {
531
+                        throw new OCSException('Invalid quota value '.$value, 103);
532
+                    }
533
+                    if ($quota === -1) {
534
+                        $quota = 'none';
535
+                    } else {
536
+                        $quota = \OCP\Util::humanFileSize($quota);
537
+                    }
538
+                }
539
+                $targetUser->setQuota($quota);
540
+                break;
541
+            case 'password':
542
+                try {
543
+                    if (!$targetUser->canChangePassword()) {
544
+                        throw new OCSException('Setting the password is not supported by the users backend', 103);
545
+                    }
546
+                    $targetUser->setPassword($value);
547
+                } catch (HintException $e) { // password policy error
548
+                    throw new OCSException($e->getMessage(), 103);
549
+                }
550
+                break;
551
+            case 'language':
552
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
553
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
554
+                    throw new OCSException('Invalid language', 102);
555
+                }
556
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
557
+                break;
558
+            case 'locale':
559
+                if (!$this->l10nFactory->localeExists($value)) {
560
+                    throw new OCSException('Invalid locale', 102);
561
+                }
562
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
563
+                break;
564
+            case AccountManager::PROPERTY_EMAIL:
565
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
566
+                    $targetUser->setEMailAddress($value);
567
+                } else {
568
+                    throw new OCSException('', 102);
569
+                }
570
+                break;
571
+            case AccountManager::PROPERTY_PHONE:
572
+            case AccountManager::PROPERTY_ADDRESS:
573
+            case AccountManager::PROPERTY_WEBSITE:
574
+            case AccountManager::PROPERTY_TWITTER:
575
+                $userAccount = $this->accountManager->getUser($targetUser);
576
+                if ($userAccount[$key]['value'] !== $value) {
577
+                    $userAccount[$key]['value'] = $value;
578
+                    $this->accountManager->updateUser($targetUser, $userAccount);
579
+                }
580
+                break;
581
+            default:
582
+                throw new OCSException('', 103);
583
+        }
584
+        return new DataResponse();
585
+    }
586
+
587
+    /**
588
+     * @PasswordConfirmationRequired
589
+     * @NoAdminRequired
590
+     *
591
+     * @param string $userId
592
+     *
593
+     * @return DataResponse
594
+     *
595
+     * @throws OCSException
596
+     */
597
+    public function wipeUserDevices(string $userId): DataResponse {
598
+        /** @var IUser $currentLoggedInUser */
599
+        $currentLoggedInUser = $this->userSession->getUser();
600
+
601
+        $targetUser = $this->userManager->get($userId);
602
+
603
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
604
+            throw new OCSException('', 101);
605
+        }
606
+
607
+        // If not permitted
608
+        $subAdminManager = $this->groupManager->getSubAdmin();
609
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
610
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
611
+        }
612
+
613
+        $this->remoteWipe->markAllTokensForWipe($targetUser);
614
+
615
+        return new DataResponse();
616
+    }
617
+
618
+    /**
619
+     * @PasswordConfirmationRequired
620
+     * @NoAdminRequired
621
+     *
622
+     * @param string $userId
623
+     * @return DataResponse
624
+     * @throws OCSException
625
+     */
626
+    public function deleteUser(string $userId): DataResponse {
627
+        $currentLoggedInUser = $this->userSession->getUser();
628
+
629
+        $targetUser = $this->userManager->get($userId);
630
+
631
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
632
+            throw new OCSException('', 101);
633
+        }
634
+
635
+        // If not permitted
636
+        $subAdminManager = $this->groupManager->getSubAdmin();
637
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
638
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
639
+        }
640
+
641
+        // Go ahead with the delete
642
+        if ($targetUser->delete()) {
643
+            return new DataResponse();
644
+        } else {
645
+            throw new OCSException('', 101);
646
+        }
647
+    }
648
+
649
+    /**
650
+     * @PasswordConfirmationRequired
651
+     * @NoAdminRequired
652
+     *
653
+     * @param string $userId
654
+     * @return DataResponse
655
+     * @throws OCSException
656
+     * @throws OCSForbiddenException
657
+     */
658
+    public function disableUser(string $userId): DataResponse {
659
+        return $this->setEnabled($userId, false);
660
+    }
661
+
662
+    /**
663
+     * @PasswordConfirmationRequired
664
+     * @NoAdminRequired
665
+     *
666
+     * @param string $userId
667
+     * @return DataResponse
668
+     * @throws OCSException
669
+     * @throws OCSForbiddenException
670
+     */
671
+    public function enableUser(string $userId): DataResponse {
672
+        return $this->setEnabled($userId, true);
673
+    }
674
+
675
+    /**
676
+     * @param string $userId
677
+     * @param bool $value
678
+     * @return DataResponse
679
+     * @throws OCSException
680
+     */
681
+    private function setEnabled(string $userId, bool $value): DataResponse {
682
+        $currentLoggedInUser = $this->userSession->getUser();
683
+
684
+        $targetUser = $this->userManager->get($userId);
685
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
686
+            throw new OCSException('', 101);
687
+        }
688
+
689
+        // If not permitted
690
+        $subAdminManager = $this->groupManager->getSubAdmin();
691
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
692
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
693
+        }
694
+
695
+        // enable/disable the user now
696
+        $targetUser->setEnabled($value);
697
+        return new DataResponse();
698
+    }
699
+
700
+    /**
701
+     * @NoAdminRequired
702
+     * @NoSubAdminRequired
703
+     *
704
+     * @param string $userId
705
+     * @return DataResponse
706
+     * @throws OCSException
707
+     */
708
+    public function getUsersGroups(string $userId): DataResponse {
709
+        $loggedInUser = $this->userSession->getUser();
710
+
711
+        $targetUser = $this->userManager->get($userId);
712
+        if ($targetUser === null) {
713
+            throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
714
+        }
715
+
716
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
717
+            // Self lookup or admin lookup
718
+            return new DataResponse([
719
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
720
+            ]);
721
+        } else {
722
+            $subAdminManager = $this->groupManager->getSubAdmin();
723
+
724
+            // Looking up someone else
725
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
726
+                // Return the group that the method caller is subadmin of for the user in question
727
+                /** @var IGroup[] $getSubAdminsGroups */
728
+                $getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
729
+                foreach ($getSubAdminsGroups as $key => $group) {
730
+                    $getSubAdminsGroups[$key] = $group->getGID();
731
+                }
732
+                $groups = array_intersect(
733
+                    $getSubAdminsGroups,
734
+                    $this->groupManager->getUserGroupIds($targetUser)
735
+                );
736
+                return new DataResponse(['groups' => $groups]);
737
+            } else {
738
+                // Not permitted
739
+                throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
740
+            }
741
+        }
742
+    }
743
+
744
+    /**
745
+     * @PasswordConfirmationRequired
746
+     * @NoAdminRequired
747
+     *
748
+     * @param string $userId
749
+     * @param string $groupid
750
+     * @return DataResponse
751
+     * @throws OCSException
752
+     */
753
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
754
+        if ($groupid === '') {
755
+            throw new OCSException('', 101);
756
+        }
757
+
758
+        $group = $this->groupManager->get($groupid);
759
+        $targetUser = $this->userManager->get($userId);
760
+        if ($group === null) {
761
+            throw new OCSException('', 102);
762
+        }
763
+        if ($targetUser === null) {
764
+            throw new OCSException('', 103);
765
+        }
766
+
767
+        // If they're not an admin, check they are a subadmin of the group in question
768
+        $loggedInUser = $this->userSession->getUser();
769
+        $subAdminManager = $this->groupManager->getSubAdmin();
770
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
771
+            throw new OCSException('', 104);
772
+        }
773
+
774
+        // Add user to group
775
+        $group->addUser($targetUser);
776
+        return new DataResponse();
777
+    }
778
+
779
+    /**
780
+     * @PasswordConfirmationRequired
781
+     * @NoAdminRequired
782
+     *
783
+     * @param string $userId
784
+     * @param string $groupid
785
+     * @return DataResponse
786
+     * @throws OCSException
787
+     */
788
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
789
+        $loggedInUser = $this->userSession->getUser();
790
+
791
+        if ($groupid === null || trim($groupid) === '') {
792
+            throw new OCSException('', 101);
793
+        }
794
+
795
+        $group = $this->groupManager->get($groupid);
796
+        if ($group === null) {
797
+            throw new OCSException('', 102);
798
+        }
799
+
800
+        $targetUser = $this->userManager->get($userId);
801
+        if ($targetUser === null) {
802
+            throw new OCSException('', 103);
803
+        }
804
+
805
+        // If they're not an admin, check they are a subadmin of the group in question
806
+        $subAdminManager = $this->groupManager->getSubAdmin();
807
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
808
+            throw new OCSException('', 104);
809
+        }
810
+
811
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
812
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
813
+            if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
814
+                if ($group->getGID() === 'admin') {
815
+                    throw new OCSException('Cannot remove yourself from the admin group', 105);
816
+                }
817
+            } else {
818
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
819
+                throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
820
+            }
821
+        } elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
822
+            /** @var IGroup[] $subAdminGroups */
823
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
824
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
825
+                return $subAdminGroup->getGID();
826
+            }, $subAdminGroups);
827
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
828
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
829
+
830
+            if (count($userSubAdminGroups) <= 1) {
831
+                // Subadmin must not be able to remove a user from all their subadmin groups.
832
+                throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
833
+            }
834
+        }
835
+
836
+        // Remove user from group
837
+        $group->removeUser($targetUser);
838
+        return new DataResponse();
839
+    }
840
+
841
+    /**
842
+     * Creates a subadmin
843
+     *
844
+     * @PasswordConfirmationRequired
845
+     *
846
+     * @param string $userId
847
+     * @param string $groupid
848
+     * @return DataResponse
849
+     * @throws OCSException
850
+     */
851
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
852
+        $group = $this->groupManager->get($groupid);
853
+        $user = $this->userManager->get($userId);
854
+
855
+        // Check if the user exists
856
+        if ($user === null) {
857
+            throw new OCSException('User does not exist', 101);
858
+        }
859
+        // Check if group exists
860
+        if ($group === null) {
861
+            throw new OCSException('Group does not exist',  102);
862
+        }
863
+        // Check if trying to make subadmin of admin group
864
+        if ($group->getGID() === 'admin') {
865
+            throw new OCSException('Cannot create subadmins for admin group', 103);
866
+        }
867
+
868
+        $subAdminManager = $this->groupManager->getSubAdmin();
869
+
870
+        // We cannot be subadmin twice
871
+        if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
872
+            return new DataResponse();
873
+        }
874
+        // Go
875
+        $subAdminManager->createSubAdmin($user, $group);
876
+        return new DataResponse();
877
+    }
878
+
879
+    /**
880
+     * Removes a subadmin from a group
881
+     *
882
+     * @PasswordConfirmationRequired
883
+     *
884
+     * @param string $userId
885
+     * @param string $groupid
886
+     * @return DataResponse
887
+     * @throws OCSException
888
+     */
889
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
890
+        $group = $this->groupManager->get($groupid);
891
+        $user = $this->userManager->get($userId);
892
+        $subAdminManager = $this->groupManager->getSubAdmin();
893
+
894
+        // Check if the user exists
895
+        if ($user === null) {
896
+            throw new OCSException('User does not exist', 101);
897
+        }
898
+        // Check if the group exists
899
+        if ($group === null) {
900
+            throw new OCSException('Group does not exist', 101);
901
+        }
902
+        // Check if they are a subadmin of this said group
903
+        if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
904
+            throw new OCSException('User is not a subadmin of this group', 102);
905
+        }
906
+
907
+        // Go
908
+        $subAdminManager->deleteSubAdmin($user, $group);
909
+        return new DataResponse();
910
+    }
911
+
912
+    /**
913
+     * Get the groups a user is a subadmin of
914
+     *
915
+     * @param string $userId
916
+     * @return DataResponse
917
+     * @throws OCSException
918
+     */
919
+    public function getUserSubAdminGroups(string $userId): DataResponse {
920
+        $groups = $this->getUserSubAdminGroupsData($userId);
921
+        return new DataResponse($groups);
922
+    }
923
+
924
+    /**
925
+     * @NoAdminRequired
926
+     * @PasswordConfirmationRequired
927
+     *
928
+     * resend welcome message
929
+     *
930
+     * @param string $userId
931
+     * @return DataResponse
932
+     * @throws OCSException
933
+     */
934
+    public function resendWelcomeMessage(string $userId): DataResponse {
935
+        $currentLoggedInUser = $this->userSession->getUser();
936
+
937
+        $targetUser = $this->userManager->get($userId);
938
+        if ($targetUser === null) {
939
+            throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
940
+        }
941
+
942
+        // Check if admin / subadmin
943
+        $subAdminManager = $this->groupManager->getSubAdmin();
944
+        if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
945
+            && !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
946
+            // No rights
947
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
948
+        }
949
+
950
+        $email = $targetUser->getEMailAddress();
951
+        if ($email === '' || $email === null) {
952
+            throw new OCSException('Email address not available', 101);
953
+        }
954
+
955
+        try {
956
+            $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
957
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
958
+        } catch (\Exception $e) {
959
+            $this->logger->logException($e, [
960
+                'message' => "Can't send new user mail to $email",
961
+                'level' => ILogger::ERROR,
962
+                'app' => 'settings',
963
+            ]);
964
+            throw new OCSException('Sending email failed', 102);
965
+        }
966
+
967
+        return new DataResponse();
968
+    }
969 969
 }
Please login to merge, or discard this patch.