Passed
Push — settings-service ( 7097d2...6957f0 )
by Matias
12:25
created

FileService::setupFS()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * @copyright Copyright (c) 2019-2020 Matias De lellis <[email protected]>
6
 *
7
 * @author Matias De lellis <[email protected]>
8
 *
9
 * @license GNU AGPL version 3 or any later version
10
 *
11
 * This program is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License as
13
 * published by the Free Software Foundation, either version 3 of the
14
 * License, or (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 *
24
 */
25
26
namespace OCA\FaceRecognition\Service;
27
28
use OCP\Files\IRootFolder;
29
use OCP\Files\File;
30
use OCP\Files\Folder;
31
use OCP\Files\Node;
32
use OCP\ITempManager;
33
34
use OCP\Files\IHomeStorage;
35
use OCP\Files\NotFoundException;
36
37
use OCA\Files_Sharing\External\Storage as SharingExternalStorage;
0 ignored issues
show
Bug introduced by
The type OCA\Files_Sharing\External\Storage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
38
39
use OCA\FaceRecognition\Helper\Requirements;
40
use OCA\FaceRecognition\Service\SettingsService;
41
42
class FileService {
43
44
	const NOMEDIA_FILE = ".nomedia";
45
46
	const FACERECOGNITION_SETTINGS_FILE = ".facerecognition.json";
47
48
	/**  @var string|null */
49
	private $userId;
50
51
	/** @var IConfig Config */
0 ignored issues
show
Bug introduced by
The type OCA\FaceRecognition\Service\IConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
52
	private $config;
0 ignored issues
show
introduced by
The private property $config is not used, and could be removed.
Loading history...
53
54
	/** @var IRootFolder */
55
	private $rootFolder;
56
57
	/** @var ITempManager */
58
	private $tempManager;
59
60
	/** @var SettingsService */
61
	private $settingsService;
62
63 1
	public function __construct($userId,
64
	                            IRootFolder     $rootFolder,
65
	                            ITempManager    $tempManager,
66
	                            SettingsService $settingsService)
67
	{
68 1
		$this->userId          = $userId;
69 1
		$this->rootFolder      = $rootFolder;
70 1
		$this->tempManager     = $tempManager;
71 1
		$this->settingsService = $settingsService;
72 1
	}
73
74
	/**
75
	 * TODO: Describe exactly when necessary.
76
	 */
77 11
	public function setupFS(string $userId) {
78 11
		\OC_Util::tearDownFS();
0 ignored issues
show
Bug introduced by
The type OC_Util was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
79 11
		\OC_Util::setupFS($userId);
80
81 11
		$this->userId = $userId;
82 11
	}
83
84
	/**
85
	 * Get a Node from userFolder
86
	 * @param int $id the id of the Node
87
	 * @param string $userId
88
	 * @return Node | null
89
	 */
90 6
	public function getFileById($fileId, $userId = null): ?Node {
91 6
		$files = $this->rootFolder->getUserFolder($this->userId ?? $userId)->getById($fileId);
92 6
		if (count($files) === 0) {
93 1
			return null;
94
		}
95
96 5
		return $files[0];
97
	}
98
99
	/**
100
	 * Get a Node from userFolder
101
	 * @param string $fullpath the fullpath of the Node
102
	 * @param string $userId
103
	 * @return Node | null
104
	 */
105
	public function getFileByPath($fullpath, $userId = null): ?Node {
106
		$file = $this->rootFolder->getUserFolder($this->userId ?? $userId)->get($fullpath);
107
		return $file;
108
	}
109
110
	/**
111
	 * Checks if this file is located somewhere under .nomedia file and should be therefore ignored.
112
	 * Or with an .facerecognition.json setting file that disable tha analysis
113
	 *
114
	 * @param File $file File to search for
115
	 * @return bool True if file is located under .nomedia or .facerecognition.json that disabled
116
	 * analysis, false otherwise
117
	 */
118 1
	public function isUnderNoDetection(Node $node): bool {
119
		// If we detect .nomedia file anywhere on the path to root folder (id===null), bail out
120 1
		$parentNode = $node->getParent();
121 1
		while (($parentNode instanceof Folder) && ($parentNode->getId() !== null)) {
122 1
			$allowDetection = $this->getDescendantDetection($parentNode);
123 1
			if (!$allowDetection)
124 1
				return true;
125 1
			$parentNode = $parentNode->getParent();
126
		}
127 1
		return false;
128
	}
129
130
	/**
131
	 * Checks if this folder has .nomedia file an .facerecognition.json setting file that
132
	 * disable that analysis.
133
	 *
134
	 * @param Folder $folder Folder to search for
135
	 * @return bool true if folder dont have an .nomedia file or .facerecognition.json that disabled
136
	 * analysis, false otherwise
137
	 */
138 3
	public function getDescendantDetection(Folder $folder): bool {
139 3
		if ($folder->nodeExists(FileService::NOMEDIA_FILE)) {
140 2
			return false;
141
		}
142 3
		if ($folder->nodeExists(FileService::FACERECOGNITION_SETTINGS_FILE)) {
143
			$file = $folder->get(FileService::FACERECOGNITION_SETTINGS_FILE);
144
			$settings = json_decode($file->getContent(), true);
0 ignored issues
show
Bug introduced by
The method getContent() does not exist on OCP\Files\Node. It seems like you code against a sub-type of OCP\Files\Node such as OCP\Files\File. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

144
			$settings = json_decode($file->/** @scrutinizer ignore-call */ getContent(), true);
Loading history...
145
			if ($settings === null || !array_key_exists('detection', $settings))
146
				return true;
147
148
			if ($settings['detection'] === 'off')
149
				return false;
150
		}
151
152 3
		return true;
153
	}
154
155
	/**
156
	 * Set this folder to enable or disable the analysis using the .facerecognition.json file.
157
	 *
158
	 * @param Folder $folder Folder to enable/disable for
159
	 * @return bool true if the change is done. False if failed.
160
	 */
161
	public function setDescendantDetection(Folder $folder, bool $detection): bool {
162
		if ($folder->nodeExists(FileService::FACERECOGNITION_SETTINGS_FILE)) {
163
			$file = $folder->get(FileService::FACERECOGNITION_SETTINGS_FILE);
164
			$settings = json_decode($file->getContent(), true);
165
			if ($settings === null) {
166
				// Invalid json.
167
				return false;
168
			}
169
		}
170
		else {
171
			$file = $folder->newFile(FileService::FACERECOGNITION_SETTINGS_FILE);
172
			$settings = array();
173
		}
174
175
		$settings['detection'] = $detection ? "on" : "off";
176
		$file->putContent(json_encode($settings));
0 ignored issues
show
Bug introduced by
The method putContent() does not exist on OCP\Files\Node. It seems like you code against a sub-type of OCP\Files\Node such as OCP\Files\File. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

176
		$file->/** @scrutinizer ignore-call */ 
177
         putContent(json_encode($settings));
Loading history...
177
178
		return true;
179
	}
180
181
	/**
182
	 * Returns if the file is inside a shared storage.
183
	 */
184 2
	public function isSharedFile(Node $node): bool {
185 2
		return $node->getStorage()->instanceOfStorage(SharingExternalStorage::class);
186
	}
187
188
	/**
189
	 * Returns if the file is inside HomeStorage.
190
	 */
191 28
	public function isUserFile(Node $node): bool {
192 28
		return $node->getStorage()->instanceOfStorage(IHomeStorage::class);
193
	}
194
195
	/**
196
	 * Returns if the Node is allowed based on preferences.
197
	 */
198 28
	public function isAllowedNode(Node $node): bool {
199 28
		if ($this->isUserFile($node)) {
200 28
			return true;
201 2
		} else if ($this->isSharedFile($node)) {
202
			return $this->settingsService->getHandleSharedFiles();
203
		}
204 2
		return false;
205
	}
206
207
	/**
208
	 * Get a path to either the local file or temporary file
209
	 *
210
	 * @param File $file
211
	 * @param int $maxSize maximum size for temporary files
212
	 * @return string
213
	 */
214 4
	public function getLocalFile(File $file, int $maxSize = null): string {
215 4
		$useTempFile = $file->isEncrypted() || !$file->getStorage()->isLocal();
216 4
		if ($useTempFile) {
217
			$absPath = $this->tempManager->getTemporaryFile();
218
219
			$content = $file->fopen('r');
220
			if ($maxSize !== null) {
221
				$content = stream_get_contents($content, $maxSize);
222
			}
223
			file_put_contents($absPath, $content);
224
225
			return $absPath;
226
		} else {
227 4
			return $file->getStorage()->getLocalFile($file->getInternalPath());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $file->getStorage...ile->getInternalPath()) could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
228
		}
229
	}
230
231
	/**
232
	 * Return all images from a given folder.
233
	 *
234
	 * TODO: It is inefficient since it copies the array recursively.
235
	 *
236
	 * @param Folder $folder Folder to get images from
237
	 * @return array List of all images and folders to continue recursive crawling
238
	 */
239 10
	public function getPicturesFromFolder(Folder $folder, $results = array()) {
240 10
		$nodes = $folder->getDirectoryListing();
241 10
		foreach ($nodes as $node) {
242 8
			if (!$this->isAllowedNode($node)) {
243
				continue;
244
			}
245 8
			if ($node instanceof Folder && $this->getDescendantDetection($node)) {
246 3
				$results = $this->getPicturesFromFolder($node, $results);
247
			}
248 8
			else if ($node instanceof File) {
249 8
				if (Requirements::isImageTypeSupported($node->getMimeType())) {
250 8
					$results[] = $node;
251
				}
252
			}
253
		}
254 10
		return $results;
255
	}
256
257
	/**
258
	 * Create a temporary file and return the path
259
	 */
260 2
	public function getTemporaryFile(string $postFix = ''): string {
261 2
		return $this->tempManager->getTemporaryFile($postFix);
262
	}
263
264
	/**
265
	 * Remove any temporary file from the service.
266
	 */
267 4
	public function clean() {
268 4
		$this->tempManager->clean();
269 4
	}
270
271
}
272