Passed
Push — shared-storage-experiments ( e96b10...711936 )
by Matias
07:09
created

Watcher   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 238
Duplicated Lines 0 %

Test Coverage

Coverage 23%

Importance

Changes 20
Bugs 5 Features 0
Metric Value
eloc 105
c 20
b 5
f 0
dl 0
loc 238
ccs 23
cts 100
cp 0.23
rs 10
wmc 28

4 Methods

Rating   Name   Duplication   Size   Complexity  
A postUserDelete() 0 4 1
C postWrite() 0 81 14
C postDelete() 0 69 12
A __construct() 0 17 1
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, Roeland Jago Douma <[email protected]>
4
 * @copyright Copyright (c) 2017, Matias De lellis <[email protected]>
5
 *
6
 * @author Roeland Jago Douma <[email protected]>
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
namespace OCA\FaceRecognition;
26
27
use OCP\Files\Folder;
28
use OCP\Files\IHomeStorage;
29
use OCP\Files\Node;
30
use OCP\IConfig;
31
use OCP\ILogger;
32
use OCP\IUserManager;
33
34
use OCA\FaceRecognition\FaceManagementService;
35
use OCA\FaceRecognition\Service\FileService;
36
37
use OCA\FaceRecognition\BackgroundJob\Tasks\AddMissingImagesTask;
38
use OCA\FaceRecognition\BackgroundJob\Tasks\StaleImagesRemovalTask;
39
use OCA\FaceRecognition\Db\Face;
40
use OCA\FaceRecognition\Db\Image;
41
use OCA\FaceRecognition\Db\FaceMapper;
42
use OCA\FaceRecognition\Db\ImageMapper;
43
use OCA\FaceRecognition\Db\PersonMapper;
44
use OCA\FaceRecognition\Helper\Requirements;
45
use OCA\FaceRecognition\Migration\AddDefaultFaceModel;
46
47
class Watcher {
48
49
	/** @var IConfig Config */
50
	private $config;
51
52
	/** @var ILogger Logger */
53
	private $logger;
54
55
	/** @var IUserManager */
56
	private $userManager;
57
58
	/** @var FaceMapper */
59
	private $faceMapper;
60
61
	/** @var ImageMapper */
62
	private $imageMapper;
63
64
	/** @var PersonMapper */
65
	private $personMapper;
66
67
	/** @var FileService */
68
	private $fileService;
69
70
	/** @var FaceManagementService */
71
	private $faceManagementService;
72
73
	/**
74
	 * Watcher constructor.
75
	 *
76
	 * @param IConfig $config
77
	 * @param ILogger $logger
78
	 * @param IUserManager $userManager
79
	 * @param FaceMapper $faceMapper
80
	 * @param ImageMapper $imageMapper
81
	 * @param PersonMapper $personMapper
82
	 * @param FileService $fileService
83
	 * @param FaceManagementService $faceManagementService
84
	 */
85 1
	public function __construct(IConfig               $config,
86
	                            ILogger               $logger,
87
	                            IUserManager          $userManager,
88
	                            FaceMapper            $faceMapper,
89
	                            ImageMapper           $imageMapper,
90
	                            PersonMapper          $personMapper,
91
	                            FileService           $fileService,
92
	                            FaceManagementService $faceManagementService)
93
	{
94 1
		$this->config                = $config;
95 1
		$this->logger                = $logger;
96 1
		$this->userManager           = $userManager;
97 1
		$this->faceMapper            = $faceMapper;
98 1
		$this->imageMapper           = $imageMapper;
99 1
		$this->personMapper          = $personMapper;
100 1
		$this->fileService           = $fileService;
101 1
		$this->faceManagementService = $faceManagementService;
102 1
	}
103
104
	/**
105
	 * A node has been updated. We just store the file id
106
	 * with the current user in the DB
107
	 *
108
	 * @param Node $node
109
	 */
110 27
	public function postWrite(Node $node) {
111 27
		$model = intval($this->config->getAppValue('facerecognition', 'model', AddDefaultFaceModel::DEFAULT_FACE_MODEL_ID));
112 27
		$handleSharedFiles = $this->config->getAppValue('facerecognition', 'handle-shared-files', 'false');
113
114 27
		if ($this->fileService->isUserFile($node)) {
115 27
			$owner = $node->getOwner()->getUid();
116
		}
117 27
		else if ($handleSharedFiles === 'true' && $this->fileService->isSharedFile($node)) {
118
			// If we are going to analyze the shared files, we must 'appropriate' it.
119
			$owner = \OC::$server->getUserSession()->getUser()->getUID();
120
		}
121
		else {
122
			// Nextcloud also sends the Hooks when create thumbnails for example.
123 27
			return;
124
		}
125
126 27
		if ($node instanceof Folder) {
127 27
			return;
128
		}
129
130
		$enabled = $this->config->getUserValue($owner, 'facerecognition', 'enabled', 'false');
131
		if ($enabled !== 'true') {
132
			$this->logger->debug('The user ' . $owner . ' not have the analysis enabled. Skipping');
133
			return;
134
		}
135
136
		if ($node->getName() === FileService::NOMEDIA_FILE) {
137
			// If user added this file, it means all images in this and all child directories should be removed.
138
			// Instead of doing that here, it's better to just add flag that image removal should be done.
139
			$this->config->setUserValue($owner, 'facerecognition', StaleImagesRemovalTask::STALE_IMAGES_REMOVAL_NEEDED_KEY, 'true');
140
			return;
141
		}
142
143
		if ($node->getName() === FileService::FACERECOGNITION_SETTINGS_FILE) {
144
			// This file can enable or disable the analysis, so I have to look for new files and forget others.
145
			$this->config->setUserValue($owner, 'facerecognition', StaleImagesRemovalTask::STALE_IMAGES_REMOVAL_NEEDED_KEY, 'true');
146
			$this->config->setUserValue($owner, 'facerecognition', AddMissingImagesTask::FULL_IMAGE_SCAN_DONE_KEY, 'false');
147
			return;
148
		}
149
150
		if (!Requirements::isImageTypeSupported($node->getMimeType())) {
151
			return;
152
		}
153
154
		if (!$this->userManager->userExists($owner)) {
155
			$this->logger->debug(
156
				"Skipping inserting image " . $node->getName() . " because it seems that user  " . $owner . " doesn't exist");
157
			return;
158
		}
159
160
		if ($this->fileService->isUnderNoDetection($node)) {
161
			$this->logger->debug(
162
				"Skipping inserting image " . $node->getName() . " because is inside an folder that contains a .nomedia file");
163
			return;
164
		}
165
166
		$this->logger->debug("Inserting/updating image " . $node->getName() . " for face recognition");
167
168
		$image = new Image();
169
		$image->setUser($owner);
170
		$image->setFile($node->getId());
171
		$image->setModel($model);
172
173
		$imageId = $this->imageMapper->imageExists($image);
174
		if ($imageId === null) {
175
			// todo: can we have larger transaction with bulk insert?
176
			$this->imageMapper->insert($image);
177
		} else {
178
			$this->imageMapper->resetImage($image);
179
			// note that invalidatePersons depends on existence of faces for a given image,
180
			// and we must invalidate before we delete faces!
181
			$this->personMapper->invalidatePersons($imageId);
182
183
			// Fetch all faces to be deleted before deleting them, and then delete them
184
			$facesToRemove = $this->faceMapper->findByImage($imageId);
185
			$this->faceMapper->removeFaces($imageId);
186
187
			// If any person is now without faces, remove those (empty) persons
188
			foreach ($facesToRemove as $faceToRemove) {
189
				if ($faceToRemove->getPerson() !== null) {
190
					$this->personMapper->removeIfEmpty($faceToRemove->getPerson());
191
				}
192
			}
193
		}
194
	}
195
196
	/**
197
	 * A node has been deleted. Remove faces with file id
198
	 * with the current user in the DB
199
	 *
200
	 * @param Node $node
201
	 */
202
	public function postDelete(Node $node) {
203
		$model = intval($this->config->getAppValue('facerecognition', 'model', AddDefaultFaceModel::DEFAULT_FACE_MODEL_ID));
204
		$handleSharedFiles = $this->config->getAppValue('facerecognition', 'handle-shared-files', 'false');
205
206
		if ($this->fileService->isUserFile($node)) {
207
			$owner = $node->getOwner()->getUid();
208
		}
209
		else if ($handleSharedFiles === 'true' && $this->fileService->isSharedFile($node)) {
210
			// If we are going to analyze the shared files, we must 'appropriate' it.
211
			$owner = \OC::$server->getUserSession()->getUser()->getUID();
212
		}
213
		else {
214
			// Nextcloud also sends the Hooks when create thumbnails for example.
215
			return;
216
		}
217
218
		if ($node instanceof Folder) {
219
			return;
220
		}
221
222
		$enabled = $this->config->getUserValue($owner, 'facerecognition', 'enabled', 'false');
223
		if ($enabled !== 'true') {
224
			$this->logger->debug('The user ' . $owner . ' not have the analysis enabled. Skipping');
225
			return;
226
		}
227
228
		if ($node->getName() === FileService::NOMEDIA_FILE) {
229
			// If user deleted file named .nomedia, that means all images in this and all child directories should be added.
230
			// But, instead of doing that here, better option seem to be to just reset flag that image scan is not done.
231
			// This will trigger another round of image crawling in AddMissingImagesTask for this user and those images will be added.
232
			$this->config->setUserValue($owner, 'facerecognition', AddMissingImagesTask::FULL_IMAGE_SCAN_DONE_KEY, 'false');
233
			return;
234
		}
235
236
		if ($node->getName() === FileService::FACERECOGNITION_SETTINGS_FILE) {
237
			// This file can enable or disable the analysis, so I have to look for new files and forget others.
238
			$this->config->setUserValue($owner, 'facerecognition', StaleImagesRemovalTask::STALE_IMAGES_REMOVAL_NEEDED_KEY, 'true');
239
			$this->config->setUserValue($owner, 'facerecognition', AddMissingImagesTask::FULL_IMAGE_SCAN_DONE_KEY, 'false');
240
			return;
241
		}
242
243
		if (!Requirements::isImageTypeSupported($node->getMimeType())) {
244
			return;
245
		}
246
247
		$this->logger->debug("Deleting image " . $node->getName() . " from face recognition");
248
249
		$image = new Image();
250
		$image->setUser($owner);
251
		$image->setFile($node->getId());
252
		$image->setModel($model);
253
254
		$imageId = $this->imageMapper->imageExists($image);
255
		if ($imageId !== null) {
256
			// note that invalidatePersons depends on existence of faces for a given image,
257
			// and we must invalidate before we delete faces!
258
			$this->personMapper->invalidatePersons($imageId);
259
260
			// Fetch all faces to be deleted before deleting them, and then delete them
261
			$facesToRemove = $this->faceMapper->findByImage($imageId);
262
			$this->faceMapper->removeFaces($imageId);
263
264
			$image->setId($imageId);
265
			$this->imageMapper->delete($image);
266
267
			// If any person is now without faces, remove those (empty) persons
268
			foreach ($facesToRemove as $faceToRemove) {
269
				if ($faceToRemove->getPerson() !== null) {
270
					$this->personMapper->removeIfEmpty($faceToRemove->getPerson());
271
				}
272
			}
273
		}
274
	}
275
276
	/**
277
	 * A user has been deleted. Cleanup everything from this user.
278
	 *
279
	 * @param \OC\User\User $user Deleted user
280
	 */
281 27
	public function postUserDelete(\OC\User\User $user) {
0 ignored issues
show
Bug introduced by
The type OC\User\User 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...
282 27
		$userId = $user->getUid();
283 27
		$this->faceManagementService->resetAllForUser($userId);
284 27
		$this->logger->info("Removed all face recognition data for deleted user " . $userId);
285 27
	}
286
}
287