Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@
'requirements' => ['apiVersion' => '(1.0)', 'collectiveId' => '\d+']],
['name' => 'collectiveUserSettings#setFavoritePages', 'url' => '/api/v{apiVersion}/collectives/{collectiveId}/userSettings/favoritePages', 'verb' => 'PUT',
'requirements' => ['apiVersion' => '(1.0)', 'collectiveId' => '\d+']],
['name' => 'collectiveUserSettings#setNotify', 'url' => '/api/v{apiVersion}/collectives/{collectiveId}/userSettings/notify', 'verb' => 'PUT',
'requirements' => ['apiVersion' => '(1.0)', 'collectiveId' => '\d+']],

// Session API
['name' => 'session#create', 'url' => '/api/v{apiVersion}/collectives/{collectiveId}/sessions', 'verb' => 'POST',
Expand Down
3 changes: 3 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use OCA\Collectives\Middleware\PublicOCSMiddleware;
use OCA\Collectives\Mount\CollectiveFolderManager;
use OCA\Collectives\Mount\MountProvider;
use OCA\Collectives\Notification\Notifier;
use OCA\Collectives\Reference\SearchablePageReferenceProvider;
use OCA\Collectives\Search\CollectiveProvider;
use OCA\Collectives\Search\PageContentProvider;
Expand Down Expand Up @@ -82,6 +83,8 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(RenderReferenceEvent::class, CollectivesReferenceListener::class);
$context->registerEventListener(MentionEvent::class, TextMentionListener::class);

$context->registerNotifierService(Notifier::class);

$context->registerMiddleware(PublicOCSMiddleware::class);

$context->registerService(MountProvider::class, fn (ContainerInterface $c) => new MountProvider(
Expand Down
24 changes: 24 additions & 0 deletions lib/Controller/CollectiveUserSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,28 @@ public function setFavoritePages(int $collectiveId, string $favoritePages): Data
}, $this->logger);
return new DataResponse([]);
}

/**
* Set whether the user wants notifications about changes in this collective
*
* @param int $collectiveId ID of the collective
* @param bool $subscribe Whether to receive notifications for changes
*
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
* @throws OCSNotFoundException Collective not found
* @throws OCSForbiddenException Not permitted
*
* 200: notify setting was set
*/
#[NoAdminRequired]
public function setNotify(int $collectiveId, bool $notify): DataResponse {
$this->handleErrorResponse(function () use ($collectiveId, $notify): void {
$this->service->setNotify(
$collectiveId,
$this->getUid(),
$notify
);
}, $this->logger);
return new DataResponse([]);
}
}
10 changes: 10 additions & 0 deletions lib/Db/Collective.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class Collective extends Entity implements JsonSerializable {
protected bool $userShowMembers = Collective::defaultShowMembers;
protected bool $userShowRecentPages = Collective::defaultShowRecentPages;
protected array $userFavoritePages = [];
protected bool $userNotify = false;
protected bool $canLeave = false;

public function getCircleId(): string {
Expand Down Expand Up @@ -233,6 +234,14 @@ public function setUserFavoritePages(array $userFavoritePages): void {
$this->userFavoritePages = $userFavoritePages;
}

public function getUserNotify(): bool {
return $this->userNotify;
}

public function setUserNotify(bool $userNotify): void {
$this->userNotify = $userNotify;
}

public function getUserPermissions(bool $isShare = false): int {
// Public shares always get permissions of a simple member plus sharing permission of owner
if ($isShare) {
Expand Down Expand Up @@ -309,6 +318,7 @@ public function jsonSerialize(): array {
'userShowMembers' => $this->userShowMembers,
'userShowRecentPages' => $this->userShowRecentPages,
'userFavoritePages' => $this->userFavoritePages,
'userNotify' => $this->userNotify,
'canLeave' => $this->getCanLeave(),
];
}
Expand Down
9 changes: 9 additions & 0 deletions lib/Db/CollectiveUserSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class CollectiveUserSettings extends Entity implements JsonSerializable {
'show_members',
'show_recent_pages',
'favorite_pages',
'notify',
];

protected ?int $collectiveId = null;
Expand Down Expand Up @@ -106,6 +107,14 @@ public function setFavoritePages(array $favoritePages): void {
$this->setSetting('favorite_pages', $favoritePages);
}

/**
* @throws NotPermittedException
* @throws JsonException
*/
public function setNotify(bool $notify): void {
$this->setSetting('notify', $notify);
}

public function jsonSerialize(): array {
return [
'id' => $this->id,
Expand Down
15 changes: 12 additions & 3 deletions lib/Listeners/NodeWrittenListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,22 @@
use OCA\Collectives\Db\PageLinkMapper;
use OCA\Collectives\Fs\MarkdownHelper;
use OCA\Collectives\Mount\CollectiveStorage;
use OCA\Collectives\Service\PageService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\File;
use OCP\IConfig;
use OCP\IUserSession;

/** @template-implements IEventListener<Event|NodeWrittenEvent> */
class NodeWrittenListener implements IEventListener {
public function __construct(
private IConfig $config,
private PageLinkMapper $pageLinkMapper,
private CollectiveMapper $collectiveMapper,
private readonly IConfig $config,
private readonly PageLinkMapper $pageLinkMapper,
private readonly CollectiveMapper $collectiveMapper,
private readonly IUserSession $userSession,
private readonly PageService $pageService,
) {
}

Expand All @@ -46,5 +50,10 @@ public function handle(Event $event): void {

$linkedPageIds = MarkdownHelper::getLinkedPageIds($collective, $node->getContent(), $this->config->getSystemValue('trusted_domains', []));
$this->pageLinkMapper->updateByPageId($node->getId(), $linkedPageIds);

$userId = $this->userSession->getUser()?->getUID();
if ($userId) {
$this->pageService->notifyContentChange($node, $collective, $userId);
}
}
}
109 changes: 109 additions & 0 deletions lib/Notification/Notifier.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Collectives\Notification;

use OCA\Collectives\AppInfo\Application;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Notification\INotification;
use OCP\Notification\INotifier;
use OCP\Notification\UnknownNotificationException;

class Notifier implements INotifier {
public const SUBJECT_PAGE_UPDATED = 'page_updated';
public const SUBJECT_PAGE_DELETED = 'page_deleted';

public function __construct(
private IFactory $factory,
private IURLGenerator $urlGenerator,
private IUserManager $userManager,
) {
}

public function getId(): string {
return Application::APP_NAME;
}

public function getName(): string {
return $this->factory->get(Application::APP_NAME)->t('Collectives');
}

public function prepare(INotification $notification, string $languageCode): INotification {
if ($notification->getApp() !== Application::APP_NAME) {
throw new UnknownNotificationException();
}

$l = $this->factory->get(Application::APP_NAME, $languageCode);
$params = $notification->getSubjectParameters();

$actingUser = $params['actingUser'];
$actingDisplayName = $this->userManager->getDisplayName($actingUser) ?? $actingUser;

$collectiveRichObject = [
'type' => 'highlight',
'id' => $params['collectiveId'],
'name' => $params['collectiveName'],
'link' => $params['collectiveLink'],
];

$pageRichObject = [
'type' => 'highlight',
'id' => $params['pageId'],
'name' => $params['pageTitle'],
];
if (!empty($params['pageLink'])) {
$pageRichObject['link'] = $params['pageLink'];
}

$userRichObject = [
'type' => 'user',
'id' => $actingUser,
'name' => $actingDisplayName,
];

$richParams = [
'user' => $userRichObject,
'collective' => $collectiveRichObject,
'page' => $pageRichObject,
];

$notification->setIcon(
$this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->imagePath('collectives', 'collectives-dark.svg')
)
);

if (!empty($params['pageLink'])) {
$notification->setLink($params['pageLink']);
} else {
$notification->setLink($params['collectiveLink']);
}

switch ($notification->getSubject()) {
case self::SUBJECT_PAGE_UPDATED:
$notification->setRichSubject(
$l->t('{user} updated {page} in {collective}'),
$richParams,
);
break;
case self::SUBJECT_PAGE_DELETED:
$notification->setRichSubject(
$l->t('{user} deleted {page} from {collective}'),
$richParams,
);
break;
default:
throw new UnknownNotificationException();
}

return $notification;
}
}
1 change: 1 addition & 0 deletions lib/Service/CollectiveHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public function getCollectivesForUser(string $userId, bool $getLevel = true, boo
$c->setUserShowMembers(($settings ? $settings->getSetting('show_members') : null) ?? Collective::defaultShowMembers);
$c->setUserShowRecentPages(($settings ? $settings->getSetting('show_recent_pages') : null) ?? Collective::defaultShowRecentPages);
$c->setUserFavoritePages(($settings ? $settings->getSetting('favorite_pages') : null) ?? []);
$c->setUserNotify(($settings ? $settings->getSetting('notify') : null) ?? false);
}
}
return $collectives;
Expand Down
15 changes: 15 additions & 0 deletions lib/Service/CollectiveUserSettingsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,19 @@ public function setFavoritePages(int $collectiveId, string $userId, string $favo
throw new NotPermittedException($e->getMessage(), 0, $e);
}
}

/**
* @throws NotFoundException
* @throws NotPermittedException
*/
public function setNotify(int $collectiveId, string $userId, bool $notify): void {
$settings = $this->initSettings($collectiveId, $userId);
$settings->setNotify($notify);

try {
$this->collectiveUserSettingsMapper->insertOrUpdate($settings);
} catch (Exception $e) {
throw new NotPermittedException($e->getMessage(), 0, $e);
}
}
}
Loading