Replaces the yes/no notify_email flag with notify_mode:
- off: no mail
- owner: only the board owner, as before (migrated from notify_email=1)
- board: everyone with access to the board, filtered by their own Deck
"Notifications" setting for it (board:<id>:notify-due: off /
assigned / all), mirroring Deck's NotificationHelper rules
Recipients are resolved via PermissionService::findUsers() (owner, ACL,
groups and circles expanded), AssignmentMapper::isUserAssigned() and
IConfig, none of which reads Deck's frozen $userId. If they cannot be
resolved, no mail is sent rather than one to the wrong people.
Moved cards are collected over the whole run and each recipient gets a
single digest mail afterwards, in their own language.
Bumps to 35.2.0 so the migrations run.
71 lines
2.5 KiB
PHP
71 lines
2.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace OCA\WorkflowDeckAutomation\Service;
|
|
|
|
/**
|
|
* Who gets a mail about a moved card — pure logic, no Nextcloud or Deck
|
|
* dependencies, so it is unit-testable like WorkflowRunner::isOverdue().
|
|
*/
|
|
class NotificationPolicy {
|
|
/** No mail at all. */
|
|
public const MODE_OFF = 'off';
|
|
/** Only the workflow's owner (the board owner), regardless of Deck settings. */
|
|
public const MODE_OWNER = 'owner';
|
|
/** Everyone with access to the board, filtered by their own Deck setting. */
|
|
public const MODE_BOARD = 'board';
|
|
|
|
public const MODES = [self::MODE_OFF, self::MODE_OWNER, self::MODE_BOARD];
|
|
|
|
/**
|
|
* Values of Deck's per-user, per-board preference
|
|
* `deck` / `board:<boardId>:notify-due` (Deck's
|
|
* ConfigService::SETTING_BOARD_NOTIFICATION_DUE_*), inlined so a renamed
|
|
* constant cannot fatal this app. It is the board's "Notifications" menu
|
|
* in Deck — the due-date reminder setting, which is exactly what fits a
|
|
* card being moved because it fell due.
|
|
*/
|
|
public const DECK_OFF = 'off';
|
|
public const DECK_ASSIGNED = 'assigned';
|
|
public const DECK_ALL = 'all';
|
|
/** What Deck assumes for a user who never touched the setting. */
|
|
public const DECK_DEFAULT = self::DECK_ASSIGNED;
|
|
|
|
public static function isValidMode(string $mode): bool {
|
|
return in_array($mode, self::MODES, true);
|
|
}
|
|
|
|
/**
|
|
* Mirrors Deck's own decision in NotificationHelper::sendCardDuedate():
|
|
*
|
|
* - `off` never notifies — not even the owner of an unshared board;
|
|
* - `all` always notifies;
|
|
* - the owner of an unshared board is notified for every card, since on a
|
|
* board nobody else can see "assigned" would otherwise mean nothing;
|
|
* - `assigned` notifies users assigned to the card, directly or through a
|
|
* group or circle.
|
|
*
|
|
* An unknown value is treated as Deck's default. Deck rejects anything
|
|
* else when storing the setting, so that only happens if the preference
|
|
* was edited by hand.
|
|
*
|
|
* @param bool|callable(): bool $isAssigned a callable is only invoked when
|
|
* the answer actually depends on it — it costs a query per user
|
|
*/
|
|
public static function shouldNotify(string $deckSetting, bool $isOwnerOfUnsharedBoard, bool|callable $isAssigned): bool {
|
|
if (!in_array($deckSetting, [self::DECK_OFF, self::DECK_ASSIGNED, self::DECK_ALL], true)) {
|
|
$deckSetting = self::DECK_DEFAULT;
|
|
}
|
|
|
|
if ($deckSetting === self::DECK_OFF) {
|
|
return false;
|
|
}
|
|
if ($deckSetting === self::DECK_ALL || $isOwnerOfUnsharedBoard) {
|
|
return true;
|
|
}
|
|
|
|
return is_callable($isAssigned) ? (bool)$isAssigned() : $isAssigned;
|
|
}
|
|
}
|