2525 */
2626namespace OCA \DAV \SystemTag ;
2727
28+ use OCA \DAV \Connector \Sabre \Directory ;
29+ use OCA \DAV \Connector \Sabre \Node ;
2830use OCP \IGroupManager ;
31+ use OCP \IUser ;
2932use OCP \IUserSession ;
3033use OCP \SystemTag \ISystemTag ;
3134use OCP \SystemTag \ISystemTagManager ;
35+ use OCP \SystemTag \ISystemTagObjectMapper ;
3236use OCP \SystemTag \TagAlreadyExistsException ;
3337use Sabre \DAV \Exception \BadRequest ;
3438use Sabre \DAV \Exception \Conflict ;
@@ -56,6 +60,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
5660 public const USERASSIGNABLE_PROPERTYNAME = '{http://owncloud.org/ns}user-assignable ' ;
5761 public const GROUPS_PROPERTYNAME = '{http://owncloud.org/ns}groups ' ;
5862 public const CANASSIGN_PROPERTYNAME = '{http://owncloud.org/ns}can-assign ' ;
63+ public const SYSTEM_TAGS_PROPERTYNAME = '{http://nextcloud.org/ns}system-tags ' ;
5964
6065 /**
6166 * @var \Sabre\DAV\Server $server
@@ -77,17 +82,23 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
7782 */
7883 protected $ groupManager ;
7984
80- /**
81- * @param ISystemTagManager $tagManager tag manager
82- * @param IGroupManager $groupManager
83- * @param IUserSession $userSession
84- */
85- public function __construct (ISystemTagManager $ tagManager ,
86- IGroupManager $ groupManager ,
87- IUserSession $ userSession ) {
85+ /** @var array<int, string[]> */
86+ private array $ cachedTagMappings = [];
87+ /** @var array<string, ISystemTag> */
88+ private array $ cachedTags = [];
89+
90+ private ISystemTagObjectMapper $ tagMapper ;
91+
92+ public function __construct (
93+ ISystemTagManager $ tagManager ,
94+ IGroupManager $ groupManager ,
95+ IUserSession $ userSession ,
96+ ISystemTagObjectMapper $ tagMapper ,
97+ ) {
8898 $ this ->tagManager = $ tagManager ;
8999 $ this ->userSession = $ userSession ;
90100 $ this ->groupManager = $ groupManager ;
101+ $ this ->tagMapper = $ tagMapper ;
91102 }
92103
93104 /**
@@ -215,11 +226,18 @@ private function createTag($data, $contentType = 'application/json') {
215226 *
216227 * @param PropFind $propFind
217228 * @param \Sabre\DAV\INode $node
229+ *
230+ * @return void
218231 */
219232 public function handleGetProperties (
220233 PropFind $ propFind ,
221234 \Sabre \DAV \INode $ node
222235 ) {
236+ if ($ node instanceof Node) {
237+ $ this ->propfindForFile ($ propFind , $ node );
238+ return ;
239+ }
240+
223241 if (!($ node instanceof SystemTagNode) && !($ node instanceof SystemTagMappingNode)) {
224242 return ;
225243 }
@@ -260,6 +278,79 @@ public function handleGetProperties(
260278 });
261279 }
262280
281+ private function propfindForFile (PropFind $ propFind , Node $ node ): void {
282+ if ($ node instanceof Directory
283+ && $ propFind ->getDepth () !== 0
284+ && !is_null ($ propFind ->getStatus (self ::SYSTEM_TAGS_PROPERTYNAME ))) {
285+ $ fileIds = [$ node ->getId ()];
286+
287+ // note: pre-fetching only supported for depth <= 1
288+ $ folderContent = $ node ->getNode ()->getDirectoryListing ();
289+ foreach ($ folderContent as $ info ) {
290+ $ fileIds [] = $ info ->getId ();
291+ }
292+
293+ $ tags = $ this ->tagMapper ->getTagIdsForObjects ($ fileIds , 'files ' );
294+
295+ $ this ->cachedTagMappings = $ this ->cachedTagMappings + $ tags ;
296+ $ emptyFileIds = array_diff ($ fileIds , array_keys ($ tags ));
297+
298+ // also cache the ones that were not found
299+ foreach ($ emptyFileIds as $ fileId ) {
300+ $ this ->cachedTagMappings [$ fileId ] = [];
301+ }
302+ }
303+
304+ $ propFind ->handle (self ::SYSTEM_TAGS_PROPERTYNAME , function () use ($ node ) {
305+ $ user = $ this ->userSession ->getUser ();
306+ if ($ user === null ) {
307+ return ;
308+ }
309+
310+ $ tags = $ this ->getTagsForFile ($ node ->getId (), $ user );
311+ return new SystemTagList ($ tags , $ this ->tagManager , $ user );
312+ });
313+ }
314+
315+ /**
316+ * @param int $fileId
317+ * @return ISystemTag[]
318+ */
319+ private function getTagsForFile (int $ fileId , IUser $ user ): array {
320+
321+ if (isset ($ this ->cachedTagMappings [$ fileId ])) {
322+ $ tagIds = $ this ->cachedTagMappings [$ fileId ];
323+ } else {
324+ $ tags = $ this ->tagMapper ->getTagIdsForObjects ([$ fileId ], 'files ' );
325+ $ fileTags = current ($ tags );
326+ if ($ fileTags ) {
327+ $ tagIds = $ fileTags ;
328+ } else {
329+ $ tagIds = [];
330+ }
331+ }
332+
333+ $ tags = array_filter (array_map (function (string $ tagId ) {
334+ return $ this ->cachedTags [$ tagId ] ?? null ;
335+ }, $ tagIds ));
336+
337+ $ uncachedTagIds = array_filter ($ tagIds , function (string $ tagId ): bool {
338+ return !isset ($ this ->cachedTags [$ tagId ]);
339+ });
340+
341+ if (count ($ uncachedTagIds )) {
342+ $ retrievedTags = $ this ->tagManager ->getTagsByIds ($ uncachedTagIds );
343+ foreach ($ retrievedTags as $ tag ) {
344+ $ this ->cachedTags [$ tag ->getId ()] = $ tag ;
345+ }
346+ $ tags += $ retrievedTags ;
347+ }
348+
349+ return array_filter ($ tags , function (ISystemTag $ tag ) use ($ user ) {
350+ return $ this ->tagManager ->canUserSeeTag ($ tag , $ user );
351+ });
352+ }
353+
263354 /**
264355 * Updates tag attributes
265356 *
0 commit comments