Files
gnilebein 28b4edc811
Build package / php-lint (8.4) (push) Successful in 43s
Build package / php-lint (8.5) (push) Successful in 40s
Build package / xml-lint (push) Successful in 12s
Build package / unit-tests (push) Successful in 38s
Build package / package (push) Successful in 52s
revert 0e66319a06
revert Let each workflow watch the due date or the start date

Deck 1.18 added a start date to cards (Card::$startdate), so a workflow
no longer has to mean "overdue". Each rule now carries a date_field
('due' | 'start'), chosen in the settings form next to board and stacks.

Existing rules keep the due date: the new column's default supplies it
for every stored row, so there is no backfill step, and
Workflow::getDateFieldOrDue() covers entities that were never near the
database as well as hand-edited values. The controller is the one place
that rejects an unknown value instead of normalising it - a client
asking for a rule it would not get should hear about it.

The probe for the field is property_exists(), not method_exists():
Deck's entities declare no getter as real code - getStartdate(),
getDuedate(), even getId() all go through Entity::__call(), which
method_exists() ignores by definition. A method_exists() guard would
have been false on every Deck version and would have turned every
start-date rule into a silent no-op.

assertDeckAvailable() now also refuses Deck older than 1.18.0. That
floor cannot live in appinfo/info.xml - neither the server's nor the app
store's schema has an app-to-app dependency element - but
<nextcloud min-version="34"> already implies it, since 1.18.x is the
only Deck release line published for Nextcloud 34.

isOverdue() becomes hasPassed(): one function for both dates, since the
comparison and the argument against Deck's day-truncating
getDaysUntilDue() are identical for either.

Also carries a pending NcSelect icon-alignment fix that was already in
the working tree.

Bump to 34.1.0.
2026-08-26 07:10:55 +02:00

10 KiB
Raw Permalink Blame History

Deck Workflow-Automatisierung

Nextcloud-App für Nextcloud Hub 26 Spring (Server 34.x), die überfällige Karten der Deck-App automatisch von einem Stapel in einen anderen verschiebt und optional eine E-Mail-Benachrichtigung verschickt.

Ersetzt das früher genutzte eigenständige PHP-Cron-Skript durch eine vollwertige App mit Oberfläche in den persönlichen Einstellungen — jeder Nutzer kann dort seine eigenen Regeln ("Workflows") anlegen, ohne Admin-Rechte oder Server-Zugriff zu benötigen.

Funktionsumfang

Pro Workflow lässt sich konfigurieren:

  • Quell-Board und Quell-Stapel
  • Ziel-Stapel, in den überfällige Karten verschoben werden
  • optionaler Filter auf zugewiesene Benutzer (Karte muss mindestens einem der gewählten Benutzer zugewiesen sein)
  • optionaler Filter auf Labels/Tags (Karte muss mindestens eines der gewählten Labels haben)
  • Checkbox: E-Mail-Benachrichtigung an den Workflow-Besitzer, sobald eine Karte verschoben wurde

Ein Nutzer kann beliebig viele Workflows anlegen, bearbeiten, deaktivieren oder löschen.

Architektur

  • Keine HTTP/OCS-Aufrufe gegen Deck. Alles Lesen (Boards, Stapel, Karten, Labels, zugewiesene Benutzer) und das Verschieben von Karten läuft ausschließlich über Decks eigene interne PHP-Klassen (OCA\Deck\Service\CardService, StackService, BoardService, OCA\Deck\Db\CardMapper, …), aufgelöst per Dependency Injection direkt im selben PHP-Prozess. Sämtlicher Deck-Zugriff ist in lib/Service/DeckIntegrationService.php gebündelt.
  • Hintergrundjob statt Seitenaufruf. lib/BackgroundJob/RunWorkflowsJob.php ist ein einfacher Job ohne eigenes Intervall: Er läuft bei jedem Cron-Durchlauf von Nextcloud (üblicherweise alle 5 Minuten) und ruft lib/Service/WorkflowRunner.php auf.
  • Rechte-Kontext je Nutzer. Ein Hintergrundjob hat standardmäßig keinen eingeloggten Nutzer, muss aber die Regeln mehrerer Nutzer in einem einzigen Lauf auswerten. Der WorkflowRunner "verkörpert" deshalb für die Dauer der jeweiligen Workflows kurzzeitig den entsprechenden Besitzer. Dazu gehört mehr als IUserSession::setUser(): Decks Berechtigungsprüfungen lesen die Session gar nicht, sondern eine Benutzer-ID, die beim Bau des Deck-Containers einmal pro Prozess eingefroren wird. DeckIntegrationService::beginUserContext() setzt diesen Wert für die Dauer der Verkörperung auf den Workflow-Besitzer und stellt ihn danach wieder her; schlägt das fehl, wird der Nutzer in diesem Lauf übersprungen, statt unter fremden Rechten zu arbeiten.
  • E-Mail läuft über Nextclouds eigenen IMailer (nutzt also den in der Nextcloud-Administration hinterlegten Mailserver) und geht an die im Profil des Workflow-Besitzers hinterlegte Adresse.

Wichtiger Hinweis zu Decks internen Klassen

OCA\Deck\* ist keine dokumentierte, stabile öffentliche API von Deck (keine @since-Markierungen, keine offizielle Zusicherung von Abwärtskompatibilität). Diese App verwendet sie trotzdem bewusst direkt, wie es die Vorgabe verlangt — jeder Zugriff läuft defensiv abgesichert über DeckIntegrationService (Prüfung, ob Deck aktiviert ist, class_exists()-Checks, try/catch mit Logging statt Absturz). Nach größeren Deck-Updates lohnt sich ein Blick ins Nextcloud-Log, falls Workflows plötzlich nicht mehr greifen.

Installation

  1. App in apps/workflow_deck_automation des Nextcloud-Servers ablegen (oder über den Appstore-Build aus der CI, siehe unten).
  2. In der Nextcloud-Administration unter Apps aktivieren.
  3. Die Deck-App muss installiert und für die jeweiligen Nutzer aktiviert sein.
  4. Sicherstellen, dass der Hintergrundjob-Modus auf Cron (empfohlen) steht, damit RunWorkflowsJob regelmäßig läuft.

Benutzung

Jeder Nutzer findet die Einstellungen unter Persönliche Einstellungen → Deck Workflow-Automatisierung. Dort können neue Workflows angelegt, bestehende bearbeitet oder gelöscht werden. Die Dropdowns für Board/Stapel/Label/Benutzer werden live aus Deck geladen. Zur Auswahl stehen nur aktive Boards — archivierte und im Papierkorb liegende Boards sowie gelöschte Stapel blendet Deck bzw. die App aus.

Gelöschte Boards und Stapel

Ein Workflow verweist per ID auf ein Board und zwei Stapel. Wird eines davon in Deck gelöscht, passiert Folgendes:

  • Beim Speichern werden Board und Stapel geprüft; ein Workflow auf nicht mehr existierende Ziele lässt sich gar nicht erst anlegen.
  • In der Übersicht wird ein betroffener Workflow rot markiert („Board nicht mehr vorhanden", „Quell-Stapel nicht mehr vorhanden" …).
  • Im Hintergrundjob wird der Workflow automatisch deaktiviert und der Besitzer einmalig per E-Mail informiert — unabhängig davon, ob für den Workflow „E-Mail senden" aktiviert ist, denn hier geht es nicht um verschobene Karten, sondern darum, dass die Automatisierung nicht mehr läuft. Danach bleibt es still: Ein deaktivierter Workflow wird nicht erneut ausgewertet.

Ist Deck vorübergehend nicht erreichbar, wird nicht deaktiviert — der Job überspringt den Workflow und versucht es beim nächsten Lauf erneut. Archivierte Boards lösen die Deaktivierung ebenfalls nicht aus, sie sind nur in den Auswahlfeldern ausgeblendet.

Gelöschte Labels und entfernte Benutzer

Filter sind ein anderer Fall: Sie werden nicht deaktiviert. Wird eines von drei ausgewählten Labels gelöscht, greifen die anderen beiden weiter — das abzuschalten wäre falsch.

Problematisch ist nur der Grenzfall, dass alle Einträge eines Filters verschwunden sind. Der Workflow läuft dann weiter, kann aber keine einzige Karte mehr treffen. Das passiert bei gelöschten Labels und ebenso, wenn ein gefilterter Benutzer die Board-Freigabe verliert — Deck entfernt dabei alle seine Kartenzuweisungen auf diesem Board.

Damit dieser Zustand nicht unsichtbar bleibt:

  • In der Übersicht wird der Workflow rot markiert („Label-Filter trifft keine Karte mehr", „Benutzer-Filter trifft keine Karte mehr").
  • Im Hintergrundjob landet bei jedem Lauf eine Warnung im nextcloud.log.

Keine E-Mail und keine Deaktivierung: Ein einziger Board-Klick kann den Filter wieder lebendig machen. Und wie bei Board und Stapel gilt auch hier — lässt sich das Board gerade nicht abfragen, wird nichts gemeldet statt etwas Falsches.

Ausführungstakt

Der Hintergrundjob hat kein eigenes Intervall und ist auch nicht konfigurierbar: Er läuft bei jedem Cron-Durchlauf von Nextcloud. Der Takt ist damit exakt der des System-Crons — üblicherweise alle 5 Minuten, bei einem häufiger eingerichteten Cron entsprechend öfter.

Bis einschließlich v0.1.0 gab es dafür die config.php-Option workflow_deck_automation.interval. Sie ist entfallen und wird ignoriert; ein vorhandener Eintrag kann aus der config.php entfernt werden.

Der Job läuft nur nachts bzw. gar nicht

Nextcloud merkt sich in der Spalte time_sensitive der Tabelle oc_jobs, ob ein Job zeitkritisch ist, und überspringt „unkritische" Jobs außerhalb des Wartungsfensters. JobList::setLastRun() setzt diese Spalte nur in eine Richtung — von „zeitkritisch" auf „unkritisch", nie zurück. Wer eine ältere Version dieser App installiert hatte (bis einschließlich v0.1.0 war der Job TIME_INSENSITIVE), hat diesen Stempel noch in der Datenbank. Er wird weder durch ein App-Update noch durch app:enable zurückgesetzt.

Symptom: Ist in der config.php ein maintenance_window_start gesetzt, läuft der Job nur in diesem 4-Stunden-Fenster und den Rest des Tages gar nicht.

Einmalig korrigieren:

UPDATE oc_jobs SET time_sensitive = 0
 WHERE class = 'OCA\\WorkflowDeckAutomation\\BackgroundJob\\RunWorkflowsJob';

Ohne Datenbankzugriff geht es auch über occ, indem der Job gelöscht und durch Aus-/Einschalten der App neu registriert wird:

php occ background-job:list --class 'OCA\WorkflowDeckAutomation\BackgroundJob\RunWorkflowsJob'
php occ background-job:delete <id>
php occ app:disable workflow_deck_automation
php occ app:enable workflow_deck_automation

Neuinstallationen sind nicht betroffen.

Entwicklung

Voraussetzungen: PHP 8.2+, Composer, Node.js 24+, npm 11+.

composer install
npm install
npm run build      # einmaliger Produktions-Build
npm run watch       # Entwicklung mit automatischem Rebuild

Tests & Linting

composer run lint       # php -l über lib/ und tests/
composer run cs:check   # nextcloud/coding-standard (php-cs-fixer)
composer run test:unit  # PHPUnit — reine Filter-Logik, benötigt keine Deck-Installation

Ein echter End-to-End-Test (Karte anlegen, Workflow konfigurieren, Hintergrundjob auslösen, Verschiebung + Mail prüfen) lässt sich nur gegen eine echte Nextcloud-34-Instanz mit installierter Deck-App durchführen, z. B. per:

# Job-ID und letzten Lauf anzeigen
php occ background-job:list --class 'OCA\WorkflowDeckAutomation\BackgroundJob\RunWorkflowsJob'

# Sofort ausführen, ohne auf den nächsten Cron-Durchlauf zu warten
php occ background-job:execute <id> --force-execute

# Oder dauerhaft als Worker laufen lassen (Argument ist die Job-Klasse, keine App-ID)
php occ background-job:worker 'OCA\WorkflowDeckAutomation\BackgroundJob\RunWorkflowsJob'

Release-Paket bauen

make appstore

Erzeugt build/artifacts/appstore/workflow_deck_automation.tar.gz.

CI (Gitea Actions)

In .gitea/workflows/:

Workflow Zweck
lint-php.yml php -l über eine PHP-8.28.4-Matrix
lint-info-xml.yml validiert appinfo/info.xml gegen das Appstore-XML-Schema
phpunit.yml führt die PHPUnit-Tests aus (SQLite/rein logisch, kein DB-Service nötig)
build-main.yml baut Frontend + Appstore-Archiv und veröffentlicht es als Release-Asset — bei Push auf main als rollendes Pre-Release latest-main, bei Push eines v*-Tags als reguläres Release

Datenmodell

Workflows werden in der Tabelle wfda_workflows gespeichert (siehe lib/Migration/Version1000Date20260813120000.php): eine Zeile pro Workflow, mit user_id-Bezug, Board-/Stapel-IDs, JSON-kodierten Filterlisten sowie enabled/notify_email/last_run.

Lizenz

AGPL-3.0-or-later