Notify board members about moved cards according to their Deck setting
Build package / unit-tests (push) Failing after 20s
Build package / xml-lint (push) Successful in 46s
Build package / php-lint (8.4) (push) Successful in 1m30s
Build package / php-lint (8.5) (push) Successful in 1m36s
Build package / package (push) Skipped

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.
This commit is contained in:
Patrick Niebeling committed 2026-09-30 10:31:45 +02:00
1 parent 27717205cd
commit 578b235f08
14 files changed
+579 -100

No files matched your search

+49 -7
View File
@@ -34,7 +34,7 @@
</td>
<td>{{ stackLabel(workflow.boardId, workflow.sourceStackId) }}</td>
<td>{{ stackLabel(workflow.boardId, workflow.targetStackId) }}</td>
<td>{{ yesNo(workflow.notifyEmail) }}</td>
<td>{{ notifyLabel(workflow.notifyMode) }}</td>
<td>{{ yesNo(workflow.enabled) }}</td>
<td class="wfda-actions">
<NcButton variant="tertiary" @click="editWorkflow(workflow)">
@@ -88,9 +88,24 @@
:disabled="!form.boardId"
placeholder="Ziel-Stapel wählen" />
<NcCheckboxRadioSwitch v-model="form.notifyEmail">
Benachrichtigungs-E-Mail an mich senden
</NcCheckboxRadioSwitch>
<fieldset class="wfda-notify">
<legend>E-Mail bei verschobenen Karten</legend>
<NcCheckboxRadioSwitch
v-for="option in notifyOptions"
:key="option.value"
v-model="form.notifyMode"
type="radio"
name="wfda-notify-mode"
:value="option.value">
{{ option.label }}
</NcCheckboxRadioSwitch>
<p v-if="form.notifyMode === 'board'" class="wfda-hint">
Jedes Board-Mitglied bekommt die E-Mail entsprechend seiner eigenen
Deck-Einstellung „Benachrichtigungen“ für dieses Board: bei „Alle Karten“ immer,
bei „Zugewiesene Karten“ nur für Karten, denen es zugewiesen ist, bei „Keine“ nie.
Mehrere Karten eines Laufs werden zu einer E-Mail zusammengefasst.
</p>
</fieldset>
<NcCheckboxRadioSwitch v-model="form.enabled">
Workflow aktiv
@@ -166,7 +181,7 @@ const sortedWorkflows = computed(() => {
workflow.title,
stackLabel(workflow.boardId, workflow.sourceStackId),
stackLabel(workflow.boardId, workflow.targetStackId),
yesNo(workflow.notifyEmail),
notifyLabel(workflow.notifyMode),
yesNo(workflow.enabled),
]
return workflows.value
@@ -229,11 +244,22 @@ function emptyForm() {
boardId: null,
sourceStackId: null,
targetStackId: null,
notifyEmail: false,
notifyMode: 'off',
enabled: true,
}
}
// Values match NotificationPolicy::MODES on the server.
const notifyOptions = [
{ value: 'off', label: 'Keine E-Mail', short: 'Keine' },
{ value: 'owner', label: 'Nur an mich', short: 'Nur ich' },
{ value: 'board', label: 'An Board-Mitglieder laut ihrer Deck-Einstellung', short: 'Board-Mitglieder' },
]
function notifyLabel(mode) {
return notifyOptions.find((option) => option.value === mode)?.short ?? mode
}
function boardLabel(boardId) {
const board = boards.value.find((entry) => entry.id === boardId)
return board ? board.title : `#${boardId}`
@@ -329,7 +355,7 @@ async function editWorkflow(workflow) {
boardId: workflow.boardId,
sourceStackId: workflow.sourceStackId,
targetStackId: workflow.targetStackId,
notifyEmail: workflow.notifyEmail,
notifyMode: workflow.notifyMode,
enabled: workflow.enabled,
})
editingId.value = workflow.id
@@ -437,6 +463,22 @@ onMounted(loadAll)
border-radius: var(--border-radius-large);
}
.wfda-notify {
display: flex;
flex-direction: column;
gap: 4px;
}
.wfda-notify legend {
font-weight: bold;
margin-bottom: 4px;
}
.wfda-hint {
color: var(--color-text-maxcontrast);
font-size: 0.9em;
}
.wfda-form-actions {
display: flex;
gap: 8px;