Passed
Push — master ( db0268...2d41f0 )
by Blizzz
16:12 queued 15s
created
apps/dav/lib/SystemTag/SystemTagPlugin.php 2 patches
Indentation   +364 added lines, -364 removed lines patch added patch discarded remove patch
@@ -53,368 +53,368 @@
 block discarded – undo
53 53
  */
54 54
 class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
55 55
 
56
-	// namespace
57
-	public const NS_OWNCLOUD = 'http://owncloud.org/ns';
58
-	public const ID_PROPERTYNAME = '{http://owncloud.org/ns}id';
59
-	public const DISPLAYNAME_PROPERTYNAME = '{http://owncloud.org/ns}display-name';
60
-	public const USERVISIBLE_PROPERTYNAME = '{http://owncloud.org/ns}user-visible';
61
-	public const USERASSIGNABLE_PROPERTYNAME = '{http://owncloud.org/ns}user-assignable';
62
-	public const GROUPS_PROPERTYNAME = '{http://owncloud.org/ns}groups';
63
-	public const CANASSIGN_PROPERTYNAME = '{http://owncloud.org/ns}can-assign';
64
-	public const SYSTEM_TAGS_PROPERTYNAME = '{http://nextcloud.org/ns}system-tags';
65
-
66
-	/**
67
-	 * @var \Sabre\DAV\Server $server
68
-	 */
69
-	private $server;
70
-
71
-	/**
72
-	 * @var ISystemTagManager
73
-	 */
74
-	protected $tagManager;
75
-
76
-	/**
77
-	 * @var IUserSession
78
-	 */
79
-	protected $userSession;
80
-
81
-	/**
82
-	 * @var IGroupManager
83
-	 */
84
-	protected $groupManager;
85
-
86
-	/** @var array<int, string[]> */
87
-	private array $cachedTagMappings = [];
88
-	/** @var array<string, ISystemTag> */
89
-	private array $cachedTags = [];
90
-
91
-	private ISystemTagObjectMapper $tagMapper;
92
-
93
-	public function __construct(
94
-		ISystemTagManager $tagManager,
95
-		IGroupManager $groupManager,
96
-		IUserSession $userSession,
97
-		ISystemTagObjectMapper $tagMapper,
98
-	) {
99
-		$this->tagManager = $tagManager;
100
-		$this->userSession = $userSession;
101
-		$this->groupManager = $groupManager;
102
-		$this->tagMapper = $tagMapper;
103
-	}
104
-
105
-	/**
106
-	 * This initializes the plugin.
107
-	 *
108
-	 * This function is called by \Sabre\DAV\Server, after
109
-	 * addPlugin is called.
110
-	 *
111
-	 * This method should set up the required event subscriptions.
112
-	 *
113
-	 * @param \Sabre\DAV\Server $server
114
-	 * @return void
115
-	 */
116
-	public function initialize(\Sabre\DAV\Server $server) {
117
-		$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
118
-
119
-		$server->protectedProperties[] = self::ID_PROPERTYNAME;
120
-
121
-		$server->on('propFind', [$this, 'handleGetProperties']);
122
-		$server->on('propPatch', [$this, 'handleUpdateProperties']);
123
-		$server->on('method:POST', [$this, 'httpPost']);
124
-
125
-		$this->server = $server;
126
-	}
127
-
128
-	/**
129
-	 * POST operation on system tag collections
130
-	 *
131
-	 * @param RequestInterface $request request object
132
-	 * @param ResponseInterface $response response object
133
-	 * @return null|false
134
-	 */
135
-	public function httpPost(RequestInterface $request, ResponseInterface $response) {
136
-		$path = $request->getPath();
137
-
138
-		// Making sure the node exists
139
-		$node = $this->server->tree->getNodeForPath($path);
140
-		if ($node instanceof SystemTagsByIdCollection || $node instanceof SystemTagsObjectMappingCollection) {
141
-			$data = $request->getBodyAsString();
142
-
143
-			$tag = $this->createTag($data, $request->getHeader('Content-Type'));
144
-
145
-			if ($node instanceof SystemTagsObjectMappingCollection) {
146
-				// also add to collection
147
-				$node->createFile($tag->getId());
148
-				$url = $request->getBaseUrl() . 'systemtags/';
149
-			} else {
150
-				$url = $request->getUrl();
151
-			}
152
-
153
-			if ($url[strlen($url) - 1] !== '/') {
154
-				$url .= '/';
155
-			}
156
-
157
-			$response->setHeader('Content-Location', $url . $tag->getId());
158
-
159
-			// created
160
-			$response->setStatus(201);
161
-			return false;
162
-		}
163
-	}
164
-
165
-	/**
166
-	 * Creates a new tag
167
-	 *
168
-	 * @param string $data JSON encoded string containing the properties of the tag to create
169
-	 * @param string $contentType content type of the data
170
-	 * @return ISystemTag newly created system tag
171
-	 *
172
-	 * @throws BadRequest if a field was missing
173
-	 * @throws Conflict if a tag with the same properties already exists
174
-	 * @throws UnsupportedMediaType if the content type is not supported
175
-	 */
176
-	private function createTag($data, $contentType = 'application/json') {
177
-		if (explode(';', $contentType)[0] === 'application/json') {
178
-			$data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
179
-		} else {
180
-			throw new UnsupportedMediaType();
181
-		}
182
-
183
-		if (!isset($data['name'])) {
184
-			throw new BadRequest('Missing "name" attribute');
185
-		}
186
-
187
-		$tagName = $data['name'];
188
-		$userVisible = true;
189
-		$userAssignable = true;
190
-
191
-		if (isset($data['userVisible'])) {
192
-			$userVisible = (bool)$data['userVisible'];
193
-		}
194
-
195
-		if (isset($data['userAssignable'])) {
196
-			$userAssignable = (bool)$data['userAssignable'];
197
-		}
198
-
199
-		$groups = [];
200
-		if (isset($data['groups'])) {
201
-			$groups = $data['groups'];
202
-			if (is_string($groups)) {
203
-				$groups = explode('|', $groups);
204
-			}
205
-		}
206
-
207
-		if ($userVisible === false || $userAssignable === false || !empty($groups)) {
208
-			if (!$this->userSession->isLoggedIn() || !$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
209
-				throw new BadRequest('Not sufficient permissions');
210
-			}
211
-		}
212
-
213
-		try {
214
-			$tag = $this->tagManager->createTag($tagName, $userVisible, $userAssignable);
215
-			if (!empty($groups)) {
216
-				$this->tagManager->setTagGroups($tag, $groups);
217
-			}
218
-			return $tag;
219
-		} catch (TagAlreadyExistsException $e) {
220
-			throw new Conflict('Tag already exists', 0, $e);
221
-		}
222
-	}
223
-
224
-
225
-	/**
226
-	 * Retrieves system tag properties
227
-	 *
228
-	 * @param PropFind $propFind
229
-	 * @param \Sabre\DAV\INode $node
230
-	 *
231
-	 * @return void
232
-	 */
233
-	public function handleGetProperties(
234
-		PropFind $propFind,
235
-		\Sabre\DAV\INode $node
236
-	) {
237
-		if ($node instanceof Node) {
238
-			$this->propfindForFile($propFind, $node);
239
-			return;
240
-		}
241
-
242
-		if (!($node instanceof SystemTagNode) && !($node instanceof SystemTagMappingNode)) {
243
-			return;
244
-		}
245
-
246
-		$propFind->handle(self::ID_PROPERTYNAME, function () use ($node) {
247
-			return $node->getSystemTag()->getId();
248
-		});
249
-
250
-		$propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function () use ($node) {
251
-			return $node->getSystemTag()->getName();
252
-		});
253
-
254
-		$propFind->handle(self::USERVISIBLE_PROPERTYNAME, function () use ($node) {
255
-			return $node->getSystemTag()->isUserVisible() ? 'true' : 'false';
256
-		});
257
-
258
-		$propFind->handle(self::USERASSIGNABLE_PROPERTYNAME, function () use ($node) {
259
-			// this is the tag's inherent property "is user assignable"
260
-			return $node->getSystemTag()->isUserAssignable() ? 'true' : 'false';
261
-		});
262
-
263
-		$propFind->handle(self::CANASSIGN_PROPERTYNAME, function () use ($node) {
264
-			// this is the effective permission for the current user
265
-			return $this->tagManager->canUserAssignTag($node->getSystemTag(), $this->userSession->getUser()) ? 'true' : 'false';
266
-		});
267
-
268
-		$propFind->handle(self::GROUPS_PROPERTYNAME, function () use ($node) {
269
-			if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
270
-				// property only available for admins
271
-				throw new Forbidden();
272
-			}
273
-			$groups = [];
274
-			// no need to retrieve groups for namespaces that don't qualify
275
-			if ($node->getSystemTag()->isUserVisible() && !$node->getSystemTag()->isUserAssignable()) {
276
-				$groups = $this->tagManager->getTagGroups($node->getSystemTag());
277
-			}
278
-			return implode('|', $groups);
279
-		});
280
-	}
281
-
282
-	private function propfindForFile(PropFind $propFind, Node $node): void {
283
-		if ($node instanceof Directory
284
-			&& $propFind->getDepth() !== 0
285
-			&& !is_null($propFind->getStatus(self::SYSTEM_TAGS_PROPERTYNAME))) {
286
-			$fileIds = [$node->getId()];
287
-
288
-			// note: pre-fetching only supported for depth <= 1
289
-			$folderContent = $node->getNode()->getDirectoryListing();
290
-			foreach ($folderContent as $info) {
291
-				$fileIds[] = $info->getId();
292
-			}
293
-
294
-			$tags = $this->tagMapper->getTagIdsForObjects($fileIds, 'files');
295
-
296
-			$this->cachedTagMappings = $this->cachedTagMappings + $tags;
297
-			$emptyFileIds = array_diff($fileIds, array_keys($tags));
298
-
299
-			// also cache the ones that were not found
300
-			foreach ($emptyFileIds as $fileId) {
301
-				$this->cachedTagMappings[$fileId] = [];
302
-			}
303
-		}
304
-
305
-		$propFind->handle(self::SYSTEM_TAGS_PROPERTYNAME, function () use ($node) {
306
-			$user = $this->userSession->getUser();
307
-			if ($user === null) {
308
-				return;
309
-			}
310
-
311
-			$tags = $this->getTagsForFile($node->getId(), $user);
312
-			usort($tags, function (ISystemTag $tagA, ISystemTag $tagB): int {
313
-				return Util::naturalSortCompare($tagA->getName(), $tagB->getName());
314
-			});
315
-			return new SystemTagList($tags, $this->tagManager, $user);
316
-		});
317
-	}
318
-
319
-	/**
320
-	 * @param int $fileId
321
-	 * @return ISystemTag[]
322
-	 */
323
-	private function getTagsForFile(int $fileId, IUser $user): array {
324
-
325
-		if (isset($this->cachedTagMappings[$fileId])) {
326
-			$tagIds = $this->cachedTagMappings[$fileId];
327
-		} else {
328
-			$tags = $this->tagMapper->getTagIdsForObjects([$fileId], 'files');
329
-			$fileTags = current($tags);
330
-			if ($fileTags) {
331
-				$tagIds = $fileTags;
332
-			} else {
333
-				$tagIds = [];
334
-			}
335
-		}
336
-
337
-		$tags = array_filter(array_map(function(string $tagId) {
338
-			return $this->cachedTags[$tagId] ?? null;
339
-		}, $tagIds));
340
-
341
-		$uncachedTagIds = array_filter($tagIds, function(string $tagId): bool {
342
-			return !isset($this->cachedTags[$tagId]);
343
-		});
344
-
345
-		if (count($uncachedTagIds)) {
346
-			$retrievedTags = $this->tagManager->getTagsByIds($uncachedTagIds);
347
-			foreach ($retrievedTags as $tag) {
348
-				$this->cachedTags[$tag->getId()] = $tag;
349
-			}
350
-			$tags += $retrievedTags;
351
-		}
352
-
353
-		return array_filter($tags, function(ISystemTag $tag) use ($user) {
354
-			return $this->tagManager->canUserSeeTag($tag, $user);
355
-		});
356
-	}
357
-
358
-	/**
359
-	 * Updates tag attributes
360
-	 *
361
-	 * @param string $path
362
-	 * @param PropPatch $propPatch
363
-	 *
364
-	 * @return void
365
-	 */
366
-	public function handleUpdateProperties($path, PropPatch $propPatch) {
367
-		$node = $this->server->tree->getNodeForPath($path);
368
-		if (!($node instanceof SystemTagNode)) {
369
-			return;
370
-		}
371
-
372
-		$propPatch->handle([
373
-			self::DISPLAYNAME_PROPERTYNAME,
374
-			self::USERVISIBLE_PROPERTYNAME,
375
-			self::USERASSIGNABLE_PROPERTYNAME,
376
-			self::GROUPS_PROPERTYNAME,
377
-		], function ($props) use ($node) {
378
-			$tag = $node->getSystemTag();
379
-			$name = $tag->getName();
380
-			$userVisible = $tag->isUserVisible();
381
-			$userAssignable = $tag->isUserAssignable();
382
-
383
-			$updateTag = false;
384
-
385
-			if (isset($props[self::DISPLAYNAME_PROPERTYNAME])) {
386
-				$name = $props[self::DISPLAYNAME_PROPERTYNAME];
387
-				$updateTag = true;
388
-			}
389
-
390
-			if (isset($props[self::USERVISIBLE_PROPERTYNAME])) {
391
-				$propValue = $props[self::USERVISIBLE_PROPERTYNAME];
392
-				$userVisible = ($propValue !== 'false' && $propValue !== '0');
393
-				$updateTag = true;
394
-			}
395
-
396
-			if (isset($props[self::USERASSIGNABLE_PROPERTYNAME])) {
397
-				$propValue = $props[self::USERASSIGNABLE_PROPERTYNAME];
398
-				$userAssignable = ($propValue !== 'false' && $propValue !== '0');
399
-				$updateTag = true;
400
-			}
401
-
402
-			if (isset($props[self::GROUPS_PROPERTYNAME])) {
403
-				if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
404
-					// property only available for admins
405
-					throw new Forbidden();
406
-				}
407
-
408
-				$propValue = $props[self::GROUPS_PROPERTYNAME];
409
-				$groupIds = explode('|', $propValue);
410
-				$this->tagManager->setTagGroups($tag, $groupIds);
411
-			}
412
-
413
-			if ($updateTag) {
414
-				$node->update($name, $userVisible, $userAssignable);
415
-			}
416
-
417
-			return true;
418
-		});
419
-	}
56
+    // namespace
57
+    public const NS_OWNCLOUD = 'http://owncloud.org/ns';
58
+    public const ID_PROPERTYNAME = '{http://owncloud.org/ns}id';
59
+    public const DISPLAYNAME_PROPERTYNAME = '{http://owncloud.org/ns}display-name';
60
+    public const USERVISIBLE_PROPERTYNAME = '{http://owncloud.org/ns}user-visible';
61
+    public const USERASSIGNABLE_PROPERTYNAME = '{http://owncloud.org/ns}user-assignable';
62
+    public const GROUPS_PROPERTYNAME = '{http://owncloud.org/ns}groups';
63
+    public const CANASSIGN_PROPERTYNAME = '{http://owncloud.org/ns}can-assign';
64
+    public const SYSTEM_TAGS_PROPERTYNAME = '{http://nextcloud.org/ns}system-tags';
65
+
66
+    /**
67
+     * @var \Sabre\DAV\Server $server
68
+     */
69
+    private $server;
70
+
71
+    /**
72
+     * @var ISystemTagManager
73
+     */
74
+    protected $tagManager;
75
+
76
+    /**
77
+     * @var IUserSession
78
+     */
79
+    protected $userSession;
80
+
81
+    /**
82
+     * @var IGroupManager
83
+     */
84
+    protected $groupManager;
85
+
86
+    /** @var array<int, string[]> */
87
+    private array $cachedTagMappings = [];
88
+    /** @var array<string, ISystemTag> */
89
+    private array $cachedTags = [];
90
+
91
+    private ISystemTagObjectMapper $tagMapper;
92
+
93
+    public function __construct(
94
+        ISystemTagManager $tagManager,
95
+        IGroupManager $groupManager,
96
+        IUserSession $userSession,
97
+        ISystemTagObjectMapper $tagMapper,
98
+    ) {
99
+        $this->tagManager = $tagManager;
100
+        $this->userSession = $userSession;
101
+        $this->groupManager = $groupManager;
102
+        $this->tagMapper = $tagMapper;
103
+    }
104
+
105
+    /**
106
+     * This initializes the plugin.
107
+     *
108
+     * This function is called by \Sabre\DAV\Server, after
109
+     * addPlugin is called.
110
+     *
111
+     * This method should set up the required event subscriptions.
112
+     *
113
+     * @param \Sabre\DAV\Server $server
114
+     * @return void
115
+     */
116
+    public function initialize(\Sabre\DAV\Server $server) {
117
+        $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
118
+
119
+        $server->protectedProperties[] = self::ID_PROPERTYNAME;
120
+
121
+        $server->on('propFind', [$this, 'handleGetProperties']);
122
+        $server->on('propPatch', [$this, 'handleUpdateProperties']);
123
+        $server->on('method:POST', [$this, 'httpPost']);
124
+
125
+        $this->server = $server;
126
+    }
127
+
128
+    /**
129
+     * POST operation on system tag collections
130
+     *
131
+     * @param RequestInterface $request request object
132
+     * @param ResponseInterface $response response object
133
+     * @return null|false
134
+     */
135
+    public function httpPost(RequestInterface $request, ResponseInterface $response) {
136
+        $path = $request->getPath();
137
+
138
+        // Making sure the node exists
139
+        $node = $this->server->tree->getNodeForPath($path);
140
+        if ($node instanceof SystemTagsByIdCollection || $node instanceof SystemTagsObjectMappingCollection) {
141
+            $data = $request->getBodyAsString();
142
+
143
+            $tag = $this->createTag($data, $request->getHeader('Content-Type'));
144
+
145
+            if ($node instanceof SystemTagsObjectMappingCollection) {
146
+                // also add to collection
147
+                $node->createFile($tag->getId());
148
+                $url = $request->getBaseUrl() . 'systemtags/';
149
+            } else {
150
+                $url = $request->getUrl();
151
+            }
152
+
153
+            if ($url[strlen($url) - 1] !== '/') {
154
+                $url .= '/';
155
+            }
156
+
157
+            $response->setHeader('Content-Location', $url . $tag->getId());
158
+
159
+            // created
160
+            $response->setStatus(201);
161
+            return false;
162
+        }
163
+    }
164
+
165
+    /**
166
+     * Creates a new tag
167
+     *
168
+     * @param string $data JSON encoded string containing the properties of the tag to create
169
+     * @param string $contentType content type of the data
170
+     * @return ISystemTag newly created system tag
171
+     *
172
+     * @throws BadRequest if a field was missing
173
+     * @throws Conflict if a tag with the same properties already exists
174
+     * @throws UnsupportedMediaType if the content type is not supported
175
+     */
176
+    private function createTag($data, $contentType = 'application/json') {
177
+        if (explode(';', $contentType)[0] === 'application/json') {
178
+            $data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
179
+        } else {
180
+            throw new UnsupportedMediaType();
181
+        }
182
+
183
+        if (!isset($data['name'])) {
184
+            throw new BadRequest('Missing "name" attribute');
185
+        }
186
+
187
+        $tagName = $data['name'];
188
+        $userVisible = true;
189
+        $userAssignable = true;
190
+
191
+        if (isset($data['userVisible'])) {
192
+            $userVisible = (bool)$data['userVisible'];
193
+        }
194
+
195
+        if (isset($data['userAssignable'])) {
196
+            $userAssignable = (bool)$data['userAssignable'];
197
+        }
198
+
199
+        $groups = [];
200
+        if (isset($data['groups'])) {
201
+            $groups = $data['groups'];
202
+            if (is_string($groups)) {
203
+                $groups = explode('|', $groups);
204
+            }
205
+        }
206
+
207
+        if ($userVisible === false || $userAssignable === false || !empty($groups)) {
208
+            if (!$this->userSession->isLoggedIn() || !$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
209
+                throw new BadRequest('Not sufficient permissions');
210
+            }
211
+        }
212
+
213
+        try {
214
+            $tag = $this->tagManager->createTag($tagName, $userVisible, $userAssignable);
215
+            if (!empty($groups)) {
216
+                $this->tagManager->setTagGroups($tag, $groups);
217
+            }
218
+            return $tag;
219
+        } catch (TagAlreadyExistsException $e) {
220
+            throw new Conflict('Tag already exists', 0, $e);
221
+        }
222
+    }
223
+
224
+
225
+    /**
226
+     * Retrieves system tag properties
227
+     *
228
+     * @param PropFind $propFind
229
+     * @param \Sabre\DAV\INode $node
230
+     *
231
+     * @return void
232
+     */
233
+    public function handleGetProperties(
234
+        PropFind $propFind,
235
+        \Sabre\DAV\INode $node
236
+    ) {
237
+        if ($node instanceof Node) {
238
+            $this->propfindForFile($propFind, $node);
239
+            return;
240
+        }
241
+
242
+        if (!($node instanceof SystemTagNode) && !($node instanceof SystemTagMappingNode)) {
243
+            return;
244
+        }
245
+
246
+        $propFind->handle(self::ID_PROPERTYNAME, function () use ($node) {
247
+            return $node->getSystemTag()->getId();
248
+        });
249
+
250
+        $propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function () use ($node) {
251
+            return $node->getSystemTag()->getName();
252
+        });
253
+
254
+        $propFind->handle(self::USERVISIBLE_PROPERTYNAME, function () use ($node) {
255
+            return $node->getSystemTag()->isUserVisible() ? 'true' : 'false';
256
+        });
257
+
258
+        $propFind->handle(self::USERASSIGNABLE_PROPERTYNAME, function () use ($node) {
259
+            // this is the tag's inherent property "is user assignable"
260
+            return $node->getSystemTag()->isUserAssignable() ? 'true' : 'false';
261
+        });
262
+
263
+        $propFind->handle(self::CANASSIGN_PROPERTYNAME, function () use ($node) {
264
+            // this is the effective permission for the current user
265
+            return $this->tagManager->canUserAssignTag($node->getSystemTag(), $this->userSession->getUser()) ? 'true' : 'false';
266
+        });
267
+
268
+        $propFind->handle(self::GROUPS_PROPERTYNAME, function () use ($node) {
269
+            if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
270
+                // property only available for admins
271
+                throw new Forbidden();
272
+            }
273
+            $groups = [];
274
+            // no need to retrieve groups for namespaces that don't qualify
275
+            if ($node->getSystemTag()->isUserVisible() && !$node->getSystemTag()->isUserAssignable()) {
276
+                $groups = $this->tagManager->getTagGroups($node->getSystemTag());
277
+            }
278
+            return implode('|', $groups);
279
+        });
280
+    }
281
+
282
+    private function propfindForFile(PropFind $propFind, Node $node): void {
283
+        if ($node instanceof Directory
284
+            && $propFind->getDepth() !== 0
285
+            && !is_null($propFind->getStatus(self::SYSTEM_TAGS_PROPERTYNAME))) {
286
+            $fileIds = [$node->getId()];
287
+
288
+            // note: pre-fetching only supported for depth <= 1
289
+            $folderContent = $node->getNode()->getDirectoryListing();
290
+            foreach ($folderContent as $info) {
291
+                $fileIds[] = $info->getId();
292
+            }
293
+
294
+            $tags = $this->tagMapper->getTagIdsForObjects($fileIds, 'files');
295
+
296
+            $this->cachedTagMappings = $this->cachedTagMappings + $tags;
297
+            $emptyFileIds = array_diff($fileIds, array_keys($tags));
298
+
299
+            // also cache the ones that were not found
300
+            foreach ($emptyFileIds as $fileId) {
301
+                $this->cachedTagMappings[$fileId] = [];
302
+            }
303
+        }
304
+
305
+        $propFind->handle(self::SYSTEM_TAGS_PROPERTYNAME, function () use ($node) {
306
+            $user = $this->userSession->getUser();
307
+            if ($user === null) {
308
+                return;
309
+            }
310
+
311
+            $tags = $this->getTagsForFile($node->getId(), $user);
312
+            usort($tags, function (ISystemTag $tagA, ISystemTag $tagB): int {
313
+                return Util::naturalSortCompare($tagA->getName(), $tagB->getName());
314
+            });
315
+            return new SystemTagList($tags, $this->tagManager, $user);
316
+        });
317
+    }
318
+
319
+    /**
320
+     * @param int $fileId
321
+     * @return ISystemTag[]
322
+     */
323
+    private function getTagsForFile(int $fileId, IUser $user): array {
324
+
325
+        if (isset($this->cachedTagMappings[$fileId])) {
326
+            $tagIds = $this->cachedTagMappings[$fileId];
327
+        } else {
328
+            $tags = $this->tagMapper->getTagIdsForObjects([$fileId], 'files');
329
+            $fileTags = current($tags);
330
+            if ($fileTags) {
331
+                $tagIds = $fileTags;
332
+            } else {
333
+                $tagIds = [];
334
+            }
335
+        }
336
+
337
+        $tags = array_filter(array_map(function(string $tagId) {
338
+            return $this->cachedTags[$tagId] ?? null;
339
+        }, $tagIds));
340
+
341
+        $uncachedTagIds = array_filter($tagIds, function(string $tagId): bool {
342
+            return !isset($this->cachedTags[$tagId]);
343
+        });
344
+
345
+        if (count($uncachedTagIds)) {
346
+            $retrievedTags = $this->tagManager->getTagsByIds($uncachedTagIds);
347
+            foreach ($retrievedTags as $tag) {
348
+                $this->cachedTags[$tag->getId()] = $tag;
349
+            }
350
+            $tags += $retrievedTags;
351
+        }
352
+
353
+        return array_filter($tags, function(ISystemTag $tag) use ($user) {
354
+            return $this->tagManager->canUserSeeTag($tag, $user);
355
+        });
356
+    }
357
+
358
+    /**
359
+     * Updates tag attributes
360
+     *
361
+     * @param string $path
362
+     * @param PropPatch $propPatch
363
+     *
364
+     * @return void
365
+     */
366
+    public function handleUpdateProperties($path, PropPatch $propPatch) {
367
+        $node = $this->server->tree->getNodeForPath($path);
368
+        if (!($node instanceof SystemTagNode)) {
369
+            return;
370
+        }
371
+
372
+        $propPatch->handle([
373
+            self::DISPLAYNAME_PROPERTYNAME,
374
+            self::USERVISIBLE_PROPERTYNAME,
375
+            self::USERASSIGNABLE_PROPERTYNAME,
376
+            self::GROUPS_PROPERTYNAME,
377
+        ], function ($props) use ($node) {
378
+            $tag = $node->getSystemTag();
379
+            $name = $tag->getName();
380
+            $userVisible = $tag->isUserVisible();
381
+            $userAssignable = $tag->isUserAssignable();
382
+
383
+            $updateTag = false;
384
+
385
+            if (isset($props[self::DISPLAYNAME_PROPERTYNAME])) {
386
+                $name = $props[self::DISPLAYNAME_PROPERTYNAME];
387
+                $updateTag = true;
388
+            }
389
+
390
+            if (isset($props[self::USERVISIBLE_PROPERTYNAME])) {
391
+                $propValue = $props[self::USERVISIBLE_PROPERTYNAME];
392
+                $userVisible = ($propValue !== 'false' && $propValue !== '0');
393
+                $updateTag = true;
394
+            }
395
+
396
+            if (isset($props[self::USERASSIGNABLE_PROPERTYNAME])) {
397
+                $propValue = $props[self::USERASSIGNABLE_PROPERTYNAME];
398
+                $userAssignable = ($propValue !== 'false' && $propValue !== '0');
399
+                $updateTag = true;
400
+            }
401
+
402
+            if (isset($props[self::GROUPS_PROPERTYNAME])) {
403
+                if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
404
+                    // property only available for admins
405
+                    throw new Forbidden();
406
+                }
407
+
408
+                $propValue = $props[self::GROUPS_PROPERTYNAME];
409
+                $groupIds = explode('|', $propValue);
410
+                $this->tagManager->setTagGroups($tag, $groupIds);
411
+            }
412
+
413
+            if ($updateTag) {
414
+                $node->update($name, $userVisible, $userAssignable);
415
+            }
416
+
417
+            return true;
418
+        });
419
+    }
420 420
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -145,7 +145,7 @@  discard block
 block discarded – undo
145 145
 			if ($node instanceof SystemTagsObjectMappingCollection) {
146 146
 				// also add to collection
147 147
 				$node->createFile($tag->getId());
148
-				$url = $request->getBaseUrl() . 'systemtags/';
148
+				$url = $request->getBaseUrl().'systemtags/';
149 149
 			} else {
150 150
 				$url = $request->getUrl();
151 151
 			}
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
 				$url .= '/';
155 155
 			}
156 156
 
157
-			$response->setHeader('Content-Location', $url . $tag->getId());
157
+			$response->setHeader('Content-Location', $url.$tag->getId());
158 158
 
159 159
 			// created
160 160
 			$response->setStatus(201);
@@ -189,11 +189,11 @@  discard block
 block discarded – undo
189 189
 		$userAssignable = true;
190 190
 
191 191
 		if (isset($data['userVisible'])) {
192
-			$userVisible = (bool)$data['userVisible'];
192
+			$userVisible = (bool) $data['userVisible'];
193 193
 		}
194 194
 
195 195
 		if (isset($data['userAssignable'])) {
196
-			$userAssignable = (bool)$data['userAssignable'];
196
+			$userAssignable = (bool) $data['userAssignable'];
197 197
 		}
198 198
 
199 199
 		$groups = [];
@@ -243,29 +243,29 @@  discard block
 block discarded – undo
243 243
 			return;
244 244
 		}
245 245
 
246
-		$propFind->handle(self::ID_PROPERTYNAME, function () use ($node) {
246
+		$propFind->handle(self::ID_PROPERTYNAME, function() use ($node) {
247 247
 			return $node->getSystemTag()->getId();
248 248
 		});
249 249
 
250
-		$propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function () use ($node) {
250
+		$propFind->handle(self::DISPLAYNAME_PROPERTYNAME, function() use ($node) {
251 251
 			return $node->getSystemTag()->getName();
252 252
 		});
253 253
 
254
-		$propFind->handle(self::USERVISIBLE_PROPERTYNAME, function () use ($node) {
254
+		$propFind->handle(self::USERVISIBLE_PROPERTYNAME, function() use ($node) {
255 255
 			return $node->getSystemTag()->isUserVisible() ? 'true' : 'false';
256 256
 		});
257 257
 
258
-		$propFind->handle(self::USERASSIGNABLE_PROPERTYNAME, function () use ($node) {
258
+		$propFind->handle(self::USERASSIGNABLE_PROPERTYNAME, function() use ($node) {
259 259
 			// this is the tag's inherent property "is user assignable"
260 260
 			return $node->getSystemTag()->isUserAssignable() ? 'true' : 'false';
261 261
 		});
262 262
 
263
-		$propFind->handle(self::CANASSIGN_PROPERTYNAME, function () use ($node) {
263
+		$propFind->handle(self::CANASSIGN_PROPERTYNAME, function() use ($node) {
264 264
 			// this is the effective permission for the current user
265 265
 			return $this->tagManager->canUserAssignTag($node->getSystemTag(), $this->userSession->getUser()) ? 'true' : 'false';
266 266
 		});
267 267
 
268
-		$propFind->handle(self::GROUPS_PROPERTYNAME, function () use ($node) {
268
+		$propFind->handle(self::GROUPS_PROPERTYNAME, function() use ($node) {
269 269
 			if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
270 270
 				// property only available for admins
271 271
 				throw new Forbidden();
@@ -302,14 +302,14 @@  discard block
 block discarded – undo
302 302
 			}
303 303
 		}
304 304
 
305
-		$propFind->handle(self::SYSTEM_TAGS_PROPERTYNAME, function () use ($node) {
305
+		$propFind->handle(self::SYSTEM_TAGS_PROPERTYNAME, function() use ($node) {
306 306
 			$user = $this->userSession->getUser();
307 307
 			if ($user === null) {
308 308
 				return;
309 309
 			}
310 310
 
311 311
 			$tags = $this->getTagsForFile($node->getId(), $user);
312
-			usort($tags, function (ISystemTag $tagA, ISystemTag $tagB): int {
312
+			usort($tags, function(ISystemTag $tagA, ISystemTag $tagB): int {
313 313
 				return Util::naturalSortCompare($tagA->getName(), $tagB->getName());
314 314
 			});
315 315
 			return new SystemTagList($tags, $this->tagManager, $user);
@@ -374,7 +374,7 @@  discard block
 block discarded – undo
374 374
 			self::USERVISIBLE_PROPERTYNAME,
375 375
 			self::USERASSIGNABLE_PROPERTYNAME,
376 376
 			self::GROUPS_PROPERTYNAME,
377
-		], function ($props) use ($node) {
377
+		], function($props) use ($node) {
378 378
 			$tag = $node->getSystemTag();
379 379
 			$name = $tag->getName();
380 380
 			$userVisible = $tag->isUserVisible();
Please login to merge, or discard this patch.