Completed
Push — master ( 2ee167...4713e8 )
by Matias
23s queued 13s
created

FileHooks::postDelete()   C

Complexity

Conditions 13
Paths 20

Size

Total Lines 77
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 149.7271

Importance

Changes 0
Metric Value
cc 13
eloc 44
nc 20
nop 1
dl 0
loc 77
ccs 3
cts 44
cp 0.0682
crap 149.7271
rs 6.6166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2016, Roeland Jago Douma <[email protected]>
5
 * @copyright Copyright (c) 2017-2021 Matias De lellis <[email protected]>
6
 *
7
 * @author Roeland Jago Douma <[email protected]>
8
 * @author Matias De lellis <[email protected]>
9
 *
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\FaceRecognition\Hooks;
28
29
use OCP\Files\IRootFolder;
30
use OCP\Files\Folder;
31
use OCP\Files\Node;
32
use OCP\ILogger;
33
use OCP\IUserManager;
34
35
use OCA\FaceRecognition\Service\FileService;
36
use OCA\FaceRecognition\Service\SettingsService;
37
38
use OCA\FaceRecognition\Db\Face;
39
use OCA\FaceRecognition\Db\Image;
40
41
use OCA\FaceRecognition\Db\FaceMapper;
42
use OCA\FaceRecognition\Db\ImageMapper;
43
use OCA\FaceRecognition\Db\PersonMapper;
44
45
class FileHooks {
46
47
	/** @var IRootFolder */
48
	private $root;
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 SettingsService */
66
	private $settingsService;
67
68
	/** @var FileService */
69
	private $fileService;
70
71
	/**
72
	 * Watcher constructor.
73
	 *
74
	 * @param IRootFolder $root
75
	 * @param ILogger $logger
76
	 * @param IUserManager $userManager
77
	 * @param FaceMapper $faceMapper
78
	 * @param ImageMapper $imageMapper
79
	 * @param PersonMapper $personMapper
80
	 * @param SettingsService $settingsService
81
	 * @param FileService $fileService
82
	 */
83
	public function __construct(IRootFolder           $root,
84
	                            ILogger               $logger,
85
	                            IUserManager          $userManager,
86
	                            FaceMapper            $faceMapper,
87
	                            ImageMapper           $imageMapper,
88
	                            PersonMapper          $personMapper,
89
	                            SettingsService       $settingsService,
90
	                            FileService           $fileService)
91
	{
92
		$this->root                  = $root;
93
		$this->logger                = $logger;
94
		$this->userManager           = $userManager;
95
		$this->faceMapper            = $faceMapper;
96
		$this->imageMapper           = $imageMapper;
97
		$this->personMapper          = $personMapper;
98
		$this->settingsService       = $settingsService;
99
		$this->fileService           = $fileService;
100
	}
101
102 28
	public function register() {
103
		// Watch on postWrite to handle new and changes files
104
		$this->root->listen('\OC\Files', 'postWrite', function (Node $node) {
105 28
			$this->postWrite($node);
106 28
		});
107
108
		// We want to react on postDelete and not preDelete as in preDelete we don't know if
109
		// file actually got deleted (locked, other errors...)
110
		$this->root->listen('\OC\Files', 'postDelete', function (Node $node) {
111 28
			$this->postDelete($node);
112 28
		});
113
	}
114
115
	/**
116
	 * A node has been updated. We just store the file id
117
	 * with the current user in the DB
118
	 *
119
	 * @param Node $node
120
	 */
121 28
	public function postWrite(Node $node) {
122 28
		if (!$this->fileService->isAllowedNode($node)) {
123
			// Nextcloud sends the Hooks when create thumbnails for example.
124 28
			return;
125
		}
126
127 28
		if ($node instanceof Folder) {
128 28
			return;
129
		}
130
131
		$modelId = $this->settingsService->getCurrentFaceModel();
132
		if ($modelId === SettingsService::FALLBACK_CURRENT_MODEL) {
133
			$this->logger->debug("Skipping inserting file since there are no configured model");
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

133
			/** @scrutinizer ignore-deprecated */ $this->logger->debug("Skipping inserting file since there are no configured model");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
134
			return;
135
		}
136
137
		$owner = null;
138
		if ($this->fileService->isUserFile($node)) {
139
			$owner = $node->getOwner()->getUid();
140
		} else {
141
			if (!\OC::$server->getUserSession()->isLoggedIn()) {
142
				$this->logger->debug('Skipping interting file ' . $node->getName() . ' since we cannot determine the owner.');
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

142
				/** @scrutinizer ignore-deprecated */ $this->logger->debug('Skipping interting file ' . $node->getName() . ' since we cannot determine the owner.');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
143
				return;
144
			}
145
			$owner = \OC::$server->getUserSession()->getUser()->getUID();
146
		}
147
148
		if (!$this->userManager->userExists($owner)) {
149
			$this->logger->debug(
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

149
			/** @scrutinizer ignore-deprecated */ $this->logger->debug(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
150
				"Skipping inserting file " . $node->getName() . " because it seems that user  " . $owner . " doesn't exist");
151
			return;
152
		}
153
154
		$enabled = $this->settingsService->getUserEnabled($owner);
155
		if (!$enabled) {
156
			$this->logger->debug('The user ' . $owner . ' not have the analysis enabled. Skipping');
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

156
			/** @scrutinizer ignore-deprecated */ $this->logger->debug('The user ' . $owner . ' not have the analysis enabled. Skipping');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
157
			return;
158
		}
159
160
		if ($node->getName() === FileService::NOMEDIA_FILE) {
161
			// If user added this file, it means all images in this and all child directories should be removed.
162
			// Instead of doing that here, it's better to just add flag that image removal should be done.
163
			$this->settingsService->setNeedRemoveStaleImages(true, $owner);
164
			return;
165
		}
166
167
		if ($node->getName() === FileService::FACERECOGNITION_SETTINGS_FILE) {
168
			// This file can enable or disable the analysis, so I have to look for new files and forget others.
169
			$this->settingsService->setNeedRemoveStaleImages(true, $owner);
170
			$this->settingsService->setUserFullScanDone(false, $owner);
171
			return;
172
		}
173
174
		if (!$this->settingsService->isAllowedMimetype($node->getMimeType())) {
175
			// The file is not an image or the model does not support it
176
			return;
177
		}
178
179
		if ($this->fileService->isUnderNoDetection($node)) {
180
			$this->logger->debug(
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

180
			/** @scrutinizer ignore-deprecated */ $this->logger->debug(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
181
				"Skipping inserting image " . $node->getName() . " because is inside an folder that contains a .nomedia file");
182
			return;
183
		}
184
185
		$this->logger->debug("Inserting/updating image " . $node->getName() . " for face recognition");
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

185
		/** @scrutinizer ignore-deprecated */ $this->logger->debug("Inserting/updating image " . $node->getName() . " for face recognition");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
186
187
		$image = new Image();
188
		$image->setUser($owner);
189
		$image->setFile($node->getId());
190
		$image->setModel($modelId);
191
192
		$imageId = $this->imageMapper->imageExists($image);
193
		if ($imageId === null) {
194
			// todo: can we have larger transaction with bulk insert?
195
			$this->imageMapper->insert($image);
196
		} else {
197
			$this->imageMapper->resetImage($image);
198
			// note that invalidatePersons depends on existence of faces for a given image,
199
			// and we must invalidate before we delete faces!
200
			$this->personMapper->invalidatePersons($imageId);
201
202
			// Fetch all faces to be deleted before deleting them, and then delete them
203
			$facesToRemove = $this->faceMapper->findByImage($imageId);
204
			$this->faceMapper->removeFromImage($imageId);
205
206
			// If any person is now without faces, remove those (empty) persons
207
			foreach ($facesToRemove as $faceToRemove) {
208
				if ($faceToRemove->getPerson() !== null) {
209
					$this->personMapper->removeIfEmpty($faceToRemove->getPerson());
210
				}
211
			}
212
		}
213
	}
214
215
	/**
216
	 * A node has been deleted. Remove faces with file id
217
	 * with the current user in the DB
218
	 *
219
	 * @param Node $node
220
	 */
221 28
	public function postDelete(Node $node) {
222 28
		if (!$this->fileService->isAllowedNode($node)) {
223
			// Nextcloud sends the Hooks when create thumbnails for example.
224 28
			return;
225
		}
226
227
		if ($node instanceof Folder) {
228
			return;
229
		}
230
231
		$modelId = $this->settingsService->getCurrentFaceModel();
232
		if ($modelId === SettingsService::FALLBACK_CURRENT_MODEL) {
233
			$this->logger->debug("Skipping deleting file since there are no configured model");
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

233
			/** @scrutinizer ignore-deprecated */ $this->logger->debug("Skipping deleting file since there are no configured model");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
234
			return;
235
		}
236
237
		$owner = null;
238
		if ($this->fileService->isUserFile($node)) {
239
			$owner = $node->getOwner()->getUid();
240
		} else {
241
			if (!\OC::$server->getUserSession()->isLoggedIn()) {
242
				$this->logger->debug('Skipping deleting the file ' . $node->getName() .  ' since we cannot determine the owner');
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

242
				/** @scrutinizer ignore-deprecated */ $this->logger->debug('Skipping deleting the file ' . $node->getName() .  ' since we cannot determine the owner');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
243
				return;
244
			}
245
			$owner = \OC::$server->getUserSession()->getUser()->getUID();
246
		}
247
248
		$enabled = $this->settingsService->getUserEnabled($owner);
249
		if (!$enabled) {
250
			$this->logger->debug('The user ' . $owner . ' not have the analysis enabled. Skipping');
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

250
			/** @scrutinizer ignore-deprecated */ $this->logger->debug('The user ' . $owner . ' not have the analysis enabled. Skipping');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
251
			return;
252
		}
253
254
		if ($node->getName() === FileService::NOMEDIA_FILE) {
255
			// If user deleted file named .nomedia, that means all images in this and all child directories should be added.
256
			// But, instead of doing that here, better option seem to be to just reset flag that image scan is not done.
257
			// This will trigger another round of image crawling in AddMissingImagesTask for this user and those images will be added.
258
			$this->settingsService->setUserFullScanDone(false, $owner);
259
			return;
260
		}
261
262
		if ($node->getName() === FileService::FACERECOGNITION_SETTINGS_FILE) {
263
			// This file can enable or disable the analysis, so I have to look for new files and forget others.
264
			$this->settingsService->setNeedRemoveStaleImages(true, $owner);
265
			$this->settingsService->setUserFullScanDone(false, $owner);
266
			return;
267
		}
268
269
		if (!$this->settingsService->isAllowedMimetype($node->getMimeType())) {
270
			// The file is not an image or the model does not support it
271
			return;
272
		}
273
274
		$this->logger->debug("Deleting image " . $node->getName() . " from face recognition");
0 ignored issues
show
Deprecated Code introduced by
The function OCP\ILogger::debug() has been deprecated: 20.0.0 use \Psr\Log\LoggerInterface::debug ( Ignorable by Annotation )

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

274
		/** @scrutinizer ignore-deprecated */ $this->logger->debug("Deleting image " . $node->getName() . " from face recognition");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
275
276
		$image = new Image();
277
		$image->setUser($owner);
278
		$image->setFile($node->getId());
279
		$image->setModel($modelId);
280
281
		$imageId = $this->imageMapper->imageExists($image);
282
		if ($imageId !== null) {
283
			// note that invalidatePersons depends on existence of faces for a given image,
284
			// and we must invalidate before we delete faces!
285
			$this->personMapper->invalidatePersons($imageId);
286
287
			// Fetch all faces to be deleted before deleting them, and then delete them
288
			$facesToRemove = $this->faceMapper->findByImage($imageId);
289
			$this->faceMapper->removeFromImage($imageId);
290
291
			$image->setId($imageId);
292
			$this->imageMapper->delete($image);
293
294
			// If any person is now without faces, remove those (empty) persons
295
			foreach ($facesToRemove as $faceToRemove) {
296
				if ($faceToRemove->getPerson() !== null) {
297
					$this->personMapper->removeIfEmpty($faceToRemove->getPerson());
298
				}
299
			}
300
		}
301
	}
302
}
303