Skip to content

Commit 634e8d2

Browse files
committed
fix: Dispatch favorite event with an actual path
The `$path` argument was added in #48612, but was never actually used by the callers. The path was therefore missing in the favorite/unfavorite events, which lead to a broken activity information. I also added a fallback to handle `addToFavorites` and `removeFromFavorites`, which are part of a public API, and are calling `tagAs` and `untag` without `$path`. Fix nextcloud/activity#2134 Signed-off-by: Louis Chemineau <louis@chmn.me>
1 parent 9eb883c commit 634e8d2

File tree

7 files changed

+65
-28
lines changed

7 files changed

+65
-28
lines changed

apps/dav/lib/Connector/Sabre/TagsPlugin.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,9 @@ private function prefetchTagsForFileIds(array $fileIds) {
176176
*
177177
* @param int $fileId
178178
* @param array $tags array of tag strings
179+
* @param string $path path of the file
179180
*/
180-
private function updateTags($fileId, $tags) {
181+
private function updateTags($fileId, $tags, string $path) {
181182
$tagger = $this->getTagger();
182183
$currentTags = $this->getTags($fileId);
183184

@@ -186,14 +187,14 @@ private function updateTags($fileId, $tags) {
186187
if ($tag === self::TAG_FAVORITE) {
187188
continue;
188189
}
189-
$tagger->tagAs($fileId, $tag);
190+
$tagger->tagAs($fileId, $tag, $path);
190191
}
191192
$deletedTags = array_diff($currentTags, $tags);
192193
foreach ($deletedTags as $tag) {
193194
if ($tag === self::TAG_FAVORITE) {
194195
continue;
195196
}
196-
$tagger->unTag($fileId, $tag);
197+
$tagger->unTag($fileId, $tag, $path);
197198
}
198199
}
199200

@@ -269,16 +270,16 @@ public function handleUpdateProperties($path, PropPatch $propPatch) {
269270
return;
270271
}
271272

272-
$propPatch->handle(self::TAGS_PROPERTYNAME, function ($tagList) use ($node) {
273-
$this->updateTags($node->getId(), $tagList->getTags());
273+
$propPatch->handle(self::TAGS_PROPERTYNAME, function ($tagList) use ($node, $path) {
274+
$this->updateTags($node->getId(), $tagList->getTags(), $path);
274275
return true;
275276
});
276277

277278
$propPatch->handle(self::FAVORITE_PROPERTYNAME, function ($favState) use ($node, $path) {
278279
if ((int)$favState === 1 || $favState === 'true') {
279-
$this->getTagger()->tagAs($node->getId(), self::TAG_FAVORITE);
280+
$this->getTagger()->tagAs($node->getId(), self::TAG_FAVORITE, $path);
280281
} else {
281-
$this->getTagger()->unTag($node->getId(), self::TAG_FAVORITE);
282+
$this->getTagger()->unTag($node->getId(), self::TAG_FAVORITE, $path);
282283
}
283284

284285
if (is_null($favState)) {

apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ public function testUpdateTags(): void {
262262

263263
// then tag as tag1 and tag2
264264
$calls = [
265-
[123, 'tag1'],
266-
[123, 'tag2'],
265+
[123, 'tag1', '/dummypath'],
266+
[123, 'tag2', '/dummypath'],
267267
];
268268
$this->tagger->expects($this->exactly(count($calls)))
269269
->method('tagAs')
@@ -315,8 +315,8 @@ public function testUpdateTagsFromScratch(): void {
315315

316316
// then tag as tag1 and tag2
317317
$calls = [
318-
[123, 'tag1'],
319-
[123, 'tag2'],
318+
[123, 'tag1', '/dummypath'],
319+
[123, 'tag2', '/dummypath'],
320320
];
321321
$this->tagger->expects($this->exactly(count($calls)))
322322
->method('tagAs')

apps/files/lib/Service/TagService.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ public function updateFileTags($path, $tags) {
5454

5555
$newTags = array_diff($tags, $currentTags);
5656
foreach ($newTags as $tag) {
57-
$this->tagger->tagAs($fileId, $tag);
57+
$this->tagger->tagAs($fileId, $tag, $path);
5858
}
5959
$deletedTags = array_diff($currentTags, $tags);
6060
foreach ($deletedTags as $tag) {
61-
$this->tagger->unTag($fileId, $tag);
61+
$this->tagger->unTag($fileId, $tag, $path);
6262
}
6363

6464
// TODO: re-read from tagger to make sure the

lib/private/TagManager.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCP\EventDispatcher\Event;
1414
use OCP\EventDispatcher\IEventDispatcher;
1515
use OCP\EventDispatcher\IEventListener;
16+
use OCP\Files\IRootFolder;
1617
use OCP\IDBConnection;
1718
use OCP\ITagManager;
1819
use OCP\ITags;
@@ -31,6 +32,7 @@ public function __construct(
3132
private IDBConnection $connection,
3233
private LoggerInterface $logger,
3334
private IEventDispatcher $dispatcher,
35+
private IRootFolder $rootFolder,
3436
) {
3537
}
3638

@@ -56,7 +58,8 @@ public function load($type, $defaultTags = [], $includeShared = false, $userId =
5658
}
5759
$userId = $this->userSession->getUser()->getUId();
5860
}
59-
return new Tags($this->mapper, $userId, $type, $this->logger, $this->connection, $this->dispatcher, $this->userSession, $defaultTags);
61+
$userFolder = $this->rootFolder->getUserFolder($userId);
62+
return new Tags($this->mapper, $userId, $type, $this->logger, $this->connection, $this->dispatcher, $this->userSession, $userFolder, $defaultTags);
6063
}
6164

6265
/**

lib/private/Tags.php

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use OCP\EventDispatcher\IEventDispatcher;
1515
use OCP\Files\Events\NodeAddedToFavorite;
1616
use OCP\Files\Events\NodeRemovedFromFavorite;
17+
use OCP\Files\Folder;
1718
use OCP\IDBConnection;
1819
use OCP\ITags;
1920
use OCP\IUserSession;
@@ -65,6 +66,7 @@ public function __construct(
6566
private IDBConnection $db,
6667
private IEventDispatcher $dispatcher,
6768
private IUserSession $userSession,
69+
private Folder $userFolder,
6870
array $defaultTags = [],
6971
) {
7072
$this->owners = [$this->user];
@@ -495,12 +497,8 @@ public function removeFromFavorites($objid) {
495497

496498
/**
497499
* Creates a tag/object relation.
498-
*
499-
* @param int $objid The id of the object
500-
* @param string $tag The id or name of the tag
501-
* @return boolean Returns false on error.
502500
*/
503-
public function tagAs($objid, $tag, string $path = '') {
501+
public function tagAs($objid, $tag, ?string $path = null) {
504502
if (is_string($tag) && !is_numeric($tag)) {
505503
$tag = trim($tag);
506504
if ($tag === '') {
@@ -531,19 +529,24 @@ public function tagAs($objid, $tag, string $path = '') {
531529
return false;
532530
}
533531
if ($tag === ITags::TAG_FAVORITE) {
532+
if ($path === null) {
533+
$node = $this->userFolder->getFirstNodeById($objid);
534+
if ($node !== null) {
535+
$path = $node->getPath();
536+
} else {
537+
throw new Exception('Failed to favorite: node with id ' . $objid . ' not found');
538+
}
539+
}
540+
534541
$this->dispatcher->dispatchTyped(new NodeAddedToFavorite($this->userSession->getUser(), $objid, $path));
535542
}
536543
return true;
537544
}
538545

539546
/**
540547
* Delete single tag/object relation from the db
541-
*
542-
* @param int $objid The id of the object
543-
* @param string $tag The id or name of the tag
544-
* @return boolean
545548
*/
546-
public function unTag($objid, $tag, string $path = '') {
549+
public function unTag($objid, $tag, ?string $path = null) {
547550
if (is_string($tag) && !is_numeric($tag)) {
548551
$tag = trim($tag);
549552
if ($tag === '') {
@@ -571,6 +574,15 @@ public function unTag($objid, $tag, string $path = '') {
571574
return false;
572575
}
573576
if ($tag === ITags::TAG_FAVORITE) {
577+
if ($path === null) {
578+
$node = $this->userFolder->getFirstNodeById($objid);
579+
if ($node !== null) {
580+
$path = $node->getPath();
581+
} else {
582+
throw new Exception('Failed to unfavorite: node with id ' . $objid . ' not found');
583+
}
584+
}
585+
574586
$this->dispatcher->dispatchTyped(new NodeRemovedFromFavorite($this->userSession->getUser(), $objid, $path));
575587
}
576588
return true;

lib/public/ITags.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,20 +184,26 @@ public function removeFromFavorites($objid);
184184
*
185185
* @param int $objid The id of the object
186186
* @param string $tag The id or name of the tag
187+
* @param string $path The optional path of the node. Used when dispatching the favorite change event.
187188
* @return boolean Returns false on database error.
188189
* @since 6.0.0
190+
* @since 31.0.0 Added the $path argument.
191+
* @since 33.0.0 Change $path default value from '' to null.
189192
*/
190-
public function tagAs($objid, $tag);
193+
public function tagAs($objid, $tag, ?string $path = null);
191194

192195
/**
193196
* Delete single tag/object relation from the db
194197
*
195198
* @param int $objid The id of the object
196199
* @param string $tag The id or name of the tag
200+
* @param string $path The optional path of the node. Used when dispatching the favorite change event.
197201
* @return boolean
198202
* @since 6.0.0
203+
* @since 31.0.0 Added the $path argument.
204+
* @since 33.0.0 Change $path default value from '' to null.
199205
*/
200-
public function unTag($objid, $tag);
206+
public function unTag($objid, $tag, ?string $path = null);
201207

202208
/**
203209
* Delete tags from the database

tests/lib/TagsTest.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
use OC\Tagging\TagMapper;
1212
use OC\TagManager;
1313
use OCP\EventDispatcher\IEventDispatcher;
14+
use OCP\Files\Folder;
15+
use OCP\Files\IRootFolder;
16+
use OCP\Files\Node;
1417
use OCP\IDBConnection;
1518
use OCP\ITagManager;
1619
use OCP\IUser;
@@ -52,10 +55,22 @@ protected function setUp(): void {
5255
->expects($this->any())
5356
->method('getUser')
5457
->willReturn($this->user);
58+
$userFolder = $this->createMock(Folder::class);
59+
$node = $this->createMock(Node::class);
60+
$this->rootFolder = $this->createMock(IRootFolder::class);
61+
$this->rootFolder
62+
->method('getUserFolder')
63+
->willReturnCallback(fn () => $userFolder);
64+
$userFolder
65+
->method('getFirstNodeById')
66+
->willReturnCallback(fn () => $node);
67+
$node
68+
->method('getPath')
69+
->willReturnCallback(fn () => 'file.txt');
5570

5671
$this->objectType = $this->getUniqueID('type_');
5772
$this->tagMapper = new TagMapper(Server::get(IDBConnection::class));
58-
$this->tagMgr = new TagManager($this->tagMapper, $this->userSession, Server::get(IDBConnection::class), Server::get(LoggerInterface::class), Server::get(IEventDispatcher::class));
73+
$this->tagMgr = new TagManager($this->tagMapper, $this->userSession, Server::get(IDBConnection::class), Server::get(LoggerInterface::class), Server::get(IEventDispatcher::class), $this->rootFolder);
5974
}
6075

6176
protected function tearDown(): void {
@@ -72,7 +87,7 @@ public function testTagManagerWithoutUserReturnsNull(): void {
7287
->expects($this->any())
7388
->method('getUser')
7489
->willReturn(null);
75-
$this->tagMgr = new TagManager($this->tagMapper, $this->userSession, Server::get(IDBConnection::class), Server::get(LoggerInterface::class), Server::get(IEventDispatcher::class));
90+
$this->tagMgr = new TagManager($this->tagMapper, $this->userSession, Server::get(IDBConnection::class), Server::get(LoggerInterface::class), Server::get(IEventDispatcher::class), $this->rootFolder);
7691
$this->assertNull($this->tagMgr->load($this->objectType));
7792
}
7893

0 commit comments

Comments
 (0)