Completed
Push — master ( b12987...4c79c2 )
by
unknown
43:53
created
apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php 1 patch
Indentation   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -19,63 +19,63 @@
 block discarded – undo
19 19
 
20 20
 #[\PHPUnit\Framework\Attributes\Group(name: 'DB')]
21 21
 class NotificationProviderManagerTest extends TestCase {
22
-	private NotificationProviderManager $providerManager;
22
+    private NotificationProviderManager $providerManager;
23 23
 
24
-	/**
25
-	 * @throws ContainerExceptionInterface
26
-	 */
27
-	protected function setUp(): void {
28
-		parent::setUp();
24
+    /**
25
+     * @throws ContainerExceptionInterface
26
+     */
27
+    protected function setUp(): void {
28
+        parent::setUp();
29 29
 
30
-		$this->providerManager = new NotificationProviderManager();
31
-		$this->providerManager->registerProvider(EmailProvider::class);
32
-	}
30
+        $this->providerManager = new NotificationProviderManager();
31
+        $this->providerManager->registerProvider(EmailProvider::class);
32
+    }
33 33
 
34
-	/**
35
-	 * @throws ProviderNotAvailableException
36
-	 * @throws NotificationTypeDoesNotExistException
37
-	 */
38
-	public function testGetProviderForUnknownType(): void {
39
-		$this->expectException(NotificationTypeDoesNotExistException::class);
40
-		$this->expectExceptionMessage('Type NOT EXISTENT is not an accepted type of notification');
34
+    /**
35
+     * @throws ProviderNotAvailableException
36
+     * @throws NotificationTypeDoesNotExistException
37
+     */
38
+    public function testGetProviderForUnknownType(): void {
39
+        $this->expectException(NotificationTypeDoesNotExistException::class);
40
+        $this->expectExceptionMessage('Type NOT EXISTENT is not an accepted type of notification');
41 41
 
42
-		$this->providerManager->getProvider('NOT EXISTENT');
43
-	}
42
+        $this->providerManager->getProvider('NOT EXISTENT');
43
+    }
44 44
 
45
-	/**
46
-	 * @throws NotificationTypeDoesNotExistException
47
-	 * @throws ProviderNotAvailableException
48
-	 */
49
-	public function testGetProviderForUnRegisteredType(): void {
50
-		$this->expectException(ProviderNotAvailableException::class);
51
-		$this->expectExceptionMessage('No notification provider for type AUDIO available');
45
+    /**
46
+     * @throws NotificationTypeDoesNotExistException
47
+     * @throws ProviderNotAvailableException
48
+     */
49
+    public function testGetProviderForUnRegisteredType(): void {
50
+        $this->expectException(ProviderNotAvailableException::class);
51
+        $this->expectExceptionMessage('No notification provider for type AUDIO available');
52 52
 
53
-		$this->providerManager->getProvider('AUDIO');
54
-	}
53
+        $this->providerManager->getProvider('AUDIO');
54
+    }
55 55
 
56
-	public function testGetProvider(): void {
57
-		$provider = $this->providerManager->getProvider('EMAIL');
58
-		$this->assertInstanceOf(EmailProvider::class, $provider);
59
-	}
56
+    public function testGetProvider(): void {
57
+        $provider = $this->providerManager->getProvider('EMAIL');
58
+        $this->assertInstanceOf(EmailProvider::class, $provider);
59
+    }
60 60
 
61
-	public function testRegisterProvider(): void {
62
-		$this->providerManager->registerProvider(PushProvider::class);
63
-		$provider = $this->providerManager->getProvider('DISPLAY');
64
-		$this->assertInstanceOf(PushProvider::class, $provider);
65
-	}
61
+    public function testRegisterProvider(): void {
62
+        $this->providerManager->registerProvider(PushProvider::class);
63
+        $provider = $this->providerManager->getProvider('DISPLAY');
64
+        $this->assertInstanceOf(PushProvider::class, $provider);
65
+    }
66 66
 
67
-	/**
68
-	 * @throws ContainerExceptionInterface
69
-	 */
70
-	public function testRegisterBadProvider(): void {
71
-		$this->expectException(\InvalidArgumentException::class);
72
-		$this->expectExceptionMessage('Invalid notification provider registered');
67
+    /**
68
+     * @throws ContainerExceptionInterface
69
+     */
70
+    public function testRegisterBadProvider(): void {
71
+        $this->expectException(\InvalidArgumentException::class);
72
+        $this->expectExceptionMessage('Invalid notification provider registered');
73 73
 
74
-		$this->providerManager->registerProvider(Capabilities::class);
75
-	}
74
+        $this->providerManager->registerProvider(Capabilities::class);
75
+    }
76 76
 
77
-	public function testHasProvider(): void {
78
-		$this->assertTrue($this->providerManager->hasProvider('EMAIL'));
79
-		$this->assertFalse($this->providerManager->hasProvider('EMAIL123'));
80
-	}
77
+    public function testHasProvider(): void {
78
+        $this->assertTrue($this->providerManager->hasProvider('EMAIL'));
79
+        $this->assertFalse($this->providerManager->hasProvider('EMAIL123'));
80
+    }
81 81
 }
Please login to merge, or discard this patch.
apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php 1 patch
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -19,51 +19,51 @@
 block discarded – undo
19 19
  */
20 20
 class NotificationProviderManager {
21 21
 
22
-	/** @var INotificationProvider[] */
23
-	private $providers = [];
22
+    /** @var INotificationProvider[] */
23
+    private $providers = [];
24 24
 
25
-	/**
26
-	 * Checks whether a provider for a given ACTION exists
27
-	 *
28
-	 * @param string $type
29
-	 * @return bool
30
-	 */
31
-	public function hasProvider(string $type):bool {
32
-		return (\in_array($type, ReminderService::REMINDER_TYPES, true)
33
-			&& isset($this->providers[$type]));
34
-	}
25
+    /**
26
+     * Checks whether a provider for a given ACTION exists
27
+     *
28
+     * @param string $type
29
+     * @return bool
30
+     */
31
+    public function hasProvider(string $type):bool {
32
+        return (\in_array($type, ReminderService::REMINDER_TYPES, true)
33
+            && isset($this->providers[$type]));
34
+    }
35 35
 
36
-	/**
37
-	 * Get provider for a given ACTION
38
-	 *
39
-	 * @param string $type
40
-	 * @return INotificationProvider
41
-	 * @throws NotificationProvider\ProviderNotAvailableException
42
-	 * @throws NotificationTypeDoesNotExistException
43
-	 */
44
-	public function getProvider(string $type):INotificationProvider {
45
-		if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
46
-			if (isset($this->providers[$type])) {
47
-				return $this->providers[$type];
48
-			}
49
-			throw new ProviderNotAvailableException($type);
50
-		}
51
-		throw new NotificationTypeDoesNotExistException($type);
52
-	}
36
+    /**
37
+     * Get provider for a given ACTION
38
+     *
39
+     * @param string $type
40
+     * @return INotificationProvider
41
+     * @throws NotificationProvider\ProviderNotAvailableException
42
+     * @throws NotificationTypeDoesNotExistException
43
+     */
44
+    public function getProvider(string $type):INotificationProvider {
45
+        if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
46
+            if (isset($this->providers[$type])) {
47
+                return $this->providers[$type];
48
+            }
49
+            throw new ProviderNotAvailableException($type);
50
+        }
51
+        throw new NotificationTypeDoesNotExistException($type);
52
+    }
53 53
 
54
-	/**
55
-	 * Registers a new provider
56
-	 *
57
-	 * @param string $providerClassName
58
-	 * @throws ContainerExceptionInterface
59
-	 */
60
-	public function registerProvider(string $providerClassName):void {
61
-		$provider = Server::get($providerClassName);
54
+    /**
55
+     * Registers a new provider
56
+     *
57
+     * @param string $providerClassName
58
+     * @throws ContainerExceptionInterface
59
+     */
60
+    public function registerProvider(string $providerClassName):void {
61
+        $provider = Server::get($providerClassName);
62 62
 
63
-		if (!$provider instanceof INotificationProvider) {
64
-			throw new \InvalidArgumentException('Invalid notification provider registered');
65
-		}
63
+        if (!$provider instanceof INotificationProvider) {
64
+            throw new \InvalidArgumentException('Invalid notification provider registered');
65
+        }
66 66
 
67
-		$this->providers[$provider::NOTIFICATION_TYPE] = $provider;
68
-	}
67
+        $this->providers[$provider::NOTIFICATION_TYPE] = $provider;
68
+    }
69 69
 }
Please login to merge, or discard this patch.
apps/dav/lib/CardDAV/UserAddressBooks.php 2 patches
Indentation   +98 added lines, -98 removed lines patch added patch discarded remove patch
@@ -33,112 +33,112 @@
 block discarded – undo
33 33
 use function array_map;
34 34
 
35 35
 class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
36
-	protected IL10N $l10n;
37
-	protected IConfig $config;
38
-	protected IAppConfig $appConfig;
36
+    protected IL10N $l10n;
37
+    protected IConfig $config;
38
+    protected IAppConfig $appConfig;
39 39
 
40
-	public function __construct(
41
-		Backend\BackendInterface $carddavBackend,
42
-		string $principalUri,
43
-		private PluginManager $pluginManager,
44
-		private ?IUser $user,
45
-		private ?IGroupManager $groupManager,
46
-	) {
47
-		parent::__construct($carddavBackend, $principalUri);
40
+    public function __construct(
41
+        Backend\BackendInterface $carddavBackend,
42
+        string $principalUri,
43
+        private PluginManager $pluginManager,
44
+        private ?IUser $user,
45
+        private ?IGroupManager $groupManager,
46
+    ) {
47
+        parent::__construct($carddavBackend, $principalUri);
48 48
 
49
-		$this->l10n = Util::getL10N('dav');
50
-		$this->config = Server::get(IConfig::class);
51
-		$this->appConfig = Server::get(IAppConfig::class);
52
-	}
49
+        $this->l10n = Util::getL10N('dav');
50
+        $this->config = Server::get(IConfig::class);
51
+        $this->appConfig = Server::get(IAppConfig::class);
52
+    }
53 53
 
54
-	/**
55
-	 * Returns a list of address books
56
-	 *
57
-	 * @return IAddressBook[]
58
-	 */
59
-	public function getChildren() {
60
-		/** @var string|array $principal */
61
-		$principal = $this->principalUri;
62
-		$addressBooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
63
-		// add the system address book
64
-		$systemAddressBook = null;
65
-		$systemAddressBookExposed = $this->appConfig->getValueBool(Application::APP_ID, ConfigLexicon::SYSTEM_ADDRESSBOOK_EXPOSED);
66
-		if ($systemAddressBookExposed && is_string($principal) && $principal !== 'principals/system/system' && $this->carddavBackend instanceof CardDavBackend) {
67
-			$systemAddressBook = $this->carddavBackend->getAddressBooksByUri('principals/system/system', 'system');
68
-			if ($systemAddressBook !== null) {
69
-				$systemAddressBook['uri'] = SystemAddressbook::URI_SHARED;
70
-			}
71
-		}
72
-		if (!is_null($systemAddressBook)) {
73
-			$addressBooks[] = $systemAddressBook;
74
-		}
54
+    /**
55
+     * Returns a list of address books
56
+     *
57
+     * @return IAddressBook[]
58
+     */
59
+    public function getChildren() {
60
+        /** @var string|array $principal */
61
+        $principal = $this->principalUri;
62
+        $addressBooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
63
+        // add the system address book
64
+        $systemAddressBook = null;
65
+        $systemAddressBookExposed = $this->appConfig->getValueBool(Application::APP_ID, ConfigLexicon::SYSTEM_ADDRESSBOOK_EXPOSED);
66
+        if ($systemAddressBookExposed && is_string($principal) && $principal !== 'principals/system/system' && $this->carddavBackend instanceof CardDavBackend) {
67
+            $systemAddressBook = $this->carddavBackend->getAddressBooksByUri('principals/system/system', 'system');
68
+            if ($systemAddressBook !== null) {
69
+                $systemAddressBook['uri'] = SystemAddressbook::URI_SHARED;
70
+            }
71
+        }
72
+        if (!is_null($systemAddressBook)) {
73
+            $addressBooks[] = $systemAddressBook;
74
+        }
75 75
 
76
-		$objects = [];
77
-		if (!empty($addressBooks)) {
78
-			/** @var IAddressBook[] $objects */
79
-			$objects = array_map(function (array $addressBook) {
80
-				$trustedServers = null;
81
-				$request = null;
82
-				try {
83
-					$trustedServers = Server::get(TrustedServers::class);
84
-					$request = Server::get(IRequest::class);
85
-				} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
86
-					// nothing to do, the request / trusted servers don't exist
87
-				}
88
-				if ($addressBook['principaluri'] === 'principals/system/system') {
89
-					return new SystemAddressbook(
90
-						$this->carddavBackend,
91
-						$addressBook,
92
-						$this->l10n,
93
-						$this->config,
94
-						Server::get(IUserSession::class),
95
-						$request,
96
-						$trustedServers,
97
-						$this->groupManager
98
-					);
99
-				}
76
+        $objects = [];
77
+        if (!empty($addressBooks)) {
78
+            /** @var IAddressBook[] $objects */
79
+            $objects = array_map(function (array $addressBook) {
80
+                $trustedServers = null;
81
+                $request = null;
82
+                try {
83
+                    $trustedServers = Server::get(TrustedServers::class);
84
+                    $request = Server::get(IRequest::class);
85
+                } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
86
+                    // nothing to do, the request / trusted servers don't exist
87
+                }
88
+                if ($addressBook['principaluri'] === 'principals/system/system') {
89
+                    return new SystemAddressbook(
90
+                        $this->carddavBackend,
91
+                        $addressBook,
92
+                        $this->l10n,
93
+                        $this->config,
94
+                        Server::get(IUserSession::class),
95
+                        $request,
96
+                        $trustedServers,
97
+                        $this->groupManager
98
+                    );
99
+                }
100 100
 
101
-				return new AddressBook($this->carddavBackend, $addressBook, $this->l10n);
102
-			}, $addressBooks);
103
-		}
104
-		/** @var IAddressBook[][] $objectsFromPlugins */
105
-		$objectsFromPlugins = array_map(function (IAddressBookProvider $plugin): array {
106
-			return $plugin->fetchAllForAddressBookHome($this->principalUri);
107
-		}, $this->pluginManager->getAddressBookPlugins());
101
+                return new AddressBook($this->carddavBackend, $addressBook, $this->l10n);
102
+            }, $addressBooks);
103
+        }
104
+        /** @var IAddressBook[][] $objectsFromPlugins */
105
+        $objectsFromPlugins = array_map(function (IAddressBookProvider $plugin): array {
106
+            return $plugin->fetchAllForAddressBookHome($this->principalUri);
107
+        }, $this->pluginManager->getAddressBookPlugins());
108 108
 
109
-		return array_merge($objects, ...$objectsFromPlugins);
110
-	}
109
+        return array_merge($objects, ...$objectsFromPlugins);
110
+    }
111 111
 
112
-	public function createExtendedCollection($name, MkCol $mkCol) {
113
-		if (ExternalAddressBook::doesViolateReservedName($name)) {
114
-			throw new MethodNotAllowed('The resource you tried to create has a reserved name');
115
-		}
112
+    public function createExtendedCollection($name, MkCol $mkCol) {
113
+        if (ExternalAddressBook::doesViolateReservedName($name)) {
114
+            throw new MethodNotAllowed('The resource you tried to create has a reserved name');
115
+        }
116 116
 
117
-		parent::createExtendedCollection($name, $mkCol);
118
-	}
117
+        parent::createExtendedCollection($name, $mkCol);
118
+    }
119 119
 
120
-	/**
121
-	 * Returns a list of ACE's for this node.
122
-	 *
123
-	 * Each ACE has the following properties:
124
-	 *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
125
-	 *     currently the only supported privileges
126
-	 *   * 'principal', a url to the principal who owns the node
127
-	 *   * 'protected' (optional), indicating that this ACE is not allowed to
128
-	 *      be updated.
129
-	 *
130
-	 * @return array
131
-	 */
132
-	public function getACL() {
133
-		$acl = parent::getACL();
134
-		if ($this->principalUri === 'principals/system/system') {
135
-			$acl[] = [
136
-				'privilege' => '{DAV:}read',
137
-				'principal' => '{DAV:}authenticated',
138
-				'protected' => true,
139
-			];
140
-		}
120
+    /**
121
+     * Returns a list of ACE's for this node.
122
+     *
123
+     * Each ACE has the following properties:
124
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
125
+     *     currently the only supported privileges
126
+     *   * 'principal', a url to the principal who owns the node
127
+     *   * 'protected' (optional), indicating that this ACE is not allowed to
128
+     *      be updated.
129
+     *
130
+     * @return array
131
+     */
132
+    public function getACL() {
133
+        $acl = parent::getACL();
134
+        if ($this->principalUri === 'principals/system/system') {
135
+            $acl[] = [
136
+                'privilege' => '{DAV:}read',
137
+                'principal' => '{DAV:}authenticated',
138
+                'protected' => true,
139
+            ];
140
+        }
141 141
 
142
-		return $acl;
143
-	}
142
+        return $acl;
143
+    }
144 144
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -76,13 +76,13 @@  discard block
 block discarded – undo
76 76
 		$objects = [];
77 77
 		if (!empty($addressBooks)) {
78 78
 			/** @var IAddressBook[] $objects */
79
-			$objects = array_map(function (array $addressBook) {
79
+			$objects = array_map(function(array $addressBook) {
80 80
 				$trustedServers = null;
81 81
 				$request = null;
82 82
 				try {
83 83
 					$trustedServers = Server::get(TrustedServers::class);
84 84
 					$request = Server::get(IRequest::class);
85
-				} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
85
+				} catch (NotFoundExceptionInterface | ContainerExceptionInterface $e) {
86 86
 					// nothing to do, the request / trusted servers don't exist
87 87
 				}
88 88
 				if ($addressBook['principaluri'] === 'principals/system/system') {
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 			}, $addressBooks);
103 103
 		}
104 104
 		/** @var IAddressBook[][] $objectsFromPlugins */
105
-		$objectsFromPlugins = array_map(function (IAddressBookProvider $plugin): array {
105
+		$objectsFromPlugins = array_map(function(IAddressBookProvider $plugin): array {
106 106
 			return $plugin->fetchAllForAddressBookHome($this->principalUri);
107 107
 		}, $this->pluginManager->getAddressBookPlugins());
108 108
 
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/Principal.php 1 patch
Indentation   +597 added lines, -597 removed lines patch added patch discarded remove patch
@@ -33,601 +33,601 @@
 block discarded – undo
33 33
 
34 34
 class Principal implements BackendInterface {
35 35
 
36
-	/** @var string */
37
-	private $principalPrefix;
38
-
39
-	/** @var bool */
40
-	private $hasGroups;
41
-
42
-	/** @var bool */
43
-	private $hasCircles;
44
-
45
-	/** @var KnownUserService */
46
-	private $knownUserService;
47
-
48
-	public function __construct(
49
-		private IUserManager $userManager,
50
-		private IGroupManager $groupManager,
51
-		private IAccountManager $accountManager,
52
-		private IShareManager $shareManager,
53
-		private IUserSession $userSession,
54
-		private IAppManager $appManager,
55
-		private ProxyMapper $proxyMapper,
56
-		KnownUserService $knownUserService,
57
-		private IConfig $config,
58
-		private IFactory $languageFactory,
59
-		string $principalPrefix = 'principals/users/',
60
-	) {
61
-		$this->principalPrefix = trim($principalPrefix, '/');
62
-		$this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
63
-		$this->knownUserService = $knownUserService;
64
-	}
65
-
66
-	use PrincipalProxyTrait {
67
-		getGroupMembership as protected traitGetGroupMembership;
68
-	}
69
-
70
-	/**
71
-	 * Returns a list of principals based on a prefix.
72
-	 *
73
-	 * This prefix will often contain something like 'principals'. You are only
74
-	 * expected to return principals that are in this base path.
75
-	 *
76
-	 * You are expected to return at least a 'uri' for every user, you can
77
-	 * return any additional properties if you wish so. Common properties are:
78
-	 *   {DAV:}displayname
79
-	 *
80
-	 * @param string $prefixPath
81
-	 * @return string[]
82
-	 */
83
-	public function getPrincipalsByPrefix($prefixPath) {
84
-		$principals = [];
85
-
86
-		if ($prefixPath === $this->principalPrefix) {
87
-			foreach ($this->userManager->search('') as $user) {
88
-				$principals[] = $this->userToPrincipal($user);
89
-			}
90
-		}
91
-
92
-		return $principals;
93
-	}
94
-
95
-	/**
96
-	 * Returns a specific principal, specified by it's path.
97
-	 * The returned structure should be the exact same as from
98
-	 * getPrincipalsByPrefix.
99
-	 *
100
-	 * @param string $path
101
-	 * @return array
102
-	 */
103
-	public function getPrincipalByPath($path) {
104
-		return $this->getPrincipalPropertiesByPath($path);
105
-	}
106
-
107
-	/**
108
-	 * Returns a specific principal, specified by its path.
109
-	 * The returned structure should be the exact same as from
110
-	 * getPrincipalsByPrefix.
111
-	 *
112
-	 * It is possible to optionally filter retrieved properties in case only a limited set is
113
-	 * required. Note that the implementation might return more properties than requested.
114
-	 *
115
-	 * @param string $path The path of the principal
116
-	 * @param string[]|null $propertyFilter A list of properties to be retrieved or all if null. An empty array will cause a very shallow principal to be retrieved.
117
-	 */
118
-	public function getPrincipalPropertiesByPath($path, ?array $propertyFilter = null): ?array {
119
-		[$prefix, $name] = \Sabre\Uri\split($path);
120
-		$decodedName = urldecode($name);
121
-
122
-		if ($name === 'calendar-proxy-write' || $name === 'calendar-proxy-read') {
123
-			[$prefix2, $name2] = \Sabre\Uri\split($prefix);
124
-
125
-			if ($prefix2 === $this->principalPrefix) {
126
-				$user = $this->userManager->get($name2);
127
-
128
-				if ($user !== null) {
129
-					return [
130
-						'uri' => 'principals/users/' . $user->getUID() . '/' . $name,
131
-					];
132
-				}
133
-				return null;
134
-			}
135
-		}
136
-
137
-		if ($prefix === $this->principalPrefix) {
138
-			// Depending on where it is called, it may happen that this function
139
-			// is called either with a urlencoded version of the name or with a non-urlencoded one.
140
-			// The urldecode function replaces %## and +, both of which are forbidden in usernames.
141
-			// Hence there can be no ambiguity here and it is safe to call urldecode on all usernames
142
-			$user = $this->userManager->get($decodedName);
143
-
144
-			if ($user !== null) {
145
-				return $this->userToPrincipal($user, $propertyFilter);
146
-			}
147
-		} elseif ($prefix === 'principals/circles') {
148
-			if ($this->userSession->getUser() !== null) {
149
-				// At the time of writing - 2021-01-19 — a mixed state is possible.
150
-				// The second condition can be removed when this is fixed.
151
-				return $this->circleToPrincipal($decodedName)
152
-					?: $this->circleToPrincipal($name);
153
-			}
154
-		} elseif ($prefix === 'principals/groups') {
155
-			// At the time of writing - 2021-01-19 — a mixed state is possible.
156
-			// The second condition can be removed when this is fixed.
157
-			$group = $this->groupManager->get($decodedName)
158
-				?: $this->groupManager->get($name);
159
-			if ($group instanceof IGroup) {
160
-				return [
161
-					'uri' => 'principals/groups/' . $name,
162
-					'{DAV:}displayname' => $group->getDisplayName(),
163
-				];
164
-			}
165
-		} elseif ($prefix === 'principals/system') {
166
-			return [
167
-				'uri' => 'principals/system/' . $name,
168
-				'{DAV:}displayname' => $this->languageFactory->get('dav')->t('Accounts'),
169
-			];
170
-		} elseif ($prefix === 'principals/shares') {
171
-			return [
172
-				'uri' => 'principals/shares/' . $name,
173
-				'{DAV:}displayname' => $name,
174
-			];
175
-		}
176
-		return null;
177
-	}
178
-
179
-	/**
180
-	 * Returns the list of groups a principal is a member of
181
-	 *
182
-	 * @param string $principal
183
-	 * @param bool $needGroups
184
-	 * @return array
185
-	 * @throws Exception
186
-	 */
187
-	public function getGroupMembership($principal, $needGroups = false) {
188
-		[$prefix, $name] = \Sabre\Uri\split($principal);
189
-
190
-		if ($prefix !== $this->principalPrefix) {
191
-			return [];
192
-		}
193
-
194
-		$user = $this->userManager->get($name);
195
-		if (!$user) {
196
-			throw new Exception('Principal not found');
197
-		}
198
-
199
-		$groups = [];
200
-
201
-		if ($this->hasGroups || $needGroups) {
202
-			$userGroups = $this->groupManager->getUserGroups($user);
203
-			foreach ($userGroups as $userGroup) {
204
-				if ($userGroup->hideFromCollaboration()) {
205
-					continue;
206
-				}
207
-				$groups[] = 'principals/groups/' . urlencode($userGroup->getGID());
208
-			}
209
-		}
210
-
211
-		$groups = array_unique(array_merge(
212
-			$groups,
213
-			$this->traitGetGroupMembership($principal, $needGroups)
214
-		));
215
-
216
-		return $groups;
217
-	}
218
-
219
-	/**
220
-	 * @param string $path
221
-	 * @param PropPatch $propPatch
222
-	 * @return int
223
-	 */
224
-	public function updatePrincipal($path, PropPatch $propPatch) {
225
-		// Updating schedule-default-calendar-URL is handled in CustomPropertiesBackend
226
-		return 0;
227
-	}
228
-
229
-	/**
230
-	 * Search user principals
231
-	 *
232
-	 * @param array $searchProperties
233
-	 * @param string $test
234
-	 * @return array
235
-	 */
236
-	protected function searchUserPrincipals(array $searchProperties, $test = 'allof') {
237
-		$results = [];
238
-
239
-		// If sharing is disabled, return the empty array
240
-		$shareAPIEnabled = $this->shareManager->shareApiEnabled();
241
-		if (!$shareAPIEnabled) {
242
-			return [];
243
-		}
244
-
245
-		$allowEnumeration = $this->shareManager->allowEnumeration();
246
-		$limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups();
247
-		$limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone();
248
-		$allowEnumerationFullMatch = $this->shareManager->allowEnumerationFullMatch();
249
-		$ignoreSecondDisplayName = $this->shareManager->ignoreSecondDisplayName();
250
-		$matchEmail = $this->shareManager->matchEmail();
251
-
252
-		// If sharing is restricted to group members only,
253
-		// return only members that have groups in common
254
-		$restrictGroups = false;
255
-		$currentUser = $this->userSession->getUser();
256
-		if ($this->shareManager->shareWithGroupMembersOnly()) {
257
-			if (!$currentUser instanceof IUser) {
258
-				return [];
259
-			}
260
-
261
-			$restrictGroups = $this->groupManager->getUserGroupIds($currentUser);
262
-		}
263
-
264
-		$currentUserGroups = [];
265
-		if ($limitEnumerationGroup) {
266
-			if ($currentUser instanceof IUser) {
267
-				$currentUserGroups = $this->groupManager->getUserGroupIds($currentUser);
268
-			}
269
-		}
270
-
271
-		$searchLimit = $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT);
272
-		if ($searchLimit <= 0) {
273
-			$searchLimit = null;
274
-		}
275
-		foreach ($searchProperties as $prop => $value) {
276
-			switch ($prop) {
277
-				case '{http://sabredav.org/ns}email-address':
278
-					if (!$allowEnumeration) {
279
-						if ($allowEnumerationFullMatch && $matchEmail) {
280
-							$users = $this->userManager->getByEmail($value);
281
-						} else {
282
-							$users = [];
283
-						}
284
-					} else {
285
-						$users = $this->userManager->getByEmail($value);
286
-						$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
287
-							if ($allowEnumerationFullMatch && $user->getSystemEMailAddress() === $value) {
288
-								return true;
289
-							}
290
-
291
-							if ($limitEnumerationPhone
292
-								&& $currentUser instanceof IUser
293
-								&& $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
294
-								// Synced phonebook match
295
-								return true;
296
-							}
297
-
298
-							if (!$limitEnumerationGroup) {
299
-								// No limitation on enumeration, all allowed
300
-								return true;
301
-							}
302
-
303
-							return !empty($currentUserGroups) && !empty(array_intersect(
304
-								$this->groupManager->getUserGroupIds($user),
305
-								$currentUserGroups
306
-							));
307
-						});
308
-					}
309
-
310
-					$results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
311
-						// is sharing restricted to groups only?
312
-						if ($restrictGroups !== false) {
313
-							$userGroups = $this->groupManager->getUserGroupIds($user);
314
-							if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
315
-								return $carry;
316
-							}
317
-						}
318
-
319
-						$carry[] = $this->principalPrefix . '/' . $user->getUID();
320
-						return $carry;
321
-					}, []);
322
-					break;
323
-
324
-				case '{DAV:}displayname':
325
-
326
-					if (!$allowEnumeration) {
327
-						if ($allowEnumerationFullMatch) {
328
-							$lowerSearch = strtolower($value);
329
-							$users = $this->userManager->searchDisplayName($value, $searchLimit);
330
-							$users = \array_filter($users, static function (IUser $user) use ($lowerSearch, $ignoreSecondDisplayName) {
331
-								$lowerDisplayName = strtolower($user->getDisplayName());
332
-								return $lowerDisplayName === $lowerSearch || ($ignoreSecondDisplayName && trim(preg_replace('/ \(.*\)$/', '', $lowerDisplayName)) === $lowerSearch);
333
-							});
334
-						} else {
335
-							$users = [];
336
-						}
337
-					} else {
338
-						$users = $this->userManager->searchDisplayName($value, $searchLimit);
339
-						$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
340
-							if ($allowEnumerationFullMatch && $user->getDisplayName() === $value) {
341
-								return true;
342
-							}
343
-
344
-							if ($limitEnumerationPhone
345
-								&& $currentUser instanceof IUser
346
-								&& $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
347
-								// Synced phonebook match
348
-								return true;
349
-							}
350
-
351
-							if (!$limitEnumerationGroup) {
352
-								// No limitation on enumeration, all allowed
353
-								return true;
354
-							}
355
-
356
-							return !empty($currentUserGroups) && !empty(array_intersect(
357
-								$this->groupManager->getUserGroupIds($user),
358
-								$currentUserGroups
359
-							));
360
-						});
361
-					}
362
-
363
-					$results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
364
-						// is sharing restricted to groups only?
365
-						if ($restrictGroups !== false) {
366
-							$userGroups = $this->groupManager->getUserGroupIds($user);
367
-							if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
368
-								return $carry;
369
-							}
370
-						}
371
-
372
-						$carry[] = $this->principalPrefix . '/' . $user->getUID();
373
-						return $carry;
374
-					}, []);
375
-					break;
376
-
377
-				case '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set':
378
-					// If you add support for more search properties that qualify as a user-address,
379
-					// please also add them to the array below
380
-					$results[] = $this->searchUserPrincipals([
381
-						// In theory this should also search for principal:principals/users/...
382
-						// but that's used internally only anyway and i don't know of any client querying that
383
-						'{http://sabredav.org/ns}email-address' => $value,
384
-					], 'anyof');
385
-					break;
386
-
387
-				default:
388
-					$results[] = [];
389
-					break;
390
-			}
391
-		}
392
-
393
-		// results is an array of arrays, so this is not the first search result
394
-		// but the results of the first searchProperty
395
-		if (count($results) === 1) {
396
-			return $results[0];
397
-		}
398
-
399
-		switch ($test) {
400
-			case 'anyof':
401
-				return array_values(array_unique(array_merge(...$results)));
402
-
403
-			case 'allof':
404
-			default:
405
-				return array_values(array_intersect(...$results));
406
-		}
407
-	}
408
-
409
-	/**
410
-	 * @param string $prefixPath
411
-	 * @param array $searchProperties
412
-	 * @param string $test
413
-	 * @return array
414
-	 */
415
-	public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
416
-		if (count($searchProperties) === 0) {
417
-			return [];
418
-		}
419
-
420
-		switch ($prefixPath) {
421
-			case 'principals/users':
422
-				return $this->searchUserPrincipals($searchProperties, $test);
423
-
424
-			default:
425
-				return [];
426
-		}
427
-	}
428
-
429
-	/**
430
-	 * @param string $uri
431
-	 * @param string $principalPrefix
432
-	 * @return string
433
-	 */
434
-	public function findByUri($uri, $principalPrefix) {
435
-		// If sharing is disabled, return the empty array
436
-		$shareAPIEnabled = $this->shareManager->shareApiEnabled();
437
-		if (!$shareAPIEnabled) {
438
-			return null;
439
-		}
440
-
441
-		// If sharing is restricted to group members only,
442
-		// return only members that have groups in common
443
-		$restrictGroups = false;
444
-		if ($this->shareManager->shareWithGroupMembersOnly()) {
445
-			$user = $this->userSession->getUser();
446
-			if (!$user) {
447
-				return null;
448
-			}
449
-
450
-			$restrictGroups = $this->groupManager->getUserGroupIds($user);
451
-		}
452
-
453
-		if (str_starts_with($uri, 'mailto:')) {
454
-			if ($principalPrefix === 'principals/users') {
455
-				$users = $this->userManager->getByEmail(substr($uri, 7));
456
-				if (count($users) !== 1) {
457
-					return null;
458
-				}
459
-				$user = $users[0];
460
-
461
-				if ($restrictGroups !== false) {
462
-					$userGroups = $this->groupManager->getUserGroupIds($user);
463
-					if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
464
-						return null;
465
-					}
466
-				}
467
-
468
-				return $this->principalPrefix . '/' . $user->getUID();
469
-			}
470
-		}
471
-		if (str_starts_with($uri, 'principal:')) {
472
-			$principal = substr($uri, 10);
473
-			$principal = $this->getPrincipalByPath($principal);
474
-			if ($principal !== null) {
475
-				return $principal['uri'];
476
-			}
477
-		}
478
-
479
-		return null;
480
-	}
481
-
482
-	/**
483
-	 * @param IUser $user
484
-	 * @param string[]|null $propertyFilter
485
-	 * @return array
486
-	 * @throws PropertyDoesNotExistException
487
-	 */
488
-	protected function userToPrincipal($user, ?array $propertyFilter = null) {
489
-		$wantsProperty = static function (string $name) use ($propertyFilter) {
490
-			if ($propertyFilter === null) {
491
-				return true;
492
-			}
493
-
494
-			return in_array($name, $propertyFilter, true);
495
-		};
496
-
497
-		$userId = $user->getUID();
498
-		$displayName = $user->getDisplayName();
499
-		$principal = [
500
-			'uri' => $this->principalPrefix . '/' . $userId,
501
-			'{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
502
-			'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
503
-		];
504
-
505
-		if ($wantsProperty('{http://nextcloud.com/ns}language')) {
506
-			$principal['{http://nextcloud.com/ns}language'] = $this->languageFactory->getUserLanguage($user);
507
-		}
508
-
509
-		if ($wantsProperty('{http://sabredav.org/ns}email-address')) {
510
-			$email = $user->getSystemEMailAddress();
511
-			if (!empty($email)) {
512
-				$principal['{http://sabredav.org/ns}email-address'] = $email;
513
-			}
514
-		}
515
-
516
-		if ($wantsProperty('{DAV:}alternate-URI-set')) {
517
-			$account = $this->accountManager->getAccount($user);
518
-			$alternativeEmails = array_map(static fn (IAccountProperty $property) => 'mailto:' . $property->getValue(), $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties());
519
-			if (!empty($alternativeEmails)) {
520
-				$principal['{DAV:}alternate-URI-set'] = $alternativeEmails;
521
-			}
522
-		}
523
-
524
-		return $principal;
525
-	}
526
-
527
-	public function getPrincipalPrefix() {
528
-		return $this->principalPrefix;
529
-	}
530
-
531
-	/**
532
-	 * @param string $circleUniqueId
533
-	 * @return array|null
534
-	 */
535
-	protected function circleToPrincipal($circleUniqueId) {
536
-		if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) {
537
-			return null;
538
-		}
539
-
540
-		try {
541
-			$circle = Circles::detailsCircle($circleUniqueId, true);
542
-		} catch (ContainerExceptionInterface $ex) {
543
-			return null;
544
-		} catch (CircleNotFoundException $ex) {
545
-			return null;
546
-		}
547
-
548
-		if (!$circle) {
549
-			return null;
550
-		}
551
-
552
-		$principal = [
553
-			'uri' => 'principals/circles/' . $circleUniqueId,
554
-			'{DAV:}displayname' => $circle->getDisplayName(),
555
-		];
556
-
557
-		return $principal;
558
-	}
559
-
560
-	/**
561
-	 * Returns the list of circles a principal is a member of
562
-	 *
563
-	 * @param string $principal
564
-	 * @return array
565
-	 * @throws Exception
566
-	 * @throws ContainerExceptionInterface
567
-	 * @suppress PhanUndeclaredClassMethod
568
-	 */
569
-	public function getCircleMembership($principal):array {
570
-		if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) {
571
-			return [];
572
-		}
573
-
574
-		[$prefix, $name] = \Sabre\Uri\split($principal);
575
-		if ($this->hasCircles && $prefix === $this->principalPrefix) {
576
-			$user = $this->userManager->get($name);
577
-			if (!$user) {
578
-				throw new Exception('Principal not found');
579
-			}
580
-
581
-			$circles = Circles::joinedCircles($name, true);
582
-
583
-			$circles = array_map(function ($circle) {
584
-				/** @var Circle $circle */
585
-				return 'principals/circles/' . urlencode($circle->getSingleId());
586
-			}, $circles);
587
-
588
-			return $circles;
589
-		}
590
-
591
-		return [];
592
-	}
593
-
594
-	/**
595
-	 * Get all email addresses associated to a principal.
596
-	 *
597
-	 * @param array $principal Data from getPrincipal*()
598
-	 * @return string[] All email addresses without the mailto: prefix
599
-	 */
600
-	public function getEmailAddressesOfPrincipal(array $principal): array {
601
-		$emailAddresses = [];
602
-
603
-		if (isset($principal['{http://sabredav.org/ns}email-address'])) {
604
-			$emailAddresses[] = $principal['{http://sabredav.org/ns}email-address'];
605
-		}
606
-
607
-		if (isset($principal['{DAV:}alternate-URI-set'])) {
608
-			foreach ($principal['{DAV:}alternate-URI-set'] as $address) {
609
-				if (str_starts_with($address, 'mailto:')) {
610
-					$emailAddresses[] = substr($address, 7);
611
-				}
612
-			}
613
-		}
614
-
615
-		if (isset($principal['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'])) {
616
-			foreach ($principal['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'] as $address) {
617
-				if (str_starts_with($address, 'mailto:')) {
618
-					$emailAddresses[] = substr($address, 7);
619
-				}
620
-			}
621
-		}
622
-
623
-		if (isset($principal['{http://calendarserver.org/ns/}email-address-set'])) {
624
-			foreach ($principal['{http://calendarserver.org/ns/}email-address-set'] as $address) {
625
-				if (str_starts_with($address, 'mailto:')) {
626
-					$emailAddresses[] = substr($address, 7);
627
-				}
628
-			}
629
-		}
630
-
631
-		return array_values(array_unique($emailAddresses));
632
-	}
36
+    /** @var string */
37
+    private $principalPrefix;
38
+
39
+    /** @var bool */
40
+    private $hasGroups;
41
+
42
+    /** @var bool */
43
+    private $hasCircles;
44
+
45
+    /** @var KnownUserService */
46
+    private $knownUserService;
47
+
48
+    public function __construct(
49
+        private IUserManager $userManager,
50
+        private IGroupManager $groupManager,
51
+        private IAccountManager $accountManager,
52
+        private IShareManager $shareManager,
53
+        private IUserSession $userSession,
54
+        private IAppManager $appManager,
55
+        private ProxyMapper $proxyMapper,
56
+        KnownUserService $knownUserService,
57
+        private IConfig $config,
58
+        private IFactory $languageFactory,
59
+        string $principalPrefix = 'principals/users/',
60
+    ) {
61
+        $this->principalPrefix = trim($principalPrefix, '/');
62
+        $this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
63
+        $this->knownUserService = $knownUserService;
64
+    }
65
+
66
+    use PrincipalProxyTrait {
67
+        getGroupMembership as protected traitGetGroupMembership;
68
+    }
69
+
70
+    /**
71
+     * Returns a list of principals based on a prefix.
72
+     *
73
+     * This prefix will often contain something like 'principals'. You are only
74
+     * expected to return principals that are in this base path.
75
+     *
76
+     * You are expected to return at least a 'uri' for every user, you can
77
+     * return any additional properties if you wish so. Common properties are:
78
+     *   {DAV:}displayname
79
+     *
80
+     * @param string $prefixPath
81
+     * @return string[]
82
+     */
83
+    public function getPrincipalsByPrefix($prefixPath) {
84
+        $principals = [];
85
+
86
+        if ($prefixPath === $this->principalPrefix) {
87
+            foreach ($this->userManager->search('') as $user) {
88
+                $principals[] = $this->userToPrincipal($user);
89
+            }
90
+        }
91
+
92
+        return $principals;
93
+    }
94
+
95
+    /**
96
+     * Returns a specific principal, specified by it's path.
97
+     * The returned structure should be the exact same as from
98
+     * getPrincipalsByPrefix.
99
+     *
100
+     * @param string $path
101
+     * @return array
102
+     */
103
+    public function getPrincipalByPath($path) {
104
+        return $this->getPrincipalPropertiesByPath($path);
105
+    }
106
+
107
+    /**
108
+     * Returns a specific principal, specified by its path.
109
+     * The returned structure should be the exact same as from
110
+     * getPrincipalsByPrefix.
111
+     *
112
+     * It is possible to optionally filter retrieved properties in case only a limited set is
113
+     * required. Note that the implementation might return more properties than requested.
114
+     *
115
+     * @param string $path The path of the principal
116
+     * @param string[]|null $propertyFilter A list of properties to be retrieved or all if null. An empty array will cause a very shallow principal to be retrieved.
117
+     */
118
+    public function getPrincipalPropertiesByPath($path, ?array $propertyFilter = null): ?array {
119
+        [$prefix, $name] = \Sabre\Uri\split($path);
120
+        $decodedName = urldecode($name);
121
+
122
+        if ($name === 'calendar-proxy-write' || $name === 'calendar-proxy-read') {
123
+            [$prefix2, $name2] = \Sabre\Uri\split($prefix);
124
+
125
+            if ($prefix2 === $this->principalPrefix) {
126
+                $user = $this->userManager->get($name2);
127
+
128
+                if ($user !== null) {
129
+                    return [
130
+                        'uri' => 'principals/users/' . $user->getUID() . '/' . $name,
131
+                    ];
132
+                }
133
+                return null;
134
+            }
135
+        }
136
+
137
+        if ($prefix === $this->principalPrefix) {
138
+            // Depending on where it is called, it may happen that this function
139
+            // is called either with a urlencoded version of the name or with a non-urlencoded one.
140
+            // The urldecode function replaces %## and +, both of which are forbidden in usernames.
141
+            // Hence there can be no ambiguity here and it is safe to call urldecode on all usernames
142
+            $user = $this->userManager->get($decodedName);
143
+
144
+            if ($user !== null) {
145
+                return $this->userToPrincipal($user, $propertyFilter);
146
+            }
147
+        } elseif ($prefix === 'principals/circles') {
148
+            if ($this->userSession->getUser() !== null) {
149
+                // At the time of writing - 2021-01-19 — a mixed state is possible.
150
+                // The second condition can be removed when this is fixed.
151
+                return $this->circleToPrincipal($decodedName)
152
+                    ?: $this->circleToPrincipal($name);
153
+            }
154
+        } elseif ($prefix === 'principals/groups') {
155
+            // At the time of writing - 2021-01-19 — a mixed state is possible.
156
+            // The second condition can be removed when this is fixed.
157
+            $group = $this->groupManager->get($decodedName)
158
+                ?: $this->groupManager->get($name);
159
+            if ($group instanceof IGroup) {
160
+                return [
161
+                    'uri' => 'principals/groups/' . $name,
162
+                    '{DAV:}displayname' => $group->getDisplayName(),
163
+                ];
164
+            }
165
+        } elseif ($prefix === 'principals/system') {
166
+            return [
167
+                'uri' => 'principals/system/' . $name,
168
+                '{DAV:}displayname' => $this->languageFactory->get('dav')->t('Accounts'),
169
+            ];
170
+        } elseif ($prefix === 'principals/shares') {
171
+            return [
172
+                'uri' => 'principals/shares/' . $name,
173
+                '{DAV:}displayname' => $name,
174
+            ];
175
+        }
176
+        return null;
177
+    }
178
+
179
+    /**
180
+     * Returns the list of groups a principal is a member of
181
+     *
182
+     * @param string $principal
183
+     * @param bool $needGroups
184
+     * @return array
185
+     * @throws Exception
186
+     */
187
+    public function getGroupMembership($principal, $needGroups = false) {
188
+        [$prefix, $name] = \Sabre\Uri\split($principal);
189
+
190
+        if ($prefix !== $this->principalPrefix) {
191
+            return [];
192
+        }
193
+
194
+        $user = $this->userManager->get($name);
195
+        if (!$user) {
196
+            throw new Exception('Principal not found');
197
+        }
198
+
199
+        $groups = [];
200
+
201
+        if ($this->hasGroups || $needGroups) {
202
+            $userGroups = $this->groupManager->getUserGroups($user);
203
+            foreach ($userGroups as $userGroup) {
204
+                if ($userGroup->hideFromCollaboration()) {
205
+                    continue;
206
+                }
207
+                $groups[] = 'principals/groups/' . urlencode($userGroup->getGID());
208
+            }
209
+        }
210
+
211
+        $groups = array_unique(array_merge(
212
+            $groups,
213
+            $this->traitGetGroupMembership($principal, $needGroups)
214
+        ));
215
+
216
+        return $groups;
217
+    }
218
+
219
+    /**
220
+     * @param string $path
221
+     * @param PropPatch $propPatch
222
+     * @return int
223
+     */
224
+    public function updatePrincipal($path, PropPatch $propPatch) {
225
+        // Updating schedule-default-calendar-URL is handled in CustomPropertiesBackend
226
+        return 0;
227
+    }
228
+
229
+    /**
230
+     * Search user principals
231
+     *
232
+     * @param array $searchProperties
233
+     * @param string $test
234
+     * @return array
235
+     */
236
+    protected function searchUserPrincipals(array $searchProperties, $test = 'allof') {
237
+        $results = [];
238
+
239
+        // If sharing is disabled, return the empty array
240
+        $shareAPIEnabled = $this->shareManager->shareApiEnabled();
241
+        if (!$shareAPIEnabled) {
242
+            return [];
243
+        }
244
+
245
+        $allowEnumeration = $this->shareManager->allowEnumeration();
246
+        $limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups();
247
+        $limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone();
248
+        $allowEnumerationFullMatch = $this->shareManager->allowEnumerationFullMatch();
249
+        $ignoreSecondDisplayName = $this->shareManager->ignoreSecondDisplayName();
250
+        $matchEmail = $this->shareManager->matchEmail();
251
+
252
+        // If sharing is restricted to group members only,
253
+        // return only members that have groups in common
254
+        $restrictGroups = false;
255
+        $currentUser = $this->userSession->getUser();
256
+        if ($this->shareManager->shareWithGroupMembersOnly()) {
257
+            if (!$currentUser instanceof IUser) {
258
+                return [];
259
+            }
260
+
261
+            $restrictGroups = $this->groupManager->getUserGroupIds($currentUser);
262
+        }
263
+
264
+        $currentUserGroups = [];
265
+        if ($limitEnumerationGroup) {
266
+            if ($currentUser instanceof IUser) {
267
+                $currentUserGroups = $this->groupManager->getUserGroupIds($currentUser);
268
+            }
269
+        }
270
+
271
+        $searchLimit = $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT);
272
+        if ($searchLimit <= 0) {
273
+            $searchLimit = null;
274
+        }
275
+        foreach ($searchProperties as $prop => $value) {
276
+            switch ($prop) {
277
+                case '{http://sabredav.org/ns}email-address':
278
+                    if (!$allowEnumeration) {
279
+                        if ($allowEnumerationFullMatch && $matchEmail) {
280
+                            $users = $this->userManager->getByEmail($value);
281
+                        } else {
282
+                            $users = [];
283
+                        }
284
+                    } else {
285
+                        $users = $this->userManager->getByEmail($value);
286
+                        $users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
287
+                            if ($allowEnumerationFullMatch && $user->getSystemEMailAddress() === $value) {
288
+                                return true;
289
+                            }
290
+
291
+                            if ($limitEnumerationPhone
292
+                                && $currentUser instanceof IUser
293
+                                && $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
294
+                                // Synced phonebook match
295
+                                return true;
296
+                            }
297
+
298
+                            if (!$limitEnumerationGroup) {
299
+                                // No limitation on enumeration, all allowed
300
+                                return true;
301
+                            }
302
+
303
+                            return !empty($currentUserGroups) && !empty(array_intersect(
304
+                                $this->groupManager->getUserGroupIds($user),
305
+                                $currentUserGroups
306
+                            ));
307
+                        });
308
+                    }
309
+
310
+                    $results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
311
+                        // is sharing restricted to groups only?
312
+                        if ($restrictGroups !== false) {
313
+                            $userGroups = $this->groupManager->getUserGroupIds($user);
314
+                            if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
315
+                                return $carry;
316
+                            }
317
+                        }
318
+
319
+                        $carry[] = $this->principalPrefix . '/' . $user->getUID();
320
+                        return $carry;
321
+                    }, []);
322
+                    break;
323
+
324
+                case '{DAV:}displayname':
325
+
326
+                    if (!$allowEnumeration) {
327
+                        if ($allowEnumerationFullMatch) {
328
+                            $lowerSearch = strtolower($value);
329
+                            $users = $this->userManager->searchDisplayName($value, $searchLimit);
330
+                            $users = \array_filter($users, static function (IUser $user) use ($lowerSearch, $ignoreSecondDisplayName) {
331
+                                $lowerDisplayName = strtolower($user->getDisplayName());
332
+                                return $lowerDisplayName === $lowerSearch || ($ignoreSecondDisplayName && trim(preg_replace('/ \(.*\)$/', '', $lowerDisplayName)) === $lowerSearch);
333
+                            });
334
+                        } else {
335
+                            $users = [];
336
+                        }
337
+                    } else {
338
+                        $users = $this->userManager->searchDisplayName($value, $searchLimit);
339
+                        $users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
340
+                            if ($allowEnumerationFullMatch && $user->getDisplayName() === $value) {
341
+                                return true;
342
+                            }
343
+
344
+                            if ($limitEnumerationPhone
345
+                                && $currentUser instanceof IUser
346
+                                && $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
347
+                                // Synced phonebook match
348
+                                return true;
349
+                            }
350
+
351
+                            if (!$limitEnumerationGroup) {
352
+                                // No limitation on enumeration, all allowed
353
+                                return true;
354
+                            }
355
+
356
+                            return !empty($currentUserGroups) && !empty(array_intersect(
357
+                                $this->groupManager->getUserGroupIds($user),
358
+                                $currentUserGroups
359
+                            ));
360
+                        });
361
+                    }
362
+
363
+                    $results[] = array_reduce($users, function (array $carry, IUser $user) use ($restrictGroups) {
364
+                        // is sharing restricted to groups only?
365
+                        if ($restrictGroups !== false) {
366
+                            $userGroups = $this->groupManager->getUserGroupIds($user);
367
+                            if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
368
+                                return $carry;
369
+                            }
370
+                        }
371
+
372
+                        $carry[] = $this->principalPrefix . '/' . $user->getUID();
373
+                        return $carry;
374
+                    }, []);
375
+                    break;
376
+
377
+                case '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set':
378
+                    // If you add support for more search properties that qualify as a user-address,
379
+                    // please also add them to the array below
380
+                    $results[] = $this->searchUserPrincipals([
381
+                        // In theory this should also search for principal:principals/users/...
382
+                        // but that's used internally only anyway and i don't know of any client querying that
383
+                        '{http://sabredav.org/ns}email-address' => $value,
384
+                    ], 'anyof');
385
+                    break;
386
+
387
+                default:
388
+                    $results[] = [];
389
+                    break;
390
+            }
391
+        }
392
+
393
+        // results is an array of arrays, so this is not the first search result
394
+        // but the results of the first searchProperty
395
+        if (count($results) === 1) {
396
+            return $results[0];
397
+        }
398
+
399
+        switch ($test) {
400
+            case 'anyof':
401
+                return array_values(array_unique(array_merge(...$results)));
402
+
403
+            case 'allof':
404
+            default:
405
+                return array_values(array_intersect(...$results));
406
+        }
407
+    }
408
+
409
+    /**
410
+     * @param string $prefixPath
411
+     * @param array $searchProperties
412
+     * @param string $test
413
+     * @return array
414
+     */
415
+    public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
416
+        if (count($searchProperties) === 0) {
417
+            return [];
418
+        }
419
+
420
+        switch ($prefixPath) {
421
+            case 'principals/users':
422
+                return $this->searchUserPrincipals($searchProperties, $test);
423
+
424
+            default:
425
+                return [];
426
+        }
427
+    }
428
+
429
+    /**
430
+     * @param string $uri
431
+     * @param string $principalPrefix
432
+     * @return string
433
+     */
434
+    public function findByUri($uri, $principalPrefix) {
435
+        // If sharing is disabled, return the empty array
436
+        $shareAPIEnabled = $this->shareManager->shareApiEnabled();
437
+        if (!$shareAPIEnabled) {
438
+            return null;
439
+        }
440
+
441
+        // If sharing is restricted to group members only,
442
+        // return only members that have groups in common
443
+        $restrictGroups = false;
444
+        if ($this->shareManager->shareWithGroupMembersOnly()) {
445
+            $user = $this->userSession->getUser();
446
+            if (!$user) {
447
+                return null;
448
+            }
449
+
450
+            $restrictGroups = $this->groupManager->getUserGroupIds($user);
451
+        }
452
+
453
+        if (str_starts_with($uri, 'mailto:')) {
454
+            if ($principalPrefix === 'principals/users') {
455
+                $users = $this->userManager->getByEmail(substr($uri, 7));
456
+                if (count($users) !== 1) {
457
+                    return null;
458
+                }
459
+                $user = $users[0];
460
+
461
+                if ($restrictGroups !== false) {
462
+                    $userGroups = $this->groupManager->getUserGroupIds($user);
463
+                    if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
464
+                        return null;
465
+                    }
466
+                }
467
+
468
+                return $this->principalPrefix . '/' . $user->getUID();
469
+            }
470
+        }
471
+        if (str_starts_with($uri, 'principal:')) {
472
+            $principal = substr($uri, 10);
473
+            $principal = $this->getPrincipalByPath($principal);
474
+            if ($principal !== null) {
475
+                return $principal['uri'];
476
+            }
477
+        }
478
+
479
+        return null;
480
+    }
481
+
482
+    /**
483
+     * @param IUser $user
484
+     * @param string[]|null $propertyFilter
485
+     * @return array
486
+     * @throws PropertyDoesNotExistException
487
+     */
488
+    protected function userToPrincipal($user, ?array $propertyFilter = null) {
489
+        $wantsProperty = static function (string $name) use ($propertyFilter) {
490
+            if ($propertyFilter === null) {
491
+                return true;
492
+            }
493
+
494
+            return in_array($name, $propertyFilter, true);
495
+        };
496
+
497
+        $userId = $user->getUID();
498
+        $displayName = $user->getDisplayName();
499
+        $principal = [
500
+            'uri' => $this->principalPrefix . '/' . $userId,
501
+            '{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
502
+            '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
503
+        ];
504
+
505
+        if ($wantsProperty('{http://nextcloud.com/ns}language')) {
506
+            $principal['{http://nextcloud.com/ns}language'] = $this->languageFactory->getUserLanguage($user);
507
+        }
508
+
509
+        if ($wantsProperty('{http://sabredav.org/ns}email-address')) {
510
+            $email = $user->getSystemEMailAddress();
511
+            if (!empty($email)) {
512
+                $principal['{http://sabredav.org/ns}email-address'] = $email;
513
+            }
514
+        }
515
+
516
+        if ($wantsProperty('{DAV:}alternate-URI-set')) {
517
+            $account = $this->accountManager->getAccount($user);
518
+            $alternativeEmails = array_map(static fn (IAccountProperty $property) => 'mailto:' . $property->getValue(), $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties());
519
+            if (!empty($alternativeEmails)) {
520
+                $principal['{DAV:}alternate-URI-set'] = $alternativeEmails;
521
+            }
522
+        }
523
+
524
+        return $principal;
525
+    }
526
+
527
+    public function getPrincipalPrefix() {
528
+        return $this->principalPrefix;
529
+    }
530
+
531
+    /**
532
+     * @param string $circleUniqueId
533
+     * @return array|null
534
+     */
535
+    protected function circleToPrincipal($circleUniqueId) {
536
+        if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) {
537
+            return null;
538
+        }
539
+
540
+        try {
541
+            $circle = Circles::detailsCircle($circleUniqueId, true);
542
+        } catch (ContainerExceptionInterface $ex) {
543
+            return null;
544
+        } catch (CircleNotFoundException $ex) {
545
+            return null;
546
+        }
547
+
548
+        if (!$circle) {
549
+            return null;
550
+        }
551
+
552
+        $principal = [
553
+            'uri' => 'principals/circles/' . $circleUniqueId,
554
+            '{DAV:}displayname' => $circle->getDisplayName(),
555
+        ];
556
+
557
+        return $principal;
558
+    }
559
+
560
+    /**
561
+     * Returns the list of circles a principal is a member of
562
+     *
563
+     * @param string $principal
564
+     * @return array
565
+     * @throws Exception
566
+     * @throws ContainerExceptionInterface
567
+     * @suppress PhanUndeclaredClassMethod
568
+     */
569
+    public function getCircleMembership($principal):array {
570
+        if (!$this->appManager->isEnabledForUser('circles') || !class_exists('\OCA\Circles\Api\v1\Circles')) {
571
+            return [];
572
+        }
573
+
574
+        [$prefix, $name] = \Sabre\Uri\split($principal);
575
+        if ($this->hasCircles && $prefix === $this->principalPrefix) {
576
+            $user = $this->userManager->get($name);
577
+            if (!$user) {
578
+                throw new Exception('Principal not found');
579
+            }
580
+
581
+            $circles = Circles::joinedCircles($name, true);
582
+
583
+            $circles = array_map(function ($circle) {
584
+                /** @var Circle $circle */
585
+                return 'principals/circles/' . urlencode($circle->getSingleId());
586
+            }, $circles);
587
+
588
+            return $circles;
589
+        }
590
+
591
+        return [];
592
+    }
593
+
594
+    /**
595
+     * Get all email addresses associated to a principal.
596
+     *
597
+     * @param array $principal Data from getPrincipal*()
598
+     * @return string[] All email addresses without the mailto: prefix
599
+     */
600
+    public function getEmailAddressesOfPrincipal(array $principal): array {
601
+        $emailAddresses = [];
602
+
603
+        if (isset($principal['{http://sabredav.org/ns}email-address'])) {
604
+            $emailAddresses[] = $principal['{http://sabredav.org/ns}email-address'];
605
+        }
606
+
607
+        if (isset($principal['{DAV:}alternate-URI-set'])) {
608
+            foreach ($principal['{DAV:}alternate-URI-set'] as $address) {
609
+                if (str_starts_with($address, 'mailto:')) {
610
+                    $emailAddresses[] = substr($address, 7);
611
+                }
612
+            }
613
+        }
614
+
615
+        if (isset($principal['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'])) {
616
+            foreach ($principal['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set'] as $address) {
617
+                if (str_starts_with($address, 'mailto:')) {
618
+                    $emailAddresses[] = substr($address, 7);
619
+                }
620
+            }
621
+        }
622
+
623
+        if (isset($principal['{http://calendarserver.org/ns/}email-address-set'])) {
624
+            foreach ($principal['{http://calendarserver.org/ns/}email-address-set'] as $address) {
625
+                if (str_starts_with($address, 'mailto:')) {
626
+                    $emailAddresses[] = substr($address, 7);
627
+                }
628
+            }
629
+        }
630
+
631
+        return array_values(array_unique($emailAddresses));
632
+    }
633 633
 }
Please login to merge, or discard this patch.
apps/dav/lib/AppInfo/PluginManager.php 1 patch
Indentation   +268 added lines, -268 removed lines patch added patch discarded remove patch
@@ -27,272 +27,272 @@
 block discarded – undo
27 27
  */
28 28
 class PluginManager {
29 29
 
30
-	/**
31
-	 * App plugins
32
-	 *
33
-	 * @var ServerPlugin[]
34
-	 */
35
-	private $plugins = [];
36
-
37
-	/**
38
-	 * App collections
39
-	 *
40
-	 * @var Collection[]
41
-	 */
42
-	private $collections = [];
43
-
44
-	/**
45
-	 * Address book plugins
46
-	 *
47
-	 * @var IAddressBookProvider[]
48
-	 */
49
-	private $addressBookPlugins = [];
50
-
51
-	/**
52
-	 * Calendar plugins
53
-	 *
54
-	 * @var ICalendarProvider[]
55
-	 */
56
-	private $calendarPlugins = [];
57
-
58
-	/** @var bool */
59
-	private $populated = false;
60
-
61
-	/**
62
-	 * Contstruct a PluginManager
63
-	 *
64
-	 * @param ServerContainer $container server container for resolving plugin classes
65
-	 * @param IAppManager $appManager app manager to loading apps and their info
66
-	 */
67
-	public function __construct(
68
-		private ServerContainer $container,
69
-		private IAppManager $appManager,
70
-	) {
71
-	}
72
-
73
-	/**
74
-	 * Returns an array of app-registered plugins
75
-	 *
76
-	 * @return ServerPlugin[]
77
-	 */
78
-	public function getAppPlugins() {
79
-		$this->populate();
80
-		return $this->plugins;
81
-	}
82
-
83
-	/**
84
-	 * Returns an array of app-registered collections
85
-	 *
86
-	 * @return array
87
-	 */
88
-	public function getAppCollections() {
89
-		$this->populate();
90
-		return $this->collections;
91
-	}
92
-
93
-	/**
94
-	 * @return IAddressBookProvider[]
95
-	 */
96
-	public function getAddressBookPlugins(): array {
97
-		$this->populate();
98
-		return $this->addressBookPlugins;
99
-	}
100
-
101
-	/**
102
-	 * Returns an array of app-registered calendar plugins
103
-	 *
104
-	 * @return ICalendarProvider[]
105
-	 */
106
-	public function getCalendarPlugins():array {
107
-		$this->populate();
108
-		return $this->calendarPlugins;
109
-	}
110
-
111
-	/**
112
-	 * Retrieve plugin and collection list and populate attributes
113
-	 */
114
-	private function populate(): void {
115
-		if ($this->populated) {
116
-			return;
117
-		}
118
-		$this->populated = true;
119
-
120
-		$this->calendarPlugins[] = $this->container->get(AppCalendarPlugin::class);
121
-
122
-		foreach ($this->appManager->getEnabledApps() as $app) {
123
-			// load plugins and collections from info.xml
124
-			$info = $this->appManager->getAppInfo($app);
125
-			if (!isset($info['types']) || !in_array('dav', $info['types'], true)) {
126
-				continue;
127
-			}
128
-			$plugins = $this->loadSabrePluginsFromInfoXml($this->extractPluginList($info));
129
-			foreach ($plugins as $plugin) {
130
-				$this->plugins[] = $plugin;
131
-			}
132
-
133
-			$collections = $this->loadSabreCollectionsFromInfoXml($this->extractCollectionList($info));
134
-			foreach ($collections as $collection) {
135
-				$this->collections[] = $collection;
136
-			}
137
-
138
-			$addresbookPlugins = $this->loadSabreAddressBookPluginsFromInfoXml($this->extractAddressBookPluginList($info));
139
-			foreach ($addresbookPlugins as $addresbookPlugin) {
140
-				$this->addressBookPlugins[] = $addresbookPlugin;
141
-			}
142
-
143
-			$calendarPlugins = $this->loadSabreCalendarPluginsFromInfoXml($this->extractCalendarPluginList($info));
144
-			foreach ($calendarPlugins as $calendarPlugin) {
145
-				$this->calendarPlugins[] = $calendarPlugin;
146
-			}
147
-		}
148
-	}
149
-
150
-	/**
151
-	 * @param array $array
152
-	 * @return string[]
153
-	 */
154
-	private function extractPluginList(array $array): array {
155
-		if (isset($array['sabre']) && is_array($array['sabre'])) {
156
-			if (isset($array['sabre']['plugins']) && is_array($array['sabre']['plugins'])) {
157
-				if (isset($array['sabre']['plugins']['plugin'])) {
158
-					$items = $array['sabre']['plugins']['plugin'];
159
-					if (!is_array($items)) {
160
-						$items = [$items];
161
-					}
162
-					return $items;
163
-				}
164
-			}
165
-		}
166
-		return [];
167
-	}
168
-
169
-	/**
170
-	 * @param array $array
171
-	 * @return string[]
172
-	 */
173
-	private function extractCollectionList(array $array): array {
174
-		if (isset($array['sabre']) && is_array($array['sabre'])) {
175
-			if (isset($array['sabre']['collections']) && is_array($array['sabre']['collections'])) {
176
-				if (isset($array['sabre']['collections']['collection'])) {
177
-					$items = $array['sabre']['collections']['collection'];
178
-					if (!is_array($items)) {
179
-						$items = [$items];
180
-					}
181
-					return $items;
182
-				}
183
-			}
184
-		}
185
-		return [];
186
-	}
187
-
188
-	/**
189
-	 * @param array $array
190
-	 * @return string[]
191
-	 */
192
-	private function extractAddressBookPluginList(array $array): array {
193
-		if (!isset($array['sabre']) || !is_array($array['sabre'])) {
194
-			return [];
195
-		}
196
-		if (!isset($array['sabre']['address-book-plugins']) || !is_array($array['sabre']['address-book-plugins'])) {
197
-			return [];
198
-		}
199
-		if (!isset($array['sabre']['address-book-plugins']['plugin'])) {
200
-			return [];
201
-		}
202
-
203
-		$items = $array['sabre']['address-book-plugins']['plugin'];
204
-		if (!is_array($items)) {
205
-			$items = [$items];
206
-		}
207
-		return $items;
208
-	}
209
-
210
-	/**
211
-	 * @param array $array
212
-	 * @return string[]
213
-	 */
214
-	private function extractCalendarPluginList(array $array): array {
215
-		if (isset($array['sabre']) && is_array($array['sabre'])) {
216
-			if (isset($array['sabre']['calendar-plugins']) && is_array($array['sabre']['calendar-plugins'])) {
217
-				if (isset($array['sabre']['calendar-plugins']['plugin'])) {
218
-					$items = $array['sabre']['calendar-plugins']['plugin'];
219
-					if (!is_array($items)) {
220
-						$items = [$items];
221
-					}
222
-					return $items;
223
-				}
224
-			}
225
-		}
226
-		return [];
227
-	}
228
-
229
-	private function createClass(string $className): object {
230
-		try {
231
-			return $this->container->get($className);
232
-		} catch (ContainerExceptionInterface $e) {
233
-			if (class_exists($className)) {
234
-				return new $className();
235
-			}
236
-		}
237
-
238
-		throw new \Exception('Could not load ' . $className, 0, $e);
239
-	}
240
-
241
-
242
-	/**
243
-	 * @param string[] $classes
244
-	 * @return ServerPlugin[]
245
-	 * @throws \Exception
246
-	 */
247
-	private function loadSabrePluginsFromInfoXml(array $classes): array {
248
-		return array_map(function (string $className): ServerPlugin {
249
-			$instance = $this->createClass($className);
250
-			if (!($instance instanceof ServerPlugin)) {
251
-				throw new \Exception('Sabre server plugin ' . $className . ' does not implement the ' . ServerPlugin::class . ' interface');
252
-			}
253
-			return $instance;
254
-		}, $classes);
255
-	}
256
-
257
-	/**
258
-	 * @param string[] $classes
259
-	 * @return Collection[]
260
-	 */
261
-	private function loadSabreCollectionsFromInfoXml(array $classes): array {
262
-		return array_map(function (string $className): Collection {
263
-			$instance = $this->createClass($className);
264
-			if (!($instance instanceof Collection)) {
265
-				throw new \Exception('Sabre collection plugin ' . $className . ' does not implement the ' . Collection::class . ' interface');
266
-			}
267
-			return $instance;
268
-		}, $classes);
269
-	}
270
-
271
-	/**
272
-	 * @param string[] $classes
273
-	 * @return IAddressBookProvider[]
274
-	 */
275
-	private function loadSabreAddressBookPluginsFromInfoXml(array $classes): array {
276
-		return array_map(function (string $className): IAddressBookProvider {
277
-			$instance = $this->createClass($className);
278
-			if (!($instance instanceof IAddressBookProvider)) {
279
-				throw new \Exception('Sabre address book plugin class ' . $className . ' does not implement the ' . IAddressBookProvider::class . ' interface');
280
-			}
281
-			return $instance;
282
-		}, $classes);
283
-	}
284
-
285
-	/**
286
-	 * @param string[] $classes
287
-	 * @return ICalendarProvider[]
288
-	 */
289
-	private function loadSabreCalendarPluginsFromInfoXml(array $classes): array {
290
-		return array_map(function (string $className): ICalendarProvider {
291
-			$instance = $this->createClass($className);
292
-			if (!($instance instanceof ICalendarProvider)) {
293
-				throw new \Exception('Sabre calendar plugin class ' . $className . ' does not implement the ' . ICalendarProvider::class . ' interface');
294
-			}
295
-			return $instance;
296
-		}, $classes);
297
-	}
30
+    /**
31
+     * App plugins
32
+     *
33
+     * @var ServerPlugin[]
34
+     */
35
+    private $plugins = [];
36
+
37
+    /**
38
+     * App collections
39
+     *
40
+     * @var Collection[]
41
+     */
42
+    private $collections = [];
43
+
44
+    /**
45
+     * Address book plugins
46
+     *
47
+     * @var IAddressBookProvider[]
48
+     */
49
+    private $addressBookPlugins = [];
50
+
51
+    /**
52
+     * Calendar plugins
53
+     *
54
+     * @var ICalendarProvider[]
55
+     */
56
+    private $calendarPlugins = [];
57
+
58
+    /** @var bool */
59
+    private $populated = false;
60
+
61
+    /**
62
+     * Contstruct a PluginManager
63
+     *
64
+     * @param ServerContainer $container server container for resolving plugin classes
65
+     * @param IAppManager $appManager app manager to loading apps and their info
66
+     */
67
+    public function __construct(
68
+        private ServerContainer $container,
69
+        private IAppManager $appManager,
70
+    ) {
71
+    }
72
+
73
+    /**
74
+     * Returns an array of app-registered plugins
75
+     *
76
+     * @return ServerPlugin[]
77
+     */
78
+    public function getAppPlugins() {
79
+        $this->populate();
80
+        return $this->plugins;
81
+    }
82
+
83
+    /**
84
+     * Returns an array of app-registered collections
85
+     *
86
+     * @return array
87
+     */
88
+    public function getAppCollections() {
89
+        $this->populate();
90
+        return $this->collections;
91
+    }
92
+
93
+    /**
94
+     * @return IAddressBookProvider[]
95
+     */
96
+    public function getAddressBookPlugins(): array {
97
+        $this->populate();
98
+        return $this->addressBookPlugins;
99
+    }
100
+
101
+    /**
102
+     * Returns an array of app-registered calendar plugins
103
+     *
104
+     * @return ICalendarProvider[]
105
+     */
106
+    public function getCalendarPlugins():array {
107
+        $this->populate();
108
+        return $this->calendarPlugins;
109
+    }
110
+
111
+    /**
112
+     * Retrieve plugin and collection list and populate attributes
113
+     */
114
+    private function populate(): void {
115
+        if ($this->populated) {
116
+            return;
117
+        }
118
+        $this->populated = true;
119
+
120
+        $this->calendarPlugins[] = $this->container->get(AppCalendarPlugin::class);
121
+
122
+        foreach ($this->appManager->getEnabledApps() as $app) {
123
+            // load plugins and collections from info.xml
124
+            $info = $this->appManager->getAppInfo($app);
125
+            if (!isset($info['types']) || !in_array('dav', $info['types'], true)) {
126
+                continue;
127
+            }
128
+            $plugins = $this->loadSabrePluginsFromInfoXml($this->extractPluginList($info));
129
+            foreach ($plugins as $plugin) {
130
+                $this->plugins[] = $plugin;
131
+            }
132
+
133
+            $collections = $this->loadSabreCollectionsFromInfoXml($this->extractCollectionList($info));
134
+            foreach ($collections as $collection) {
135
+                $this->collections[] = $collection;
136
+            }
137
+
138
+            $addresbookPlugins = $this->loadSabreAddressBookPluginsFromInfoXml($this->extractAddressBookPluginList($info));
139
+            foreach ($addresbookPlugins as $addresbookPlugin) {
140
+                $this->addressBookPlugins[] = $addresbookPlugin;
141
+            }
142
+
143
+            $calendarPlugins = $this->loadSabreCalendarPluginsFromInfoXml($this->extractCalendarPluginList($info));
144
+            foreach ($calendarPlugins as $calendarPlugin) {
145
+                $this->calendarPlugins[] = $calendarPlugin;
146
+            }
147
+        }
148
+    }
149
+
150
+    /**
151
+     * @param array $array
152
+     * @return string[]
153
+     */
154
+    private function extractPluginList(array $array): array {
155
+        if (isset($array['sabre']) && is_array($array['sabre'])) {
156
+            if (isset($array['sabre']['plugins']) && is_array($array['sabre']['plugins'])) {
157
+                if (isset($array['sabre']['plugins']['plugin'])) {
158
+                    $items = $array['sabre']['plugins']['plugin'];
159
+                    if (!is_array($items)) {
160
+                        $items = [$items];
161
+                    }
162
+                    return $items;
163
+                }
164
+            }
165
+        }
166
+        return [];
167
+    }
168
+
169
+    /**
170
+     * @param array $array
171
+     * @return string[]
172
+     */
173
+    private function extractCollectionList(array $array): array {
174
+        if (isset($array['sabre']) && is_array($array['sabre'])) {
175
+            if (isset($array['sabre']['collections']) && is_array($array['sabre']['collections'])) {
176
+                if (isset($array['sabre']['collections']['collection'])) {
177
+                    $items = $array['sabre']['collections']['collection'];
178
+                    if (!is_array($items)) {
179
+                        $items = [$items];
180
+                    }
181
+                    return $items;
182
+                }
183
+            }
184
+        }
185
+        return [];
186
+    }
187
+
188
+    /**
189
+     * @param array $array
190
+     * @return string[]
191
+     */
192
+    private function extractAddressBookPluginList(array $array): array {
193
+        if (!isset($array['sabre']) || !is_array($array['sabre'])) {
194
+            return [];
195
+        }
196
+        if (!isset($array['sabre']['address-book-plugins']) || !is_array($array['sabre']['address-book-plugins'])) {
197
+            return [];
198
+        }
199
+        if (!isset($array['sabre']['address-book-plugins']['plugin'])) {
200
+            return [];
201
+        }
202
+
203
+        $items = $array['sabre']['address-book-plugins']['plugin'];
204
+        if (!is_array($items)) {
205
+            $items = [$items];
206
+        }
207
+        return $items;
208
+    }
209
+
210
+    /**
211
+     * @param array $array
212
+     * @return string[]
213
+     */
214
+    private function extractCalendarPluginList(array $array): array {
215
+        if (isset($array['sabre']) && is_array($array['sabre'])) {
216
+            if (isset($array['sabre']['calendar-plugins']) && is_array($array['sabre']['calendar-plugins'])) {
217
+                if (isset($array['sabre']['calendar-plugins']['plugin'])) {
218
+                    $items = $array['sabre']['calendar-plugins']['plugin'];
219
+                    if (!is_array($items)) {
220
+                        $items = [$items];
221
+                    }
222
+                    return $items;
223
+                }
224
+            }
225
+        }
226
+        return [];
227
+    }
228
+
229
+    private function createClass(string $className): object {
230
+        try {
231
+            return $this->container->get($className);
232
+        } catch (ContainerExceptionInterface $e) {
233
+            if (class_exists($className)) {
234
+                return new $className();
235
+            }
236
+        }
237
+
238
+        throw new \Exception('Could not load ' . $className, 0, $e);
239
+    }
240
+
241
+
242
+    /**
243
+     * @param string[] $classes
244
+     * @return ServerPlugin[]
245
+     * @throws \Exception
246
+     */
247
+    private function loadSabrePluginsFromInfoXml(array $classes): array {
248
+        return array_map(function (string $className): ServerPlugin {
249
+            $instance = $this->createClass($className);
250
+            if (!($instance instanceof ServerPlugin)) {
251
+                throw new \Exception('Sabre server plugin ' . $className . ' does not implement the ' . ServerPlugin::class . ' interface');
252
+            }
253
+            return $instance;
254
+        }, $classes);
255
+    }
256
+
257
+    /**
258
+     * @param string[] $classes
259
+     * @return Collection[]
260
+     */
261
+    private function loadSabreCollectionsFromInfoXml(array $classes): array {
262
+        return array_map(function (string $className): Collection {
263
+            $instance = $this->createClass($className);
264
+            if (!($instance instanceof Collection)) {
265
+                throw new \Exception('Sabre collection plugin ' . $className . ' does not implement the ' . Collection::class . ' interface');
266
+            }
267
+            return $instance;
268
+        }, $classes);
269
+    }
270
+
271
+    /**
272
+     * @param string[] $classes
273
+     * @return IAddressBookProvider[]
274
+     */
275
+    private function loadSabreAddressBookPluginsFromInfoXml(array $classes): array {
276
+        return array_map(function (string $className): IAddressBookProvider {
277
+            $instance = $this->createClass($className);
278
+            if (!($instance instanceof IAddressBookProvider)) {
279
+                throw new \Exception('Sabre address book plugin class ' . $className . ' does not implement the ' . IAddressBookProvider::class . ' interface');
280
+            }
281
+            return $instance;
282
+        }, $classes);
283
+    }
284
+
285
+    /**
286
+     * @param string[] $classes
287
+     * @return ICalendarProvider[]
288
+     */
289
+    private function loadSabreCalendarPluginsFromInfoXml(array $classes): array {
290
+        return array_map(function (string $className): ICalendarProvider {
291
+            $instance = $this->createClass($className);
292
+            if (!($instance instanceof ICalendarProvider)) {
293
+                throw new \Exception('Sabre calendar plugin class ' . $className . ' does not implement the ' . ICalendarProvider::class . ' interface');
294
+            }
295
+            return $instance;
296
+        }, $classes);
297
+    }
298 298
 }
Please login to merge, or discard this patch.
apps/files_external/lib/MountConfig.php 1 patch
Indentation   +160 added lines, -160 removed lines patch added patch discarded remove patch
@@ -27,176 +27,176 @@
 block discarded – undo
27 27
  * Class to configure mount.json globally and for users
28 28
  */
29 29
 class MountConfig {
30
-	// TODO: make this class non-static and give it a proper namespace
30
+    // TODO: make this class non-static and give it a proper namespace
31 31
 
32
-	public const MOUNT_TYPE_GLOBAL = 'global';
33
-	public const MOUNT_TYPE_GROUP = 'group';
34
-	public const MOUNT_TYPE_USER = 'user';
35
-	public const MOUNT_TYPE_PERSONAL = 'personal';
32
+    public const MOUNT_TYPE_GLOBAL = 'global';
33
+    public const MOUNT_TYPE_GROUP = 'group';
34
+    public const MOUNT_TYPE_USER = 'user';
35
+    public const MOUNT_TYPE_PERSONAL = 'personal';
36 36
 
37
-	// whether to skip backend test (for unit tests, as this static class is not mockable)
38
-	public static $skipTest = false;
37
+    // whether to skip backend test (for unit tests, as this static class is not mockable)
38
+    public static $skipTest = false;
39 39
 
40
-	public function __construct(
41
-		private UserGlobalStoragesService $userGlobalStorageService,
42
-		private UserStoragesService $userStorageService,
43
-		private GlobalStoragesService $globalStorageService,
44
-	) {
45
-	}
40
+    public function __construct(
41
+        private UserGlobalStoragesService $userGlobalStorageService,
42
+        private UserStoragesService $userStorageService,
43
+        private GlobalStoragesService $globalStorageService,
44
+    ) {
45
+    }
46 46
 
47
-	/**
48
-	 * @param mixed $input
49
-	 * @param string|null $userId
50
-	 * @return mixed
51
-	 * @throws ContainerExceptionInterface
52
-	 * @since 16.0.0
53
-	 */
54
-	public static function substitutePlaceholdersInConfig($input, ?string $userId = null) {
55
-		/** @var BackendService $backendService */
56
-		$backendService = Server::get(BackendService::class);
57
-		/** @var IConfigHandler[] $handlers */
58
-		$handlers = $backendService->getConfigHandlers();
59
-		foreach ($handlers as $handler) {
60
-			if ($handler instanceof UserContext && $userId !== null) {
61
-				$handler->setUserId($userId);
62
-			}
63
-			$input = $handler->handle($input);
64
-		}
65
-		return $input;
66
-	}
47
+    /**
48
+     * @param mixed $input
49
+     * @param string|null $userId
50
+     * @return mixed
51
+     * @throws ContainerExceptionInterface
52
+     * @since 16.0.0
53
+     */
54
+    public static function substitutePlaceholdersInConfig($input, ?string $userId = null) {
55
+        /** @var BackendService $backendService */
56
+        $backendService = Server::get(BackendService::class);
57
+        /** @var IConfigHandler[] $handlers */
58
+        $handlers = $backendService->getConfigHandlers();
59
+        foreach ($handlers as $handler) {
60
+            if ($handler instanceof UserContext && $userId !== null) {
61
+                $handler->setUserId($userId);
62
+            }
63
+            $input = $handler->handle($input);
64
+        }
65
+        return $input;
66
+    }
67 67
 
68
-	/**
69
-	 * Test connecting using the given backend configuration
70
-	 *
71
-	 * @param string $class backend class name
72
-	 * @param array $options backend configuration options
73
-	 * @param boolean $isPersonal
74
-	 * @return int see self::STATUS_*
75
-	 * @throws \Exception
76
-	 */
77
-	public static function getBackendStatus($class, $options) {
78
-		if (self::$skipTest) {
79
-			return StorageNotAvailableException::STATUS_SUCCESS;
80
-		}
81
-		foreach ($options as $key => &$option) {
82
-			if ($key === 'password') {
83
-				// no replacements in passwords
84
-				continue;
85
-			}
86
-			$option = self::substitutePlaceholdersInConfig($option);
87
-		}
88
-		if (class_exists($class)) {
89
-			try {
90
-				/** @var Common $storage */
91
-				$storage = new $class($options);
68
+    /**
69
+     * Test connecting using the given backend configuration
70
+     *
71
+     * @param string $class backend class name
72
+     * @param array $options backend configuration options
73
+     * @param boolean $isPersonal
74
+     * @return int see self::STATUS_*
75
+     * @throws \Exception
76
+     */
77
+    public static function getBackendStatus($class, $options) {
78
+        if (self::$skipTest) {
79
+            return StorageNotAvailableException::STATUS_SUCCESS;
80
+        }
81
+        foreach ($options as $key => &$option) {
82
+            if ($key === 'password') {
83
+                // no replacements in passwords
84
+                continue;
85
+            }
86
+            $option = self::substitutePlaceholdersInConfig($option);
87
+        }
88
+        if (class_exists($class)) {
89
+            try {
90
+                /** @var Common $storage */
91
+                $storage = new $class($options);
92 92
 
93
-				try {
94
-					$result = $storage->test();
95
-					$storage->setAvailability($result);
96
-					if ($result) {
97
-						return StorageNotAvailableException::STATUS_SUCCESS;
98
-					}
99
-				} catch (\Exception $e) {
100
-					$storage->setAvailability(false);
101
-					throw $e;
102
-				}
103
-			} catch (\Exception $exception) {
104
-				Server::get(LoggerInterface::class)->error($exception->getMessage(), ['exception' => $exception, 'app' => 'files_external']);
105
-				throw $exception;
106
-			}
107
-		}
108
-		return StorageNotAvailableException::STATUS_ERROR;
109
-	}
93
+                try {
94
+                    $result = $storage->test();
95
+                    $storage->setAvailability($result);
96
+                    if ($result) {
97
+                        return StorageNotAvailableException::STATUS_SUCCESS;
98
+                    }
99
+                } catch (\Exception $e) {
100
+                    $storage->setAvailability(false);
101
+                    throw $e;
102
+                }
103
+            } catch (\Exception $exception) {
104
+                Server::get(LoggerInterface::class)->error($exception->getMessage(), ['exception' => $exception, 'app' => 'files_external']);
105
+                throw $exception;
106
+            }
107
+        }
108
+        return StorageNotAvailableException::STATUS_ERROR;
109
+    }
110 110
 
111
-	/**
112
-	 * Encrypt passwords in the given config options
113
-	 *
114
-	 * @param array $options mount options
115
-	 * @return array updated options
116
-	 */
117
-	public static function encryptPasswords($options) {
118
-		if (isset($options['password'])) {
119
-			$options['password_encrypted'] = self::encryptPassword($options['password']);
120
-			// do not unset the password, we want to keep the keys order
121
-			// on load... because that's how the UI currently works
122
-			$options['password'] = '';
123
-		}
124
-		return $options;
125
-	}
111
+    /**
112
+     * Encrypt passwords in the given config options
113
+     *
114
+     * @param array $options mount options
115
+     * @return array updated options
116
+     */
117
+    public static function encryptPasswords($options) {
118
+        if (isset($options['password'])) {
119
+            $options['password_encrypted'] = self::encryptPassword($options['password']);
120
+            // do not unset the password, we want to keep the keys order
121
+            // on load... because that's how the UI currently works
122
+            $options['password'] = '';
123
+        }
124
+        return $options;
125
+    }
126 126
 
127
-	/**
128
-	 * Decrypt passwords in the given config options
129
-	 *
130
-	 * @param array $options mount options
131
-	 * @return array updated options
132
-	 */
133
-	public static function decryptPasswords($options) {
134
-		// note: legacy options might still have the unencrypted password in the "password" field
135
-		if (isset($options['password_encrypted'])) {
136
-			$options['password'] = self::decryptPassword($options['password_encrypted']);
137
-			unset($options['password_encrypted']);
138
-		}
139
-		return $options;
140
-	}
127
+    /**
128
+     * Decrypt passwords in the given config options
129
+     *
130
+     * @param array $options mount options
131
+     * @return array updated options
132
+     */
133
+    public static function decryptPasswords($options) {
134
+        // note: legacy options might still have the unencrypted password in the "password" field
135
+        if (isset($options['password_encrypted'])) {
136
+            $options['password'] = self::decryptPassword($options['password_encrypted']);
137
+            unset($options['password_encrypted']);
138
+        }
139
+        return $options;
140
+    }
141 141
 
142
-	/**
143
-	 * Encrypt a single password
144
-	 *
145
-	 * @param string $password plain text password
146
-	 * @return string encrypted password
147
-	 */
148
-	private static function encryptPassword($password) {
149
-		$cipher = self::getCipher();
150
-		$iv = Server::get(ISecureRandom::class)->generate(16);
151
-		$cipher->setIV($iv);
152
-		return base64_encode($iv . $cipher->encrypt($password));
153
-	}
142
+    /**
143
+     * Encrypt a single password
144
+     *
145
+     * @param string $password plain text password
146
+     * @return string encrypted password
147
+     */
148
+    private static function encryptPassword($password) {
149
+        $cipher = self::getCipher();
150
+        $iv = Server::get(ISecureRandom::class)->generate(16);
151
+        $cipher->setIV($iv);
152
+        return base64_encode($iv . $cipher->encrypt($password));
153
+    }
154 154
 
155
-	/**
156
-	 * Decrypts a single password
157
-	 *
158
-	 * @param string $encryptedPassword encrypted password
159
-	 * @return string plain text password
160
-	 */
161
-	private static function decryptPassword($encryptedPassword) {
162
-		$cipher = self::getCipher();
163
-		$binaryPassword = base64_decode($encryptedPassword);
164
-		$iv = substr($binaryPassword, 0, 16);
165
-		$cipher->setIV($iv);
166
-		$binaryPassword = substr($binaryPassword, 16);
167
-		return $cipher->decrypt($binaryPassword);
168
-	}
155
+    /**
156
+     * Decrypts a single password
157
+     *
158
+     * @param string $encryptedPassword encrypted password
159
+     * @return string plain text password
160
+     */
161
+    private static function decryptPassword($encryptedPassword) {
162
+        $cipher = self::getCipher();
163
+        $binaryPassword = base64_decode($encryptedPassword);
164
+        $iv = substr($binaryPassword, 0, 16);
165
+        $cipher->setIV($iv);
166
+        $binaryPassword = substr($binaryPassword, 16);
167
+        return $cipher->decrypt($binaryPassword);
168
+    }
169 169
 
170
-	/**
171
-	 * Returns the encryption cipher
172
-	 *
173
-	 * @return AES
174
-	 */
175
-	private static function getCipher() {
176
-		$cipher = new AES(AES::MODE_CBC);
177
-		$cipher->setKey(Server::get(IConfig::class)->getSystemValue('passwordsalt', null));
178
-		return $cipher;
179
-	}
170
+    /**
171
+     * Returns the encryption cipher
172
+     *
173
+     * @return AES
174
+     */
175
+    private static function getCipher() {
176
+        $cipher = new AES(AES::MODE_CBC);
177
+        $cipher->setKey(Server::get(IConfig::class)->getSystemValue('passwordsalt', null));
178
+        return $cipher;
179
+    }
180 180
 
181
-	/**
182
-	 * Computes a hash based on the given configuration.
183
-	 * This is mostly used to find out whether configurations
184
-	 * are the same.
185
-	 *
186
-	 * @param array $config
187
-	 * @return string
188
-	 */
189
-	public static function makeConfigHash($config) {
190
-		$data = json_encode(
191
-			[
192
-				'c' => $config['backend'],
193
-				'a' => $config['authMechanism'],
194
-				'm' => $config['mountpoint'],
195
-				'o' => $config['options'],
196
-				'p' => $config['priority'] ?? -1,
197
-				'mo' => $config['mountOptions'] ?? [],
198
-			]
199
-		);
200
-		return hash('md5', $data);
201
-	}
181
+    /**
182
+     * Computes a hash based on the given configuration.
183
+     * This is mostly used to find out whether configurations
184
+     * are the same.
185
+     *
186
+     * @param array $config
187
+     * @return string
188
+     */
189
+    public static function makeConfigHash($config) {
190
+        $data = json_encode(
191
+            [
192
+                'c' => $config['backend'],
193
+                'a' => $config['authMechanism'],
194
+                'm' => $config['mountpoint'],
195
+                'o' => $config['options'],
196
+                'p' => $config['priority'] ?? -1,
197
+                'mo' => $config['mountOptions'] ?? [],
198
+            ]
199
+        );
200
+        return hash('md5', $data);
201
+    }
202 202
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Config/ConfigAdapter.php 2 patches
Indentation   +139 added lines, -139 removed lines patch added patch discarded remove patch
@@ -34,143 +34,143 @@
 block discarded – undo
34 34
  * Make the old files_external config work with the new public mount config api
35 35
  */
36 36
 class ConfigAdapter implements IMountProvider, IAuthoritativeMountProvider {
37
-	public function __construct(
38
-		private UserStoragesService $userStoragesService,
39
-		private UserGlobalStoragesService $userGlobalStoragesService,
40
-		private ClockInterface $clock,
41
-	) {
42
-	}
43
-
44
-	/**
45
-	 * @param class-string $class
46
-	 * @return class-string<IObjectStore>
47
-	 * @throws \InvalidArgumentException
48
-	 * @psalm-taint-escape callable
49
-	 */
50
-	private function validateObjectStoreClassString(string $class): string {
51
-		if (!\is_subclass_of($class, IObjectStore::class)) {
52
-			throw new \InvalidArgumentException('Invalid object store');
53
-		}
54
-		return $class;
55
-	}
56
-
57
-	/**
58
-	 * Process storage ready for mounting
59
-	 *
60
-	 * @throws ContainerExceptionInterface
61
-	 */
62
-	private function prepareStorageConfig(StorageConfig &$storage, IUser $user): void {
63
-		foreach ($storage->getBackendOptions() as $option => $value) {
64
-			$storage->setBackendOption($option, MountConfig::substitutePlaceholdersInConfig($value, $user->getUID()));
65
-		}
66
-
67
-		$objectStore = $storage->getBackendOption('objectstore');
68
-		if ($objectStore) {
69
-			$objectClass = $this->validateObjectStoreClassString($objectStore['class']);
70
-			$storage->setBackendOption('objectstore', new $objectClass($objectStore));
71
-		}
72
-
73
-		$storage->getAuthMechanism()->manipulateStorageConfig($storage, $user);
74
-		$storage->getBackend()->manipulateStorageConfig($storage, $user);
75
-	}
76
-
77
-	public function constructStorageForUser(IUser $user, StorageConfig $storage) {
78
-		$this->prepareStorageConfig($storage, $user);
79
-		return $this->constructStorage($storage);
80
-	}
81
-
82
-	/**
83
-	 * Construct the storage implementation
84
-	 *
85
-	 * @param StorageConfig $storageConfig
86
-	 */
87
-	private function constructStorage(StorageConfig $storageConfig): IStorage {
88
-		$class = $storageConfig->getBackend()->getStorageClass();
89
-		if (!is_a($class, IConstructableStorage::class, true)) {
90
-			Server::get(LoggerInterface::class)->warning('Building a storage not implementing IConstructableStorage is deprecated since 31.0.0', ['class' => $class]);
91
-		}
92
-		$storage = new $class($storageConfig->getBackendOptions());
93
-
94
-		// auth mechanism should fire first
95
-		$storage = $storageConfig->getBackend()->wrapStorage($storage);
96
-		$storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);
97
-
98
-		return $storage;
99
-	}
100
-
101
-	/**
102
-	 * Get all mountpoints applicable for the user
103
-	 *
104
-	 * @return IMountPoint[]
105
-	 */
106
-	public function getMountsForUser(IUser $user, IStorageFactory $loader) {
107
-		$this->userStoragesService->setUser($user);
108
-		$this->userGlobalStoragesService->setUser($user);
109
-
110
-		$storageConfigs = $this->userGlobalStoragesService->getAllStoragesForUser();
111
-
112
-		$storages = array_map(function (StorageConfig $storageConfig) use ($user) {
113
-			try {
114
-				return $this->constructStorageForUser($user, $storageConfig);
115
-			} catch (\Exception $e) {
116
-				// propagate exception into filesystem
117
-				return new FailedStorage(['exception' => $e]);
118
-			}
119
-		}, $storageConfigs);
120
-
121
-
122
-		Storage::getGlobalCache()->loadForStorageIds(array_map(function (IStorage $storage) {
123
-			return $storage->getId();
124
-		}, $storages));
125
-
126
-		$availableStorages = array_map(function (IStorage $storage, StorageConfig $storageConfig): IStorage {
127
-			try {
128
-				$availability = $storage->getAvailability();
129
-				if (!$availability['available'] && !Availability::shouldRecheck($availability)) {
130
-					$storage = new FailedStorage([
131
-						'exception' => new StorageNotAvailableException('Storage with mount id ' . $storageConfig->getId() . ' is not available'),
132
-					]);
133
-				}
134
-			} catch (\Exception $e) {
135
-				// propagate exception into filesystem
136
-				$storage = new FailedStorage(['exception' => $e]);
137
-			}
138
-			return $storage;
139
-		}, $storages, $storageConfigs);
140
-
141
-		$mounts = array_map(function (StorageConfig $storageConfig, IStorage $storage) use ($user, $loader) {
142
-			$storage->setOwner($user->getUID());
143
-			if ($storageConfig->getType() === StorageConfig::MOUNT_TYPE_PERSONAL) {
144
-				return new PersonalMount(
145
-					$this->userStoragesService,
146
-					$storageConfig,
147
-					$storageConfig->getId(),
148
-					new KnownMtime([
149
-						'storage' => $storage,
150
-						'clock' => $this->clock,
151
-					]),
152
-					'/' . $user->getUID() . '/files' . $storageConfig->getMountPoint(),
153
-					null,
154
-					$loader,
155
-					$storageConfig->getMountOptions(),
156
-					$storageConfig->getId(),
157
-				);
158
-			} else {
159
-				return new SystemMountPoint(
160
-					$storageConfig,
161
-					$storage,
162
-					'/' . $user->getUID() . '/files' . $storageConfig->getMountPoint(),
163
-					null,
164
-					$loader,
165
-					$storageConfig->getMountOptions(),
166
-					$storageConfig->getId(),
167
-				);
168
-			}
169
-		}, $storageConfigs, $availableStorages);
170
-
171
-		$this->userStoragesService->resetUser();
172
-		$this->userGlobalStoragesService->resetUser();
173
-
174
-		return $mounts;
175
-	}
37
+    public function __construct(
38
+        private UserStoragesService $userStoragesService,
39
+        private UserGlobalStoragesService $userGlobalStoragesService,
40
+        private ClockInterface $clock,
41
+    ) {
42
+    }
43
+
44
+    /**
45
+     * @param class-string $class
46
+     * @return class-string<IObjectStore>
47
+     * @throws \InvalidArgumentException
48
+     * @psalm-taint-escape callable
49
+     */
50
+    private function validateObjectStoreClassString(string $class): string {
51
+        if (!\is_subclass_of($class, IObjectStore::class)) {
52
+            throw new \InvalidArgumentException('Invalid object store');
53
+        }
54
+        return $class;
55
+    }
56
+
57
+    /**
58
+     * Process storage ready for mounting
59
+     *
60
+     * @throws ContainerExceptionInterface
61
+     */
62
+    private function prepareStorageConfig(StorageConfig &$storage, IUser $user): void {
63
+        foreach ($storage->getBackendOptions() as $option => $value) {
64
+            $storage->setBackendOption($option, MountConfig::substitutePlaceholdersInConfig($value, $user->getUID()));
65
+        }
66
+
67
+        $objectStore = $storage->getBackendOption('objectstore');
68
+        if ($objectStore) {
69
+            $objectClass = $this->validateObjectStoreClassString($objectStore['class']);
70
+            $storage->setBackendOption('objectstore', new $objectClass($objectStore));
71
+        }
72
+
73
+        $storage->getAuthMechanism()->manipulateStorageConfig($storage, $user);
74
+        $storage->getBackend()->manipulateStorageConfig($storage, $user);
75
+    }
76
+
77
+    public function constructStorageForUser(IUser $user, StorageConfig $storage) {
78
+        $this->prepareStorageConfig($storage, $user);
79
+        return $this->constructStorage($storage);
80
+    }
81
+
82
+    /**
83
+     * Construct the storage implementation
84
+     *
85
+     * @param StorageConfig $storageConfig
86
+     */
87
+    private function constructStorage(StorageConfig $storageConfig): IStorage {
88
+        $class = $storageConfig->getBackend()->getStorageClass();
89
+        if (!is_a($class, IConstructableStorage::class, true)) {
90
+            Server::get(LoggerInterface::class)->warning('Building a storage not implementing IConstructableStorage is deprecated since 31.0.0', ['class' => $class]);
91
+        }
92
+        $storage = new $class($storageConfig->getBackendOptions());
93
+
94
+        // auth mechanism should fire first
95
+        $storage = $storageConfig->getBackend()->wrapStorage($storage);
96
+        $storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);
97
+
98
+        return $storage;
99
+    }
100
+
101
+    /**
102
+     * Get all mountpoints applicable for the user
103
+     *
104
+     * @return IMountPoint[]
105
+     */
106
+    public function getMountsForUser(IUser $user, IStorageFactory $loader) {
107
+        $this->userStoragesService->setUser($user);
108
+        $this->userGlobalStoragesService->setUser($user);
109
+
110
+        $storageConfigs = $this->userGlobalStoragesService->getAllStoragesForUser();
111
+
112
+        $storages = array_map(function (StorageConfig $storageConfig) use ($user) {
113
+            try {
114
+                return $this->constructStorageForUser($user, $storageConfig);
115
+            } catch (\Exception $e) {
116
+                // propagate exception into filesystem
117
+                return new FailedStorage(['exception' => $e]);
118
+            }
119
+        }, $storageConfigs);
120
+
121
+
122
+        Storage::getGlobalCache()->loadForStorageIds(array_map(function (IStorage $storage) {
123
+            return $storage->getId();
124
+        }, $storages));
125
+
126
+        $availableStorages = array_map(function (IStorage $storage, StorageConfig $storageConfig): IStorage {
127
+            try {
128
+                $availability = $storage->getAvailability();
129
+                if (!$availability['available'] && !Availability::shouldRecheck($availability)) {
130
+                    $storage = new FailedStorage([
131
+                        'exception' => new StorageNotAvailableException('Storage with mount id ' . $storageConfig->getId() . ' is not available'),
132
+                    ]);
133
+                }
134
+            } catch (\Exception $e) {
135
+                // propagate exception into filesystem
136
+                $storage = new FailedStorage(['exception' => $e]);
137
+            }
138
+            return $storage;
139
+        }, $storages, $storageConfigs);
140
+
141
+        $mounts = array_map(function (StorageConfig $storageConfig, IStorage $storage) use ($user, $loader) {
142
+            $storage->setOwner($user->getUID());
143
+            if ($storageConfig->getType() === StorageConfig::MOUNT_TYPE_PERSONAL) {
144
+                return new PersonalMount(
145
+                    $this->userStoragesService,
146
+                    $storageConfig,
147
+                    $storageConfig->getId(),
148
+                    new KnownMtime([
149
+                        'storage' => $storage,
150
+                        'clock' => $this->clock,
151
+                    ]),
152
+                    '/' . $user->getUID() . '/files' . $storageConfig->getMountPoint(),
153
+                    null,
154
+                    $loader,
155
+                    $storageConfig->getMountOptions(),
156
+                    $storageConfig->getId(),
157
+                );
158
+            } else {
159
+                return new SystemMountPoint(
160
+                    $storageConfig,
161
+                    $storage,
162
+                    '/' . $user->getUID() . '/files' . $storageConfig->getMountPoint(),
163
+                    null,
164
+                    $loader,
165
+                    $storageConfig->getMountOptions(),
166
+                    $storageConfig->getId(),
167
+                );
168
+            }
169
+        }, $storageConfigs, $availableStorages);
170
+
171
+        $this->userStoragesService->resetUser();
172
+        $this->userGlobalStoragesService->resetUser();
173
+
174
+        return $mounts;
175
+    }
176 176
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -59,7 +59,7 @@  discard block
 block discarded – undo
59 59
 	 *
60 60
 	 * @throws ContainerExceptionInterface
61 61
 	 */
62
-	private function prepareStorageConfig(StorageConfig &$storage, IUser $user): void {
62
+	private function prepareStorageConfig(StorageConfig & $storage, IUser $user): void {
63 63
 		foreach ($storage->getBackendOptions() as $option => $value) {
64 64
 			$storage->setBackendOption($option, MountConfig::substitutePlaceholdersInConfig($value, $user->getUID()));
65 65
 		}
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
 
110 110
 		$storageConfigs = $this->userGlobalStoragesService->getAllStoragesForUser();
111 111
 
112
-		$storages = array_map(function (StorageConfig $storageConfig) use ($user) {
112
+		$storages = array_map(function(StorageConfig $storageConfig) use ($user) {
113 113
 			try {
114 114
 				return $this->constructStorageForUser($user, $storageConfig);
115 115
 			} catch (\Exception $e) {
@@ -119,16 +119,16 @@  discard block
 block discarded – undo
119 119
 		}, $storageConfigs);
120 120
 
121 121
 
122
-		Storage::getGlobalCache()->loadForStorageIds(array_map(function (IStorage $storage) {
122
+		Storage::getGlobalCache()->loadForStorageIds(array_map(function(IStorage $storage) {
123 123
 			return $storage->getId();
124 124
 		}, $storages));
125 125
 
126
-		$availableStorages = array_map(function (IStorage $storage, StorageConfig $storageConfig): IStorage {
126
+		$availableStorages = array_map(function(IStorage $storage, StorageConfig $storageConfig): IStorage {
127 127
 			try {
128 128
 				$availability = $storage->getAvailability();
129 129
 				if (!$availability['available'] && !Availability::shouldRecheck($availability)) {
130 130
 					$storage = new FailedStorage([
131
-						'exception' => new StorageNotAvailableException('Storage with mount id ' . $storageConfig->getId() . ' is not available'),
131
+						'exception' => new StorageNotAvailableException('Storage with mount id '.$storageConfig->getId().' is not available'),
132 132
 					]);
133 133
 				}
134 134
 			} catch (\Exception $e) {
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
 			return $storage;
139 139
 		}, $storages, $storageConfigs);
140 140
 
141
-		$mounts = array_map(function (StorageConfig $storageConfig, IStorage $storage) use ($user, $loader) {
141
+		$mounts = array_map(function(StorageConfig $storageConfig, IStorage $storage) use ($user, $loader) {
142 142
 			$storage->setOwner($user->getUID());
143 143
 			if ($storageConfig->getType() === StorageConfig::MOUNT_TYPE_PERSONAL) {
144 144
 				return new PersonalMount(
@@ -149,7 +149,7 @@  discard block
 block discarded – undo
149 149
 						'storage' => $storage,
150 150
 						'clock' => $this->clock,
151 151
 					]),
152
-					'/' . $user->getUID() . '/files' . $storageConfig->getMountPoint(),
152
+					'/'.$user->getUID().'/files'.$storageConfig->getMountPoint(),
153 153
 					null,
154 154
 					$loader,
155 155
 					$storageConfig->getMountOptions(),
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
 				return new SystemMountPoint(
160 160
 					$storageConfig,
161 161
 					$storage,
162
-					'/' . $user->getUID() . '/files' . $storageConfig->getMountPoint(),
162
+					'/'.$user->getUID().'/files'.$storageConfig->getMountPoint(),
163 163
 					null,
164 164
 					$loader,
165 165
 					$storageConfig->getMountOptions(),
Please login to merge, or discard this patch.
apps/files_external/lib/AppInfo/Application.php 1 patch
Indentation   +98 added lines, -98 removed lines patch added patch discarded remove patch
@@ -65,102 +65,102 @@
 block discarded – undo
65 65
  * @package OCA\Files_External\AppInfo
66 66
  */
67 67
 class Application extends App implements IBackendProvider, IAuthMechanismProvider, IBootstrap {
68
-	public const APP_ID = 'files_external';
69
-
70
-	/**
71
-	 * Application constructor.
72
-	 *
73
-	 * @throws ContainerExceptionInterface
74
-	 */
75
-	public function __construct(array $urlParams = []) {
76
-		parent::__construct(self::APP_ID, $urlParams);
77
-	}
78
-
79
-	public function register(IRegistrationContext $context): void {
80
-		$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
81
-		$context->registerEventListener(GroupDeletedEvent::class, GroupDeletedListener::class);
82
-		$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalListener::class);
83
-		$context->registerEventListener(StorageCreatedEvent::class, MountCacheService::class);
84
-		$context->registerEventListener(StorageDeletedEvent::class, MountCacheService::class);
85
-		$context->registerEventListener(StorageUpdatedEvent::class, MountCacheService::class);
86
-		$context->registerEventListener(BeforeGroupDeletedEvent::class, MountCacheService::class);
87
-		$context->registerEventListener(UserCreatedEvent::class, MountCacheService::class);
88
-		$context->registerEventListener(UserAddedEvent::class, MountCacheService::class);
89
-		$context->registerEventListener(UserRemovedEvent::class, MountCacheService::class);
90
-		$context->registerEventListener(PostLoginEvent::class, MountCacheService::class);
91
-
92
-		$context->registerConfigLexicon(ConfigLexicon::class);
93
-	}
94
-
95
-	public function boot(IBootContext $context): void {
96
-		$context->injectFn(function (IMountProviderCollection $mountProviderCollection, ConfigAdapter $configAdapter): void {
97
-			$mountProviderCollection->registerProvider($configAdapter);
98
-		});
99
-		$context->injectFn(function (BackendService $backendService, UserPlaceholderHandler $userConfigHandler): void {
100
-			$backendService->registerBackendProvider($this);
101
-			$backendService->registerAuthMechanismProvider($this);
102
-			$backendService->registerConfigHandler('user', function () use ($userConfigHandler) {
103
-				return $userConfigHandler;
104
-			});
105
-		});
106
-	}
107
-
108
-	/**
109
-	 * @{inheritdoc}
110
-	 */
111
-	public function getBackends() {
112
-		$container = $this->getContainer();
113
-
114
-		$backends = [
115
-			$container->get(Local::class),
116
-			$container->get(FTP::class),
117
-			$container->get(DAV::class),
118
-			$container->get(OwnCloud::class),
119
-			$container->get(SFTP::class),
120
-			$container->get(AmazonS3::class),
121
-			$container->get(Swift::class),
122
-			$container->get(SFTP_Key::class),
123
-			$container->get(SMB::class),
124
-			$container->get(SMB_OC::class),
125
-		];
126
-
127
-		return $backends;
128
-	}
129
-
130
-	/**
131
-	 * @{inheritdoc}
132
-	 */
133
-	public function getAuthMechanisms() {
134
-		$container = $this->getContainer();
135
-
136
-		return [
137
-			// AuthMechanism::SCHEME_NULL mechanism
138
-			$container->get(NullMechanism::class),
139
-
140
-			// AuthMechanism::SCHEME_BUILTIN mechanism
141
-			$container->get(Builtin::class),
142
-
143
-			// AuthMechanism::SCHEME_PASSWORD mechanisms
144
-			$container->get(Password::class),
145
-			$container->get(SessionCredentials::class),
146
-			$container->get(LoginCredentials::class),
147
-			$container->get(UserProvided::class),
148
-			$container->get(GlobalAuth::class),
149
-			$container->get(UserGlobalAuth::class),
150
-
151
-			// AuthMechanism::SCHEME_PUBLICKEY mechanisms
152
-			$container->get(RSA::class),
153
-			$container->get(RSAPrivateKey::class),
154
-
155
-			// AuthMechanism::SCHEME_OPENSTACK mechanisms
156
-			$container->get(OpenStackV2::class),
157
-			$container->get(OpenStackV3::class),
158
-			$container->get(Rackspace::class),
159
-
160
-			// Specialized mechanisms
161
-			$container->get(AccessKey::class),
162
-			$container->get(KerberosAuth::class),
163
-			$container->get(KerberosApacheAuth::class),
164
-		];
165
-	}
68
+    public const APP_ID = 'files_external';
69
+
70
+    /**
71
+     * Application constructor.
72
+     *
73
+     * @throws ContainerExceptionInterface
74
+     */
75
+    public function __construct(array $urlParams = []) {
76
+        parent::__construct(self::APP_ID, $urlParams);
77
+    }
78
+
79
+    public function register(IRegistrationContext $context): void {
80
+        $context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
81
+        $context->registerEventListener(GroupDeletedEvent::class, GroupDeletedListener::class);
82
+        $context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalListener::class);
83
+        $context->registerEventListener(StorageCreatedEvent::class, MountCacheService::class);
84
+        $context->registerEventListener(StorageDeletedEvent::class, MountCacheService::class);
85
+        $context->registerEventListener(StorageUpdatedEvent::class, MountCacheService::class);
86
+        $context->registerEventListener(BeforeGroupDeletedEvent::class, MountCacheService::class);
87
+        $context->registerEventListener(UserCreatedEvent::class, MountCacheService::class);
88
+        $context->registerEventListener(UserAddedEvent::class, MountCacheService::class);
89
+        $context->registerEventListener(UserRemovedEvent::class, MountCacheService::class);
90
+        $context->registerEventListener(PostLoginEvent::class, MountCacheService::class);
91
+
92
+        $context->registerConfigLexicon(ConfigLexicon::class);
93
+    }
94
+
95
+    public function boot(IBootContext $context): void {
96
+        $context->injectFn(function (IMountProviderCollection $mountProviderCollection, ConfigAdapter $configAdapter): void {
97
+            $mountProviderCollection->registerProvider($configAdapter);
98
+        });
99
+        $context->injectFn(function (BackendService $backendService, UserPlaceholderHandler $userConfigHandler): void {
100
+            $backendService->registerBackendProvider($this);
101
+            $backendService->registerAuthMechanismProvider($this);
102
+            $backendService->registerConfigHandler('user', function () use ($userConfigHandler) {
103
+                return $userConfigHandler;
104
+            });
105
+        });
106
+    }
107
+
108
+    /**
109
+     * @{inheritdoc}
110
+     */
111
+    public function getBackends() {
112
+        $container = $this->getContainer();
113
+
114
+        $backends = [
115
+            $container->get(Local::class),
116
+            $container->get(FTP::class),
117
+            $container->get(DAV::class),
118
+            $container->get(OwnCloud::class),
119
+            $container->get(SFTP::class),
120
+            $container->get(AmazonS3::class),
121
+            $container->get(Swift::class),
122
+            $container->get(SFTP_Key::class),
123
+            $container->get(SMB::class),
124
+            $container->get(SMB_OC::class),
125
+        ];
126
+
127
+        return $backends;
128
+    }
129
+
130
+    /**
131
+     * @{inheritdoc}
132
+     */
133
+    public function getAuthMechanisms() {
134
+        $container = $this->getContainer();
135
+
136
+        return [
137
+            // AuthMechanism::SCHEME_NULL mechanism
138
+            $container->get(NullMechanism::class),
139
+
140
+            // AuthMechanism::SCHEME_BUILTIN mechanism
141
+            $container->get(Builtin::class),
142
+
143
+            // AuthMechanism::SCHEME_PASSWORD mechanisms
144
+            $container->get(Password::class),
145
+            $container->get(SessionCredentials::class),
146
+            $container->get(LoginCredentials::class),
147
+            $container->get(UserProvided::class),
148
+            $container->get(GlobalAuth::class),
149
+            $container->get(UserGlobalAuth::class),
150
+
151
+            // AuthMechanism::SCHEME_PUBLICKEY mechanisms
152
+            $container->get(RSA::class),
153
+            $container->get(RSAPrivateKey::class),
154
+
155
+            // AuthMechanism::SCHEME_OPENSTACK mechanisms
156
+            $container->get(OpenStackV2::class),
157
+            $container->get(OpenStackV3::class),
158
+            $container->get(Rackspace::class),
159
+
160
+            // Specialized mechanisms
161
+            $container->get(AccessKey::class),
162
+            $container->get(KerberosAuth::class),
163
+            $container->get(KerberosApacheAuth::class),
164
+        ];
165
+    }
166 166
 }
Please login to merge, or discard this patch.
apps/files/lib/Collaboration/Resources/ResourceProvider.php 1 patch
Indentation   +76 added lines, -76 removed lines patch added patch discarded remove patch
@@ -18,91 +18,91 @@
 block discarded – undo
18 18
 use OCP\IUser;
19 19
 
20 20
 class ResourceProvider implements IProvider {
21
-	public const RESOURCE_TYPE = 'file';
21
+    public const RESOURCE_TYPE = 'file';
22 22
 
23
-	protected array $nodes = [];
23
+    protected array $nodes = [];
24 24
 
25
-	public function __construct(
26
-		protected IRootFolder $rootFolder,
27
-		private IPreview $preview,
28
-		private IURLGenerator $urlGenerator,
29
-	) {
30
-	}
25
+    public function __construct(
26
+        protected IRootFolder $rootFolder,
27
+        private IPreview $preview,
28
+        private IURLGenerator $urlGenerator,
29
+    ) {
30
+    }
31 31
 
32
-	private function getNode(IResource $resource): ?Node {
33
-		if (isset($this->nodes[(int)$resource->getId()])) {
34
-			return $this->nodes[(int)$resource->getId()];
35
-		}
36
-		$node = $this->rootFolder->getFirstNodeById((int)$resource->getId());
37
-		if ($node) {
38
-			$this->nodes[(int)$resource->getId()] = $node;
39
-			return $this->nodes[(int)$resource->getId()];
40
-		}
41
-		return null;
42
-	}
32
+    private function getNode(IResource $resource): ?Node {
33
+        if (isset($this->nodes[(int)$resource->getId()])) {
34
+            return $this->nodes[(int)$resource->getId()];
35
+        }
36
+        $node = $this->rootFolder->getFirstNodeById((int)$resource->getId());
37
+        if ($node) {
38
+            $this->nodes[(int)$resource->getId()] = $node;
39
+            return $this->nodes[(int)$resource->getId()];
40
+        }
41
+        return null;
42
+    }
43 43
 
44
-	/**
45
-	 * @param IResource $resource
46
-	 * @return array
47
-	 * @since 16.0.0
48
-	 */
49
-	public function getResourceRichObject(IResource $resource): array {
50
-		if (isset($this->nodes[(int)$resource->getId()])) {
51
-			$node = $this->nodes[(int)$resource->getId()]->getPath();
52
-		} else {
53
-			$node = $this->getNode($resource);
54
-		}
44
+    /**
45
+     * @param IResource $resource
46
+     * @return array
47
+     * @since 16.0.0
48
+     */
49
+    public function getResourceRichObject(IResource $resource): array {
50
+        if (isset($this->nodes[(int)$resource->getId()])) {
51
+            $node = $this->nodes[(int)$resource->getId()]->getPath();
52
+        } else {
53
+            $node = $this->getNode($resource);
54
+        }
55 55
 
56
-		if ($node instanceof Node) {
57
-			$link = $this->urlGenerator->linkToRouteAbsolute(
58
-				'files.viewcontroller.showFile',
59
-				['fileid' => $resource->getId()]
60
-			);
61
-			return [
62
-				'type' => 'file',
63
-				'id' => $resource->getId(),
64
-				'name' => $node->getName(),
65
-				'path' => $node->getInternalPath(),
66
-				'link' => $link,
67
-				'mimetype' => $node->getMimetype(),
68
-				'preview-available' => $this->preview->isAvailable($node),
69
-			];
70
-		}
56
+        if ($node instanceof Node) {
57
+            $link = $this->urlGenerator->linkToRouteAbsolute(
58
+                'files.viewcontroller.showFile',
59
+                ['fileid' => $resource->getId()]
60
+            );
61
+            return [
62
+                'type' => 'file',
63
+                'id' => $resource->getId(),
64
+                'name' => $node->getName(),
65
+                'path' => $node->getInternalPath(),
66
+                'link' => $link,
67
+                'mimetype' => $node->getMimetype(),
68
+                'preview-available' => $this->preview->isAvailable($node),
69
+            ];
70
+        }
71 71
 
72
-		throw new ResourceException('File not found');
73
-	}
72
+        throw new ResourceException('File not found');
73
+    }
74 74
 
75
-	/**
76
-	 * Can a user/guest access the collection
77
-	 *
78
-	 * @param IResource $resource
79
-	 * @param IUser $user
80
-	 * @return bool
81
-	 * @since 16.0.0
82
-	 */
83
-	public function canAccessResource(IResource $resource, ?IUser $user = null): bool {
84
-		if (!$user instanceof IUser) {
85
-			return false;
86
-		}
75
+    /**
76
+     * Can a user/guest access the collection
77
+     *
78
+     * @param IResource $resource
79
+     * @param IUser $user
80
+     * @return bool
81
+     * @since 16.0.0
82
+     */
83
+    public function canAccessResource(IResource $resource, ?IUser $user = null): bool {
84
+        if (!$user instanceof IUser) {
85
+            return false;
86
+        }
87 87
 
88
-		$userFolder = $this->rootFolder->getUserFolder($user->getUID());
89
-		$node = $userFolder->getById((int)$resource->getId());
88
+        $userFolder = $this->rootFolder->getUserFolder($user->getUID());
89
+        $node = $userFolder->getById((int)$resource->getId());
90 90
 
91
-		if ($node) {
92
-			$this->nodes[(int)$resource->getId()] = $node;
93
-			return true;
94
-		}
91
+        if ($node) {
92
+            $this->nodes[(int)$resource->getId()] = $node;
93
+            return true;
94
+        }
95 95
 
96
-		return false;
97
-	}
96
+        return false;
97
+    }
98 98
 
99
-	/**
100
-	 * Get the resource type of the provider
101
-	 *
102
-	 * @return string
103
-	 * @since 16.0.0
104
-	 */
105
-	public function getType(): string {
106
-		return self::RESOURCE_TYPE;
107
-	}
99
+    /**
100
+     * Get the resource type of the provider
101
+     *
102
+     * @return string
103
+     * @since 16.0.0
104
+     */
105
+    public function getType(): string {
106
+        return self::RESOURCE_TYPE;
107
+    }
108 108
 }
Please login to merge, or discard this patch.