2525 */
2626namespace OCA \DAV \SystemTag ;
2727
28+ use OCA \DAV \Connector \Sabre \Directory ;
29+ use OCA \DAV \Connector \Sabre \Node ;
2830use OCP \IGroupManager ;
2931use OCP \IUserSession ;
3032use OCP \SystemTag \ISystemTag ;
3133use OCP \SystemTag \ISystemTagManager ;
34+ use OCP \SystemTag \ISystemTagObjectMapper ;
3235use OCP \SystemTag \TagAlreadyExistsException ;
3336use Sabre \DAV \Exception \BadRequest ;
3437use Sabre \DAV \Exception \Conflict ;
@@ -56,6 +59,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
5659 public const USERASSIGNABLE_PROPERTYNAME = '{http://owncloud.org/ns}user-assignable ' ;
5760 public const GROUPS_PROPERTYNAME = '{http://owncloud.org/ns}groups ' ;
5861 public const CANASSIGN_PROPERTYNAME = '{http://owncloud.org/ns}can-assign ' ;
62+ public const SYSTEM_TAGS_PROPERTYNAME = '{http://nextcloud.org/ns}system-tags ' ;
5963
6064 /**
6165 * @var \Sabre\DAV\Server $server
@@ -77,17 +81,23 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
7781 */
7882 protected $ groupManager ;
7983
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 ) {
84+ /** @var array<int, int[]> */
85+ private array $ cachedTagMappings = [];
86+ /** @var array<int, ISystemTag> */
87+ private array $ cachedTags = [];
88+
89+ private ISystemTagObjectMapper $ tagMapper ;
90+
91+ public function __construct (
92+ ISystemTagManager $ tagManager ,
93+ IGroupManager $ groupManager ,
94+ IUserSession $ userSession ,
95+ ISystemTagObjectMapper $ tagMapper ,
96+ ) {
8897 $ this ->tagManager = $ tagManager ;
8998 $ this ->userSession = $ userSession ;
9099 $ this ->groupManager = $ groupManager ;
100+ $ this ->tagMapper = $ tagMapper ;
91101 }
92102
93103 /**
@@ -220,6 +230,11 @@ public function handleGetProperties(
220230 PropFind $ propFind ,
221231 \Sabre \DAV \INode $ node
222232 ) {
233+ if ($ node instanceof Node) {
234+ $ this ->propfindForFile ($ propFind , $ node );
235+ return ;
236+ }
237+
223238 if (!($ node instanceof SystemTagNode) && !($ node instanceof SystemTagMappingNode)) {
224239 return ;
225240 }
@@ -260,6 +275,68 @@ public function handleGetProperties(
260275 });
261276 }
262277
278+ private function propfindForFile (PropFind $ propFind , Node $ node ): void {
279+ if ($ node instanceof Directory
280+ && $ propFind ->getDepth () !== 0
281+ && !is_null ($ propFind ->getStatus (self ::SYSTEM_TAGS_PROPERTYNAME ))) {
282+ // note: pre-fetching only supported for depth <= 1
283+ $ folderContent = $ node ->getNode ()->getDirectoryListing ();
284+ $ fileIds [] = (int )$ node ->getId ();
285+ foreach ($ folderContent as $ info ) {
286+ $ fileIds [] = (int )$ info ->getId ();
287+ }
288+ $ tags = $ this ->tagMapper ->getTagIdsForObjects ($ fileIds , 'files ' );
289+
290+ $ this ->cachedTagMappings = $ this ->cachedTagMappings + $ tags ;
291+ $ emptyFileIds = array_diff ($ fileIds , array_keys ($ tags ));
292+ // also cache the ones that were not found
293+ foreach ($ emptyFileIds as $ fileId ) {
294+ $ this ->cachedTagMappings [$ fileId ] = [];
295+ }
296+ }
297+
298+ $ propFind ->handle (self ::SYSTEM_TAGS_PROPERTYNAME , function () use ($ node ) {
299+ $ tags = $ this ->getTagsForFile ($ node ->getId ());
300+ return new SystemTagList ($ tags , $ this ->tagManager , $ this ->userSession ->getUser ());
301+ });
302+ }
303+
304+ /**
305+ * @param int $fileId
306+ * @return ISystemTag[]
307+ */
308+ private function getTagsForFile (int $ fileId ): array {
309+ $ user = $ this ->userSession ->getUser ();
310+ if (isset ($ this ->cachedTagMappings [$ fileId ])) {
311+ $ tagIds = $ this ->cachedTagMappings [$ fileId ];
312+ } else {
313+ $ tags = $ this ->tagMapper ->getTagIdsForObjects ([$ fileId ], 'files ' );
314+ $ fileTags = current ($ tags );
315+ if ($ fileTags ) {
316+ $ tagIds = $ fileTags ;
317+ } else {
318+ $ tagIds = [];
319+ }
320+ }
321+ $ tags = array_filter (array_map (function ($ tagId ) {
322+ return $ this ->cachedTags [$ tagId ] ?? null ;
323+ }, $ tagIds ));
324+
325+ $ uncachedTagIds = array_filter ($ tagIds , function ($ tagId ): bool {
326+ return !isset ($ this ->cachedTags [$ tagId ]);
327+ });
328+ if (count ($ uncachedTagIds )) {
329+ $ retrievedTags = $ this ->tagManager ->getTagsByIds ($ uncachedTagIds );
330+ foreach ($ retrievedTags as $ tag ) {
331+ $ this ->cachedTags [$ tag ->getId ()] = $ tag ;
332+ }
333+ $ tags += $ retrievedTags ;
334+ }
335+ return array_filter ($ tags , function (ISystemTag $ tag ) use ($ user ) {
336+ return $ this ->tagManager ->canUserSeeTag ($ tag , $ user );
337+ });
338+ }
339+
263340 /**
264341 * Updates tag attributes
265342 *
0 commit comments