Cancelled
Push — self-contained-model ( d2adf7...394059 )
by Matias
03:40
created

Watcher::postUserDelete()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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