Completed
Push — master ( 339ba8...aff2db )
by Maxence
11:17 queued 05:10
created

ProviderService::loadProvidersFromList()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.6666
c 0
b 0
f 0
cc 7
nc 5
nop 2
1
<?php
2
declare(strict_types=1);
3
4
5
/**
6
 * FullTextSearch - Full text search framework for Nextcloud
7
 *
8
 * This file is licensed under the Affero General Public License version 3 or
9
 * later. See the COPYING file.
10
 *
11
 * @author Maxence Lange <[email protected]>
12
 * @copyright 2018
13
 * @license GNU AGPL version 3 or any later version
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
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
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 */
29
30
31
namespace OCA\FullTextSearch\Service;
32
33
34
use Exception;
35
use OC\App\AppManager;
36
use OCA\FullTextSearch\AppInfo\Application;
37
use OCA\FullTextSearch\Exceptions\ProviderDoesNotExistException;
38
use OCA\FullTextSearch\Exceptions\ProviderIsNotCompatibleException;
39
use OCA\FullTextSearch\Exceptions\ProviderIsNotUniqueException;
40
use OCA\FullTextSearch\Exceptions\ProviderOptionsDoesNotExistException;
41
use OCA\FullTextSearch\Model\ProviderWrapper;
42
use OCP\AppFramework\QueryException;
43
use OCP\FullTextSearch\IFullTextSearchProvider;
44
use OCP\FullTextSearch\Service\IProviderService;
45
use OCP\Util;
46
47
48
/**
49
 * Class ProviderService
50
 *
51
 * @package OCA\FullTextSearch\Service
52
 */
53
class ProviderService implements IProviderService {
54
55
56
	/** @var AppManager */
57
	private $appManager;
58
59
	/** @var ConfigService */
60
	private $configService;
61
62
	/** @var MiscService */
63
	private $miscService;
64
65
	/** @var ProviderWrapper[] */
66
	private $providers = [];
67
68
	/** @var bool */
69
	private $providersLoaded = false;
70
71
72
	/**
73
	 * ProviderService constructor.
74
	 *
75
	 * @param AppManager $appManager
76
	 * @param ConfigService $configService
77
	 * @param MiscService $miscService
78
	 */
79
	public function __construct(
80
		AppManager $appManager, ConfigService $configService, MiscService $miscService
81
	) {
82
		$this->appManager = $appManager;
83
		$this->configService = $configService;
84
		$this->miscService = $miscService;
85
	}
86
87
88
	/**
89
	 * Load all FullTextSearchProviders set in any info.xml file
90
	 *
91
	 * @throws Exception
92
	 */
93
	private function loadProviders() {
94
		if ($this->providersLoaded) {
95
			return;
96
		}
97
98
		try {
99
			$apps = $this->appManager->getInstalledApps();
100
			foreach ($apps as $appId) {
101
				$this->loadProvidersFromApp($appId);
102
			}
103
		} catch (Exception $e) {
104
			$this->miscService->log($e->getMessage());
105
		}
106
107
		$this->providersLoaded = true;
108
	}
109
110
111
	/**
112
	 * @param string $appId
113
	 * @param string $providerId
114
	 *
115
	 * @throws ProviderIsNotCompatibleException
116
	 * @throws ProviderIsNotUniqueException
117
	 * @throws QueryException
118
	 */
119
	public function loadProvider(string $appId, string $providerId) {
120
121
		$provider = \OC::$server->query((string)$providerId);
122
		if (!($provider instanceof IFullTextSearchProvider)) {
0 ignored issues
show
Bug introduced by
The class OCP\FullTextSearch\IFullTextSearchProvider does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
123
			throw new ProviderIsNotCompatibleException(
124
				$providerId . ' is not a compatible IFullTextSearchProvider'
125
			);
126
		}
127
128
		$this->providerIdMustBeUnique($provider);
129
130
		try {
131
			$provider->loadProvider();
132
			$wrapper = new ProviderWrapper($appId, $provider);
133
			$wrapper->setVersion($this->configService->getAppVersion($appId));
134
			$this->providers[] = $wrapper;
135
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
136
		}
137
	}
138
139
140
	/**
141
	 * @return ProviderWrapper[]
142
	 * @throws Exception
143
	 */
144
	public function getProviders(): array {
145
		$this->loadProviders();
146
147
		return $this->providers;
148
	}
149
150
	/**
151
	 * @return IFullTextSearchProvider[]
152
	 * @throws Exception
153
	 */
154
	public function getConfiguredProviders(): array {
155
		$this->loadProviders();
156
157
		$providers = [];
158
		foreach ($this->providers as $providerWrapper) {
159
			$provider = $providerWrapper->getProvider();
160
			if ($this->isProviderIndexed($provider->getId())) {
161
				$providers[] = $provider;
162
			}
163
		}
164
165
		return $providers;
166
	}
167
168
169
	/**
170
	 * @param array $providerList
171
	 *
172
	 * @return IFullTextSearchProvider[]
173
	 * @throws Exception
174
	 * @throws ProviderDoesNotExistException
175
	 */
176
	public function getFilteredProviders(array $providerList): array {
177
		$this->loadProviders();
178
179
		$providers = $this->getConfiguredProviders();
180
		if (in_array('all', $providerList)) {
181
			return $providers;
182
		}
183
184
		$ret = [];
185
		foreach ($providerList as $providerId) {
186
			if ($this->isProviderIndexed($providerId)) {
187
				$providerWrapper = $this->getProvider($providerId);
188
				$ret[] = $providerWrapper->getProvider();
189
			}
190
		}
191
192
		return $ret;
193
	}
194
195
196
	/**
197
	 * @param string $providerId
198
	 *
199
	 * @return ProviderWrapper
200
	 * @throws Exception
201
	 * @throws ProviderDoesNotExistException
202
	 */
203
	public function getProvider(string $providerId): ProviderWrapper {
204
205
		$providers = $this->getProviders();
206
		foreach ($providers as $providerWrapper) {
207
			$provider = $providerWrapper->getProvider();
208
			if ($provider->getId() === $providerId) {
209
				return $providerWrapper;
210
			}
211
		}
212
213
		throw new ProviderDoesNotExistException('Provider \'' . $providerId . '\' does not exist');
214
	}
215
216
217
	/**
218
	 * @param string $providerId
219
	 *
220
	 * @return bool
221
	 */
222
	public function isProviderIndexed(string $providerId): bool {
223
		try {
224
			$indexed = $this->configService->getProviderOptions(
225
				$providerId, ConfigService::PROVIDER_INDEXED
226
			);
227
		} catch (ProviderOptionsDoesNotExistException $e) {
228
			return false;
229
		}
230
231
		if ($indexed === '1') {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return $indexed === '1';.
Loading history...
232
			return true;
233
		}
234
235
		return false;
236
237
	}
238
239
240
	/**
241
	 * @param IFullTextSearchProvider $provider
242
	 * @param bool $boolean
243
	 */
244
	public function setProviderAsIndexed(IFullTextSearchProvider $provider, bool $boolean) {
245
		$this->configService->setProviderOptions(
246
			$provider->getId(), ConfigService::PROVIDER_INDEXED, (($boolean) ? '1' : '0')
247
		);
248
	}
249
250
251
	/**
252
	 *
253
	 */
254
	public function setProvidersAsNotIndexed() {
255
		$this->configService->resetProviderOptions(ConfigService::PROVIDER_INDEXED);
256
	}
257
258
259
	/**
260
	 * @param string $appId
261
	 *
262
	 * @throws ProviderIsNotCompatibleException
263
	 * @throws ProviderIsNotUniqueException
264
	 * @throws QueryException
265
	 */
266
	private function loadProvidersFromApp(string $appId) {
267
		$appInfo = $this->appManager->getAppInfo($appId);
268
		if (!is_array($appInfo) || !array_key_exists('fulltextsearch', $appInfo)
269
			|| !is_array($appInfo['fulltextsearch'])
270
			|| !key_exists('provider', $appInfo['fulltextsearch'])) {
271
			return;
272
		}
273
274
		$providers = $appInfo['fulltextsearch']['provider'];
275
		if (!is_array($providers)) {
276
			$providers = [$providers];
277
		}
278
279
		$this->loadProvidersFromList($appId, $providers);
280
	}
281
282
283
	/**
284
	 * @param string $appId
285
	 * @param array $providers
286
	 *
287
	 * @throws ProviderIsNotCompatibleException
288
	 * @throws ProviderIsNotUniqueException
289
	 * @throws QueryException
290
	 */
291
	private function loadProvidersFromList(string $appId, array $providers) {
292
		$version = $this->configService->getCloudVersion();
293
		foreach ($providers AS $provider) {
294
			if (is_array($provider)) {
295
				$attributes = $provider['@attributes'];
296
				if (array_key_exists('min-version', $attributes)
297
					&& $version < (int)$attributes['min-version']) {
298
					continue;
299
				}
300
				if (array_key_exists('max-version', $attributes)
301
					&& $version > (int)$attributes['max-version']) {
302
					continue;
303
				}
304
305
				$provider = $provider['@value'];
306
			}
307
308
			$this->loadProvider($appId, $provider);
309
		}
310
	}
311
312
313
	/**
314
	 * @param IFullTextSearchProvider $provider
315
	 *
316
	 * @throws ProviderIsNotUniqueException
317
	 * @throws Exception
318
	 */
319
	private function providerIdMustBeUnique(IFullTextSearchProvider $provider) {
320
		foreach ($this->providers AS $providerWrapper) {
321
			$knownProvider = $providerWrapper->getProvider();
322
			if ($knownProvider->getId() === $provider->getId()) {
323
				throw new ProviderIsNotUniqueException(
324
					'FullTextSearchProvider ' . $provider->getId() . ' already exist'
325
				);
326
			}
327
		}
328
	}
329
330
331
	/**
332
	 * @param IFullTextSearchProvider[] $providers
333
	 *
334
	 * @return array
335
	 */
336
	public function serialize(array $providers): array {
337
		$arr = [];
338
		foreach ($providers as $provider) {
339
			$arr[] = [
340
				'id'   => $provider->getId(),
341
				'name' => $provider->getName()
342
			];
343
		}
344
345
		return $arr;
346
	}
347
348
349
	/**
350
	 *
351
	 */
352
	public function addJavascriptAPI() {
353
		Util::addStyle(Application::APP_NAME, 'fulltextsearch');
354
		Util::addScript(Application::APP_NAME, 'fulltextsearch.v1.api');
355
		Util::addScript(Application::APP_NAME, 'fulltextsearch.v1.settings');
356
		Util::addScript(Application::APP_NAME, 'fulltextsearch.v1.searchbox');
357
		Util::addScript(Application::APP_NAME, 'fulltextsearch.v1.result');
358
		Util::addScript(Application::APP_NAME, 'fulltextsearch.v1.navigation');
359
		Util::addScript(Application::APP_NAME, 'fulltextsearch.v1');
360
	}
361
362
363
}
364