Skip to content

Commit 29e84ef

Browse files
authored
Merge pull request #2436 from nextcloud/fix/context-chat-upgrade
fix(ContextChat): Upgrade context chat integration to use OCP classes
2 parents 4441397 + 154d2f2 commit 29e84ef

7 files changed

Lines changed: 379 additions & 34 deletions

File tree

lib/AppInfo/Application.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@
2929
use OCA\Bookmarks\Reference\BookmarkReferenceProvider;
3030
use OCA\Bookmarks\Search\Provider;
3131
use OCA\Bookmarks\Service\TreeCacheManager;
32-
use OCA\ContextChat\Event\ContentProviderRegisterEvent;
3332
use OCP\AppFramework\App;
3433
use OCP\AppFramework\Bootstrap\IBootContext;
3534
use OCP\AppFramework\Bootstrap\IBootstrap;
3635
use OCP\AppFramework\Bootstrap\IRegistrationContext;
3736
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
3837
use OCP\Collaboration\Reference\RenderReferenceEvent;
38+
use OCP\ContextChat\Events\ContentProviderRegisterEvent;
39+
use OCP\ContextChat\IContentManager;
3940
use OCP\EventDispatcher\IEventDispatcher;
4041
use OCP\Group\Events\BeforeGroupDeletedEvent;
4142
use OCP\Group\Events\UserAddedEvent;
@@ -104,15 +105,18 @@ public function register(IRegistrationContext $context): void {
104105
*/
105106
public function boot(IBootContext $context): void {
106107
// Register with ContextChat
107-
if (class_exists(ContentProviderRegisterEvent::class)) {
108-
$this->getContainer()->get(ContextChatProvider::class)->register();
109-
$eventDispatcher = $this->getContainer()->get(IEventDispatcher::class);
110-
$eventDispatcher->addServiceListener(InsertEvent::class, ContextChatProvider::class);
111-
$eventDispatcher->addServiceListener(ManipulateEvent::class, ContextChatProvider::class);
112-
$eventDispatcher->addServiceListener(BeforeDeleteEvent::class, ContextChatProvider::class);
108+
$serverContainer = $context->getServerContainer();
109+
if ($serverContainer->has(IContentManager::class)) {
110+
$contentManager = $serverContainer->get(IContentManager::class);
111+
if ($contentManager->isContextChatAvailable()) {
112+
$eventDispatcher = $this->getContainer()->get(IEventDispatcher::class);
113+
$eventDispatcher->addServiceListener(InsertEvent::class, ContextChatProvider::class);
114+
$eventDispatcher->addServiceListener(ManipulateEvent::class, ContextChatProvider::class);
115+
$eventDispatcher->addServiceListener(BeforeDeleteEvent::class, ContextChatProvider::class);
116+
}
113117
}
114118
// Register with Nextcloud Flow
115-
$container = $context->getServerContainer();
119+
$container = $serverContainer;
116120
CreateBookmark::register($container->get(IEventDispatcher::class));
117121
}
118122
}

lib/BackgroundJobs/ContextChatIndexJob.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@
1212
use OCA\Bookmarks\ContextChat\ContextChatProvider;
1313
use OCA\Bookmarks\Service\BookmarkService;
1414
use OCA\Bookmarks\Service\UserSettingsService;
15-
use OCA\ContextChat\Public\ContentItem;
16-
use OCA\ContextChat\Public\ContentManager;
1715
use OCP\AppFramework\Utility\ITimeFactory;
1816
use OCP\BackgroundJob\QueuedJob;
17+
use OCP\ContextChat\ContentItem;
18+
use OCP\ContextChat\IContentManager;
1919
use OCP\IUserManager;
2020

2121
class ContextChatIndexJob extends QueuedJob {
2222

2323
public function __construct(
2424
ITimeFactory $timeFactory,
2525
private BookmarkService $bookmarkService,
26-
private ?ContentManager $contentManager,
26+
private ?IContentManager $contentManager,
2727
private IUserManager $userManager,
2828
private ContextChatProvider $provider,
2929
private UserSettingsService $userSettings,
@@ -32,7 +32,7 @@ public function __construct(
3232
}
3333

3434
protected function run($argument) {
35-
if ($this->contentManager === null) {
35+
if ($this->contentManager === null || !$this->contentManager->isContextChatAvailable()) {
3636
return;
3737
}
3838
if (!isset($argument['user'])) {

lib/ContextChat/ContextChatProvider.php

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
use OCA\Bookmarks\Events\ManipulateEvent;
1818
use OCA\Bookmarks\Service\BookmarkService;
1919
use OCA\Bookmarks\Service\UserSettingsService;
20-
use OCA\ContextChat\Event\ContentProviderRegisterEvent;
21-
use OCA\ContextChat\Public\ContentItem;
22-
use OCA\ContextChat\Public\ContentManager;
23-
use OCA\ContextChat\Public\IContentProvider;
2420
use OCP\BackgroundJob\IJobList;
21+
use OCP\ContextChat\ContentItem;
22+
use OCP\ContextChat\Events\ContentProviderRegisterEvent;
23+
use OCP\ContextChat\IContentManager;
24+
use OCP\ContextChat\IContentProvider;
2525
use OCP\EventDispatcher\Event;
2626
use OCP\EventDispatcher\IEventListener;
2727
use OCP\IUser;
@@ -35,18 +35,18 @@ class ContextChatProvider implements IContentProvider, IEventListener {
3535
public function __construct(
3636
private BookmarkService $bookmarkService,
3737
private IUserManager $userManager,
38-
private ?ContentManager $contentManager,
38+
private ?IContentManager $contentManager,
3939
private IJobList $jobList,
4040
private UserSettingsService $userSettings,
4141
) {
4242
}
4343

4444
public function handle(Event $event): void {
45-
if ($this->contentManager === null) {
45+
if ($event instanceof ContentProviderRegisterEvent) {
46+
$this->register($event);
4647
return;
4748
}
48-
if ($event instanceof ContentProviderRegisterEvent) {
49-
$this->register();
49+
if (!$this->isContextChatAvailable()) {
5050
return;
5151
}
5252
if (!$event instanceof ChangeEvent) {
@@ -84,10 +84,23 @@ public function handle(Event $event): void {
8484
}
8585
}
8686

87-
public function register(): void {
87+
public function register(?ContentProviderRegisterEvent $event = null): void {
88+
if ($event !== null) {
89+
$event->registerContentProvider($this->getAppId(), $this->getId(), self::class);
90+
return;
91+
}
92+
93+
if (!$this->isContextChatAvailable()) {
94+
return;
95+
}
96+
8897
$this->contentManager->registerContentProvider($this->getAppId(), $this->getId(), self::class);
8998
}
9099

100+
private function isContextChatAvailable(): bool {
101+
return $this->contentManager !== null && $this->contentManager->isContextChatAvailable();
102+
}
103+
91104
/**
92105
* The ID of the provider
93106
*

lib/Controller/WebViewController.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use OCA\Bookmarks\Service\SettingsService;
1818
use OCA\Bookmarks\Service\UserSettingsService;
1919
use OCA\Viewer\Event\LoadViewer;
20-
use OCP\App\IAppManager;
2120
use OCP\AppFramework\Controller;
2221
use OCP\AppFramework\Db\DoesNotExistException;
2322
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
@@ -31,6 +30,7 @@
3130
use OCP\AppFramework\Http\NotFoundResponse;
3231
use OCP\AppFramework\Http\StreamResponse;
3332
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
33+
use OCP\ContextChat\IContentManager;
3434
use OCP\DB\Exception;
3535
use OCP\EventDispatcher\IEventDispatcher;
3636
use OCP\IConfig;
@@ -57,7 +57,7 @@ public function __construct(
5757
private BookmarkMapper $bookmarkMapper,
5858
private UserSettingsService $userSettingsService,
5959
private SettingsService $settings,
60-
private IAppManager $appManager,
60+
private ?IContentManager $contentManager,
6161
private IConfig $config,
6262
private IEventDispatcher $eventDispatcher,
6363
private LoggerInterface $logger,
@@ -108,7 +108,7 @@ public function index(): AugmentedTemplateResponse {
108108
}
109109

110110
$this->initialState->provideInitialState($this->appName, 'tags', $this->tagsController->fullTags(true)->getData());
111-
$this->initialState->provideInitialState($this->appName, 'contextChatInstalled', $this->appManager->isEnabledForUser('context_chat'));
111+
$this->initialState->provideInitialState($this->appName, 'contextChatInstalled', $this->contentManager?->isContextChatAvailable() ?? false);
112112
$this->initialState->provideInitialState($this->appName, 'appStoreEnabled', $this->config->getSystemValueBool('appstoreenabled', true));
113113

114114
$settings = $this->userSettingsService->toArray();

tests/ContextChatIndexJobTest.php

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
/*
4+
* Copyright (c) 2026. The Nextcloud Bookmarks contributors.
5+
*
6+
* This file is licensed under the Affero General Public License version 3 or later. See the COPYING file.
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace OCA\Bookmarks\Tests;
12+
13+
use OCA\Bookmarks\AppInfo\Application;
14+
use OCA\Bookmarks\BackgroundJobs\ContextChatIndexJob;
15+
use OCA\Bookmarks\ContextChat\ContextChatProvider;
16+
use OCA\Bookmarks\Db\Bookmark;
17+
use OCA\Bookmarks\Service\BookmarkService;
18+
use OCA\Bookmarks\Service\UserSettingsService;
19+
use OCP\AppFramework\Utility\ITimeFactory;
20+
use OCP\BackgroundJob\IJobList;
21+
use OCP\ContextChat\ContentItem;
22+
use OCP\ContextChat\IContentManager;
23+
use OCP\IUser;
24+
use OCP\IUserManager;
25+
use PHPUnit\Framework\MockObject\MockObject;
26+
27+
class ContextChatIndexJobTest extends TestCase {
28+
private ITimeFactory&MockObject $timeFactory;
29+
private BookmarkService&MockObject $bookmarkService;
30+
private IContentManager&MockObject $contentManager;
31+
private IUserManager&MockObject $userManager;
32+
private ContextChatProvider&MockObject $provider;
33+
private UserSettingsService&MockObject $userSettings;
34+
private IJobList&MockObject $jobList;
35+
private ContextChatIndexJob $job;
36+
37+
protected function setUp(): void {
38+
parent::setUp();
39+
40+
$this->timeFactory = $this->createMock(ITimeFactory::class);
41+
$this->timeFactory->method('getTime')->willReturn(1710000000);
42+
$this->bookmarkService = $this->createMock(BookmarkService::class);
43+
$this->contentManager = $this->createMock(IContentManager::class);
44+
$this->userManager = $this->createMock(IUserManager::class);
45+
$this->provider = $this->createMock(ContextChatProvider::class);
46+
$this->userSettings = $this->createMock(UserSettingsService::class);
47+
$this->jobList = $this->createMock(IJobList::class);
48+
49+
$this->job = new ContextChatIndexJob(
50+
$this->timeFactory,
51+
$this->bookmarkService,
52+
$this->contentManager,
53+
$this->userManager,
54+
$this->provider,
55+
$this->userSettings,
56+
);
57+
$this->job->setArgument(['user' => 'alice']);
58+
}
59+
60+
public function testDoesNothingWhenContextChatUnavailable(): void {
61+
$this->contentManager->expects(self::once())
62+
->method('isContextChatAvailable')
63+
->willReturn(false);
64+
$this->userManager->expects(self::never())->method('get');
65+
$this->contentManager->expects(self::never())->method('submitContent');
66+
$this->jobList->expects(self::once())->method('remove');
67+
$this->jobList->expects(self::once())->method('setLastRun');
68+
$this->jobList->expects(self::once())->method('setExecutionTime');
69+
70+
$this->job->start($this->jobList);
71+
}
72+
73+
public function testIndexesBookmarksForEnabledUser(): void {
74+
$user = $this->createConfiguredMock(IUser::class, ['getUID' => 'alice']);
75+
$bookmarks = [
76+
$this->createBookmark(1, 'alice', 'One', 'First content'),
77+
$this->createBookmark(2, 'alice', 'Two', 'Second content'),
78+
];
79+
80+
$this->contentManager->expects(self::once())
81+
->method('isContextChatAvailable')
82+
->willReturn(true);
83+
$this->userManager->expects(self::once())
84+
->method('get')
85+
->with('alice')
86+
->willReturn($user);
87+
$this->userSettings->expects(self::once())
88+
->method('setUserId')
89+
->with('alice');
90+
$this->userSettings->expects(self::once())
91+
->method('get')
92+
->with('contextchat.enabled')
93+
->willReturn('true');
94+
$this->bookmarkService->expects(self::once())
95+
->method('getIterator')
96+
->with('alice')
97+
->willReturn((function () use ($bookmarks) {
98+
foreach ($bookmarks as $bookmark) {
99+
yield $bookmark;
100+
}
101+
})());
102+
$this->provider->expects(self::exactly(2))
103+
->method('getId')
104+
->willReturn('bookmarks');
105+
$this->contentManager->expects(self::once())
106+
->method('submitContent')
107+
->with(
108+
Application::APP_ID,
109+
self::callback(function (array $items): bool {
110+
self::assertCount(2, $items);
111+
self::assertContainsOnlyInstancesOf(ContentItem::class, $items);
112+
self::assertSame('1', $items[0]->itemId);
113+
self::assertSame('2', $items[1]->itemId);
114+
self::assertSame(['alice'], $items[0]->users);
115+
return true;
116+
})
117+
);
118+
$this->jobList->expects(self::once())->method('remove');
119+
$this->jobList->expects(self::once())->method('setLastRun');
120+
$this->jobList->expects(self::once())->method('setExecutionTime');
121+
122+
$this->job->start($this->jobList);
123+
}
124+
125+
public function testDoesNotIndexWhenUserDisabledContextChat(): void {
126+
$user = $this->createConfiguredMock(IUser::class, ['getUID' => 'alice']);
127+
128+
$this->contentManager->expects(self::once())
129+
->method('isContextChatAvailable')
130+
->willReturn(true);
131+
$this->userManager->expects(self::once())
132+
->method('get')
133+
->with('alice')
134+
->willReturn($user);
135+
$this->userSettings->expects(self::once())
136+
->method('setUserId')
137+
->with('alice');
138+
$this->userSettings->expects(self::once())
139+
->method('get')
140+
->with('contextchat.enabled')
141+
->willReturn('false');
142+
$this->bookmarkService->expects(self::never())->method('getIterator');
143+
$this->contentManager->expects(self::never())->method('submitContent');
144+
$this->jobList->expects(self::once())->method('remove');
145+
$this->jobList->expects(self::once())->method('setLastRun');
146+
$this->jobList->expects(self::once())->method('setExecutionTime');
147+
148+
$this->job->start($this->jobList);
149+
}
150+
151+
private function createBookmark(int $id, string $userId, string $title, string $textContent): Bookmark {
152+
return Bookmark::fromArray([
153+
'id' => $id,
154+
'userId' => $userId,
155+
'url' => 'https://example.com/' . $id,
156+
'title' => $title,
157+
'description' => '',
158+
'lastmodified' => 1710000000 + $id,
159+
'added' => 1710000000,
160+
'clickcount' => 0,
161+
'lastPreview' => 0,
162+
'available' => true,
163+
'archivedFile' => 0,
164+
'textContent' => $textContent,
165+
'htmlContent' => '',
166+
'urlHash' => 'hash-' . $id,
167+
]);
168+
}
169+
}

0 commit comments

Comments
 (0)