Passed
Push — master ( 45118d...84de7a )
by Roeland
18:24 queued 02:59
created

PluginManager::populate()   B

Complexity

Conditions 9
Paths 19

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 19
nc 19
nop 0
dl 0
loc 30
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud GmbH.
7
 *
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Georg Ehrke <[email protected]>
10
 * @author Julius Härtl <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Vincent Petry <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OCA\DAV\AppInfo;
31
32
use OC\ServerContainer;
33
use OCA\DAV\CalDAV\Integration\ICalendarProvider;
34
use OCA\DAV\CardDAV\Integration\IAddressBookProvider;
35
use OCP\App\IAppManager;
36
use OCP\AppFramework\QueryException;
37
use Sabre\DAV\Collection;
38
use Sabre\DAV\ServerPlugin;
39
use function array_map;
40
use function class_exists;
41
use function is_array;
42
43
/**
44
 * Manager for DAV plugins from apps, used to register them
45
 * to the Sabre server.
46
 */
47
class PluginManager {
48
49
	/**
50
	 * @var ServerContainer
51
	 */
52
	private $container;
53
54
	/**
55
	 * @var IAppManager
56
	 */
57
	private $appManager;
58
59
	/**
60
	 * App plugins
61
	 *
62
	 * @var ServerPlugin[]
63
	 */
64
	private $plugins = [];
65
66
	/**
67
	 * App collections
68
	 *
69
	 * @var Collection[]
70
	 */
71
	private $collections = [];
72
73
	/**
74
	 * Address book plugins
75
	 *
76
	 * @var IAddressBookProvider[]
77
	 */
78
	private $addressBookPlugins = [];
79
80
	/**
81
	 * Calendar plugins
82
	 *
83
	 * @var ICalendarProvider[]
84
	 */
85
	private $calendarPlugins = [];
86
87
	/** @var bool */
88
	private $populated = false;
89
90
	/**
91
	 * Contstruct a PluginManager
92
	 *
93
	 * @param ServerContainer $container server container for resolving plugin classes
94
	 * @param IAppManager $appManager app manager to loading apps and their info
95
	 */
96
	public function __construct(ServerContainer $container, IAppManager $appManager) {
97
		$this->container = $container;
98
		$this->appManager = $appManager;
99
	}
100
101
	/**
102
	 * Returns an array of app-registered plugins
103
	 *
104
	 * @return ServerPlugin[]
105
	 */
106
	public function getAppPlugins() {
107
		$this->populate();
108
		return $this->plugins;
109
	}
110
111
	/**
112
	 * Returns an array of app-registered collections
113
	 *
114
	 * @return array
115
	 */
116
	public function getAppCollections() {
117
		$this->populate();
118
		return $this->collections;
119
	}
120
121
	/**
122
	 * @return IAddressBookProvider[]
123
	 */
124
	public function getAddressBookPlugins(): array {
125
		$this->populate();
126
		return $this->addressBookPlugins;
127
	}
128
129
	/**
130
	 * Returns an array of app-registered calendar plugins
131
	 *
132
	 * @return ICalendarProvider[]
133
	 */
134
	public function getCalendarPlugins():array {
135
		$this->populate();
136
		return $this->calendarPlugins;
137
	}
138
139
	/**
140
	 * Retrieve plugin and collection list and populate attributes
141
	 */
142
	private function populate(): void {
143
		if ($this->populated) {
144
			return;
145
		}
146
		$this->populated = true;
147
148
		foreach ($this->appManager->getInstalledApps() as $app) {
149
			// load plugins and collections from info.xml
150
			$info = $this->appManager->getAppInfo($app);
151
			if (!isset($info['types']) || !in_array('dav', $info['types'], true)) {
152
				continue;
153
			}
154
			$plugins = $this->loadSabrePluginsFromInfoXml($this->extractPluginList($info));
155
			foreach ($plugins as $plugin) {
156
				$this->plugins[] = $plugin;
157
			}
158
159
			$collections = $this->loadSabreCollectionsFromInfoXml($this->extractCollectionList($info));
160
			foreach ($collections as $collection) {
161
				$this->collections[] = $collection;
162
			}
163
164
			$addresbookPlugins = $this->loadSabreAddressBookPluginsFromInfoXml($this->extractAddressBookPluginList($info));
165
			foreach ($addresbookPlugins as $addresbookPlugin) {
166
				$this->addressBookPlugins[] = $addresbookPlugin;
167
			}
168
169
			$calendarPlugins = $this->loadSabreCalendarPluginsFromInfoXml($this->extractCalendarPluginList($info));
170
			foreach ($calendarPlugins as $calendarPlugin) {
171
				$this->calendarPlugins[] = $calendarPlugin;
172
			}
173
		}
174
	}
175
176
	/**
177
	 * @param array $array
178
	 * @return string[]
179
	 */
180
	private function extractPluginList(array $array): array {
181
		if (isset($array['sabre']) && is_array($array['sabre'])) {
182
			if (isset($array['sabre']['plugins']) && is_array($array['sabre']['plugins'])) {
183
				if (isset($array['sabre']['plugins']['plugin'])) {
184
					$items = $array['sabre']['plugins']['plugin'];
185
					if (!is_array($items)) {
186
						$items = [$items];
187
					}
188
					return $items;
189
				}
190
			}
191
		}
192
		return [];
193
	}
194
195
	/**
196
	 * @param array $array
197
	 * @return string[]
198
	 */
199
	private function extractCollectionList(array $array): array {
200
		if (isset($array['sabre']) && is_array($array['sabre'])) {
201
			if (isset($array['sabre']['collections']) && is_array($array['sabre']['collections'])) {
202
				if (isset($array['sabre']['collections']['collection'])) {
203
					$items = $array['sabre']['collections']['collection'];
204
					if (!is_array($items)) {
205
						$items = [$items];
206
					}
207
					return $items;
208
				}
209
			}
210
		}
211
		return [];
212
	}
213
214
	/**
215
	 * @param array $array
216
	 * @return string[]
217
	 */
218
	private function extractAddressBookPluginList(array $array): array {
219
		if (!isset($array['sabre']) || !is_array($array['sabre'])) {
220
			return [];
221
		}
222
		if (!isset($array['sabre']['address-book-plugins']) || !is_array($array['sabre']['address-book-plugins'])) {
223
			return [];
224
		}
225
		if (!isset($array['sabre']['address-book-plugins']['plugin'])) {
226
			return [];
227
		}
228
229
		$items = $array['sabre']['address-book-plugins']['plugin'];
230
		if (!is_array($items)) {
231
			$items = [$items];
232
		}
233
		return $items;
234
	}
235
236
	/**
237
	 * @param array $array
238
	 * @return string[]
239
	 */
240
	private function extractCalendarPluginList(array $array): array {
241
		if (isset($array['sabre']) && is_array($array['sabre'])) {
242
			if (isset($array['sabre']['calendar-plugins']) && is_array($array['sabre']['calendar-plugins'])) {
243
				if (isset($array['sabre']['calendar-plugins']['plugin'])) {
244
					$items = $array['sabre']['calendar-plugins']['plugin'];
245
					if (!is_array($items)) {
246
						$items = [$items];
247
					}
248
					return $items;
249
				}
250
			}
251
		}
252
		return [];
253
	}
254
255
	private function createClass(string $className): object {
256
		try {
257
			return $this->container->query($className);
258
		} catch (QueryException $e) {
259
			if (class_exists($className)) {
260
				return new $className();
261
			}
262
		}
263
264
		throw new \Exception('Could not load ' . $className, 0, $e);
265
	}
266
267
268
	/**
269
	 * @param string[] $classes
270
	 * @return ServerPlugin[]
271
	 * @throws \Exception
272
	 */
273
	private function loadSabrePluginsFromInfoXml(array $classes): array {
274
		return array_map(function (string $className): ServerPlugin {
275
			$instance = $this->createClass($className);
276
			if (!($instance instanceof ServerPlugin)) {
277
				throw new \Exception('Sabre server plugin ' . $className . ' does not implement the ' . ServerPlugin::class . ' interface');
278
			}
279
			return $instance;
280
		}, $classes);
281
	}
282
283
	/**
284
	 * @param string[] $classes
285
	 * @return Collection[]
286
	 */
287
	private function loadSabreCollectionsFromInfoXml(array $classes): array {
288
		return array_map(function (string $className): Collection {
289
			$instance = $this->createClass($className);
290
			if (!($instance instanceof Collection)) {
291
				throw new \Exception('Sabre collection plugin ' . $className . ' does not implement the ' . Collection::class . ' interface');
292
			}
293
			return $instance;
294
		}, $classes);
295
	}
296
297
	/**
298
	 * @param string[] $classes
299
	 * @return IAddressBookProvider[]
300
	 */
301
	private function loadSabreAddressBookPluginsFromInfoXml(array $classes): array {
302
		return array_map(function (string $className): IAddressBookProvider {
303
			$instance = $this->createClass($className);
304
			if (!($instance instanceof IAddressBookProvider)) {
305
				throw new \Exception('Sabre address book plugin class ' . $className . ' does not implement the ' . IAddressBookProvider::class . ' interface');
306
			}
307
			return $instance;
308
		}, $classes);
309
	}
310
311
	/**
312
	 * @param string[] $classes
313
	 * @return ICalendarProvider[]
314
	 */
315
	private function loadSabreCalendarPluginsFromInfoXml(array $classes): array {
316
		return array_map(function (string $className): ICalendarProvider {
317
			$instance = $this->createClass($className);
318
			if (!($instance instanceof ICalendarProvider)) {
319
				throw new \Exception('Sabre calendar plugin class ' . $className . ' does not implement the ' . ICalendarProvider::class . ' interface');
320
			}
321
			return $instance;
322
		}, $classes);
323
	}
324
}
325