Skip to main content

Gantt Projects - Releases

← App details

Nextcloud 35

Gantt Projects 2.3.0
Release Details
UpdatedSept. 30, 2026, 11:45 a.m.
Changelog

Added

  • Aufgaben und Teilprojekte per Drag & Drop umsortieren (Issue #299): Vor jeder Aufgabe und jedem Teilprojekt steht in der Tabelle ein Ziehgriff (☰, ganz links, unabhängig von der Einrückung); eine blaue Linie zeigt die Einfügeposition. Verschoben wird nur innerhalb desselben Elternobjekts und unter Zeilen desselben Typs, sonst zeigt der Mauszeiger „nicht erlaubt“ und ein Ablegen bricht ab. Neuer Endpunkt PUT /api/v1/projects/{projectId}/row-order (kind, id, before_id) mit RowOrderService: vergibt die sort_order aller Geschwister fortlaufend neu (auch bei gleichen Werten), Schreibrecht nötig. Bewusst nicht Teil: Griff für Meilensteine (sie haben keine sort_order, stehen vor den Aufgaben ihres Elternobjekts) und Projekte, Umhängen in ein anderes Teilprojekt; der Zeiger ist der Standard-„nicht erlaubt“-Zeiger des Browsers. Doku (FEATURES.md, de/en) ergänzt. Neue PHPUnit-Tests (RowOrderServiceTest), Vitest-Tests (rowDrag.test.js) und der E2E-Test row-reorder.spec.js

Fixed

  • Klick auf das Deck-Abweichungssymbol öffnet den Dialog wieder beim Bereich „Abweichungen von Deck“ (jetzt im Reiter „Verbindungen“): focusDeckSection wurde im Dialog-Kontext nicht mehr weitergereicht. E2E-Tests für die Reiter angepasst
  • Tabelle und Gantt-Chart scrollen vertikal zuverlässig gemeinsam (Issue #292): Tabellenzeilen und Balken standen schon ganz oben um 9 px versetzt (Tabellenkopf 52 px, erste Chart-Zeile erst bei header_height + padding / 2 = 61 px), und weil nur das Chart den Ressourcen-Bereich enthält, konnte es weiter scrollen als die Tabelle – die Tabelle blieb am Ende hängen, das Chart zeigte einen anderen Ausschnitt oder sprang zurück. Der Tabellenkopf ist jetzt so hoch wie der Chart-Kopf bis zur ersten Zeile, ein Platzhalter unter den Tabellenzeilen gleicht Ressourcen-Bereich und Scrollleisten aus (per ResizeObserver laufend nachgeführt), sodass beide Seiten denselben maximalen scrollTop haben. Der Sync überträgt nur noch bei tatsächlicher Abweichung statt über ein synchron zurückgesetztes syncing-Flag, das asynchrone Scroll-Events nicht abfangen konnte. Geometrie in src/gantt/tableScrollSync.js (Vitest), neuer E2E-Test table-scroll-sync.spec.js

Added

  • Deck-Karten schon beim Anlegen einer Aufgabe verknüpfen oder neu anlegen (Issue #306): Der Deck-Bereich im Reiter „Verbindungen“ war bei neuen Aufgaben gesperrt („Speichere zuerst die Aufgabe“). Jetzt lassen sich Karten auswählen und „+“ vormerken; beim Speichern verknüpft bzw. legt die App sie an (deckCardsSection().commit()). Schlägt eine Deck-Aktion fehl, bleibt die Aufgabe gespeichert und ein Hinweis nennt die Zahl der Fehlschläge. Board und Kartenliste des Pickers folgen dem gewählten Projekt und übergeordneten Objekt (auch beim projektlosen Anlegen über „Aufgabe“). Der Termin-Abgleichsdialog entfällt dabei; Abweichungen zeigt danach der Bereich „Abweichungen von Deck“
  • Reiter „Anhänge“ in allen Detaildialogen (Projekt, Teilprojekt, Aufgabe, Meilenstein; vor „Verbindungen“) mit den Bereichen Dateien und Notizen: beliebig viele Dateien (Nextcloud-Dateiauswahldialog mit eigenen Ordnern) und Notizen (Auswahl aus der App Notes im Stil der Deck-Kartenauswahl) verknüpfen. Der Server teilt jede angehängte Datei automatisch mit allen Personen, die Zugriff auf das Projekt haben (Betrachter lesen, Bearbeiter/Admins dürfen ändern) und gleicht die Freigaben nach jeder Änderung an Anhängen, Projektfreigaben und Löschungen ab; entfernt werden nur die von der App angelegten Freigaben (nc_gantt_attach_shares). Die Freigabe gilt je Datei, nicht je Objekt: Sie bleibt bestehen, solange die Datei an mindestens einem Objekt der App hängt. Doku ergänzt in FEATURES.md (Abschnitt „Anhänge: Dateien und Notizen“, de/en), READMEs, HOWTO (Schritt 10) und Store-Beschreibung (info.xml). Neue Tabellen nc_gantt_attachments und nc_gantt_attach_shares (Migration Version1044), Endpunkte GET/POST /attachments, DELETE /attachments/{id}, AttachmentService. Die Dateiauswahl hängt sich in den modalen Detaildialog ein (sonst läge sie unsichtbar dahinter), und nachgeladene Bundle-Teile (Dateiauswahl, Datumsformate) nutzen den tatsächlichen Installationsort der App als Adresse (src/publicPath.js; die Vorgabe /apps/nc_gantt/js/ griff z. B. unter custom_apps nicht). Neue PHPUnit- (AttachmentServiceTest) und Vitest-Tests (attachments.test.js) sowie der E2E-Test attachments.spec.js
  • Aufgaben können von Meilensteinen abhängen (Issue #308): Vorgänger m-<id> neben t-<id> (keine Migration), in allen vier Typen mit positivem und negativem Abstand. Ein Meilenstein zählt als Punkt mit Start = Ende = seinem Datum; ein fester verschiebt sich nie und zieht seine abhängigen Aufgaben samt Nachfolgern mit, ein abgeleiteter folgt weiter seinem Ziel und gibt dessen Verschiebung an die Aufgaben dahinter weiter. TaskSchedulingService (Prüfung, Termine, Propagation, Neuberechnung) kennt Meilensteine als Knoten; die Zyklenprüfung berücksichtigt die Kanten Zielaufgaben → abgeleiteter Meilenstein und läuft auch beim Speichern eines Meilensteins (dependency_cycle); der Dialog sperrt solche Meilensteine als Vorgänger bzw. Ziele mit Hinweis. Im kritischen Pfad sind Meilensteine Knoten: ein fester ersetzt das Projektende als spätestes Ende für die Aufgaben, die über eine Abhängigkeit vor ihm liegen; ein negativer Puffer verhindert das Speichern mit dem Hinweis auf die betroffene Aufgabe (negative_float:<Titel>, bereits negativer Puffer wird nicht erneut gemeldet). Beim Löschen eines Meilensteins (auch über Aufgabe/Teilprojekt) entfallen die Abhängigkeiten auf ihn, ProjectDuplicationService und JSON-Import/-Export münzen m-<id> auf die neuen Meilensteine um. Oberfläche: Auswahl M<id> <Titel> im Abhängigkeits-Editor, Kurznotation M3EA-2d in der Vorgänger-Spalte, Pfeile von und zu Meilenstein-Balken, Nummerierung M<id> im Diagramm, Glossar und FEATURES.md (de/en) nachgezogen. Bekannte Vereinfachungen: die Pufferrechnung zählt den Meilenstein-Termin in Arbeitstagen des globalen Kalenders ab dem frühesten Aufgabenbeginn, und ein Meilenstein-Termin verschiebt keine Deck-Karten (Deck-Abgleich läuft nur bei Änderungen an Aufgaben). Neue PHPUnit-Tests (TaskSchedulingMilestoneTest, CriticalPathMilestoneTest, ergänzt: Meilenstein-, Duplizierungs-, Lösch- und Datums-Tests), Vitest-Tests und der E2E-Test milestone-dependency.spec.js
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureS4LN+K40y1DKrwQo02FOHkiugxuFllfcAsVp7IBoUthbKYnNdZtSmzBH+ZxY0V8C
U954MnYEijFZD14jMuW2wkvMomspSCRGfCfEHytRCLfNwmsK9AV5UfxutjQBe6Y/
ATjKYnhOEPrE2iXLOZ+P73VbC8GBPY6W21P1APsy0Af+XUoFycIdTnLJUU5QJgWY
+K/N7nUfXyqVA17kCO+/Zhw6w5wl5O7ZIMezWbNn3ApvOeIC8AE/CwJ/JccrQgE9
F4A1RTqFZcK0hCrluGelanyva6ygwAmYPoJtkR+CeiJr7vFdSSq6HJe0lG0y9SAm
RNFnDuEzt6uFqSl9EqK6ykVp7ns0k6npvld3gSqGN0Frq4dwGjxSjcRkpPGAYT7M
IH6YJDVlNbVdF/wRJtEmOvO1duI4uzBhjX3PIFX1LBoFR0p5OsKuMIAXGEupbBi4
BV9Zu5578h5VQzZt32gFnsQcGVsvAn2nayCCbcQx/WUHSW57NmBdktF4l+7tme/p
vL+hzncZ2sdDbhDrrDq2G167QwkAJgpGJFQR+34YpGrY3DDhj767+Q/bMHwm4zmk
VYm+g4MAf49P2CKyqwVB1E9SGe3MgTK2rDRnibhVIlgVEDvih1Uf/E5wigpBJerf
E1DvLXTLEtteko53AjIpT/GWpiiD9md4QpCUUdfGMnU=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.2.0
Release Details
UpdatedSept. 28, 2026, 7:36 a.m.
Changelog

Added

  • Implizite Kopfzahl je Aufgabe (Issue #252, Schritt C aus #249; R11): Der Aufgabendialog nennt unter jeder Zuordnung mit Aufwand im Klartext, wie viele Personen gleichzeitig nötig sind („Erfordert im Schnitt 2,0 × Java-Entwickler gleichzeitig“) — Aufwand ÷ Arbeitstage des Balkens ÷ Tageskapazität einer vollen Kraft, je Zuordnung, live bei Änderung von Aufwand, Start, Ende oder Dauer. Gezählt wird nach dem globalen Kalender (Mo–Fr abzüglich globaler Feiertage) für Rollen und Personen gleich; Personenkalender zählen nicht (Entscheidung in #252, daher entfällt das Akzeptanzkriterium „Teilzeitkalender“). Ab der Warnschwelle (globale App-Einstellung headcount_warn_threshold, Standard 3,0, gültig 0,5–100, im Admin-Bereich unter „Warnen ab“; GET/PUT /settings liefern und setzen headcount_threshold, ein PUT ohne den Wert lässt ihn unverändert, Initial State für das erste Rendern) erscheint dort zusätzlich die Konsequenz („mit weniger Personen dauert die Aufgabe entsprechend länger“), und die Tabelle zeigt vor dem Titel ein Personen-Symbol mit Tooltip — ein Hinweis, keine Blockade. Aufgaben ohne Aufwand oder ohne Arbeitstage und Zuordnungen in anderen Einheiten als PT/h zeigen nichts. Die Rechnung liegt als reine Funktion in src/utils/headcount.js, die Schwelle in WorkTimeService. Nicht Teil: Hinweis am Balken im Diagramm, Prüfung der tatsächlichen Verfügbarkeit (Schritt B, #251). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue Vitest-Tests (headcount.test.js, tableRows.test.js, settings.test.js) und PHPUnit-Tests (WorkTimeServiceTest)
  • Kapazitätsprüfung über Rollen hinweg: Engpass-Teilmenge statt Einzelrolle, Alarm über ein gleitendes 5-Tage-Fenster (Issue #251, Schritt B aus #249; R6, R10): Rollenkapazität ist nicht additiv — bei zwei Java-Entwicklern, von denen einer zusätzlich IT-Architekt ist, besteht „2× Java + 1× Architekt“ die Einzelprüfung (2 ≤ 2, 1 ≤ 1), ist aber nicht zu leisten. Neuer UI- und datenbankfreier Rechenkern: CapacityFlow löst je Fenster ein Flussproblem (Bedarfsknoten = Rollen und direkt zugewiesene Personen im selben Fluss, Kante wo die Person die Rolle ausfüllt, Personenkapazität zur Senke; Dinic in CapacityFlowGraph) und liest die Engpass-Teilmenge aus dem Min-Cut, zerlegt in unabhängige Engpässe samt den Personen mit Mehrfachrolle als Ursache; CapacityWindowCheck legt darüber das gleitende Fenster von 5 Arbeitstagen (fest, nicht konfigurierbar, Feiertage verkürzen es nicht): rot = irgendein Fenster fließt nicht (nicht durch Verschieben lösbar), gelb = ein einzelner Tag fließt nicht, aber alle Fenster um ihn (Tagesspitze, umsortierbar); Tage, an denen niemand des Knotens arbeitet, zählen nicht als Spitze (Teilzeit Mo–Do erzeugt keine Meldung); überlappende Verstöße mit gemeinsamen Rollen werden zu einem Zeitraum zusammengefasst. CapacityCheckService trägt Aufgaben der für den Betrachter sichtbaren Projekte (Lücke bewusst akzeptiert, wie in #181), Rollen und Personenkapazität zusammen — Bedarf auf dem globalen Kalender, Kapazität aus den Personenkalendern (ResourceUtilizationService::personCapacityFn() ist dafür öffentlich), Material ausgeschlossen, Rolle ohne Personen meldet rot — und liefert GET /api/v1/capacity-check (Rollen, direkte Personen, Zeitraum, Fehlbetrag in Stunden, Ursache, betroffene Aufgaben). Die Tabelle zeigt vor dem Titel eine kleine Person in Gelb oder Rot mit Tooltip („Java-Entwickler + IT-Architekt zusammen überbucht (KW 42). Fehlbetrag: 5 PT. Ursache: Mehrfachrollen von …“), keine Benachrichtigungen; der Store capacityCheck lädt bei Änderungen an Aufgaben, Rollen, Kapazität oder Feiertagen neu. Ressourcen-Raster und Workload-Chart färben nach demselben Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder über 90 % im Fenster, Tooltip mit Reserve), die Rollenzeile übernimmt das Urteil der Prüfung. Nicht Teil: Sequenzierungsvorschlag (#282), Kostenminimierung der Zuordnung (entfällt), Projektkalender (#284). Bekannte Grenzen (in FEATURES.md): nur sichtbare Projekte, Aufgaben kürzer als das Fenster werden verdünnt (fängt die implizite Kopfzahl aus #252 auf). Neue Texte in allen Übersetzungen; FEATURES.md/HOWTO.md (de/en) und die Integrationsmatrix sind nachgezogen, der Hinweis auf die naive Rechnung aus #250 ist entfernt. Neue PHPUnit-Tests (CapacityFlowTest, CapacityWindowCheckTest, CapacityCheckServiceTest; u. a. Prüffall, Kette über drei Rollen, Bedarf exakt auf Kapazität, Rolle ohne Personen, Laufzeit 30 Personen/10 Rollen/1 Jahr), Vitest-Tests (capacityCheck.test.js, workloadModel.test.js, tableRows.test.js) und der E2E-Test capacity-check.spec.js

  • Projekt duplizieren bzw. als Vorlage für ein neues Projekt verwenden (Issue #270): Die Aktion Duplizieren im Menü des Projekts in der Navigation (auch mit reinem Leserecht) und im Projektdialog legt ein neues Projekt der ausführenden Person an. Der Dialog fragt Titel („Kopie von …“), neuen Starttermin (Vorschlag: bisheriger Projektstart = frühester Aufgabenbeginn) und die Ressourcenzuordnungen (alle / nur Rollen / keine). Alle Termine — Aufgaben, Meilensteine, Planungszeiträume von Projekt und Teilprojekten — verschieben sich um die Differenz in Arbeitstagen des globalen Kalenders, ein Start auf einem freien Tag rückt auf den nächsten Arbeitstag, bei unverändertem Start bleiben die Termine unberührt. Übernommen werden Teilprojekte samt Hierarchie, Aufgaben, Abhängigkeiten mit Typ und Abstand, Meilensteine mit Zuordnung und Abhängigkeitstyp (abgeleitete folgen dem kopierten Termin), Zuordnungen samt Aufwand (nicht sichtbare Ressourcen entfallen, der Aufwand wird bei weggelassenen Zuordnungen neu gebildet), Farben, Beschreibungen und Projektregeln; zurückgesetzt werden Fortschritt (Modus „Deck“ wird „manuell“), Status, Ist-Kosten, Baselines, Änderungsprotokoll, Freigaben, ICS-Token, Deck-Board, Deck-Listen und Kartenverknüpfungen (Deck-Abgleich „Aus“). Neuer ProjectDuplicationService legt alles in einer Transaktion über die Mapper an (kein Änderungsprotokoll, keine Benachrichtigungen, kein Deck-Zugriff); GET /projects/{id}/duplicate liefert Start und Umfang für den Dialog, POST /projects/{id}/duplicate (title, start_date, resources) die Kopie (201). Nicht Teil: eigene Vorlagenverwaltung (Projekte als „Vorlage“ markieren, Galerie beim Anlegen). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue PHPUnit-Tests (ProjectDuplicationServiceTest), Vitest-Tests (duplicate.test.js, ioDialogs.duplicate.test.js) und der E2E-Test project-duplicate.spec.js

Changed

  • Rollenzeile im Ressourcen-Raster ohne „naiv additiv“ (#251): GET /resources/{id}/utilization liefert für Rollen nur noch capacity_derived; capacity_naive und multi_role_member_ids entfallen, ebenso der Hinweis „zu optimistisch“ im Raster (ersetzt durch den Verweis auf die Prüfung über alle Rollen). Das Workload-Chart unter dem Diagramm färbt Perioden nicht mehr je Periode rot, sondern nach dem 5-Tage-Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder knapp)

Fixed

  • Server-Zeitzone der Arbeitszeiten aus default_timezone statt immer UTC (Issue #294): Nextcloud setzt die PHP-Zeitzone in lib/base.php fest auf UTC, deshalb zeigte der Admin-Bereich unter „Arbeitszeiten“ immer „UTC (+00:00)“ und Arbeitsbeginn/-ende wurden als UTC ausgelegt — neue Termine im Detaildialog/Import und die Deck-Fälligkeiten lagen dadurch 1–2 Stunden daneben. Neuer ServerTimeZoneService liest default_timezone aus der config.php (fehlt der Wert oder ist er ungültig, bleibt es bei UTC); GET/PUT /settings liefern dessen Namen und aktuellen Versatz, der Deck-Terminabgleich legt work_start/work_end in dieser Zeitzone aus. Neue PHPUnit-Tests (ServerTimeZoneServiceTest, DeckDateSyncServiceTest)
  • Schrift im Balken passte nicht zur Balkenfarbe (Issue #287): .bar-label stand in theme.css fest auf Weiß, unabhängig von Balkenfarbe und Theme — auf hellen und mittleren Farben, darunter die Standardfarbe #0082c9 (Kontrast 4,2:1), war der Titel im Balken im hellen Theme schwer lesbar, während der Titel außerhalb des Balkens (.big) bereits die Textfarbe des Themes bekam. injectProjectColors() setzt jetzt je Farbklasse die Schriftfarbe, die nach WCAG den höheren Kontrast liefert (labelTextColor() in colors.js: Weiß oder #111111). Die Balkenfarbe ist in beiden Themes dieselbe, die Schrift deshalb ebenfalls. Bei Projekt- und Teilprojektzeilen (Summenbalken, nur eine auf 18 % abgeschwächte Klammerfläche darunter) war die vorgesehene Textfarbe des Themes wirkungslos: .gantt-summary-bar .bar-label und .gantt .bar-label hatten gleiche Spezifität und beide !important, die spätere gewann — der Titel blieb im hellen Theme weiß. Die Regel ist jetzt spezifischer (.gantt .bar-wrapper.gantt-summary-bar .bar-label); Meilenstein und Titel neben dem Balken (.big) behalten ihre Regeln. Neue Vitest-Tests in colors.test.js
  • JSON-Export ohne Datenbank-IDs der Abhängigkeiten (Issue #289): Je Abhängigkeit enthält der Export nur noch predecessor_id, dependency_type und lag_days; id und task_id (Datenbank-IDs des Quellsystems, im Zielsystem bedeutungslos) entfallen. Der Import bleibt kompatibel, ältere Exporte mit diesen Feldern werden weiter gelesen. Der E2E-Test zum Round-Trip aus #269 war seit seiner Einführung rot (erwartete exakt drei Felder je Abhängigkeit und übersah den Import-Optionsdialog) und ist korrigiert.

Security

  • Benutzersuche beachtet die Enumeration-Einstellungen von Nextcloud und leakt keine E-Mail-Adressen mehr (Issue #285): GET /users/search rief IUserManager::search() direkt auf und lieferte jedem angemeldeten Nutzer zu jedem Treffer UID, Anzeigenamen und E-Mail-Adresse — mit kurzen Suchbegriffen ließen sich so alle Konten der Instanz auslesen, auch wenn die Admins das Auflisten abgeschaltet oder auf Gruppen/Telefonbuch beschränkt hatten. Die Suche läuft jetzt über die Collaborators-API (ISearch::search() mit IShare::TYPE_USER, ohne Lookup-Server) wie der Teilen-Dialog im Kern und beachtet damit shareapi_allow_share_dialog_user_enumeration, shareapi_restrict_user_enumeration_to_group/_to_phone/_full_match* und shareapi_only_share_with_group_members; exakte Treffer stehen vorn. subname stammt ausschließlich aus deren Ergebnis, die App lädt keine E-Mail-Adresse mehr selbst nach. Das eigene Konto (nur mit include_self, #204) ergänzt der Controller selbst, wenn Benutzer-ID, Anzeigename oder E-Mail den Suchbegriff enthalten, weil die Collaborators-API es nie liefert. Neue PHPUnit-Tests in UserControllerTest (u. a. keine Fremdkonten bei abgeschalteter Enumeration)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureesXfyynRSXx6Sc+EbEb+n8jhv9KklmBcayRslKKo0dpavjZ1x7CE/oUKrOppOVm7
YsCst1sFoTz/sPZ4qveDz/erE0STxAi2UMyiHHUglJ3JmcCJKtF5byki45BAm02V
PV7dn+EYQP8bd4jWy96mQa66eGzuuAsgR/mA7S1UC9SvcKq6UF6A0LJpJqMyInaR
ycEg6H78WtrIOKAuB5WyaEo20IRsXIechdfyl39Xz1bl1IextE3DPey0OaV6CzfF
6YQeMho+ja2iqh6JKfAzq2zKgTDBLo/bcgk8Gy/b4djBX5KES2w96x535UG7a6JM
fYEg7QdRUqOGgXoCTRSwIAGtqX8bq9sc7058+5NqO9xYOiGDzfv6dJ94x6+KXpd4
eN1N1P0+MfL9TKP27NQLsy46vXPU36w4cSijoMmxyrYGUbVVudrb8XANUvv/jZc+
d/yNvPzhYRDHvoGJ3gn5exgVpQW2M7qQMOBVDYDkROo1UqKv1k3da9s14VMHtX6y
PyhIdtt6Qi7Lu0cMykFH0rYQYoe84Ot3Xb547ldyNCCDAeta4vTczDbA5dTrdstM
HCrbbSl4iF/Lu7zeVhlxnWnQyuC5HjUg0vVuikxkEyYScruNJw4prkXD04XHOMyG
1HOBUrG2tiFl60B8XGyibo5+fc1ioQFwe2iIjdLOW3M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.1.0
Release Details
UpdatedSept. 25, 2026, 6:24 a.m.
Changelog

Changed

  • Detaildialog in abgesetzte Bereiche gegliedert (Issue #263): Die Formulare für Projekt, Teilprojekt, Aufgabe und Meilenstein fassen ihre Felder jetzt in Bereiche mit Rahmen, farbig hinterlegtem Kopf, Symbol und Überschrift zusammen (z. B. Grundinformationen, Darstellung, Terminplanung, Budget und Kosten, Ressourcen, Risikostufe, Fortschritt und Status, Deck, Erinnerungen, Baselines, Export und Import). Die Bereiche heben sich vor einem dezent getönten Dialoghintergrund ab, der Dialogkopf zeigt ein Symbol der Objektart, Feldbeschriftungen stehen in normaler statt Versal-Schrift, und die Risikostufen tragen ihre Ampelfarbe auch ungewählt. Die Farbtöne sind aus den Nextcloud-Farbvariablen gemischt und funktionieren im hellen wie im dunklen Design. Die Trennlinien zwischen den einzelnen Feldern aus dem ersten Anlauf entfallen dafür. Symbole kommen aus @mdi/js, das jetzt direkte Abhängigkeit ist
  • Neue App-Store-Screenshots zur überarbeiteten Oberfläche: Die zehn verlinkten Bilder in appinfo/info.xml sind neu aufgenommen (Projektstruktur, Abhängigkeiten im gegliederten Detaildialog, Tabelle, kritischer Pfad, Ressourcen, Kalender und Feiertage, Abweichungsbericht, Kennzahlen/EVM, Simulation, Mehrprojektansicht mit Filter). Sie liegen jetzt unter screenshots/appstore/ statt screenshots/usecases/, damit der App Store nicht auf zwischengespeicherte alte Bilder unter den bisherigen URLs zurückgreift; die übrigen, veralteten Bilder aus screenshots/ sind entfernt. Das Showcase-Skript scrollt dafür in UC 2 den Abschnitt „Abhängigkeiten“ des Dialogs ins Bild und in UC 5 das Ressourcenraster ganz nach links
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureZQ2bK6Wi8ifFLdOZU9fKEitqGU2/V3SC8sQadWpViyGbPp9TFzXz/m+6VOgxRM0K
88XrfFLwkUN5CrQ8Mzj8rzUUKAp0ANWZVMC43yWzABpywSQ+FosMPTPoPyS7dymn
eKXUnbqfSE3yebLbOtpQaK9VIMVXfvIpMQuIaSnnSX/Mi3ofY4oN3eyBy9NT1uH+
icAEBU4EFZbByJe3tF+IdKP81OX0kFydqeuiSgWJph7yRxP+ucBioO+BsjGB1ZGh
2kiCYjlYoZPbgc4WSoWfe3WGY/cUiqr/6sPEkdWfYHrG6c/tUuNhM1E1pmBXlwIK
S3IzZVklRWFkFcTHdYLAg5QKWTN2+m0sCpPkMkZQn2NYXQNQ5oAPKNoDTMBmW+zG
B72pYfEAs5TQFGcn1M6k6e4IdVjnLtM6AdmyeMdu9y8hKUH8en1RT/MVbXZlFN7O
fZoXKZSwpdocdqMZOqaT3qoUTFUZbmOBzcQQ3xeYQ4H2UvIp4PlyPkXeiFEHKY93
hZWcmtTHthlbPGhtnAutXA7zCzruiXisJXCNaf7ZM+xSbtW3GeLqRMwkLImyxydK
1C+BekGHiJaWI32PyqZH1mwR+V1qgqLLcD3yb+gKOBOUSBW4pdMnNpmvnO7y8KBg
jw8MQf3e5b0JVVoksbULqjqvCOm2ug60NIZYCfp59BA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.1
Release Details
UpdatedSept. 24, 2026, 7:49 a.m.
Changelog

Changed

  • App-Store-Beschreibung und README neu gefasst: Zusammenfassung und Beschreibung (info.xml, Deutsch und Englisch) stellen jetzt das professionelle Projektmanagement mit der Deck-Integration als Alleinstellungsmerkmal in den Vordergrund, die Funktionsliste ist kompakter, und ein eigener Abschnitt erklärt die Datensouveränität (alle Daten bleiben auf der eigenen Instanz). Das englische README und docs/de/README.md übernehmen den Wortlaut. In den übrigen Sprachen entfällt an zwei Emojis der Variation Selector
  • Toolbar-Schalter einheitlich: Die segmentierten Schalter der Toolbars (Zeitskala und Darstellung im Diagramm, Zeitskala im Ressourcenraster) und der Ansichts-Umschalter der App teilen sich jetzt dieselbe Optik: eckige Segmente ohne eigenen Rand und ohne eigene Rundung, sodass Hover- und Aktiv-Flächen bis zur Kante reichen und kein Randstreifen mehr stehen bleibt; die Rundung kommt allein vom Container. Die gemeinsame Regel liegt in der neuen segmented-switch.css statt in mehreren nahezu identischen Blöcken

Fixed

  • Leerzustand im Diagramm nicht mittig: Der Leerzustand des Diagramms (GanttChart.vue) steht jetzt mittig, und sein Icon ist optisch zentriert (der viewBox war seitlich versetzt)
  • Stylelint-Fehler in ResourceGrid.vue: fehlende Leerzeile vor einer Regel (rule-empty-line-before), die den JS-Job der CI rot färbte
  • JSON-Import brach bei Abhängigkeiten ab, Meilensteine fehlten im Export (Issue #269): importJson() übergab predecessor_id-Werte unverändert an store.createTask() – die alten IDs des Quellprojekts bestanden den IDOR-Schutz aus #190 nicht (Import in ein anderes Projekt/eine andere Instanz brach mit invalid_dependency ab und hinterließ ein halb angelegtes Projekt) bzw. zeigten beim Import ins selbe Projekt auf die falschen (alten) Aufgaben. Aufgaben werden jetzt zunächst ohne Abhängigkeiten angelegt und diese erst danach mit auf die importierten Aufgaben umgemünzten predecessor_id-Werten nachgetragen – unabhängig davon, in welcher Reihenfolge Vorgänger und Nachfolger in der Datei stehen. exportJson() nimmt außerdem Meilensteine samt ihrer Zuordnung zu Teilprojekt/Aufgabe und ihres dependency_type mit; der Import schreibt diese Bezüge ebenfalls auf die neuen IDs um
  • Balken mit Abhängigkeiten ziehen schrieb doppelt nach Deck (Issue #267): frappe-gantt zieht beim Verschieben/Resizen eines Balkens dessen Abhängigkeits-Balken in der Vorschau um denselben Pixel-Versatz mit und feuerte on_date_change für jeden von ihnen einzeln – der Server berechnet die Kette beim Speichern des tatsächlich gezogenen Balkens aber bereits selbst (propagated). Der zweite, nur aus der Vorschau abgeleitete Save überschrieb die korrekt propagierten Termine mit dem naiven Pixel-Versatz und erzeugte bei aktiver Deck-Anbindung eine doppelte Rückfrage bzw. Erfolgsmeldung. GanttChart.vue verarbeitet on_date_change jetzt nur noch für den Balken, auf dem die Ziehbewegung tatsächlich begann
  • Picker für eine zweite Deck-Karte konnte dauerhaft deaktiviert bleiben (Issue #267): Der Deck-Bereich im Aufgabendialog rendert einmal sofort (Ladezustand) und ein zweites Mal, sobald die Verknüpfungen geladen sind – traf eine Auswahl im Picker genau in dieses Fenster, ersetzte der zweite Render Select und „Add“-Button durch neue, wieder deaktivierte Knoten und die Auswahl ging verloren. Der Bereich rendert jetzt erst, nachdem die Verknüpfungen geladen sind
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureblE0iU/iUXgQIlpljnjDbdufoMyB2l1S9iGos0PQ3DDhQU9wFB13iopDY4YfCjrW
Y2bVw//uxXAO1S4wzURGPTAEgXGI3NM4A2JaHsxMREiJoU6ANjCSOnt45f9yvgNC
se02XpRKzbTtIUXTIGsH2KUe3grZ7hHg95x4riQhhrLhsDESZMZANpO60O52/AMM
gb4yZiCTbUUr1IzM/IowbHWJcghd6RG8KG+EwONJpRqJVjZhcqhNgcM9k/nKyCxY
tw6o7MIlwQmOUzh6kZXxl8p4auRpiHcGrruiWzJwEb6WSmH/XFTdGwP/ZV8lNnoj
KrR2e+yaKiPMR7UQLrtE8yUe1LGmghJejF/cDO3uLEOGD87gZfHjqXAJOrHI+zyx
FcT65clu8MUG2Tk1PQammIkYty0sPaHKwB/woLOlEBWX8LqrAK9ylOD7NHTdArhJ
TibyfWC9UI1f/iyBYu0xCEq11T23DtrFcpun7ELMNUiQ2z260/PykzEK6WtEhRVd
hgLyzf3YoFSqRNOM5IfewWlGt9YdyM4J3bLIYP01GY4MFV90y9M7/RcaehOG3U/5
DFnQswUXRr9ivk4S7dFt7J0WqltzLDqbIwtXi5f6TEqahrYD1Y46W4JXArdhOK+P
FE4VR6KQrLzKQ9PQhTbrfqCLj0tewWnxc8g6OgIGGk4=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.0
Release Details
UpdatedSept. 23, 2026, 9 a.m.
Changelog

Major-Release mit zwei Schwerpunkten. Die Einzelheiten stehen in den Einträgen darunter.

  • Komplett überarbeitete Deck-Schnittstelle (Zielbild #234, Issues #209–#211, #235–#241, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit geworden. Pro Projekt gibt es eine Stufe für den Deck-Abgleich: Aus (Standard), Nur lesen, Schreiben mit Rückfrage oder Schreiben ohne Rückfrage. Eine Aufgabe kann mehrere Karten haben, und Karten lassen sich direkt aus der Aufgabe anlegen. Termine werden mit Konfliktregel nach Deck geschrieben. Fortschritt und Status kommen sofort aus Deck. Abweichungen zwischen Plan und Deck erscheinen im Überblick, lassen sich annehmen und filtern. Beim Löschen können verknüpfte Karten archiviert werden, doppelte Erinnerungen werden unterdrückt, und ein occ-Befehl gleicht einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich am Verhalten nichts; Bestandsprojekte starten höchstens auf Nur lesen.
  • Neuer Umgang mit Planungsdaten für Projekte und Teilprojekte (Issue #229): Bisher ergaben sich Start und Ende von Projekten und Teilprojekten ausschließlich aus ihren Aufgaben. Jetzt lässt sich zusätzlich ein eigener Planungszeitraum setzen, im Detaildialog oder direkt in der Tabelle. Gezeigt wird er statt des berechneten Zeitraums, auch bei noch leeren Teilprojekten. Laufen die Aufgaben über den Rahmen hinaus, markiert ihn ein rotes Ausrufezeichen. Ampel, kritischer Pfad, Filter und Rollups rechnen weiter mit dem tatsächlich verplanten Inhalt. Ohne gesetzten Planungszeitraum bleibt alles wie bisher.
  • Warum eine neue Hauptversion: Die API-Felder haben sich teilweise geändert. deck_card_id an Aufgaben ist nur noch ein berechnetes Lesefeld, Karten werden über /tasks/{id}/deck-links verknüpft (siehe „Changed“). Löschen über DELETE liefert jetzt einen Körper mit deck. Beim Update laufen die Migrationen Version1039–Version1043 automatisch; vorhandene Kartenverknüpfungen werden übernommen.

Added

  • Detail-Dialog per Klick aus der Tabelle öffnen (Issue #264): Bislang öffnete der Detail-Dialog nur über einen Klick auf den Balken im Gantt-Diagramm, was bei größeren Projekten erst Scrollen zum richtigen Monat verlangte. Die Titel-Spalte der Tabelle hat jetzt einen geteilten Klickbereich: Bei Aufgaben und Meilensteinen öffnet ein Klick auf den Platz vor dem Bezeichnungs-Rahmen (Zusammenklapp-Pfeil, Farbpunkt, Abweichungssymbol/-zähler) den Detail-Dialog, während der Rahmen selbst wie bisher die Inline-Bearbeitung der Bezeichnung öffnet. Bei Projekten und Teilprojekten öffnet der gesamte bisherige Rahmen den Detail-Dialog; die Inline-Bearbeitung der Bezeichnung entfällt dort, da beide Objekttypen ohnehin nur das Titelfeld inline editierbar hatten. Der Rahmen ist jetzt als sichtbarer Rand um die Bezeichnung dargestellt (GanttTable.vue)
  • Deck-Anbindung abgeschlossen (Stufen 1–3 aus #234, Issues #209–#211, #235–#240, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit mit Deck geworden. Je Projekt lässt sich eine von vier Stufen wählen — Aus, Nur lesen (Fortschritt und Status kommen sofort aus Deck), Schreiben mit Rückfrage und Schreiben ohne Rückfrage (beide erlauben zusätzlich, Karten aus einer Aufgabe anzulegen und ihre Termine sowie einmalig die Zuständigen nachzuführen). Wo Deck und Plan auseinanderlaufen — Karte gelöscht/archiviert, Termin oder Zuständigkeit von Hand geändert, Karte überfällig, ziehende Person ohne die geforderte Rolle —, zeigt die App das an, statt es aufzulösen; angenommen wird je Aufgabe. Doppelte Erinnerungen zwischen Gantt und Deck werden unterdrückt, ein occ-Befehl gleicht bei Bedarf alles einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich nichts am bisherigen Verhalten. Die folgenden Einträge beschreiben die einzelnen Schritte im Detail; Endstand siehe FEATURES.md Kapitel 16 und die Integrationsmatrix, HOWTO.md und README („Optionale Abhängigkeit zu Deck“)
  • Deck-Fundament: Adapter, Verknüpfungstabelle (1:n) und Opt-in-Stufen je Projekt (Issue #209, Stufe 1, Schritte 1–2 aus #234): Die Migration Version1039 legt nc_gantt_deck_links an (eine Zeile je Karte mit Aufgabe, Board, zuletzt gesehenem Kartenstand, zuletzt geschriebenen Terminen, angenommenem Stand je Abweichungsart und Zeitpunkt des Abgleichs; eindeutiger Index auf der Karten-ID) und übernimmt vorhandene deck_card_id-Werte. Die Spalte deck_card_id bleibt lesbar, ist veraltet und wird bis zum Rückbau mit #237 aus der Tabelle befüllt. Das Löschen einer Aufgabe entfernt ihre Verknüpfungen, die Karte bleibt. Neue API: GET/POST /tasks/{id}/deck-links, DELETE /tasks/{id}/deck-links/{cardId}, GET /deck-links/occupied?project_id=|board_id= (belegte Karten → Aufgabe)
  • DeckGateway als einzige Andockstelle zu Deck (Deck-Services erst im Aufruf aus dem Container, isAvailable(), supportsStartDate(), readCard(), permissions(), Ergebnisobjekt DeckResult statt Exception, nur mit angemeldetem Benutzer, Schreibmethoden nur in den beiden Schreibstufen — ihre Umsetzung folgt mit #210, #211, #237, #238)
  • Opt-in-Stufe „Deck-Abgleich“ je Projekt mit vier Stufen (Aus — Standard, Nur lesen, Schreiben mit Rückfrage, Schreiben ohne Rückfrage); serverseitig beantwortet DeckSyncService getrennt „darf lesen“, „darf schreiben“, „muss fragen“. Projektdialog mit Vorschlag „Nur lesen“ beim Verknüpfen eines Boards, Hinweis beim Umschalten auf eine Schreibstufe und ausgegraut mit sichtbarem Hinweistext ohne Deck. Bestandsprojekte mit Board und Aufgabe im Fortschrittsmodus „aus Deck“ starten auf Nur lesen, alle übrigen auf Aus; nie auf einer Schreibstufe. Der clientseitige Abgleich aus #221 prüft die Stufe
  • Abweichungsdienst (DeckDeviationService, DeckDeviationProvider) mit einheitlichem „Annehmen“ (GET /tasks/{id}/deck-deviations, POST /tasks/{id}/deck-deviations/accept) und Bereich „Abweichungen zu Deck“ im Aufgabendialog; die Abweichungsarten liefern die Folgetickets
  • Vertragstest gegen Deck (make test-deck-contract, tests/deck-contract/, CI-Job Deck-Vertragstest) und README-Abschnitt „Optionale Abhängigkeit zu Deck“; FEATURES.md (de/en) um Stufen und mehrere Karten ergänzt
  • Deck im E2E-Setup optional: setup-dev.sh --no-deck, tests/e2e/helpers/deck.js (Tests ohne Deck überspringen sich selbst), tests/e2e/specs/deck-foundation.spec.js
  • Deck-Rückmeldung per Event-Listener: Fortschritt und Status sofort übernehmen (Issue #235, Stufe 1, Schritt 3 aus #234): DeckCardListener ist für CardCreatedEvent, CardUpdatedEvent und CardDeletedEvent per Klassenname registriert und wertet nur getWebhookSerializable()['card'] aus; er wirft nie. DeckCardSyncService aktualisiert ab der Stufe Nur lesen den zuletzt gesehenen Kartenstand an der Verknüpfung (erledigt, archiviert, gelöscht, Fälligkeit, Start, Zuweisungen, Checkliste) und berechnet für Aufgaben im Modus „aus Deck“ Fortschritt (Mittelwert je Karte: erledigt = 100 %, sonst Checklistenanteil; archiviert zählt als erledigt, gelöscht zählt nicht mit; ohne mitzählende Karte bleibt alles stehen) und Status (done / in_progress / not_started, blocked bleibt). Die Formel DeckProgress in PHP spiegelt cardProgress(); beide prüfen tests/fixtures/deck-progress-cases.json. Neue Abweichungsarten „Karte archiviert“ / „Karte gelöscht“ über den Abweichungsdienst; Übernahmen stehen mit Operations-ID deck:… im Änderungsprotokoll. Der clientseitige Abgleich beim Öffnen bleibt als Fallback und setzt den Status nach denselben Regeln (PUT /tasks/{id}/progress/sync akzeptiert done). Der Aufgabendialog weist im Modus „aus Deck“ neben dem Statusfeld darauf hin, dass der Status Deck folgt. Vertragstest, README-Abschnitt und FEATURES.md (de/en) sind nachgezogen
  • occ nc_gantt:deck:sync --user=<uid> [--project=<id>] [--dry-run]: einmaliger Vollabgleich aus Deck (Issue #236, Stufe 1, Schritt 4 aus #234): Der Befehl setzt den Kontext des Kontos per setVolatileActiveUser, liest jede verknüpfte Karte, die das Konto in Deck sehen darf, und übernimmt sie über dieselbe Logik wie der Listener aus #235 (DeckCardSyncService::applyStates(), gemeinsamer Weg statt zweiter Implementierung). Nur Projekte ab Nur lesen; nach Deck wird nie geschrieben. Ausgabe je Projekt: geprüfte und geänderte Aufgaben, übersprungene Karten (keine Berechtigung / nicht gefunden); Rückgabewert ≠ 0 nur bei Fehlern, auch wenn Deck fehlt. --dry-run speichert nichts. Registriert in appinfo/info.xml (<commands>); FEATURES.md (de/en) und README ergänzt
  • Mehrere Deck-Karten je Aufgabe: Picker, „+“ zum Anlegen und Rolle der Deck-Listen je Projekt (Issue #237, Stufe 2, Schritt 5 aus #234; UC1 „Übergabe“, O1, O2, O8, O14): Der Aufgabendialog hat den Bereich „Deck-Karten“ (für bestehende Aufgaben) mit der Liste der verknüpften Karten (Titel, Liste, erledigt, „In Deck öffnen“, „Lösen“ — das Lösen ändert die Karte nie), dem Picker (Karten an einer anderen Aufgabe sind gesperrt und nennen sie) und „+“, das über den Server und DeckGateway::createCard() eine Karte mit Titel und Beschreibung der Aufgabe samt Rücklink „Geplant in Gantt-Projekte: <URL>“ (Deeplink ?project=&task=), Fälligkeit = Ende, ab Deck 1.18 Start = Start anlegt; „+“ gibt es erst ab „Schreiben mit Rückfrage“ und mit Bearbeitungsrecht am Board (sonst gesperrt mit Hinweis auf Grund und Stufe) und braucht keine zweite Rückfrage. DeckGateway::setDates() ist umgesetzt (Fälligkeit, ab Deck 1.18 Start; Ergebnisobjekt statt Exception; nur in einer Schreibstufe; prüft nach dem Schreiben, ob Deck die Termine übernommen hat) — #210 baut darauf auf. Beim Verknüpfen einer Karte mit abweichendem Datum fragt der Dialog sofort: Plan in Deck durchsetzen (nur in einer Schreibstufe, selbst die Bestätigung), Plan anpassen oder Abweichung stehen lassen; eine datumslose Karte bekommt die Planlage ohne Rückfrage, aber nur ab einer Schreibstufe. Der Stand wird als „zuletzt gesehen“ bzw. „zuletzt von Gantt geschrieben“ an der Verknüpfung gespeichert. Projekteinstellung „Rolle der Deck-Listen“ (deck_list_role: subproject — Standard, storage, none) mit „?“-Hilfe; das Listenfeld am Teilprojekt entfällt bei none, gespeicherte Listen-IDs bleiben; ein Wechsel ändert keine Verknüpfung. Ohne Liste am Teilprojekt nutzt „+“ die Liste des nächsten übergeordneten Teilprojekts, sonst die erste Liste des Boards. Neue API: POST /tasks/{id}/deck-cards, POST /tasks/{id}/deck-links mit optionalem date_action. Der Fortschritt „aus Deck“ im Dialog bildet bei mehreren Karten den Mittelwert. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kapitel 16 und Integrationsmatrix), HOWTO.md und README (Deck-Vertrag) sind nachgezogen; Vertragstest um CardService::create()/update(), StackMapper::findAll() und den Startparameter ergänzt

  • Termine der Aufgabe in die verknüpften Deck-Karten schreiben, mit Konfliktregel und gebündelter Rückfrage (Issue #210, Stufe 2, Schritt 6 aus #234; UC3 „Umplanung“, O4, O8, O9, O11, O12): Ab einer Schreibstufe folgen alle verknüpften Karten den Terminen der Aufgabe — Fälligkeit = Ende zum Arbeitstagsende (Einstellung „Arbeitszeit“), ab Deck 1.18 zusätzlich Start — bei jedem Weg der Terminänderung (Dialog, Tabelle, Ziehen im Diagramm, Propagation über Abhängigkeiten inklusive Karten fremder Aufgaben, „Termine neu berechnen“, Undo). Der neue DeckDateSyncService schreibt synchron im Request nach dem Speichern über DeckGateway::setDates(), fehlertolerant (Deck-Fehler brechen das Speichern nie ab); das Frontend zeigt danach „3 Deck-Karten aktualisiert“, aufklappbar mit Liste, auch fehlgeschlagene Karten. Konfliktregel: Geschrieben wird nur, wenn die Karte noch den zuletzt von Gantt geschriebenen Wert trägt (ohne geschriebenen Wert: wenn sie kein Datum trägt); sonst bleibt sie unangetastet und der Abweichungsdienst zeigt „Fälligkeit/Start in Deck abweichend“ mit Deck- und Plan-Datum. Aktionen im Aufgabendialog: Plan in Deck durchsetzen (POST /tasks/{id}/deck-deviations/enforce, ohne Rückfrage), Plan anpassen (Deck-Datum wird Ende der Aufgabe, gespeichert mit dem Dialog) und Annehmen. Rückfrage in „Schreiben mit Rückfrage“: POST /projects/{id}/deck-preview rechnet die Propagation ohne Speichern; ein Dialog listet einmal gebündelt alle betroffenen Karten mit altem und neuem Wert, Karten fremder Aufgaben hervorgehoben, „Abbrechen“ verwirft die gesamte Änderung, kein „Nicht mehr fragen“; ohne Bestätigung (deck_confirmed) wird nicht geschrieben. Bei „Schreiben ohne Rückfrage“ entfällt der Dialog, bei „Aus“ und „Nur lesen“ wird nichts geschrieben. Rechte: Ohne Bearbeitungsrecht am Board wird die Karte als „Deck-Abgleich ausstehend (keine Berechtigung)“ markiert und beim nächsten Speichern durch eine Person mit Rechten mitgeschrieben (Migration Version1041, Spalte sync_pending); eine in Deck gelöschte oder nicht auffindbare Karte bleibt verknüpft und erscheint als Abweichung „Karte gelöscht“. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kapitel 16) und HOWTO.md sind nachgezogen, der Hinweis „Terminverschiebungen im Gantt ändern derzeit keine Deck-Karten“ ist ersetzt. Neue PHPUnit-Tests (DeckDateSyncServiceTest, TaskDeckSyncTest, DeckDateDeviationProviderTest, DeckDeviationActionServiceTest), Vitest-Tests (deckWrite.test.js, deckWriteDialogs.test.js, deckDeviations.test.js, projects.test.js) und der E2E-Test deck-date-sync.spec.js

  • Verknüpfte Deck-Karten beim Löschen auf Rückfrage archivieren (Issue #238, Stufe 2, Schritt 7 aus #234; O10, O11, O12): Der Löschdialog einer Aufgabe mit Karten fragt „Verknüpfte Deck-Karte(n) ebenfalls archivieren?“ mit der Kartenzahl, Standard „Nein“; Teilprojekt und Projekt stellen eine Sammelrückfrage mit der Gesamtzahl. Gefragt wird nur in den beiden Schreibstufen und nur für Karten an Boards mit Bearbeitungsrecht (DeckGateway::permissions()); für die übrigen sagt der Dialog „N Karten bleiben unverändert“, der Löschdialog ist die Bestätigung (keine zweite Rückfrage). DeckGateway::archive() ist umgesetzt (CardService::archive(), Erfolg erst nach erneutem Lesen); DeckDeletionService plant vor dem Löschen (die Kaskade entfernt die Verknüpfungen) und archiviert synchron danach, fehlertolerant: scheitert Deck, ist die Aufgabe trotzdem gelöscht und die Meldung nennt die nicht archivierten Karten, sonst die archivierten mit Link. Neu: GET /tasks|subprojects|projects/{id}/deck-delete-preview; DELETE nimmt archive_deck_cards an und liefert jetzt deck (archived, failed) statt eines leeren Körpers. Verknüpfung lösen ändert die Karte nie; ein Undo für das Löschen gibt es nicht. PHPUnit mit gemocktem Gateway, Vitest, E2E deck-archive-on-delete.spec.js; Vertragstest um CardService::archive(id) ergänzt; FEATURES.md (de/en) und README ergänzt
  • Rollen als Ressourcentyp: Aufgaben fordern Rollen, Personen tragen die Kapazität (Issue #250, Schritt A aus #249; R2–R5): nc_gantt_resources.type (person — Standard für alle bestehenden Zeilen —, role, material) und die n:m-Tabelle nc_gantt_resource_role (Person ↔ Rolle, eindeutige Kombination; Migration Version1042, dazu nc_gantt_projects.roles_only_assignment). Eine Rolle ist Zuweisungsziel ohne Konto, Arbeitszeitmodell, Kalender und eigene Kapazität (Kapazitätswerte an Rollen werden nicht angelegt); TaskResource und ResourceGroup bleiben unverändert, Rollen sind eine zur Gruppe orthogonale Achse. Rollen werden in der Ressourcenverwaltung über „Neue Rolle“ angelegt, umbenannt und gelöscht und getrennt in der Navigation geführt; die Zuordnung Person ↔ Rolle ist von beiden Seiten pflegbar (role_ids an der Person, person_ids an der Rolle, ohne Admin-Rechte). Löschen einer Rolle mit Aufgaben fragt nach und nennt sie (GET /resources/{id}/usage), Löschen einer Person entfernt ihre Zuordnungen, die Rollen bleiben. Im Aufgabendialog lassen sich Rollen und Personen gemischt mit Aufwand zuordnen, die Auswahl kennzeichnet „(Rolle)“/„(Person)“ (ohne Rollen unverändert); keine Migration bestehender Zuordnungen. Projekteinstellung „Nur Rollenzuweisung zulassen“ (Standard aus) verhindert nur die Neuzuweisung von Personen. ResourceUtilizationService leitet die Kapazität einer Rolle je Tag aus der Summe ihrer Personen ab (Urlaub/Teilzeit senkt alle ihre Rollen) — bewusst naiv additiv, das Ressourcen-Raster weist auf die Mehrfachzählung hin (capacity_naive); Personen- und Rollenzuordnungen laufen durch dieselbe Auswertung, das Workload-Chart summiert Rollen aus ihren Personen. Sichtbarkeit und Rechte folgen den bestehenden Ressourcenregeln. Nicht Teil: Konflikterkennung über Rollen (Schritt B), Kopfzahl (Schritt C), Rollen-Level, Historisierung, Deck-Schnittstelle. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 11 und Integrationsmatrix) und HOWTO.md sind nachgezogen. Neue PHPUnit-Tests (ResourceServiceTest, ResourceUtilizationServiceTest, TaskResourceAssignmentServiceTest, ResourceRolesMigrationTest), Vitest-Tests (resourceType.test.js, resourceDelete.test.js, resources.test.js) und der E2E-Test resource-roles.spec.js
  • Zuständigkeit bei der Kartenanlage übernehmen, danach Abweichungen anzeigen (Issue #211, Stufe 3, Schritt 8 aus #234; O7): „+“ im Aufgabendialog weist der neuen Karte alle Nextcloud-Konten zu, die mit den Ressourcen der Aufgabe verknüpft sind (DeckGateway::assign() über AssignmentService::assignUser(), jedes Konto einzeln). Der Dialog unterscheidet beim Überspringen Person ohne Konto (Lücke: „2 Ressourcen ohne Nextcloud-Konto wurden nicht zugewiesen“), Rolle (#250; sachliche Auskunft, die Karte bleibt absichtlich frei zum Ziehen, nicht als „nicht zugewiesen“ gezählt) und Konto ohne Zugriff auf das Board (übersprungen und genannt; Gantt teilt das Board nicht). Scheitert die Zuweisung, bleibt die Karte angelegt und verknüpft. Danach gibt es keine laufende Synchronisation. Neue Abweichungsart assignees („Zuständige abweichend“, DeckAssigneeDeviationProvider über den Abweichungsdienst aus #209): Menge der zugewiesenen Benutzer der Karte gegen Menge der verknüpften Konten der Ressourcen, in beide Richtungen, mit Annehmen (Hinweis kehrt erst bei erneuter Änderung zurück); Gruppen/Teams und Rollen erzeugen keine Abweichung (Gruppen erscheinen als „auch zugewiesen: …“ an der Karte, bei gemischter Zuordnung zählt nur der Personenanteil). Der Kartenstand trennt jetzt assignees (Benutzer) und assigned_groups. Neue Antwortfelder assignment bei POST /tasks/{id}/deck-cards; Vertragstest und README um AssignmentService::assignUser() und BadRequestException ergänzt. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kap. 16) und HOWTO.md nachgezogen. PHPUnit (DeckAssigneeDeviationProviderTest, DeckAssigneeResolverTest, DeckAssigneesTest, DeckLinkServiceTest, DeckGatewayTest), Vitest (deckAssignees.test.js, deckCards.test.js, deckDeviations.test.js) und E2E deck-assignment.spec.js
  • Abweichungen zwischen Plan und Deck im Überblick anzeigen, annehmen und filtern (Issue #239, Stufe 3, Schritt 9 aus #234; UC4 „Abweichungen erkennen“, Leitprinzip 8): Zwei neue Abweichungsarten über den gemeinsamen Abweichungsdienst aus #209 — overdue (DeckOverdueDeviationProvider: Fälligkeit vor heute, nicht erledigt, nicht archiviert/gelöscht, ausschließlich aus dem zuletzt gesehenen Kartenstand, kein Deck-Zugriff nötig) und role (DeckRoleDeviationProvider, setzt #250 voraus: eine Karte einer Aufgabe mit Rollenzuweisung ist einer Person zugewiesen, die keiner der geforderten Rollen zugeordnet ist — ermittelt über ResourceRoleMapper::findPersonIdsOfRoles(); ohne Rollenzuweisung an der Aufgabe greift die Art nicht, bei gemischter Zuordnung läuft sie unabhängig neben assignees). Neu DeckDeviationService::kindsByTask() fasst die offenen Arten je Aufgabe eines Projekts in einem Rutsch zusammen (ein Durchlauf über alle Verknüpfungen statt eine Anfrage je Aufgabe); TaskService hängt daraus das berechnete Lesefeld deck_deviation_kinds an jede Aufgabe aus GET /projects/{id}/tasks, RollupService liefert zusätzlich deck_deviation_count (Summe über das Projekt) im bestehenden Rollup-Endpunkt. Im Frontend: Warnsymbol an der Aufgabe in Tabelle (GanttTable.vue, echtes <button>) und Diagramm (gantt/overlays/deckDeviationOverlay.js, fokussierbares SVG mit eigenem Klick-/Tastatur-Handler statt des abgeschalteten Bibliotheks-Popups) mit Tooltip/aria-label über die betroffenen Arten (utils/deckDeviationKinds.js); ein Klick öffnet den Aufgabendialog gescrollt und fokussiert beim Bereich „Abweichungen zu Deck“ (detailModal.js, neue Option focusDeckSection, Ereigniskette open-deck-deviations/edit-task-deck). Zähler an Teilprojekt (rekursiv über verschachtelte Teilprojekte) und Projekt in der Tabelle sowie, über den Rollup, in der Projektliste und im Projekt-Rollup-Balken. Neuer Filter „Deck weicht ab“ (filters.js, filterMatch.js, FilterBar.vue) wirkt wie die übrigen Filter auf Tabelle und Diagramm. Für „Karte gelöscht/archiviert“ gibt es im Aufgabendialog zusätzlich die Aktion Verknüpfung lösen (DELETE /tasks/{id}/deck-links/{cardId}, ändert die Karte nie); nach Annehmen, Durchsetzen oder Verknüpfung lösen lädt der Dialog die Aufgabenliste des Projekts neu (onDeviationsChanged), damit Symbol und Zähler ohne Neuladen der Seite mitziehen. Wie immer gilt: Abweichungen nur ab der Projektstufe „Nur lesen“, Annehmen nur mit Bearbeitungsrecht. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, neuer Abschnitt in Kap. 16 und Filterliste in Kap. 5) und HOWTO.md (de/en) nachgezogen. PHPUnit (DeckOverdueDeviationProviderTest, DeckRoleDeviationProviderTest, DeckCardStateDeviationProviderTest — bislang fehlend zu #235 —, erweiterte DeckDeviationServiceTest/RollupServiceTest), Vitest (deckDeviationKinds.test.js, erweiterte filterMatch.test.js/tableRows.test.js/deckDeviations.test.js) und E2E deck-deviations-overview.spec.js
  • Doppelte Signale vermeiden: Gantt-Erinnerungen und Tasks-Einträge bei Aufgaben mit Deck-Karte unterdrücken (Issue #240, Stufe 3, Schritt 10 aus #234; O13): Neuer DeckSuppressionService beantwortet für Aufgaben mit mindestens einer verknüpften, nicht archivierten Karte (nach dem zuletzt gesehenen Kartenstand aus #235) in einem Projekt ab Stufe „Nur lesen“, ob NC-Gantt für diese Aufgabe still bleiben soll — DueReminderService, EventNotificationService::notifyAssigned() und TaskCalendarSyncService fragen dort statt die Regel je einmal selbst nachzubilden, statt selbst zu unterdrücken. Unterdrückt werden die Fälligkeits-Erinnerung der Aufgabe, die Zuweisungsmeldung und ihr Eintrag in der Tasks-App; nicht unterdrückt bleiben notifyRescheduled() (Deck kennt keine Kettenverschiebung) und Meilenstein-Erinnerungen (Meilensteine haben keine Karte). Persönliche Einstellung „Gantt-Benachrichtigungen auch für Aufgaben mit Deck-Karte erhalten“ im Abschnitt „Gantt-Projekte“ der Nextcloud-Benutzereinstellungen (PUT /api/v1/settings/reminder-deck-override), Standard aus — die Unterdrückung ist also aktiv; gilt gleichermaßen für Benachrichtigungen und Tasks-App-Einträge. Wird die Unterdrückung wirksam oder aufgehoben, räumt bzw. holt der jeweils nächste Lauf von DueReminderService/TaskCalendarSyncService die betroffenen Erinnerungen bzw. To-dos ab — über denselben Soll-/Ist-Abgleich, der schon das Stummschalten trägt, ohne eigenen Sonderfall. Neue Texte in l10n/de.json/de.js; FEATURES.md (de/en, Integrationsmatrix, Kap. 14, 18 und 20) nachgezogen. Neue PHPUnit-Tests (DeckSuppressionServiceTest, erweiterte DueReminderServiceTest/EventNotificationServiceTest/TaskCalendarSyncServiceTest) und Vitest (stores/settings.test.js)
  • Optionaler Planungszeitraum für Projekte und Teilprojekte (Issue #229): Projekte und Teilprojekte können neben dem aus den Aufgaben berechneten Zeitraum einen eigenen, bewusst gesetzten Rahmen haben (plan_start/plan_end, Migration Version1043; leer für alle Bestandsprojekte, die sich damit unverändert verhalten). Eingabe im Detaildialog (Abschnitt „Planungszeitraum“) und per Inline-Editing in den Spalten Start/Ende der Tabelle, die dafür erstmals auch bei Projekt- und Teilprojektzeilen editierbar sind. Start und Ende sind nur gemeinsam gesetzt: ein leeres Gegenfeld übernimmt den eingegebenen Wert, ein vorhandener wird nie überschrieben, das Leeren eines Felds entfernt den Zeitraum; Start ≤ Ende und Schreibrecht werden serverseitig geprüft (PlanningPeriod, Fehlercodes invalid_plan_date, plan_start_after_end). Ist ein Planungszeitraum gesetzt, zeigen Balken, Tabelle (Start/Ende/Dauer), PDF/Druck und ICS-Feed ihn anstelle des berechneten Zeitraums — auch ein Teilprojekt ohne Aufgaben wird so sichtbar; ein Elternobjekt ohne eigenen Planungszeitraum umfasst in der Darstellung die Zeiträume seiner Kind-Teilprojekte. Liegen die Aufgaben nicht vollständig im Rahmen oder ragt ein Kind-Teilprojekt mit Planungszeitraum heraus, markiert ein rotes Ausrufezeichen die Zeile in Tabelle und Diagramm (planWarningOverlay.js), der Tooltip nennt die Ursache; der Rahmen passt sich nie selbst an und verschiebt keine Aufgaben. Ampel, Rollups, kritischer Pfad, Meilensteine, Filter, Fortschritt und Budget rechnen weiter mit dem berechneten Zeitraum; Baselines und Deck bleiben unberührt. Änderungen stehen mit Operations-ID im Änderungsprotokoll und lassen sich per Toast rückgängig machen (revertOperation() kennt dafür jetzt auch Projekte und liefert entity_types); der JSON-Export/-Import führt den Planungszeitraum mit. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 1, 3, 4, 10, 14) und HOWTO.md (de/en) nachgezogen. PHPUnit (PlanningPeriodTest, erweiterte ProjectServiceTest/SubProjectServiceTest/ChangeEventServiceTest/CalendarFeedServiceTest), Vitest (planningPeriod.test.js, erweiterte rowModel.test.js/tableRows.test.js/io.test.js) und E2E planning-period.spec.js
  • Rich-Vorschau für Links auf Projekte und Aufgaben (Issue #241, Stufe 4, Schritt 12 aus #234; O14): Eigener GanttReferenceProvider (OCP\Collaboration\Reference, ohne jede Deck-Abhängigkeit) erkennt Links der Form ?project=<id> bzw. ?project=<id>&task=<id>, mit und ohne index.php, und löst nur auf, wenn die anfragende Person laut AccessService mindestens Leserecht auf das Projekt hat — sonst bleibt der Link ein nackter Link. Die Vorschau einer Aufgabe zeigt Titel, Projekt/Teilprojekt, Start–Ende, Fortschritt, Status und Zuständige, die eines Projekts Titel, Zeitraum, Rollup-Fortschritt und den nächsten Meilenstein; ein eigenes Vue-Widget (registerWidget aus @nextcloud/vue, geladen nur beim RenderReferenceEvent) rendert das im Nextcloud-Look mit Dark-Mode. Cache-Präfix je Projekt bzw. Aufgabe und Cache-Schlüssel je Benutzer (ReferenceCachePrefix); TaskService, ProjectService und MilestoneService verwerfen den betroffenen Cache über IReferenceManager::invalidateCache() bei jeder Änderung, damit die Vorschau nicht bis zu einer Stunde veraltet bleibt. FEATURES.md (de/en, neues Kapitel 23 und Integrationsmatrix) nachgezogen. Neue PHPUnit-Tests (GanttReferenceProviderTest, TaskServiceReferenceCacheTest, ProjectServiceReferenceCacheTest, MilestoneServiceReferenceCacheTest) und E2E-Test reference-preview.spec.js (prüft über den Core-Endpunkt references/resolve, denselben Weg, den Text, Notizen und Talk nutzen). Nicht Teil: ein Smart-Picker-Eintrag zum Einfügen einer Gantt-Aufgabe per Suche (geplanter Folgeschritt zusammen mit der Talk-Integration)

Changed

  • Aufgaben-API: deck_card_id ist nur noch ein berechnetes Lesefeld (Issue #237): Die Migration Version1040 entfernt die Spalte nc_gantt_tasks.deck_card_id (Werte, die noch nicht in nc_gantt_deck_links stehen, werden vorher übernommen) und ergänzt nc_gantt_projects.deck_list_role. Die API liefert deck_card_id weiter — als erste Verknüpfung der Aufgabe —, damit bestehende Nutzer nicht brechen; geschrieben wird nur noch über die Verknüpfungs-Endpunkte, ein mitgesendetes deck_card_id bei POST/PUT auf Aufgaben wird ignoriert. Der Aufgabendialog und alle Frontend-Pfade lesen die Verknüpfungen aus der Tabelle

Fixed

  • Rollenkapazität im Ressourcen-Raster stimmte nicht mit den Personenzeilen überein (Issue #260): Drei unabhängige Fehler wirkten zusammen. Ein Options-API-Watcher auf store.activeIds (Getter-Pfad, nicht deep) feuerte nie, weil toggle() das Array per push()/splice() mutierte statt neu zuzuweisen — eine nachträglich in der Seitenleiste aktivierte Ressource blieb bis zum Moduswechsel ohne Auslastungsfarbe, Tooltip oder abgeleiteten Rollenwert. ResourceUtilizationService und die Personenzeile der Tabelle (getVal()) lasen unterschiedliche Kapazitätsquellen: die Rolle über die 8h/Tag-Pauschale von WorkTimeService::getHoursPerPt() für jeden ungepflegten Tag, die Person über den Mittelwert der gepflegten Tage hochgerechnet auf die ganze Periode. Beide laufen jetzt über dieselbe Tagesfunktion (personCapacityFn() im Backend, resourceCapacity.js im Frontend) — die Pauschale greift nur noch, wenn für eine Person überhaupt keine Kapazität gepflegt ist, nicht mehr für einzelne Lücken innerhalb eines gepflegten Verlaufs, und ist dann auch in der Personenzeile sichtbar. Ein gepflegter Tageswert auf einem Feiertag zählte bisher voll statt 0h, sowohl in der Auslastungsberechnung als auch in resources.js::capacityOf() (Überbuchungsprüfung/Auslastungsgraph im Gantt-Diagramm) — beide prüfen jetzt Arbeitszeitmodell und Feiertagskalender der Ressource unabhängig vom gepflegten Wert. „Wert bis Monats-/Jahresende kopieren“ (doFill()) legt keine Kapazitätssätze mehr auf Feiertagen oder arbeitsfreien Tagen an. Eine Rollenspalte gilt im Tagesmodus als arbeitsfrei, wenn keine ihrer Personen an diesem Tag arbeitet. Neue PHPUnit-Tests (ResourceUtilizationServiceTest), Vitest (resourceCapacity.test.js, erweiterte resources.test.js) und E2E-Test (resource-roles.spec.js)
  • Deck-Vertragstest schlug falsch an (Issue #256): Für CardService::create()/update() prüfte er die Parameter positional, obwohl NC-Gantt sie über DeckGateway::invokeNamed() nach Namen aufruft. Ab Deck 1.18 steht startdate hinter dem von NC-Gantt nicht genutzten done, der CI-Job Deck-Vertragstest wurde dadurch rot, ohne dass etwas kaputt war. Die beiden Methoden stehen in contract.php jetzt unter named: geprüft wird, dass es die Parameternamen gibt und dass NC-Gantt jeden Pflichtparameter der Deck-Methode kennt — die Reihenfolge in Deck ist gleichgültig, neue Parameter dazwischen (zuletzt color) brechen den Vertrag nicht mehr. Positional aufgerufene Methoden bleiben unverändert
  • Startdatum einer Deck-Karte wurde nie gelesen (DeckGateway::cardState()): Deck-Entities stellen ihre Getter über __call bereit, method_exists() war daher immer falsch; jetzt is_callable()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturekJ6Xo6R3EOShOn1PWZI+CpEgV34eRXUZy4CNPkmb+e7uqUL4Tu/tWsqGpu15PCRr
8HR2enQnsTmTk2fkKQUKBqbcfvO3vfylEzNRxU7YuOtyGW6sFAdLSKK8MDnq9XxH
1A91Bmumu7dxP+nh3u5EyGswDoH84tXcxVH0BHEr22ny4fo1/irWKT5RDcumVhyQ
SZSqcwL1iEsYzmmFZJoyqQu7Cfy6IbZXko4OJJGYWLTBvu3CBfDpQFoCg0GaMevf
SccjJXjY6+00oW3OGGn1JH3vz1dMm7l2vD7QfT04AoeFNxJkVcwFo9mTapZijalO
3kgLc7GOLXG6I7hgmqyRBHuUfJAskET9WUl9Ya92UC2Fa8NGwiBFkkQnzy8BGzEp
lbsQ/cDrFw9/U8c1RIfs3T0uOsjtSDeKIAaKQz/SJYAAYdv3SJvqWZxoIt6p04Vk
4fBM02pxBrMH+rNR+X2frwCD3+q4zBtRSxfZNWzz0kHuo9vt714yVLh5dVynwPjP
+6/5b6eE9BsobfL6M6HyZdAzZYIBkPZbfm11TA6nDT5WwUD5r+KBUJDT2hTChAVS
n7pHjc3PQ6wWgVNjvJqmtV8mvH+qvWnLFGGFd5ttHMYjfB555hcStB2UF5cAzkji
2hhP2aeeujkdcPkZ4XETBfQpkbNHaIPUxiEgUHz2oEs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.5
Release Details
UpdatedSept. 21, 2026, 8:05 a.m.
Changelog

Changed

  • Freigabe für Nextcloud 35: max-version in appinfo/info.xml von 34 auf 35 angehoben, unterstützter Bereich damit NC 31–35; die Angabe in der App-Store-Beschreibung (alle sieben Sprachen), README.md, FEATURES.md und INSTALL.md ist nachgezogen. Verifiziert gegen Nextcloud 35.0.0 mit PHP 8.5 und MariaDB 11.4 (302 Unit-Tests im Frontend, 431 PHPUnit-Tests, 98 E2E-Tests) – am Code der App war dafür keine Anpassung nötig. Die lokale Dev-Umgebung (setup-dev.sh) setzt jetzt auf nextcloud:35-apache und installiert Deck mit, weil die Fortschritts- und Karten-Integration von nc_gantt darauf aufsetzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureOtLYluq0hTNN8+vQY8AK0ALLooVxvUUxymkOjs0nAtdES78R6Sii2b9n1POirXvO
EwnIrLzKWwiT05WSV2dsrWliGU+hBhXmvUO0RviY4jmBpu9+mexpLgoHP5dUwIna
OwAN1TgJg0TOod9/bLSQts6qhr8ONk/giqNpMjId1dvQX+hymeQXFchZHk8ohVx4
4d2BaKizT6HQweBNOxxR+FGd6juzsfw4sC9ujASI6IzxPKhH9piiDFniKaERJuGo
iUvDbljcRA4YVRgM003aD9ytwdzdJ4Z3+JfI4L+LBuwaUsXFyQsC3q8bbaZWSj+w
VZX536GBNNwb0RE/nKkgGQ8WLmwG+dosiL26+Wh+a6lX2EWLFZaw9L9iwGFYG92I
iZmFDOnIpqSicG8PmCTV9YSg5u90fxPg53CaVlv6/eCxqY2mUADyErpBSHSDc1tn
g/JM58znHYQF3PfmpFc3Vh901bBytBPTkUY3p0LF1JKoH7tnZ75ar4fTORoF8nEO
Hfr50lB+bWYhEdj1LCvD9yHsRfGcaj5BZ5m6NGvFzvAf/DStAgzyF2jyz/0+YvnM
8XF+LLYY9TktnVyxHNbwjaoE32sHc/le6ABbWSLdI/R/pYWul1EVPKagxYlAQIS1
0IEJbQarL1SKdaQrVfxtxafKvVVsPLcV6DgEBzeem14=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32

Nextcloud 34

Gantt Projects 2.3.0
Release Details
UpdatedSept. 30, 2026, 11:45 a.m.
Changelog

Added

  • Aufgaben und Teilprojekte per Drag & Drop umsortieren (Issue #299): Vor jeder Aufgabe und jedem Teilprojekt steht in der Tabelle ein Ziehgriff (☰, ganz links, unabhängig von der Einrückung); eine blaue Linie zeigt die Einfügeposition. Verschoben wird nur innerhalb desselben Elternobjekts und unter Zeilen desselben Typs, sonst zeigt der Mauszeiger „nicht erlaubt“ und ein Ablegen bricht ab. Neuer Endpunkt PUT /api/v1/projects/{projectId}/row-order (kind, id, before_id) mit RowOrderService: vergibt die sort_order aller Geschwister fortlaufend neu (auch bei gleichen Werten), Schreibrecht nötig. Bewusst nicht Teil: Griff für Meilensteine (sie haben keine sort_order, stehen vor den Aufgaben ihres Elternobjekts) und Projekte, Umhängen in ein anderes Teilprojekt; der Zeiger ist der Standard-„nicht erlaubt“-Zeiger des Browsers. Doku (FEATURES.md, de/en) ergänzt. Neue PHPUnit-Tests (RowOrderServiceTest), Vitest-Tests (rowDrag.test.js) und der E2E-Test row-reorder.spec.js

Fixed

  • Klick auf das Deck-Abweichungssymbol öffnet den Dialog wieder beim Bereich „Abweichungen von Deck“ (jetzt im Reiter „Verbindungen“): focusDeckSection wurde im Dialog-Kontext nicht mehr weitergereicht. E2E-Tests für die Reiter angepasst
  • Tabelle und Gantt-Chart scrollen vertikal zuverlässig gemeinsam (Issue #292): Tabellenzeilen und Balken standen schon ganz oben um 9 px versetzt (Tabellenkopf 52 px, erste Chart-Zeile erst bei header_height + padding / 2 = 61 px), und weil nur das Chart den Ressourcen-Bereich enthält, konnte es weiter scrollen als die Tabelle – die Tabelle blieb am Ende hängen, das Chart zeigte einen anderen Ausschnitt oder sprang zurück. Der Tabellenkopf ist jetzt so hoch wie der Chart-Kopf bis zur ersten Zeile, ein Platzhalter unter den Tabellenzeilen gleicht Ressourcen-Bereich und Scrollleisten aus (per ResizeObserver laufend nachgeführt), sodass beide Seiten denselben maximalen scrollTop haben. Der Sync überträgt nur noch bei tatsächlicher Abweichung statt über ein synchron zurückgesetztes syncing-Flag, das asynchrone Scroll-Events nicht abfangen konnte. Geometrie in src/gantt/tableScrollSync.js (Vitest), neuer E2E-Test table-scroll-sync.spec.js

Added

  • Deck-Karten schon beim Anlegen einer Aufgabe verknüpfen oder neu anlegen (Issue #306): Der Deck-Bereich im Reiter „Verbindungen“ war bei neuen Aufgaben gesperrt („Speichere zuerst die Aufgabe“). Jetzt lassen sich Karten auswählen und „+“ vormerken; beim Speichern verknüpft bzw. legt die App sie an (deckCardsSection().commit()). Schlägt eine Deck-Aktion fehl, bleibt die Aufgabe gespeichert und ein Hinweis nennt die Zahl der Fehlschläge. Board und Kartenliste des Pickers folgen dem gewählten Projekt und übergeordneten Objekt (auch beim projektlosen Anlegen über „Aufgabe“). Der Termin-Abgleichsdialog entfällt dabei; Abweichungen zeigt danach der Bereich „Abweichungen von Deck“
  • Reiter „Anhänge“ in allen Detaildialogen (Projekt, Teilprojekt, Aufgabe, Meilenstein; vor „Verbindungen“) mit den Bereichen Dateien und Notizen: beliebig viele Dateien (Nextcloud-Dateiauswahldialog mit eigenen Ordnern) und Notizen (Auswahl aus der App Notes im Stil der Deck-Kartenauswahl) verknüpfen. Der Server teilt jede angehängte Datei automatisch mit allen Personen, die Zugriff auf das Projekt haben (Betrachter lesen, Bearbeiter/Admins dürfen ändern) und gleicht die Freigaben nach jeder Änderung an Anhängen, Projektfreigaben und Löschungen ab; entfernt werden nur die von der App angelegten Freigaben (nc_gantt_attach_shares). Die Freigabe gilt je Datei, nicht je Objekt: Sie bleibt bestehen, solange die Datei an mindestens einem Objekt der App hängt. Doku ergänzt in FEATURES.md (Abschnitt „Anhänge: Dateien und Notizen“, de/en), READMEs, HOWTO (Schritt 10) und Store-Beschreibung (info.xml). Neue Tabellen nc_gantt_attachments und nc_gantt_attach_shares (Migration Version1044), Endpunkte GET/POST /attachments, DELETE /attachments/{id}, AttachmentService. Die Dateiauswahl hängt sich in den modalen Detaildialog ein (sonst läge sie unsichtbar dahinter), und nachgeladene Bundle-Teile (Dateiauswahl, Datumsformate) nutzen den tatsächlichen Installationsort der App als Adresse (src/publicPath.js; die Vorgabe /apps/nc_gantt/js/ griff z. B. unter custom_apps nicht). Neue PHPUnit- (AttachmentServiceTest) und Vitest-Tests (attachments.test.js) sowie der E2E-Test attachments.spec.js
  • Aufgaben können von Meilensteinen abhängen (Issue #308): Vorgänger m-<id> neben t-<id> (keine Migration), in allen vier Typen mit positivem und negativem Abstand. Ein Meilenstein zählt als Punkt mit Start = Ende = seinem Datum; ein fester verschiebt sich nie und zieht seine abhängigen Aufgaben samt Nachfolgern mit, ein abgeleiteter folgt weiter seinem Ziel und gibt dessen Verschiebung an die Aufgaben dahinter weiter. TaskSchedulingService (Prüfung, Termine, Propagation, Neuberechnung) kennt Meilensteine als Knoten; die Zyklenprüfung berücksichtigt die Kanten Zielaufgaben → abgeleiteter Meilenstein und läuft auch beim Speichern eines Meilensteins (dependency_cycle); der Dialog sperrt solche Meilensteine als Vorgänger bzw. Ziele mit Hinweis. Im kritischen Pfad sind Meilensteine Knoten: ein fester ersetzt das Projektende als spätestes Ende für die Aufgaben, die über eine Abhängigkeit vor ihm liegen; ein negativer Puffer verhindert das Speichern mit dem Hinweis auf die betroffene Aufgabe (negative_float:<Titel>, bereits negativer Puffer wird nicht erneut gemeldet). Beim Löschen eines Meilensteins (auch über Aufgabe/Teilprojekt) entfallen die Abhängigkeiten auf ihn, ProjectDuplicationService und JSON-Import/-Export münzen m-<id> auf die neuen Meilensteine um. Oberfläche: Auswahl M<id> <Titel> im Abhängigkeits-Editor, Kurznotation M3EA-2d in der Vorgänger-Spalte, Pfeile von und zu Meilenstein-Balken, Nummerierung M<id> im Diagramm, Glossar und FEATURES.md (de/en) nachgezogen. Bekannte Vereinfachungen: die Pufferrechnung zählt den Meilenstein-Termin in Arbeitstagen des globalen Kalenders ab dem frühesten Aufgabenbeginn, und ein Meilenstein-Termin verschiebt keine Deck-Karten (Deck-Abgleich läuft nur bei Änderungen an Aufgaben). Neue PHPUnit-Tests (TaskSchedulingMilestoneTest, CriticalPathMilestoneTest, ergänzt: Meilenstein-, Duplizierungs-, Lösch- und Datums-Tests), Vitest-Tests und der E2E-Test milestone-dependency.spec.js
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureS4LN+K40y1DKrwQo02FOHkiugxuFllfcAsVp7IBoUthbKYnNdZtSmzBH+ZxY0V8C
U954MnYEijFZD14jMuW2wkvMomspSCRGfCfEHytRCLfNwmsK9AV5UfxutjQBe6Y/
ATjKYnhOEPrE2iXLOZ+P73VbC8GBPY6W21P1APsy0Af+XUoFycIdTnLJUU5QJgWY
+K/N7nUfXyqVA17kCO+/Zhw6w5wl5O7ZIMezWbNn3ApvOeIC8AE/CwJ/JccrQgE9
F4A1RTqFZcK0hCrluGelanyva6ygwAmYPoJtkR+CeiJr7vFdSSq6HJe0lG0y9SAm
RNFnDuEzt6uFqSl9EqK6ykVp7ns0k6npvld3gSqGN0Frq4dwGjxSjcRkpPGAYT7M
IH6YJDVlNbVdF/wRJtEmOvO1duI4uzBhjX3PIFX1LBoFR0p5OsKuMIAXGEupbBi4
BV9Zu5578h5VQzZt32gFnsQcGVsvAn2nayCCbcQx/WUHSW57NmBdktF4l+7tme/p
vL+hzncZ2sdDbhDrrDq2G167QwkAJgpGJFQR+34YpGrY3DDhj767+Q/bMHwm4zmk
VYm+g4MAf49P2CKyqwVB1E9SGe3MgTK2rDRnibhVIlgVEDvih1Uf/E5wigpBJerf
E1DvLXTLEtteko53AjIpT/GWpiiD9md4QpCUUdfGMnU=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.2.0
Release Details
UpdatedSept. 28, 2026, 7:36 a.m.
Changelog

Added

  • Implizite Kopfzahl je Aufgabe (Issue #252, Schritt C aus #249; R11): Der Aufgabendialog nennt unter jeder Zuordnung mit Aufwand im Klartext, wie viele Personen gleichzeitig nötig sind („Erfordert im Schnitt 2,0 × Java-Entwickler gleichzeitig“) — Aufwand ÷ Arbeitstage des Balkens ÷ Tageskapazität einer vollen Kraft, je Zuordnung, live bei Änderung von Aufwand, Start, Ende oder Dauer. Gezählt wird nach dem globalen Kalender (Mo–Fr abzüglich globaler Feiertage) für Rollen und Personen gleich; Personenkalender zählen nicht (Entscheidung in #252, daher entfällt das Akzeptanzkriterium „Teilzeitkalender“). Ab der Warnschwelle (globale App-Einstellung headcount_warn_threshold, Standard 3,0, gültig 0,5–100, im Admin-Bereich unter „Warnen ab“; GET/PUT /settings liefern und setzen headcount_threshold, ein PUT ohne den Wert lässt ihn unverändert, Initial State für das erste Rendern) erscheint dort zusätzlich die Konsequenz („mit weniger Personen dauert die Aufgabe entsprechend länger“), und die Tabelle zeigt vor dem Titel ein Personen-Symbol mit Tooltip — ein Hinweis, keine Blockade. Aufgaben ohne Aufwand oder ohne Arbeitstage und Zuordnungen in anderen Einheiten als PT/h zeigen nichts. Die Rechnung liegt als reine Funktion in src/utils/headcount.js, die Schwelle in WorkTimeService. Nicht Teil: Hinweis am Balken im Diagramm, Prüfung der tatsächlichen Verfügbarkeit (Schritt B, #251). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue Vitest-Tests (headcount.test.js, tableRows.test.js, settings.test.js) und PHPUnit-Tests (WorkTimeServiceTest)
  • Kapazitätsprüfung über Rollen hinweg: Engpass-Teilmenge statt Einzelrolle, Alarm über ein gleitendes 5-Tage-Fenster (Issue #251, Schritt B aus #249; R6, R10): Rollenkapazität ist nicht additiv — bei zwei Java-Entwicklern, von denen einer zusätzlich IT-Architekt ist, besteht „2× Java + 1× Architekt“ die Einzelprüfung (2 ≤ 2, 1 ≤ 1), ist aber nicht zu leisten. Neuer UI- und datenbankfreier Rechenkern: CapacityFlow löst je Fenster ein Flussproblem (Bedarfsknoten = Rollen und direkt zugewiesene Personen im selben Fluss, Kante wo die Person die Rolle ausfüllt, Personenkapazität zur Senke; Dinic in CapacityFlowGraph) und liest die Engpass-Teilmenge aus dem Min-Cut, zerlegt in unabhängige Engpässe samt den Personen mit Mehrfachrolle als Ursache; CapacityWindowCheck legt darüber das gleitende Fenster von 5 Arbeitstagen (fest, nicht konfigurierbar, Feiertage verkürzen es nicht): rot = irgendein Fenster fließt nicht (nicht durch Verschieben lösbar), gelb = ein einzelner Tag fließt nicht, aber alle Fenster um ihn (Tagesspitze, umsortierbar); Tage, an denen niemand des Knotens arbeitet, zählen nicht als Spitze (Teilzeit Mo–Do erzeugt keine Meldung); überlappende Verstöße mit gemeinsamen Rollen werden zu einem Zeitraum zusammengefasst. CapacityCheckService trägt Aufgaben der für den Betrachter sichtbaren Projekte (Lücke bewusst akzeptiert, wie in #181), Rollen und Personenkapazität zusammen — Bedarf auf dem globalen Kalender, Kapazität aus den Personenkalendern (ResourceUtilizationService::personCapacityFn() ist dafür öffentlich), Material ausgeschlossen, Rolle ohne Personen meldet rot — und liefert GET /api/v1/capacity-check (Rollen, direkte Personen, Zeitraum, Fehlbetrag in Stunden, Ursache, betroffene Aufgaben). Die Tabelle zeigt vor dem Titel eine kleine Person in Gelb oder Rot mit Tooltip („Java-Entwickler + IT-Architekt zusammen überbucht (KW 42). Fehlbetrag: 5 PT. Ursache: Mehrfachrollen von …“), keine Benachrichtigungen; der Store capacityCheck lädt bei Änderungen an Aufgaben, Rollen, Kapazität oder Feiertagen neu. Ressourcen-Raster und Workload-Chart färben nach demselben Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder über 90 % im Fenster, Tooltip mit Reserve), die Rollenzeile übernimmt das Urteil der Prüfung. Nicht Teil: Sequenzierungsvorschlag (#282), Kostenminimierung der Zuordnung (entfällt), Projektkalender (#284). Bekannte Grenzen (in FEATURES.md): nur sichtbare Projekte, Aufgaben kürzer als das Fenster werden verdünnt (fängt die implizite Kopfzahl aus #252 auf). Neue Texte in allen Übersetzungen; FEATURES.md/HOWTO.md (de/en) und die Integrationsmatrix sind nachgezogen, der Hinweis auf die naive Rechnung aus #250 ist entfernt. Neue PHPUnit-Tests (CapacityFlowTest, CapacityWindowCheckTest, CapacityCheckServiceTest; u. a. Prüffall, Kette über drei Rollen, Bedarf exakt auf Kapazität, Rolle ohne Personen, Laufzeit 30 Personen/10 Rollen/1 Jahr), Vitest-Tests (capacityCheck.test.js, workloadModel.test.js, tableRows.test.js) und der E2E-Test capacity-check.spec.js

  • Projekt duplizieren bzw. als Vorlage für ein neues Projekt verwenden (Issue #270): Die Aktion Duplizieren im Menü des Projekts in der Navigation (auch mit reinem Leserecht) und im Projektdialog legt ein neues Projekt der ausführenden Person an. Der Dialog fragt Titel („Kopie von …“), neuen Starttermin (Vorschlag: bisheriger Projektstart = frühester Aufgabenbeginn) und die Ressourcenzuordnungen (alle / nur Rollen / keine). Alle Termine — Aufgaben, Meilensteine, Planungszeiträume von Projekt und Teilprojekten — verschieben sich um die Differenz in Arbeitstagen des globalen Kalenders, ein Start auf einem freien Tag rückt auf den nächsten Arbeitstag, bei unverändertem Start bleiben die Termine unberührt. Übernommen werden Teilprojekte samt Hierarchie, Aufgaben, Abhängigkeiten mit Typ und Abstand, Meilensteine mit Zuordnung und Abhängigkeitstyp (abgeleitete folgen dem kopierten Termin), Zuordnungen samt Aufwand (nicht sichtbare Ressourcen entfallen, der Aufwand wird bei weggelassenen Zuordnungen neu gebildet), Farben, Beschreibungen und Projektregeln; zurückgesetzt werden Fortschritt (Modus „Deck“ wird „manuell“), Status, Ist-Kosten, Baselines, Änderungsprotokoll, Freigaben, ICS-Token, Deck-Board, Deck-Listen und Kartenverknüpfungen (Deck-Abgleich „Aus“). Neuer ProjectDuplicationService legt alles in einer Transaktion über die Mapper an (kein Änderungsprotokoll, keine Benachrichtigungen, kein Deck-Zugriff); GET /projects/{id}/duplicate liefert Start und Umfang für den Dialog, POST /projects/{id}/duplicate (title, start_date, resources) die Kopie (201). Nicht Teil: eigene Vorlagenverwaltung (Projekte als „Vorlage“ markieren, Galerie beim Anlegen). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue PHPUnit-Tests (ProjectDuplicationServiceTest), Vitest-Tests (duplicate.test.js, ioDialogs.duplicate.test.js) und der E2E-Test project-duplicate.spec.js

Changed

  • Rollenzeile im Ressourcen-Raster ohne „naiv additiv“ (#251): GET /resources/{id}/utilization liefert für Rollen nur noch capacity_derived; capacity_naive und multi_role_member_ids entfallen, ebenso der Hinweis „zu optimistisch“ im Raster (ersetzt durch den Verweis auf die Prüfung über alle Rollen). Das Workload-Chart unter dem Diagramm färbt Perioden nicht mehr je Periode rot, sondern nach dem 5-Tage-Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder knapp)

Fixed

  • Server-Zeitzone der Arbeitszeiten aus default_timezone statt immer UTC (Issue #294): Nextcloud setzt die PHP-Zeitzone in lib/base.php fest auf UTC, deshalb zeigte der Admin-Bereich unter „Arbeitszeiten“ immer „UTC (+00:00)“ und Arbeitsbeginn/-ende wurden als UTC ausgelegt — neue Termine im Detaildialog/Import und die Deck-Fälligkeiten lagen dadurch 1–2 Stunden daneben. Neuer ServerTimeZoneService liest default_timezone aus der config.php (fehlt der Wert oder ist er ungültig, bleibt es bei UTC); GET/PUT /settings liefern dessen Namen und aktuellen Versatz, der Deck-Terminabgleich legt work_start/work_end in dieser Zeitzone aus. Neue PHPUnit-Tests (ServerTimeZoneServiceTest, DeckDateSyncServiceTest)
  • Schrift im Balken passte nicht zur Balkenfarbe (Issue #287): .bar-label stand in theme.css fest auf Weiß, unabhängig von Balkenfarbe und Theme — auf hellen und mittleren Farben, darunter die Standardfarbe #0082c9 (Kontrast 4,2:1), war der Titel im Balken im hellen Theme schwer lesbar, während der Titel außerhalb des Balkens (.big) bereits die Textfarbe des Themes bekam. injectProjectColors() setzt jetzt je Farbklasse die Schriftfarbe, die nach WCAG den höheren Kontrast liefert (labelTextColor() in colors.js: Weiß oder #111111). Die Balkenfarbe ist in beiden Themes dieselbe, die Schrift deshalb ebenfalls. Bei Projekt- und Teilprojektzeilen (Summenbalken, nur eine auf 18 % abgeschwächte Klammerfläche darunter) war die vorgesehene Textfarbe des Themes wirkungslos: .gantt-summary-bar .bar-label und .gantt .bar-label hatten gleiche Spezifität und beide !important, die spätere gewann — der Titel blieb im hellen Theme weiß. Die Regel ist jetzt spezifischer (.gantt .bar-wrapper.gantt-summary-bar .bar-label); Meilenstein und Titel neben dem Balken (.big) behalten ihre Regeln. Neue Vitest-Tests in colors.test.js
  • JSON-Export ohne Datenbank-IDs der Abhängigkeiten (Issue #289): Je Abhängigkeit enthält der Export nur noch predecessor_id, dependency_type und lag_days; id und task_id (Datenbank-IDs des Quellsystems, im Zielsystem bedeutungslos) entfallen. Der Import bleibt kompatibel, ältere Exporte mit diesen Feldern werden weiter gelesen. Der E2E-Test zum Round-Trip aus #269 war seit seiner Einführung rot (erwartete exakt drei Felder je Abhängigkeit und übersah den Import-Optionsdialog) und ist korrigiert.

Security

  • Benutzersuche beachtet die Enumeration-Einstellungen von Nextcloud und leakt keine E-Mail-Adressen mehr (Issue #285): GET /users/search rief IUserManager::search() direkt auf und lieferte jedem angemeldeten Nutzer zu jedem Treffer UID, Anzeigenamen und E-Mail-Adresse — mit kurzen Suchbegriffen ließen sich so alle Konten der Instanz auslesen, auch wenn die Admins das Auflisten abgeschaltet oder auf Gruppen/Telefonbuch beschränkt hatten. Die Suche läuft jetzt über die Collaborators-API (ISearch::search() mit IShare::TYPE_USER, ohne Lookup-Server) wie der Teilen-Dialog im Kern und beachtet damit shareapi_allow_share_dialog_user_enumeration, shareapi_restrict_user_enumeration_to_group/_to_phone/_full_match* und shareapi_only_share_with_group_members; exakte Treffer stehen vorn. subname stammt ausschließlich aus deren Ergebnis, die App lädt keine E-Mail-Adresse mehr selbst nach. Das eigene Konto (nur mit include_self, #204) ergänzt der Controller selbst, wenn Benutzer-ID, Anzeigename oder E-Mail den Suchbegriff enthalten, weil die Collaborators-API es nie liefert. Neue PHPUnit-Tests in UserControllerTest (u. a. keine Fremdkonten bei abgeschalteter Enumeration)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureesXfyynRSXx6Sc+EbEb+n8jhv9KklmBcayRslKKo0dpavjZ1x7CE/oUKrOppOVm7
YsCst1sFoTz/sPZ4qveDz/erE0STxAi2UMyiHHUglJ3JmcCJKtF5byki45BAm02V
PV7dn+EYQP8bd4jWy96mQa66eGzuuAsgR/mA7S1UC9SvcKq6UF6A0LJpJqMyInaR
ycEg6H78WtrIOKAuB5WyaEo20IRsXIechdfyl39Xz1bl1IextE3DPey0OaV6CzfF
6YQeMho+ja2iqh6JKfAzq2zKgTDBLo/bcgk8Gy/b4djBX5KES2w96x535UG7a6JM
fYEg7QdRUqOGgXoCTRSwIAGtqX8bq9sc7058+5NqO9xYOiGDzfv6dJ94x6+KXpd4
eN1N1P0+MfL9TKP27NQLsy46vXPU36w4cSijoMmxyrYGUbVVudrb8XANUvv/jZc+
d/yNvPzhYRDHvoGJ3gn5exgVpQW2M7qQMOBVDYDkROo1UqKv1k3da9s14VMHtX6y
PyhIdtt6Qi7Lu0cMykFH0rYQYoe84Ot3Xb547ldyNCCDAeta4vTczDbA5dTrdstM
HCrbbSl4iF/Lu7zeVhlxnWnQyuC5HjUg0vVuikxkEyYScruNJw4prkXD04XHOMyG
1HOBUrG2tiFl60B8XGyibo5+fc1ioQFwe2iIjdLOW3M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.1.0
Release Details
UpdatedSept. 25, 2026, 6:24 a.m.
Changelog

Changed

  • Detaildialog in abgesetzte Bereiche gegliedert (Issue #263): Die Formulare für Projekt, Teilprojekt, Aufgabe und Meilenstein fassen ihre Felder jetzt in Bereiche mit Rahmen, farbig hinterlegtem Kopf, Symbol und Überschrift zusammen (z. B. Grundinformationen, Darstellung, Terminplanung, Budget und Kosten, Ressourcen, Risikostufe, Fortschritt und Status, Deck, Erinnerungen, Baselines, Export und Import). Die Bereiche heben sich vor einem dezent getönten Dialoghintergrund ab, der Dialogkopf zeigt ein Symbol der Objektart, Feldbeschriftungen stehen in normaler statt Versal-Schrift, und die Risikostufen tragen ihre Ampelfarbe auch ungewählt. Die Farbtöne sind aus den Nextcloud-Farbvariablen gemischt und funktionieren im hellen wie im dunklen Design. Die Trennlinien zwischen den einzelnen Feldern aus dem ersten Anlauf entfallen dafür. Symbole kommen aus @mdi/js, das jetzt direkte Abhängigkeit ist
  • Neue App-Store-Screenshots zur überarbeiteten Oberfläche: Die zehn verlinkten Bilder in appinfo/info.xml sind neu aufgenommen (Projektstruktur, Abhängigkeiten im gegliederten Detaildialog, Tabelle, kritischer Pfad, Ressourcen, Kalender und Feiertage, Abweichungsbericht, Kennzahlen/EVM, Simulation, Mehrprojektansicht mit Filter). Sie liegen jetzt unter screenshots/appstore/ statt screenshots/usecases/, damit der App Store nicht auf zwischengespeicherte alte Bilder unter den bisherigen URLs zurückgreift; die übrigen, veralteten Bilder aus screenshots/ sind entfernt. Das Showcase-Skript scrollt dafür in UC 2 den Abschnitt „Abhängigkeiten“ des Dialogs ins Bild und in UC 5 das Ressourcenraster ganz nach links
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureZQ2bK6Wi8ifFLdOZU9fKEitqGU2/V3SC8sQadWpViyGbPp9TFzXz/m+6VOgxRM0K
88XrfFLwkUN5CrQ8Mzj8rzUUKAp0ANWZVMC43yWzABpywSQ+FosMPTPoPyS7dymn
eKXUnbqfSE3yebLbOtpQaK9VIMVXfvIpMQuIaSnnSX/Mi3ofY4oN3eyBy9NT1uH+
icAEBU4EFZbByJe3tF+IdKP81OX0kFydqeuiSgWJph7yRxP+ucBioO+BsjGB1ZGh
2kiCYjlYoZPbgc4WSoWfe3WGY/cUiqr/6sPEkdWfYHrG6c/tUuNhM1E1pmBXlwIK
S3IzZVklRWFkFcTHdYLAg5QKWTN2+m0sCpPkMkZQn2NYXQNQ5oAPKNoDTMBmW+zG
B72pYfEAs5TQFGcn1M6k6e4IdVjnLtM6AdmyeMdu9y8hKUH8en1RT/MVbXZlFN7O
fZoXKZSwpdocdqMZOqaT3qoUTFUZbmOBzcQQ3xeYQ4H2UvIp4PlyPkXeiFEHKY93
hZWcmtTHthlbPGhtnAutXA7zCzruiXisJXCNaf7ZM+xSbtW3GeLqRMwkLImyxydK
1C+BekGHiJaWI32PyqZH1mwR+V1qgqLLcD3yb+gKOBOUSBW4pdMnNpmvnO7y8KBg
jw8MQf3e5b0JVVoksbULqjqvCOm2ug60NIZYCfp59BA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.1
Release Details
UpdatedSept. 24, 2026, 7:49 a.m.
Changelog

Changed

  • App-Store-Beschreibung und README neu gefasst: Zusammenfassung und Beschreibung (info.xml, Deutsch und Englisch) stellen jetzt das professionelle Projektmanagement mit der Deck-Integration als Alleinstellungsmerkmal in den Vordergrund, die Funktionsliste ist kompakter, und ein eigener Abschnitt erklärt die Datensouveränität (alle Daten bleiben auf der eigenen Instanz). Das englische README und docs/de/README.md übernehmen den Wortlaut. In den übrigen Sprachen entfällt an zwei Emojis der Variation Selector
  • Toolbar-Schalter einheitlich: Die segmentierten Schalter der Toolbars (Zeitskala und Darstellung im Diagramm, Zeitskala im Ressourcenraster) und der Ansichts-Umschalter der App teilen sich jetzt dieselbe Optik: eckige Segmente ohne eigenen Rand und ohne eigene Rundung, sodass Hover- und Aktiv-Flächen bis zur Kante reichen und kein Randstreifen mehr stehen bleibt; die Rundung kommt allein vom Container. Die gemeinsame Regel liegt in der neuen segmented-switch.css statt in mehreren nahezu identischen Blöcken

Fixed

  • Leerzustand im Diagramm nicht mittig: Der Leerzustand des Diagramms (GanttChart.vue) steht jetzt mittig, und sein Icon ist optisch zentriert (der viewBox war seitlich versetzt)
  • Stylelint-Fehler in ResourceGrid.vue: fehlende Leerzeile vor einer Regel (rule-empty-line-before), die den JS-Job der CI rot färbte
  • JSON-Import brach bei Abhängigkeiten ab, Meilensteine fehlten im Export (Issue #269): importJson() übergab predecessor_id-Werte unverändert an store.createTask() – die alten IDs des Quellprojekts bestanden den IDOR-Schutz aus #190 nicht (Import in ein anderes Projekt/eine andere Instanz brach mit invalid_dependency ab und hinterließ ein halb angelegtes Projekt) bzw. zeigten beim Import ins selbe Projekt auf die falschen (alten) Aufgaben. Aufgaben werden jetzt zunächst ohne Abhängigkeiten angelegt und diese erst danach mit auf die importierten Aufgaben umgemünzten predecessor_id-Werten nachgetragen – unabhängig davon, in welcher Reihenfolge Vorgänger und Nachfolger in der Datei stehen. exportJson() nimmt außerdem Meilensteine samt ihrer Zuordnung zu Teilprojekt/Aufgabe und ihres dependency_type mit; der Import schreibt diese Bezüge ebenfalls auf die neuen IDs um
  • Balken mit Abhängigkeiten ziehen schrieb doppelt nach Deck (Issue #267): frappe-gantt zieht beim Verschieben/Resizen eines Balkens dessen Abhängigkeits-Balken in der Vorschau um denselben Pixel-Versatz mit und feuerte on_date_change für jeden von ihnen einzeln – der Server berechnet die Kette beim Speichern des tatsächlich gezogenen Balkens aber bereits selbst (propagated). Der zweite, nur aus der Vorschau abgeleitete Save überschrieb die korrekt propagierten Termine mit dem naiven Pixel-Versatz und erzeugte bei aktiver Deck-Anbindung eine doppelte Rückfrage bzw. Erfolgsmeldung. GanttChart.vue verarbeitet on_date_change jetzt nur noch für den Balken, auf dem die Ziehbewegung tatsächlich begann
  • Picker für eine zweite Deck-Karte konnte dauerhaft deaktiviert bleiben (Issue #267): Der Deck-Bereich im Aufgabendialog rendert einmal sofort (Ladezustand) und ein zweites Mal, sobald die Verknüpfungen geladen sind – traf eine Auswahl im Picker genau in dieses Fenster, ersetzte der zweite Render Select und „Add“-Button durch neue, wieder deaktivierte Knoten und die Auswahl ging verloren. Der Bereich rendert jetzt erst, nachdem die Verknüpfungen geladen sind
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureblE0iU/iUXgQIlpljnjDbdufoMyB2l1S9iGos0PQ3DDhQU9wFB13iopDY4YfCjrW
Y2bVw//uxXAO1S4wzURGPTAEgXGI3NM4A2JaHsxMREiJoU6ANjCSOnt45f9yvgNC
se02XpRKzbTtIUXTIGsH2KUe3grZ7hHg95x4riQhhrLhsDESZMZANpO60O52/AMM
gb4yZiCTbUUr1IzM/IowbHWJcghd6RG8KG+EwONJpRqJVjZhcqhNgcM9k/nKyCxY
tw6o7MIlwQmOUzh6kZXxl8p4auRpiHcGrruiWzJwEb6WSmH/XFTdGwP/ZV8lNnoj
KrR2e+yaKiPMR7UQLrtE8yUe1LGmghJejF/cDO3uLEOGD87gZfHjqXAJOrHI+zyx
FcT65clu8MUG2Tk1PQammIkYty0sPaHKwB/woLOlEBWX8LqrAK9ylOD7NHTdArhJ
TibyfWC9UI1f/iyBYu0xCEq11T23DtrFcpun7ELMNUiQ2z260/PykzEK6WtEhRVd
hgLyzf3YoFSqRNOM5IfewWlGt9YdyM4J3bLIYP01GY4MFV90y9M7/RcaehOG3U/5
DFnQswUXRr9ivk4S7dFt7J0WqltzLDqbIwtXi5f6TEqahrYD1Y46W4JXArdhOK+P
FE4VR6KQrLzKQ9PQhTbrfqCLj0tewWnxc8g6OgIGGk4=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.0
Release Details
UpdatedSept. 23, 2026, 9 a.m.
Changelog

Major-Release mit zwei Schwerpunkten. Die Einzelheiten stehen in den Einträgen darunter.

  • Komplett überarbeitete Deck-Schnittstelle (Zielbild #234, Issues #209–#211, #235–#241, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit geworden. Pro Projekt gibt es eine Stufe für den Deck-Abgleich: Aus (Standard), Nur lesen, Schreiben mit Rückfrage oder Schreiben ohne Rückfrage. Eine Aufgabe kann mehrere Karten haben, und Karten lassen sich direkt aus der Aufgabe anlegen. Termine werden mit Konfliktregel nach Deck geschrieben. Fortschritt und Status kommen sofort aus Deck. Abweichungen zwischen Plan und Deck erscheinen im Überblick, lassen sich annehmen und filtern. Beim Löschen können verknüpfte Karten archiviert werden, doppelte Erinnerungen werden unterdrückt, und ein occ-Befehl gleicht einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich am Verhalten nichts; Bestandsprojekte starten höchstens auf Nur lesen.
  • Neuer Umgang mit Planungsdaten für Projekte und Teilprojekte (Issue #229): Bisher ergaben sich Start und Ende von Projekten und Teilprojekten ausschließlich aus ihren Aufgaben. Jetzt lässt sich zusätzlich ein eigener Planungszeitraum setzen, im Detaildialog oder direkt in der Tabelle. Gezeigt wird er statt des berechneten Zeitraums, auch bei noch leeren Teilprojekten. Laufen die Aufgaben über den Rahmen hinaus, markiert ihn ein rotes Ausrufezeichen. Ampel, kritischer Pfad, Filter und Rollups rechnen weiter mit dem tatsächlich verplanten Inhalt. Ohne gesetzten Planungszeitraum bleibt alles wie bisher.
  • Warum eine neue Hauptversion: Die API-Felder haben sich teilweise geändert. deck_card_id an Aufgaben ist nur noch ein berechnetes Lesefeld, Karten werden über /tasks/{id}/deck-links verknüpft (siehe „Changed“). Löschen über DELETE liefert jetzt einen Körper mit deck. Beim Update laufen die Migrationen Version1039–Version1043 automatisch; vorhandene Kartenverknüpfungen werden übernommen.

Added

  • Detail-Dialog per Klick aus der Tabelle öffnen (Issue #264): Bislang öffnete der Detail-Dialog nur über einen Klick auf den Balken im Gantt-Diagramm, was bei größeren Projekten erst Scrollen zum richtigen Monat verlangte. Die Titel-Spalte der Tabelle hat jetzt einen geteilten Klickbereich: Bei Aufgaben und Meilensteinen öffnet ein Klick auf den Platz vor dem Bezeichnungs-Rahmen (Zusammenklapp-Pfeil, Farbpunkt, Abweichungssymbol/-zähler) den Detail-Dialog, während der Rahmen selbst wie bisher die Inline-Bearbeitung der Bezeichnung öffnet. Bei Projekten und Teilprojekten öffnet der gesamte bisherige Rahmen den Detail-Dialog; die Inline-Bearbeitung der Bezeichnung entfällt dort, da beide Objekttypen ohnehin nur das Titelfeld inline editierbar hatten. Der Rahmen ist jetzt als sichtbarer Rand um die Bezeichnung dargestellt (GanttTable.vue)
  • Deck-Anbindung abgeschlossen (Stufen 1–3 aus #234, Issues #209–#211, #235–#240, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit mit Deck geworden. Je Projekt lässt sich eine von vier Stufen wählen — Aus, Nur lesen (Fortschritt und Status kommen sofort aus Deck), Schreiben mit Rückfrage und Schreiben ohne Rückfrage (beide erlauben zusätzlich, Karten aus einer Aufgabe anzulegen und ihre Termine sowie einmalig die Zuständigen nachzuführen). Wo Deck und Plan auseinanderlaufen — Karte gelöscht/archiviert, Termin oder Zuständigkeit von Hand geändert, Karte überfällig, ziehende Person ohne die geforderte Rolle —, zeigt die App das an, statt es aufzulösen; angenommen wird je Aufgabe. Doppelte Erinnerungen zwischen Gantt und Deck werden unterdrückt, ein occ-Befehl gleicht bei Bedarf alles einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich nichts am bisherigen Verhalten. Die folgenden Einträge beschreiben die einzelnen Schritte im Detail; Endstand siehe FEATURES.md Kapitel 16 und die Integrationsmatrix, HOWTO.md und README („Optionale Abhängigkeit zu Deck“)
  • Deck-Fundament: Adapter, Verknüpfungstabelle (1:n) und Opt-in-Stufen je Projekt (Issue #209, Stufe 1, Schritte 1–2 aus #234): Die Migration Version1039 legt nc_gantt_deck_links an (eine Zeile je Karte mit Aufgabe, Board, zuletzt gesehenem Kartenstand, zuletzt geschriebenen Terminen, angenommenem Stand je Abweichungsart und Zeitpunkt des Abgleichs; eindeutiger Index auf der Karten-ID) und übernimmt vorhandene deck_card_id-Werte. Die Spalte deck_card_id bleibt lesbar, ist veraltet und wird bis zum Rückbau mit #237 aus der Tabelle befüllt. Das Löschen einer Aufgabe entfernt ihre Verknüpfungen, die Karte bleibt. Neue API: GET/POST /tasks/{id}/deck-links, DELETE /tasks/{id}/deck-links/{cardId}, GET /deck-links/occupied?project_id=|board_id= (belegte Karten → Aufgabe)
  • DeckGateway als einzige Andockstelle zu Deck (Deck-Services erst im Aufruf aus dem Container, isAvailable(), supportsStartDate(), readCard(), permissions(), Ergebnisobjekt DeckResult statt Exception, nur mit angemeldetem Benutzer, Schreibmethoden nur in den beiden Schreibstufen — ihre Umsetzung folgt mit #210, #211, #237, #238)
  • Opt-in-Stufe „Deck-Abgleich“ je Projekt mit vier Stufen (Aus — Standard, Nur lesen, Schreiben mit Rückfrage, Schreiben ohne Rückfrage); serverseitig beantwortet DeckSyncService getrennt „darf lesen“, „darf schreiben“, „muss fragen“. Projektdialog mit Vorschlag „Nur lesen“ beim Verknüpfen eines Boards, Hinweis beim Umschalten auf eine Schreibstufe und ausgegraut mit sichtbarem Hinweistext ohne Deck. Bestandsprojekte mit Board und Aufgabe im Fortschrittsmodus „aus Deck“ starten auf Nur lesen, alle übrigen auf Aus; nie auf einer Schreibstufe. Der clientseitige Abgleich aus #221 prüft die Stufe
  • Abweichungsdienst (DeckDeviationService, DeckDeviationProvider) mit einheitlichem „Annehmen“ (GET /tasks/{id}/deck-deviations, POST /tasks/{id}/deck-deviations/accept) und Bereich „Abweichungen zu Deck“ im Aufgabendialog; die Abweichungsarten liefern die Folgetickets
  • Vertragstest gegen Deck (make test-deck-contract, tests/deck-contract/, CI-Job Deck-Vertragstest) und README-Abschnitt „Optionale Abhängigkeit zu Deck“; FEATURES.md (de/en) um Stufen und mehrere Karten ergänzt
  • Deck im E2E-Setup optional: setup-dev.sh --no-deck, tests/e2e/helpers/deck.js (Tests ohne Deck überspringen sich selbst), tests/e2e/specs/deck-foundation.spec.js
  • Deck-Rückmeldung per Event-Listener: Fortschritt und Status sofort übernehmen (Issue #235, Stufe 1, Schritt 3 aus #234): DeckCardListener ist für CardCreatedEvent, CardUpdatedEvent und CardDeletedEvent per Klassenname registriert und wertet nur getWebhookSerializable()['card'] aus; er wirft nie. DeckCardSyncService aktualisiert ab der Stufe Nur lesen den zuletzt gesehenen Kartenstand an der Verknüpfung (erledigt, archiviert, gelöscht, Fälligkeit, Start, Zuweisungen, Checkliste) und berechnet für Aufgaben im Modus „aus Deck“ Fortschritt (Mittelwert je Karte: erledigt = 100 %, sonst Checklistenanteil; archiviert zählt als erledigt, gelöscht zählt nicht mit; ohne mitzählende Karte bleibt alles stehen) und Status (done / in_progress / not_started, blocked bleibt). Die Formel DeckProgress in PHP spiegelt cardProgress(); beide prüfen tests/fixtures/deck-progress-cases.json. Neue Abweichungsarten „Karte archiviert“ / „Karte gelöscht“ über den Abweichungsdienst; Übernahmen stehen mit Operations-ID deck:… im Änderungsprotokoll. Der clientseitige Abgleich beim Öffnen bleibt als Fallback und setzt den Status nach denselben Regeln (PUT /tasks/{id}/progress/sync akzeptiert done). Der Aufgabendialog weist im Modus „aus Deck“ neben dem Statusfeld darauf hin, dass der Status Deck folgt. Vertragstest, README-Abschnitt und FEATURES.md (de/en) sind nachgezogen
  • occ nc_gantt:deck:sync --user=<uid> [--project=<id>] [--dry-run]: einmaliger Vollabgleich aus Deck (Issue #236, Stufe 1, Schritt 4 aus #234): Der Befehl setzt den Kontext des Kontos per setVolatileActiveUser, liest jede verknüpfte Karte, die das Konto in Deck sehen darf, und übernimmt sie über dieselbe Logik wie der Listener aus #235 (DeckCardSyncService::applyStates(), gemeinsamer Weg statt zweiter Implementierung). Nur Projekte ab Nur lesen; nach Deck wird nie geschrieben. Ausgabe je Projekt: geprüfte und geänderte Aufgaben, übersprungene Karten (keine Berechtigung / nicht gefunden); Rückgabewert ≠ 0 nur bei Fehlern, auch wenn Deck fehlt. --dry-run speichert nichts. Registriert in appinfo/info.xml (<commands>); FEATURES.md (de/en) und README ergänzt
  • Mehrere Deck-Karten je Aufgabe: Picker, „+“ zum Anlegen und Rolle der Deck-Listen je Projekt (Issue #237, Stufe 2, Schritt 5 aus #234; UC1 „Übergabe“, O1, O2, O8, O14): Der Aufgabendialog hat den Bereich „Deck-Karten“ (für bestehende Aufgaben) mit der Liste der verknüpften Karten (Titel, Liste, erledigt, „In Deck öffnen“, „Lösen“ — das Lösen ändert die Karte nie), dem Picker (Karten an einer anderen Aufgabe sind gesperrt und nennen sie) und „+“, das über den Server und DeckGateway::createCard() eine Karte mit Titel und Beschreibung der Aufgabe samt Rücklink „Geplant in Gantt-Projekte: <URL>“ (Deeplink ?project=&task=), Fälligkeit = Ende, ab Deck 1.18 Start = Start anlegt; „+“ gibt es erst ab „Schreiben mit Rückfrage“ und mit Bearbeitungsrecht am Board (sonst gesperrt mit Hinweis auf Grund und Stufe) und braucht keine zweite Rückfrage. DeckGateway::setDates() ist umgesetzt (Fälligkeit, ab Deck 1.18 Start; Ergebnisobjekt statt Exception; nur in einer Schreibstufe; prüft nach dem Schreiben, ob Deck die Termine übernommen hat) — #210 baut darauf auf. Beim Verknüpfen einer Karte mit abweichendem Datum fragt der Dialog sofort: Plan in Deck durchsetzen (nur in einer Schreibstufe, selbst die Bestätigung), Plan anpassen oder Abweichung stehen lassen; eine datumslose Karte bekommt die Planlage ohne Rückfrage, aber nur ab einer Schreibstufe. Der Stand wird als „zuletzt gesehen“ bzw. „zuletzt von Gantt geschrieben“ an der Verknüpfung gespeichert. Projekteinstellung „Rolle der Deck-Listen“ (deck_list_role: subproject — Standard, storage, none) mit „?“-Hilfe; das Listenfeld am Teilprojekt entfällt bei none, gespeicherte Listen-IDs bleiben; ein Wechsel ändert keine Verknüpfung. Ohne Liste am Teilprojekt nutzt „+“ die Liste des nächsten übergeordneten Teilprojekts, sonst die erste Liste des Boards. Neue API: POST /tasks/{id}/deck-cards, POST /tasks/{id}/deck-links mit optionalem date_action. Der Fortschritt „aus Deck“ im Dialog bildet bei mehreren Karten den Mittelwert. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kapitel 16 und Integrationsmatrix), HOWTO.md und README (Deck-Vertrag) sind nachgezogen; Vertragstest um CardService::create()/update(), StackMapper::findAll() und den Startparameter ergänzt

  • Termine der Aufgabe in die verknüpften Deck-Karten schreiben, mit Konfliktregel und gebündelter Rückfrage (Issue #210, Stufe 2, Schritt 6 aus #234; UC3 „Umplanung“, O4, O8, O9, O11, O12): Ab einer Schreibstufe folgen alle verknüpften Karten den Terminen der Aufgabe — Fälligkeit = Ende zum Arbeitstagsende (Einstellung „Arbeitszeit“), ab Deck 1.18 zusätzlich Start — bei jedem Weg der Terminänderung (Dialog, Tabelle, Ziehen im Diagramm, Propagation über Abhängigkeiten inklusive Karten fremder Aufgaben, „Termine neu berechnen“, Undo). Der neue DeckDateSyncService schreibt synchron im Request nach dem Speichern über DeckGateway::setDates(), fehlertolerant (Deck-Fehler brechen das Speichern nie ab); das Frontend zeigt danach „3 Deck-Karten aktualisiert“, aufklappbar mit Liste, auch fehlgeschlagene Karten. Konfliktregel: Geschrieben wird nur, wenn die Karte noch den zuletzt von Gantt geschriebenen Wert trägt (ohne geschriebenen Wert: wenn sie kein Datum trägt); sonst bleibt sie unangetastet und der Abweichungsdienst zeigt „Fälligkeit/Start in Deck abweichend“ mit Deck- und Plan-Datum. Aktionen im Aufgabendialog: Plan in Deck durchsetzen (POST /tasks/{id}/deck-deviations/enforce, ohne Rückfrage), Plan anpassen (Deck-Datum wird Ende der Aufgabe, gespeichert mit dem Dialog) und Annehmen. Rückfrage in „Schreiben mit Rückfrage“: POST /projects/{id}/deck-preview rechnet die Propagation ohne Speichern; ein Dialog listet einmal gebündelt alle betroffenen Karten mit altem und neuem Wert, Karten fremder Aufgaben hervorgehoben, „Abbrechen“ verwirft die gesamte Änderung, kein „Nicht mehr fragen“; ohne Bestätigung (deck_confirmed) wird nicht geschrieben. Bei „Schreiben ohne Rückfrage“ entfällt der Dialog, bei „Aus“ und „Nur lesen“ wird nichts geschrieben. Rechte: Ohne Bearbeitungsrecht am Board wird die Karte als „Deck-Abgleich ausstehend (keine Berechtigung)“ markiert und beim nächsten Speichern durch eine Person mit Rechten mitgeschrieben (Migration Version1041, Spalte sync_pending); eine in Deck gelöschte oder nicht auffindbare Karte bleibt verknüpft und erscheint als Abweichung „Karte gelöscht“. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kapitel 16) und HOWTO.md sind nachgezogen, der Hinweis „Terminverschiebungen im Gantt ändern derzeit keine Deck-Karten“ ist ersetzt. Neue PHPUnit-Tests (DeckDateSyncServiceTest, TaskDeckSyncTest, DeckDateDeviationProviderTest, DeckDeviationActionServiceTest), Vitest-Tests (deckWrite.test.js, deckWriteDialogs.test.js, deckDeviations.test.js, projects.test.js) und der E2E-Test deck-date-sync.spec.js

  • Verknüpfte Deck-Karten beim Löschen auf Rückfrage archivieren (Issue #238, Stufe 2, Schritt 7 aus #234; O10, O11, O12): Der Löschdialog einer Aufgabe mit Karten fragt „Verknüpfte Deck-Karte(n) ebenfalls archivieren?“ mit der Kartenzahl, Standard „Nein“; Teilprojekt und Projekt stellen eine Sammelrückfrage mit der Gesamtzahl. Gefragt wird nur in den beiden Schreibstufen und nur für Karten an Boards mit Bearbeitungsrecht (DeckGateway::permissions()); für die übrigen sagt der Dialog „N Karten bleiben unverändert“, der Löschdialog ist die Bestätigung (keine zweite Rückfrage). DeckGateway::archive() ist umgesetzt (CardService::archive(), Erfolg erst nach erneutem Lesen); DeckDeletionService plant vor dem Löschen (die Kaskade entfernt die Verknüpfungen) und archiviert synchron danach, fehlertolerant: scheitert Deck, ist die Aufgabe trotzdem gelöscht und die Meldung nennt die nicht archivierten Karten, sonst die archivierten mit Link. Neu: GET /tasks|subprojects|projects/{id}/deck-delete-preview; DELETE nimmt archive_deck_cards an und liefert jetzt deck (archived, failed) statt eines leeren Körpers. Verknüpfung lösen ändert die Karte nie; ein Undo für das Löschen gibt es nicht. PHPUnit mit gemocktem Gateway, Vitest, E2E deck-archive-on-delete.spec.js; Vertragstest um CardService::archive(id) ergänzt; FEATURES.md (de/en) und README ergänzt
  • Rollen als Ressourcentyp: Aufgaben fordern Rollen, Personen tragen die Kapazität (Issue #250, Schritt A aus #249; R2–R5): nc_gantt_resources.type (person — Standard für alle bestehenden Zeilen —, role, material) und die n:m-Tabelle nc_gantt_resource_role (Person ↔ Rolle, eindeutige Kombination; Migration Version1042, dazu nc_gantt_projects.roles_only_assignment). Eine Rolle ist Zuweisungsziel ohne Konto, Arbeitszeitmodell, Kalender und eigene Kapazität (Kapazitätswerte an Rollen werden nicht angelegt); TaskResource und ResourceGroup bleiben unverändert, Rollen sind eine zur Gruppe orthogonale Achse. Rollen werden in der Ressourcenverwaltung über „Neue Rolle“ angelegt, umbenannt und gelöscht und getrennt in der Navigation geführt; die Zuordnung Person ↔ Rolle ist von beiden Seiten pflegbar (role_ids an der Person, person_ids an der Rolle, ohne Admin-Rechte). Löschen einer Rolle mit Aufgaben fragt nach und nennt sie (GET /resources/{id}/usage), Löschen einer Person entfernt ihre Zuordnungen, die Rollen bleiben. Im Aufgabendialog lassen sich Rollen und Personen gemischt mit Aufwand zuordnen, die Auswahl kennzeichnet „(Rolle)“/„(Person)“ (ohne Rollen unverändert); keine Migration bestehender Zuordnungen. Projekteinstellung „Nur Rollenzuweisung zulassen“ (Standard aus) verhindert nur die Neuzuweisung von Personen. ResourceUtilizationService leitet die Kapazität einer Rolle je Tag aus der Summe ihrer Personen ab (Urlaub/Teilzeit senkt alle ihre Rollen) — bewusst naiv additiv, das Ressourcen-Raster weist auf die Mehrfachzählung hin (capacity_naive); Personen- und Rollenzuordnungen laufen durch dieselbe Auswertung, das Workload-Chart summiert Rollen aus ihren Personen. Sichtbarkeit und Rechte folgen den bestehenden Ressourcenregeln. Nicht Teil: Konflikterkennung über Rollen (Schritt B), Kopfzahl (Schritt C), Rollen-Level, Historisierung, Deck-Schnittstelle. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 11 und Integrationsmatrix) und HOWTO.md sind nachgezogen. Neue PHPUnit-Tests (ResourceServiceTest, ResourceUtilizationServiceTest, TaskResourceAssignmentServiceTest, ResourceRolesMigrationTest), Vitest-Tests (resourceType.test.js, resourceDelete.test.js, resources.test.js) und der E2E-Test resource-roles.spec.js
  • Zuständigkeit bei der Kartenanlage übernehmen, danach Abweichungen anzeigen (Issue #211, Stufe 3, Schritt 8 aus #234; O7): „+“ im Aufgabendialog weist der neuen Karte alle Nextcloud-Konten zu, die mit den Ressourcen der Aufgabe verknüpft sind (DeckGateway::assign() über AssignmentService::assignUser(), jedes Konto einzeln). Der Dialog unterscheidet beim Überspringen Person ohne Konto (Lücke: „2 Ressourcen ohne Nextcloud-Konto wurden nicht zugewiesen“), Rolle (#250; sachliche Auskunft, die Karte bleibt absichtlich frei zum Ziehen, nicht als „nicht zugewiesen“ gezählt) und Konto ohne Zugriff auf das Board (übersprungen und genannt; Gantt teilt das Board nicht). Scheitert die Zuweisung, bleibt die Karte angelegt und verknüpft. Danach gibt es keine laufende Synchronisation. Neue Abweichungsart assignees („Zuständige abweichend“, DeckAssigneeDeviationProvider über den Abweichungsdienst aus #209): Menge der zugewiesenen Benutzer der Karte gegen Menge der verknüpften Konten der Ressourcen, in beide Richtungen, mit Annehmen (Hinweis kehrt erst bei erneuter Änderung zurück); Gruppen/Teams und Rollen erzeugen keine Abweichung (Gruppen erscheinen als „auch zugewiesen: …“ an der Karte, bei gemischter Zuordnung zählt nur der Personenanteil). Der Kartenstand trennt jetzt assignees (Benutzer) und assigned_groups. Neue Antwortfelder assignment bei POST /tasks/{id}/deck-cards; Vertragstest und README um AssignmentService::assignUser() und BadRequestException ergänzt. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kap. 16) und HOWTO.md nachgezogen. PHPUnit (DeckAssigneeDeviationProviderTest, DeckAssigneeResolverTest, DeckAssigneesTest, DeckLinkServiceTest, DeckGatewayTest), Vitest (deckAssignees.test.js, deckCards.test.js, deckDeviations.test.js) und E2E deck-assignment.spec.js
  • Abweichungen zwischen Plan und Deck im Überblick anzeigen, annehmen und filtern (Issue #239, Stufe 3, Schritt 9 aus #234; UC4 „Abweichungen erkennen“, Leitprinzip 8): Zwei neue Abweichungsarten über den gemeinsamen Abweichungsdienst aus #209 — overdue (DeckOverdueDeviationProvider: Fälligkeit vor heute, nicht erledigt, nicht archiviert/gelöscht, ausschließlich aus dem zuletzt gesehenen Kartenstand, kein Deck-Zugriff nötig) und role (DeckRoleDeviationProvider, setzt #250 voraus: eine Karte einer Aufgabe mit Rollenzuweisung ist einer Person zugewiesen, die keiner der geforderten Rollen zugeordnet ist — ermittelt über ResourceRoleMapper::findPersonIdsOfRoles(); ohne Rollenzuweisung an der Aufgabe greift die Art nicht, bei gemischter Zuordnung läuft sie unabhängig neben assignees). Neu DeckDeviationService::kindsByTask() fasst die offenen Arten je Aufgabe eines Projekts in einem Rutsch zusammen (ein Durchlauf über alle Verknüpfungen statt eine Anfrage je Aufgabe); TaskService hängt daraus das berechnete Lesefeld deck_deviation_kinds an jede Aufgabe aus GET /projects/{id}/tasks, RollupService liefert zusätzlich deck_deviation_count (Summe über das Projekt) im bestehenden Rollup-Endpunkt. Im Frontend: Warnsymbol an der Aufgabe in Tabelle (GanttTable.vue, echtes <button>) und Diagramm (gantt/overlays/deckDeviationOverlay.js, fokussierbares SVG mit eigenem Klick-/Tastatur-Handler statt des abgeschalteten Bibliotheks-Popups) mit Tooltip/aria-label über die betroffenen Arten (utils/deckDeviationKinds.js); ein Klick öffnet den Aufgabendialog gescrollt und fokussiert beim Bereich „Abweichungen zu Deck“ (detailModal.js, neue Option focusDeckSection, Ereigniskette open-deck-deviations/edit-task-deck). Zähler an Teilprojekt (rekursiv über verschachtelte Teilprojekte) und Projekt in der Tabelle sowie, über den Rollup, in der Projektliste und im Projekt-Rollup-Balken. Neuer Filter „Deck weicht ab“ (filters.js, filterMatch.js, FilterBar.vue) wirkt wie die übrigen Filter auf Tabelle und Diagramm. Für „Karte gelöscht/archiviert“ gibt es im Aufgabendialog zusätzlich die Aktion Verknüpfung lösen (DELETE /tasks/{id}/deck-links/{cardId}, ändert die Karte nie); nach Annehmen, Durchsetzen oder Verknüpfung lösen lädt der Dialog die Aufgabenliste des Projekts neu (onDeviationsChanged), damit Symbol und Zähler ohne Neuladen der Seite mitziehen. Wie immer gilt: Abweichungen nur ab der Projektstufe „Nur lesen“, Annehmen nur mit Bearbeitungsrecht. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, neuer Abschnitt in Kap. 16 und Filterliste in Kap. 5) und HOWTO.md (de/en) nachgezogen. PHPUnit (DeckOverdueDeviationProviderTest, DeckRoleDeviationProviderTest, DeckCardStateDeviationProviderTest — bislang fehlend zu #235 —, erweiterte DeckDeviationServiceTest/RollupServiceTest), Vitest (deckDeviationKinds.test.js, erweiterte filterMatch.test.js/tableRows.test.js/deckDeviations.test.js) und E2E deck-deviations-overview.spec.js
  • Doppelte Signale vermeiden: Gantt-Erinnerungen und Tasks-Einträge bei Aufgaben mit Deck-Karte unterdrücken (Issue #240, Stufe 3, Schritt 10 aus #234; O13): Neuer DeckSuppressionService beantwortet für Aufgaben mit mindestens einer verknüpften, nicht archivierten Karte (nach dem zuletzt gesehenen Kartenstand aus #235) in einem Projekt ab Stufe „Nur lesen“, ob NC-Gantt für diese Aufgabe still bleiben soll — DueReminderService, EventNotificationService::notifyAssigned() und TaskCalendarSyncService fragen dort statt die Regel je einmal selbst nachzubilden, statt selbst zu unterdrücken. Unterdrückt werden die Fälligkeits-Erinnerung der Aufgabe, die Zuweisungsmeldung und ihr Eintrag in der Tasks-App; nicht unterdrückt bleiben notifyRescheduled() (Deck kennt keine Kettenverschiebung) und Meilenstein-Erinnerungen (Meilensteine haben keine Karte). Persönliche Einstellung „Gantt-Benachrichtigungen auch für Aufgaben mit Deck-Karte erhalten“ im Abschnitt „Gantt-Projekte“ der Nextcloud-Benutzereinstellungen (PUT /api/v1/settings/reminder-deck-override), Standard aus — die Unterdrückung ist also aktiv; gilt gleichermaßen für Benachrichtigungen und Tasks-App-Einträge. Wird die Unterdrückung wirksam oder aufgehoben, räumt bzw. holt der jeweils nächste Lauf von DueReminderService/TaskCalendarSyncService die betroffenen Erinnerungen bzw. To-dos ab — über denselben Soll-/Ist-Abgleich, der schon das Stummschalten trägt, ohne eigenen Sonderfall. Neue Texte in l10n/de.json/de.js; FEATURES.md (de/en, Integrationsmatrix, Kap. 14, 18 und 20) nachgezogen. Neue PHPUnit-Tests (DeckSuppressionServiceTest, erweiterte DueReminderServiceTest/EventNotificationServiceTest/TaskCalendarSyncServiceTest) und Vitest (stores/settings.test.js)
  • Optionaler Planungszeitraum für Projekte und Teilprojekte (Issue #229): Projekte und Teilprojekte können neben dem aus den Aufgaben berechneten Zeitraum einen eigenen, bewusst gesetzten Rahmen haben (plan_start/plan_end, Migration Version1043; leer für alle Bestandsprojekte, die sich damit unverändert verhalten). Eingabe im Detaildialog (Abschnitt „Planungszeitraum“) und per Inline-Editing in den Spalten Start/Ende der Tabelle, die dafür erstmals auch bei Projekt- und Teilprojektzeilen editierbar sind. Start und Ende sind nur gemeinsam gesetzt: ein leeres Gegenfeld übernimmt den eingegebenen Wert, ein vorhandener wird nie überschrieben, das Leeren eines Felds entfernt den Zeitraum; Start ≤ Ende und Schreibrecht werden serverseitig geprüft (PlanningPeriod, Fehlercodes invalid_plan_date, plan_start_after_end). Ist ein Planungszeitraum gesetzt, zeigen Balken, Tabelle (Start/Ende/Dauer), PDF/Druck und ICS-Feed ihn anstelle des berechneten Zeitraums — auch ein Teilprojekt ohne Aufgaben wird so sichtbar; ein Elternobjekt ohne eigenen Planungszeitraum umfasst in der Darstellung die Zeiträume seiner Kind-Teilprojekte. Liegen die Aufgaben nicht vollständig im Rahmen oder ragt ein Kind-Teilprojekt mit Planungszeitraum heraus, markiert ein rotes Ausrufezeichen die Zeile in Tabelle und Diagramm (planWarningOverlay.js), der Tooltip nennt die Ursache; der Rahmen passt sich nie selbst an und verschiebt keine Aufgaben. Ampel, Rollups, kritischer Pfad, Meilensteine, Filter, Fortschritt und Budget rechnen weiter mit dem berechneten Zeitraum; Baselines und Deck bleiben unberührt. Änderungen stehen mit Operations-ID im Änderungsprotokoll und lassen sich per Toast rückgängig machen (revertOperation() kennt dafür jetzt auch Projekte und liefert entity_types); der JSON-Export/-Import führt den Planungszeitraum mit. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 1, 3, 4, 10, 14) und HOWTO.md (de/en) nachgezogen. PHPUnit (PlanningPeriodTest, erweiterte ProjectServiceTest/SubProjectServiceTest/ChangeEventServiceTest/CalendarFeedServiceTest), Vitest (planningPeriod.test.js, erweiterte rowModel.test.js/tableRows.test.js/io.test.js) und E2E planning-period.spec.js
  • Rich-Vorschau für Links auf Projekte und Aufgaben (Issue #241, Stufe 4, Schritt 12 aus #234; O14): Eigener GanttReferenceProvider (OCP\Collaboration\Reference, ohne jede Deck-Abhängigkeit) erkennt Links der Form ?project=<id> bzw. ?project=<id>&task=<id>, mit und ohne index.php, und löst nur auf, wenn die anfragende Person laut AccessService mindestens Leserecht auf das Projekt hat — sonst bleibt der Link ein nackter Link. Die Vorschau einer Aufgabe zeigt Titel, Projekt/Teilprojekt, Start–Ende, Fortschritt, Status und Zuständige, die eines Projekts Titel, Zeitraum, Rollup-Fortschritt und den nächsten Meilenstein; ein eigenes Vue-Widget (registerWidget aus @nextcloud/vue, geladen nur beim RenderReferenceEvent) rendert das im Nextcloud-Look mit Dark-Mode. Cache-Präfix je Projekt bzw. Aufgabe und Cache-Schlüssel je Benutzer (ReferenceCachePrefix); TaskService, ProjectService und MilestoneService verwerfen den betroffenen Cache über IReferenceManager::invalidateCache() bei jeder Änderung, damit die Vorschau nicht bis zu einer Stunde veraltet bleibt. FEATURES.md (de/en, neues Kapitel 23 und Integrationsmatrix) nachgezogen. Neue PHPUnit-Tests (GanttReferenceProviderTest, TaskServiceReferenceCacheTest, ProjectServiceReferenceCacheTest, MilestoneServiceReferenceCacheTest) und E2E-Test reference-preview.spec.js (prüft über den Core-Endpunkt references/resolve, denselben Weg, den Text, Notizen und Talk nutzen). Nicht Teil: ein Smart-Picker-Eintrag zum Einfügen einer Gantt-Aufgabe per Suche (geplanter Folgeschritt zusammen mit der Talk-Integration)

Changed

  • Aufgaben-API: deck_card_id ist nur noch ein berechnetes Lesefeld (Issue #237): Die Migration Version1040 entfernt die Spalte nc_gantt_tasks.deck_card_id (Werte, die noch nicht in nc_gantt_deck_links stehen, werden vorher übernommen) und ergänzt nc_gantt_projects.deck_list_role. Die API liefert deck_card_id weiter — als erste Verknüpfung der Aufgabe —, damit bestehende Nutzer nicht brechen; geschrieben wird nur noch über die Verknüpfungs-Endpunkte, ein mitgesendetes deck_card_id bei POST/PUT auf Aufgaben wird ignoriert. Der Aufgabendialog und alle Frontend-Pfade lesen die Verknüpfungen aus der Tabelle

Fixed

  • Rollenkapazität im Ressourcen-Raster stimmte nicht mit den Personenzeilen überein (Issue #260): Drei unabhängige Fehler wirkten zusammen. Ein Options-API-Watcher auf store.activeIds (Getter-Pfad, nicht deep) feuerte nie, weil toggle() das Array per push()/splice() mutierte statt neu zuzuweisen — eine nachträglich in der Seitenleiste aktivierte Ressource blieb bis zum Moduswechsel ohne Auslastungsfarbe, Tooltip oder abgeleiteten Rollenwert. ResourceUtilizationService und die Personenzeile der Tabelle (getVal()) lasen unterschiedliche Kapazitätsquellen: die Rolle über die 8h/Tag-Pauschale von WorkTimeService::getHoursPerPt() für jeden ungepflegten Tag, die Person über den Mittelwert der gepflegten Tage hochgerechnet auf die ganze Periode. Beide laufen jetzt über dieselbe Tagesfunktion (personCapacityFn() im Backend, resourceCapacity.js im Frontend) — die Pauschale greift nur noch, wenn für eine Person überhaupt keine Kapazität gepflegt ist, nicht mehr für einzelne Lücken innerhalb eines gepflegten Verlaufs, und ist dann auch in der Personenzeile sichtbar. Ein gepflegter Tageswert auf einem Feiertag zählte bisher voll statt 0h, sowohl in der Auslastungsberechnung als auch in resources.js::capacityOf() (Überbuchungsprüfung/Auslastungsgraph im Gantt-Diagramm) — beide prüfen jetzt Arbeitszeitmodell und Feiertagskalender der Ressource unabhängig vom gepflegten Wert. „Wert bis Monats-/Jahresende kopieren“ (doFill()) legt keine Kapazitätssätze mehr auf Feiertagen oder arbeitsfreien Tagen an. Eine Rollenspalte gilt im Tagesmodus als arbeitsfrei, wenn keine ihrer Personen an diesem Tag arbeitet. Neue PHPUnit-Tests (ResourceUtilizationServiceTest), Vitest (resourceCapacity.test.js, erweiterte resources.test.js) und E2E-Test (resource-roles.spec.js)
  • Deck-Vertragstest schlug falsch an (Issue #256): Für CardService::create()/update() prüfte er die Parameter positional, obwohl NC-Gantt sie über DeckGateway::invokeNamed() nach Namen aufruft. Ab Deck 1.18 steht startdate hinter dem von NC-Gantt nicht genutzten done, der CI-Job Deck-Vertragstest wurde dadurch rot, ohne dass etwas kaputt war. Die beiden Methoden stehen in contract.php jetzt unter named: geprüft wird, dass es die Parameternamen gibt und dass NC-Gantt jeden Pflichtparameter der Deck-Methode kennt — die Reihenfolge in Deck ist gleichgültig, neue Parameter dazwischen (zuletzt color) brechen den Vertrag nicht mehr. Positional aufgerufene Methoden bleiben unverändert
  • Startdatum einer Deck-Karte wurde nie gelesen (DeckGateway::cardState()): Deck-Entities stellen ihre Getter über __call bereit, method_exists() war daher immer falsch; jetzt is_callable()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturekJ6Xo6R3EOShOn1PWZI+CpEgV34eRXUZy4CNPkmb+e7uqUL4Tu/tWsqGpu15PCRr
8HR2enQnsTmTk2fkKQUKBqbcfvO3vfylEzNRxU7YuOtyGW6sFAdLSKK8MDnq9XxH
1A91Bmumu7dxP+nh3u5EyGswDoH84tXcxVH0BHEr22ny4fo1/irWKT5RDcumVhyQ
SZSqcwL1iEsYzmmFZJoyqQu7Cfy6IbZXko4OJJGYWLTBvu3CBfDpQFoCg0GaMevf
SccjJXjY6+00oW3OGGn1JH3vz1dMm7l2vD7QfT04AoeFNxJkVcwFo9mTapZijalO
3kgLc7GOLXG6I7hgmqyRBHuUfJAskET9WUl9Ya92UC2Fa8NGwiBFkkQnzy8BGzEp
lbsQ/cDrFw9/U8c1RIfs3T0uOsjtSDeKIAaKQz/SJYAAYdv3SJvqWZxoIt6p04Vk
4fBM02pxBrMH+rNR+X2frwCD3+q4zBtRSxfZNWzz0kHuo9vt714yVLh5dVynwPjP
+6/5b6eE9BsobfL6M6HyZdAzZYIBkPZbfm11TA6nDT5WwUD5r+KBUJDT2hTChAVS
n7pHjc3PQ6wWgVNjvJqmtV8mvH+qvWnLFGGFd5ttHMYjfB555hcStB2UF5cAzkji
2hhP2aeeujkdcPkZ4XETBfQpkbNHaIPUxiEgUHz2oEs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.5
Release Details
UpdatedSept. 21, 2026, 8:05 a.m.
Changelog

Changed

  • Freigabe für Nextcloud 35: max-version in appinfo/info.xml von 34 auf 35 angehoben, unterstützter Bereich damit NC 31–35; die Angabe in der App-Store-Beschreibung (alle sieben Sprachen), README.md, FEATURES.md und INSTALL.md ist nachgezogen. Verifiziert gegen Nextcloud 35.0.0 mit PHP 8.5 und MariaDB 11.4 (302 Unit-Tests im Frontend, 431 PHPUnit-Tests, 98 E2E-Tests) – am Code der App war dafür keine Anpassung nötig. Die lokale Dev-Umgebung (setup-dev.sh) setzt jetzt auf nextcloud:35-apache und installiert Deck mit, weil die Fortschritts- und Karten-Integration von nc_gantt darauf aufsetzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureOtLYluq0hTNN8+vQY8AK0ALLooVxvUUxymkOjs0nAtdES78R6Sii2b9n1POirXvO
EwnIrLzKWwiT05WSV2dsrWliGU+hBhXmvUO0RviY4jmBpu9+mexpLgoHP5dUwIna
OwAN1TgJg0TOod9/bLSQts6qhr8ONk/giqNpMjId1dvQX+hymeQXFchZHk8ohVx4
4d2BaKizT6HQweBNOxxR+FGd6juzsfw4sC9ujASI6IzxPKhH9piiDFniKaERJuGo
iUvDbljcRA4YVRgM003aD9ytwdzdJ4Z3+JfI4L+LBuwaUsXFyQsC3q8bbaZWSj+w
VZX536GBNNwb0RE/nKkgGQ8WLmwG+dosiL26+Wh+a6lX2EWLFZaw9L9iwGFYG92I
iZmFDOnIpqSicG8PmCTV9YSg5u90fxPg53CaVlv6/eCxqY2mUADyErpBSHSDc1tn
g/JM58znHYQF3PfmpFc3Vh901bBytBPTkUY3p0LF1JKoH7tnZ75ar4fTORoF8nEO
Hfr50lB+bWYhEdj1LCvD9yHsRfGcaj5BZ5m6NGvFzvAf/DStAgzyF2jyz/0+YvnM
8XF+LLYY9TktnVyxHNbwjaoE32sHc/le6ABbWSLdI/R/pYWul1EVPKagxYlAQIS1
0IEJbQarL1SKdaQrVfxtxafKvVVsPLcV6DgEBzeem14=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.4
Release Details
UpdatedSept. 21, 2026, 6:44 a.m.
Changelog

Fixed

  • Fortschritt „aus Deck“ war nicht mehr erreichbar (Issue #221): Seit der Umstellung der Detail-Dialoge auf <dialog> speicherte der Aufgabendialog fest progress_mode: 'manual' – eine Aufgabe, die per API auf deck stand, wurde beim Speichern still zurückgesetzt, und fetchDeckProgress(), syncTaskProgress() und PUT /tasks/{id}/progress/sync hatten keinen Aufrufer mehr. Der Modus ist wiederhergestellt, jetzt mit der verknüpften Karte als Grundlage statt des ganzen Boards: Unter „Fortschritt“ schaltet der Dialog zwischen Manuell und Aus Deck um (neues Feld src/detail/forms/taskProgress.js). Eine erledigte Karte zählt 100 %, sonst der Anteil der abgehakten Punkte ihrer Markdown-Checkliste (cardProgress() in src/utils/deckProgress.js); der Dialog zeigt die Grundlage an und lädt die Karte per „Abgleichen“ neu. Beim Aktivieren eines Projekts übernimmt die neue Store-Aktion syncDeckProgress() den Wert für alle Aufgaben im Modus Aus Deck über den vorhandenen Endpunkt – nur geänderte Werte und nur mit Bearbeitungsrecht, ohne das Laden des Diagramms aufzuhalten. Ist Deck nicht erreichbar oder die Karte verschwunden, bleiben Modus und letzter Wert erhalten. Backend und Datenmodell bleiben unverändert. FEATURES.md (Übersicht und Kapitel 16), HOWTO.md und die App-Store-Beschreibung beschreiben den Modus wieder; neue Texte in allen Übersetzungen. Neue Unit-Tests (deckProgress.test.js, taskProgress.test.js, projects.test.js, TaskServiceTest) und E2E-Test tests/e2e/specs/deck-progress.spec.js, der die Deck-API nachbildet
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturedkpQCpaBGHbxMN8d8IvmDWANDU90LVl0Tp5u2TiH7QHbEz7fovU1pmVNKkGAF6VB
eTFeVCkt+Nq8ZdpfnmfC3ZF4wEO/wxd/fPDjm2pZM6WXb03HGpA7/J49KTifC/mj
KY68+niFgI7cjdxaIkI3KZPsWSnhsIZnTQA+HVaQxNRSIuXMV21NYkVQNZaCwZ1y
DmqesNT1/A9457BNtdk8tYpTF1IF9K98fgIB68MmJ3guBEF1jdh1eHqsp9Zhluxv
MgZNf5wNcBTdLT+9z98jxDoSZGx9+7U57ACA54Kd08f3TZLey+nBSIjjSusgyFYl
WDyYjimUfFMlNNktHMkinpvw0qTlu62cIG4yqO1Kd0mzJ4+ueUrwBFuvRIMscVjy
pXuaC2Try+5ZBw8pUl6XDbhPslzvfKI1uO8nUachDNWJR1ULS6HdX8PsWRTA3PFq
NdETu9H9tcLeheaEiGqNQbPEy/2A8TEWqsMcdzl3iX/W3tQJfj1O+KVMOPKC+Kek
rrZiNfEd4KtVgVH0YLU5xb3XyXDFG0q2Q1b3gJBsoKcn2Wr0D7ijK0FLb3U97RDj
tBlHHoy7gVE1JnXvO/Z8oau5KvytSVffwIbdxKAG6oW5Pi92+HTN4J0icMz3+NIr
LHSTqB4XsIdKGjWdKxMj7KOo2FqWwZR0rq0QuFWxWSY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.3
Release Details
UpdatedSept. 18, 2026, 8:33 p.m.
Changelog

Changed

  • Angleichung an die Nextcloud-Entwicklerstandards (Issue #227): App-Werte (Arbeitszeit, Stunden je PT, Erinnerungen) werden über die typisierten Getter und Setter von IAppConfig statt über das seit NC 29 veraltete IConfig::getAppValue()/setAppValue() gelesen und geschrieben; vorhandene Werte werden beim nächsten Speichern typisiert, Benutzerwerte bleiben bei IConfig (IUserConfig gibt es erst ab NC 32). Die Controller bekommen die Benutzer-ID wie im Developer Manual vorgesehen als ?string $userId per Dependency Injection, statt sie in jeder Methode aus der Session zu lesen. npm run lint läuft ohne Warnungen: require-jsdoc gilt nur noch für exportierte Funktionen, die leeren JSDoc-Hüllen an internen Funktionen sind entfernt, exportierte Funktionen vollständig dokumentiert. <style>-Blöcke und detail.css sind wie der übrige Code mit Tabs eingerückt (reine Whitespace-Änderung, .editorconfig hält das für CSS fest)

Fixed

  • Feste Signalfarben ignorierten Dark Mode und Kontrast-Themes (Issue #227): Chart-Overlays, Popups, Tabelle, Dialoge, Rollup, Report und Dashboard-Widget nutzten feste Rot-, Gelb- und Grüntöne. Sie kommen jetzt aus dem Nextcloud-Theming: Text über --color-error-text/-warning-text/-success-text, Flächen und Linien über --color-element-error usw. (ab NC 32) mit Rückfall auf --color-error für NC 31, Tönungen per color-mix()
  • Teilprojekte standen auf einer Ebene mit ihrem Projekt (Issue #230): In Tabelle und Chart-Beschriftung waren Teilprojekte der obersten Ebene nicht eingerückt, während direkte Aufgaben und Meilensteine des Projekts eine Stufe tiefer standen – Projekt und Teilprojekt ließen sich so nur schwer unterscheiden, und in der Übersicht mehrerer Projekte verschwamm die Grenze zwischen ihnen. buildRowModel() reiht sie jetzt mit Tiefe 1 ein; alle darunterliegenden Ebenen rücken entsprechend eine Stufe nach
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturewwr3SUZOKRGnM7uwZfWOLH4FzXNvzjqMOcqVDH3mlte3y8BbmOzAeE5tvPajrvld
xLoe/OaOJ6tLpE8e/7MRaKiB10CuP0sFMA0j5nzOLw7sDPpTpqAV7xVRBbtZhio6
ZWQ0HjSdRU2JGnlP+MFPi7b/c+fSYVz/M9/q/b79Wyf1x2cDRhUJymN8tmFeD8f0
VVL0kXoMiX8oIJVHj0FrMdEWj+nz8pMPLF17bXluzu5REBHi4YLAnjWdO1YYkTOL
QNxQ/b3lAKZG4jvHXTADGlwbGh3iMjidMSjhpnKt2GsDylHxGozgBFb+rCDiSr9Y
PTM6dC/Nmyy4O3/p8Sbs/ZBpqb8wgO51k7Qd6ltXLU+n7eiSoHQyXL+uQihGA2dg
tBbHeKhcPPR5gz2/cSSqkeEyVkesmg015dLkbdStJoq5zR6s7fGYB3ZoUjqoqR2O
5cVF8Jf4k/swhXM7fMQ2zjoZj8yKePguCx7CqppbUFvr5DXltbHh0KDWVVrSgfXM
/zjL9lqQQXrrgTT16oXJfmq1dajsd1e6qwP/OqRY304W9HceyRCh/jQu0oUoQcSz
+fKenL7yWw3IB2UCGHdiXqzGqyGcGdVL0eVPvZhkzpqoDrr82mjPkXshmM/oF//U
942f6SK2u3YG+bKx/n1Up3ZX90WhZxNsZdW+eDqCbmY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.2
Release Details
UpdatedSept. 17, 2026, 1:01 p.m.
Changelog

Changed

  • PHPUnit-Stubs auf Nextcloud 31 angehoben (Issue #219): nextcloud/ocp steht in composer.json/composer.lock jetzt auf dev-stable31 statt dev-stable28 und damit auf der kleinsten unterstützten Nextcloud-Version; psr/log zieht dabei von 1.1 auf 3.0 nach. OCP-Klassen ab NC 29 sind in Unit-Tests damit verfügbar, die Suite lief ohne Anpassungen an Mocks durch. Nachgeholt ist der Unit-Test für das Mapping ParameterOutOfRangeException → 400 invalid_<parameter> in ExceptionMiddlewareTest (bisher nur per E2E abgedeckt). Entfallen ist die Laufzeitprüfung im Notifier, die nur wegen der alten Stubs nötig war: Er wirft jetzt direkt OCP\Notification\UnknownNotificationException (seit NC 30). Das Release-Artefakt enthält die Stubs nicht
  • Wartbarkeit im Frontend (Issue #226): Der tasks-Watcher in GanttChart.vue ruft refreshOverlays() nicht mehr zusätzlich auf – renderGantt() plant das selbst ein, Workload-Diagramm, Baselines, Klammern, Fortschritt und Meilensteine wurden bei jeder Datenänderung doppelt gezeichnet. Das Dashboard-Widget beendet sein Aktualisierungsintervall beim Unmount. Das Entpacken von API-Antworten (data.ocs?.data ?? data, 61 Stellen) übernimmt der Helfer unwrap() in src/api.js. AdminHolidays.vue arbeitet über die Stores für Einstellungen, Kalender und Feiertage (holidays.js hat dafür load() und remove() bekommen), PersonalReminders.vue über den Settings-Store (neu: remindersMuted, setRemindersMuted(); die persönliche Einstellungsseite nutzt dafür jetzt Pinia), ShareDialog.vue über api() aus src/api.js statt einer eigenen Kopie. Der Projekt-Store spiegelt die Lösch-Kaskade des Backends: Beim Löschen eines Teilprojekts verschwinden auch Unter-Teilprojekte, deren Aufgaben und die Meilensteine, beim Löschen eines Projekts dessen Teilprojekte, Aufgaben und Meilensteine, und beim Löschen von Aufgaben die Abhängigkeiten anderer Aufgaben darauf. Kleinkram: keine Überschattung der Übersetzungsfunktion t mehr, ungenutzte catch-Variable entfernt. Der E2E-Test aus #223 wartet vor dem Ziehen auf eine stabile Balkenposition, weil die nachladende Tabelle das Diagramm sonst unter dem Mauszeiger verschob
  • Wartbarkeit im Backend (Issue #226): TaskService::create() und update() laufen in einer Transaktion (TTransactional::atomic()): Speichern, Ressourcen-Zuordnungen, Abhängigkeiten, Constraints, Propagation und Change-Events gelingen gemeinsam oder gar nicht, Benachrichtigungen gehen erst nach dem Commit hinaus. Die drei identischen Parser in TaskController sind zu parseJsonArray() zusammengelegt. Die seit dem 3-Ebenen-Modell ungenutzte Tabelle nc_gantt_sp_deps entfernt Migration Version1038Date20260917000000, Mapper und Entity sind gelöscht. Milestone hat nur noch einen Docblock (die @method-Annotationen des ersten waren für IDE und statische Analyse unsichtbar), Holiday die fehlenden Annotationen und keinen toten DateTime-Zweig mehr. WorkCalendarController und WorkScheduleController verzichten beim Ändern und Löschen auf die Eigentümerprüfung, die für die ohnehin nur Nextcloud-Admins zugänglichen Endpunkte immer zutraf. ExceptionMiddleware unterscheidet reguläre Controller: Eine ApiException wird dort zu einer JSONResponse mit Status und Meldung statt zu einer OCSException ohne OCS-Kontext, andere Exceptions behandelt Nextcloud selbst. POST /admin/holidays lehnt ein Datum außerhalb von Y-m-d bzw. einen nicht existierenden Kalendertag mit 400 invalid_date ab und einen unbekannten Kalender mit 400 invalid_calendar_id
  • Statische Analyse in der CI (Issue #226): PHPStan Level 5 prüft lib/ gegen die OCP-Stubs (phpstan.neon, composer phpstan) und läuft in jedem PHP-Job der CI nach dem Coding-Standard. Die Klassen der dav-App und von Sabre in DavTaskCalendarBackend sind ausgenommen, weil es dafür keine Stubs gibt. Behoben sind dabei die übrigen Befunde: HolidayMapper deklariert seinen Entity-Typ, ApiException ihre möglichen Status, ChangeEventService speichert Aufgaben und Teilprojekte typgerecht, Rohdaten des Clients sind nicht mehr als bereits geprüfte Strukturen annotiert, TaskCalendarSyncService erkennt neue Zuordnungszeilen ohne Vergleich einer nie leeren ID, und die Zyklusprüfung der Teilprojekt-Hierarchie greift nicht mehr auf ein fehlendes Array-Element zu (PHP-Warnung bei Altdaten mit verwaistem parent_id). Das Release-Archiv enthält phpstan.neon nicht

Fixed

  • Abhängigkeitsmodus „Pflicht“ griff beim Ziehen und in der Tabelle nicht (Issue #223): Der Server erzwingt die Abhängigkeiten der geänderten Aufgabe nur, wenn der Client dep_mode: 'strict' mitsendet. Das taten nur der Detail-Dialog und die Spalte „Vorgänger“, nicht aber Drag&Drop im Chart und die Start-/Endzellen der Tabelle – ausgerechnet die häufigsten Wege, einen Termin zu ändern. Eine Aufgabe ließ sich so vor das Ende ihres Vorgängers schieben und wurde dort gespeichert. store.updateTask() ergänzt den eingestellten Modus jetzt selbst, sobald start_date oder end_date im Payload stehen; ein explizit übergebener dep_mode hat Vorrang. Das gilt damit auch für „Simulation übernehmen“
  • Fehlgeschlagenes Speichern nach Drag&Drop blieb ohne Rückmeldung (Issue #223): Lehnte der Server eine Verschiebung ab (fehlende Rechte, ungültiges Datum, Netzwerkfehler), blieb der Balken an der neuen Position stehen, obwohl der alte Termin galt, und der Fehler landete nur in der Browser-Konsole. GanttChart.vue zeigt jetzt „Speichern fehlgeschlagen“ und setzt den Chart auf den Stand des Stores zurück, der Balken springt zurück – auch für Meilensteine und im Simulationsmodus. Neue Unit-Tests src/stores/projects.test.js, neuer E2E-Test tests/e2e/specs/dependency-mode.spec.js
  • Löschen hinterließ verwaiste Datensätze (Issue #224): Die Lösch-Kaskade einer Aufgabe war beim Löschen von Aufgaben, Teilprojekten und Projekten jeweils von Hand geschrieben und dabei auseinandergelaufen. Alle drei Wege nutzen jetzt den neuen TaskDeletionService (Abhängigkeiten in beide Richtungen, Meilensteine an der Aufgabe, Ressourcen-Zuordnungen). Dadurch räumen Teilprojekt- und Projektlöschung auch die Ressourcen-Zuordnungen ab, die bisher stehen blieben. Die Teilprojektlöschung entfernt zusätzlich die Meilensteine direkt an den gelöschten Teilprojekten (auf das Projekt eingeschränkt, vgl. Issue #179), die Projektlöschung Baselines samt Einträgen. Das Löschen einer Ressource entfernt ihre Zuordnungen und Kapazitätseinträge; war sie die primäre Ressource einer Aufgabe, rückt wie beim Speichern die nächste Zuordnung nach, sonst wird resource_id null. Den Aufwand (work) der Aufgaben lässt es bewusst unverändert: Die Arbeit fällt mit der Ressource nicht weg, sondern ist neu zu verteilen, und eine Neuberechnung senkte stillschweigend Plankosten und Fortschrittsgewichte. Aufgaben- und Ressourcenlöschung laufen jetzt wie die übrigen Löschwege in einer Transaktion. Bereits vorhandene Waisen werden nicht nachträglich bereinigt. Entfallen sind die dadurch ungenutzten TaskMapper::deleteByProject() und TaskSchedulingService::deleteDependencies()
  • Gelöschte Nextcloud-Konten wurden nicht aufgeräumt (Issue #225): Die App reagierte nicht auf das Löschen eines Kontos. Freigaben an das Konto blieben stehen, und ein später gleichnamig angelegtes Konto erbte darüber den Zugriff auf die Projekte – ebenso das Eigentum an den Projekten des gelöschten Kontos. Der neue UserDeletedListener (registriert für OCP\User\Events\UserDeletedEvent) delegiert an UserDeletionService, der in einer Transaktion die Freigaben an das Konto löscht, die Verknüpfung von Ressourcen mit dem Konto (uid, Issue #203) aufhebt und Projekte, Ressourcen, Ressourcengruppen, Kapazitäten, Wochenmodelle und Kalender des Kontos erhält, aber auf einen Platzhalter-Eigentümer #deleted-<zufällig> überträgt. # ist in Nextcloud-Benutzer-IDs nicht zulässig, der Platzhalter kann also nie einem Konto gehören; er ist je Löschung neu und für alle Tabellen derselben Löschung gleich, sodass Projekte und Ressourcen des Kontos zusammengehörig bleiben. Verwalten können diese Daten danach Nextcloud-Administratoren und Inhaber einer Admin-Freigabe. Historie (change_events.changed_by, baselines.created_by) bleibt unverändert; Erinnerungen und Tasks-Synchronisation gleichen sich wie bisher selbst ab. Neuer E2E-Test tests/e2e/specs/user-deletion.spec.js
  • Drag&Drop öffnete zusätzlich den Bearbeiten-Dialog: Nach dem Ziehen eines Balkens löst der Browser einen Klick auf den Balken aus, und GanttChart.vue öffnete daraufhin den Detail-Dialog – nach jeder Verschiebung musste man ihn wieder schließen. _onBarClick() ignoriert den Klick jetzt, wenn sich die Maus zwischen Drücken und Loslassen um mehr als 4 Pixel bewegt hat. Der E2E-Test zum Verschieben per Drag prüft, dass der Dialog geschlossen bleibt
  • Glossar beschrieb noch Abhängigkeiten zwischen Teilprojekten: Seit dem 3-Ebenen-Modell hängen Abhängigkeiten, kritischer Pfad und Puffer an Aufgaben, und der Abweichungsbericht rechnet über die Aufgaben der Baseline. Das Glossar sprach dagegen von Teilprojekten, die voneinander abhängen, eigene Termine und eigenen Aufwand haben. Korrigiert sind die Einträge Teilprojekt, Kritischer Pfad, Puffer, Abhängigkeit, Gesamte Terminabweichung und Ø Fortschritt (samt Formel) – auf Englisch und in allen Übersetzungen (de, es, fr, it, nl, pt_BR)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureKOPH7uJRRB6f+Iqi2oeNidpUdRwDrpvn6N99cSmWLf+W+QrWztXKyDbTLOq71mBy
cXEkYbN+4KnMt0Zie40fLjvXqI8sWgouPHQdobRBCvq60TUpHVT1umUSAWrP7KDJ
XMyCnbKt6jqpTmUusE7sMGkZwBB1SEdx5tAilm2gu67wzR5WQakpZXXsp3Au+GyM
sxPgqD5bgRMcKdRMF+VXIF5gZTsJrNgal9J3SZZ1m8KGMArFPm+dEJaxAF+z0NxX
NTNCKR/tF2y20xgc2Rt4KLNOg5TTrRLB931vwkoM0eLUjFavTXjFAMAoVndvX5yb
37sRCzJX2tzekoch1/e6BWVbo5PH5YJmP5at2lWmrhDcWHrzFxTQZ2ZiY6GYISAO
b/oKUbvmmTs5VFToXRWAQASOC9afDEbKdWes+zoglIP3euOwok+2VD4rXZtVUNlU
fZ/FwsIYt8OB9UF7pQWKQSma8OPsZNnsBaoJVbFinieAwgKs35XtYHeTUWfsXAsL
7GaRK2AJ4jkV4uu35/mHQOjm2DM0ZfkC4OpUI3qXhLM70YTn3BIC0Y3k1P8ZmG4J
RtMSXNmqjBDMO+A+qclcRULziVStXCSlkDVAIw3I37JdrGYlB+avZ9yTX3EsNR0C
gVjxSTao2fhfLKp6o1tQO0+ImF1XqRIxPE1vAWS0hOE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.0
Release Details
UpdatedSept. 16, 2026, 1:50 p.m.
Changelog

Added

  • Ressource optional mit einem Nextcloud-Benutzerkonto verknüpfen (Issue #203, Datenmodell + API): Ressourcen haben ein neues, optionales Feld uid (Migration Version1034Date20260915000000, Spalte nc_gantt_resources.uid, nullable und indiziert). POST /resources und PUT /resources/{id} nehmen es entgegen, GET /resources liefert es mit; uid: null bzw. "" hebt die Verknüpfung auf. Ein nicht existierendes Konto lehnt ResourceService über IUserManager::userExists() mit 400 user_not_found ab; je Eigentümer der Ressource darf ein Konto nur einer Ressource zugeordnet sein, sonst 409 uid_already_assigned (verhindert später doppelte Benachrichtigungen). Ressourcen ohne Konto — Maschinen, externe Dienstleister, Sammelrollen — bleiben uneingeschränkt nutzbar. Der JSON-Export enthält uid je Ressource; beim Import wird eine im Zielsystem unbekannte oder dort bereits vergebene uid verworfen, die Ressource ohne Verknüpfung angelegt und nach dem Import ein Hinweis mit den betroffenen Konten angezeigt, statt abzubrechen. Die Auswahl des Kontos in der Oberfläche siehe Issue #204
  • Nextcloud-Benutzer in der Ressourcenverwaltung auswählen (Issue #204): Der Ressourcen-Dialog hat ein optionales Feld „Nextcloud-Benutzer“ mit Typeahead-Suche über die vorhandene Benutzersuche (GET /users/search, keine Vollliste); geleert bleibt bzw. wird die Ressource ohne Konto. Die Auswahl übernimmt den Anzeigenamen als Vorschlag ins Namensfeld, solange dort noch nichts Eigenes steht — ein leeres Feld oder der unveränderte vorige Vorschlag wird ersetzt, ein selbst eingegebener Name bleibt. Lehnt der Server die Verknüpfung ab (400 user_not_found/invalid_uid, 409 uid_already_assigned), erscheint die Meldung direkt am Feld statt als allgemeiner Speicherfehler; beim Bearbeiten wird die uid nur bei einer Änderung mitgesendet, damit ein inzwischen gelöschtes Konto das Speichern anderer Felder nicht blockiert. Verknüpfte Ressourcen zeigen in der Ressourcenliste, im Kapazitätsraster und in der Ressourcen-Zuordnung des Aufgabendialogs Avatar und Anzeigenamen des Kontos (Ressourcenname, bei Abweichung ergänzt um den Anzeigenamen in Klammern); nicht verknüpfte bleiben unverändert. Dafür liefert die Ressourcen-API zusätzlich das berechnete Feld uid_display_name (bei gelöschtem Konto null, die Oberfläche zeigt dann die uid), und GET /users/search nimmt mit include_self=1 auch das eigene Konto auf, damit man sich selbst als Ressource verplanen kann — der Teilen-Dialog schließt es weiterhin aus. Das „+“-Menü im Kapazitätsraster schließt sich jetzt nach „Neue Ressourcengruppe“/„Neue Ressource“ (close-after-click); bisher blieb es über der Ansicht offen und entzog dem ersten Feld der Sidebar beim Schließen den Fokus
  • „Meine Aufgaben“: Filter, Endpunkt und Dashboard-Widget (Issue #205): Auf Basis der Verknüpfung aus #203 sieht man projektübergreifend, was einem selbst zugewiesen ist. Neuer Endpunkt GET /api/v1/my-tasks (optional include_done, limit 1–500) liefert die Aufgaben, deren primäre oder zusätzliche Ressource (Issue #103) mit dem eigenen Konto verknüpft ist — gleich, wem die Ressource gehört —, sortiert nach Ende, dann Start. Offen heißt Fortschritt < 100 % und Status nicht „erledigt“; overdue folgt der Kennzahl „kritisch“ des Projekt-Widgets. Die Verknüpfung allein gewährt keinen Zugriff: MyTasksService prüft je Projekt AccessService::canViewOrOwn(). has_linked_resource unterscheidet „keine Ressource verknüpft“ von „nichts zu tun“. Das neue Dashboard-Widget „Meine Gantt-Aufgaben“ (nc_gantt_my_tasks) ist ein API-Widget (IAPIWidgetV2, IButtonWidget, IIconWidget, IReloadableWidget), das Dashboard rendert es ohne eigenes Bundle: die nächsten fälligen und überfälligen Aufgaben mit Projekt und Fälligkeit, überfällige mit rotem Overlay-Symbol, jeder Eintrag ein Direktlink. Ohne verknüpfte Ressource zeigt es einen erklärenden Leerzustand mit Setup-Button zur Ressourcenverwaltung. Direktlinks ?project=<id>&task=<id> aktivieren das Projekt und öffnen den Aufgabendialog, ?view=resources öffnet die Ressourcenverwaltung; die Parameter verschwinden danach aus der URL. In der Filterleiste dimmt der neue Filter „Mir zugewiesen“ in Chart, Tabelle und Projektliste alles ohne verknüpfte Ressource; nicht aktivierte Projekte mit zugewiesenen Aufgaben erkennt die Projektliste über den Endpunkt. Ist keine Ressource mit dem Konto verknüpft, erklärt ein Hinweis im Filter das und führt zur Ressourcenverwaltung
  • Fälligkeits-Erinnerungen als Nextcloud-Benachrichtigung (Issue #206): Ein Hintergrund-Job (DueReminderJob, appinfo/info.xml) meldet einmal täglich jede offene Aufgabe und jeden Meilenstein, die heute fällig sind, in den nächsten Tagen fällig werden oder überfällig sind. Der Job läuft stündlich und meldet höchstens einmal je Tag, sobald die Serverzeit die eingestellte Stunde erreicht hat (reminder_hour, Standard 6 Uhr); die Vorlaufzeit steuert reminder_lead_days (Standard 1 Tag) — die Oberfläche zu beidem kommt mit Issue #207. Empfänger sind die über Issue #203 verknüpften Konten der zugeordneten Ressourcen (primäre wie zusätzliche, Issue #103); ist keine Ressource verknüpft, erhält der Projekteigentümer die Meldung, damit die Funktion auch ohne gepflegte Verknüpfungen nützt. Ein Meilenstein erbt die Empfänger seiner Aufgabe. Wie bei „Meine Aufgaben“ gewährt die Verknüpfung allein keinen Zugriff: wer das Projekt nicht sehen darf, wird nicht benachrichtigt. Der übersetzte Notifier verlinkt direkt auf das Element (?project=<id>&task=<id>, beim Meilenstein auf das Projekt) und kommt ohne Datenbankzugriff aus — alles Nötige steht in den Parametern der Benachrichtigung. Gegen Dubletten führt die App über die neue Tabelle nc_gantt_reminders (Migration Version1035Date20260916000000, eindeutig über Element und Empfänger) selbst Buch: je Element, Empfänger und Anlass höchstens eine offene Benachrichtigung. Jeder Lauf gleicht Soll und Ist ab — wechselt der Anlass („wird bald fällig“ → „überfällig“), ersetzt der Job die Benachrichtigung; ist das Element erledigt, verschoben oder gelöscht, zieht er sie zurück. Ohne aktivierte Notifications-App tut der Job nichts und protokolliert keine Fehler
  • Erinnerungen konfigurierbar: Vorlaufzeit, Umfang, Abschalten (Issue #207): Jedes Projekt legt selbst fest, ob es erinnert, wie weit im Voraus (0–14 Arbeitstage; ohne eigenen Wert gilt die Vorlaufzeit der Instanz), worüber (Meilensteine und Aufgaben oder nur Meilensteine) und ob auch an Überfälliges erinnert wird. Der Detaildialog des Projekts hat dafür den Abschnitt „Erinnerungen"; die Felder liegen in nc_gantt_projects (Migration Version1036Date20260916100000), PUT /projects/{id} nimmt reminders_enabled, reminder_lead_days, reminder_scope und reminder_overdue entgegen, GET /projects liefert sie mit, unzulässige Werte weist die API mit 400 invalid_reminder_lead_days bzw. invalid_reminder_scope ab. Die Vorlaufzeit zählt jetzt Arbeitstage statt Kalendertage — dieselben Wochenmodelle und Feiertage wie die Terminplanung —, sodass eine montags fällige Aufgabe bei einem Vorlauf von einem Tag am Freitag davor gemeldet wird und nicht am arbeitsfreien Sonntag. In den persönlichen Nextcloud-Einstellungen (Abschnitt „Gantt-Projekte") lässt sich die Erinnerung der App für das eigene Konto stummschalten (PUT /settings/reminder-mute); bereits zugestellte Benachrichtigungen zieht der nächste Lauf dann zurück. Die Vorbelegung aller neuen Felder entspricht dem Verhalten aus Issue #206

  • Ereignis-Benachrichtigungen: Zuweisung und Terminverschiebung (Issue #208): Zusätzlich zum Tageslauf aus Issue #206 meldet die App Änderungen in dem Moment, in dem sie passieren. Wer über eine Ressourcenzuordnung neu für eine Aufgabe zuständig wird, bekommt „Aufgabe … wurde dir zugewiesen"; ändert sich ein Start- oder Enddatum, geht „Der Termin der Aufgabe … hat sich verschoben" an die Zuständigen — auch dann, wenn die Verschiebung erst über die Propagationskette (TaskSchedulingService) oder über „Termine neu berechnen" entstanden ist. Verschiebt eine Aktion eine ganze Kette, erhält jede Person eine Meldung, die die angefasste Aufgabe benennt und die Zahl der weiteren für sie betroffenen Elemente nennt, statt einer Flut von Einzelmeldungen. Wer die Änderung auslöst, hört nichts davon; stummgeschaltete Konten und Projekte mit abgeschalteten Erinnerungen (Issue #207) bleiben still, und wer das Projekt nicht sehen darf, wird nicht benachrichtigt. Anders als die Fälligkeits-Erinnerungen fallen die Ereignis-Meldungen nicht auf den Projekteigentümer zurück: gemeldet wird nur, wer über eine verknüpfte Ressource (Issue #203, #103) tatsächlich zuständig ist. Die Meldungen tragen den eigenen Objekttyp task_event, damit der Tageslauf sie beim Aufräumen seiner eigenen Erinnerungen zum selben Element nicht mit zurückzieht; je Aufgabe und Empfänger bleibt höchstens eine offene Ereignis-Meldung stehen — die neue ersetzt die vorige. Die Empfängerermittlung samt Stummschaltung liegt dafür jetzt im gemeinsamen NotificationRecipientService, den sich DueReminderService und der neue EventNotificationService teilen. Die persönliche Einstellung heißt entsprechend „Benachrichtigungen" und schaltet beides stumm

Fixed

  • Parameter außerhalb des Wertebereichs ergaben 500 statt 400 (gefunden bei Issue #205): Nextclouds Dispatcher prüft Integer-Parameter mit @param int<min, max> — und limit ohne Angabe auf 1–500 — noch vor dem Controller. Die ParameterOutOfRangeException machte die ExceptionMiddleware bisher zu 500 internal_error samt Fehlerprotokoll; jetzt antwortet die API mit 400 invalid_<parameter>
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureHgTu9/xKCVwgEQ94i1KF1Wc1ucKK+FK0W47ldxlBDNCRKgbhuvGpV7QKdpXiYR5G
Y18Tdot2H1tVhxwYqNPF4/dRt2zuaXzudAIrMrZ70Rxhxo15HNIcFulblH97Td76
HpBO3VxAlBeB6bSQS+YnC3HRC0koDMVJu7VtabL0duzWYZap0539AhczP2rXiAnN
+PxyU8dubrSLOljWGiL6jExiK7Eg6L1luFujNp4MnI8/cBqlBobjRgGmXF5SVfF0
kIcBN8T3Dcpg1w8fuGe68YnaR0xv3ML63zn1nBFb+5zkANv8Yb08dMIQdaNmkCEq
SNSWazd81/HbHDyLEUTK1fpDol9Hn8fv1+4u3MuvCzOcVZjxrvDozAyLLGQX7Y/Y
bup5FbA/WUYXwW6H4XyltYjW60+ivbTsbKflYn6lrbdevFb+z7s0niDgRzSGXIc3
jFh+7NpYzrCVnawKHxJjnyQGrjUNuuozbeAgUh2MaU0D7hh2LuZlLJ7pf5hZTvO+
q1yqdLMiSnmHIFeBh35qH7wywsUmkQYViopegvbRt0kz+ksyHmaxgOE9TOp6JoIc
kivnQLil1Tl0J0gK8w3Nk8q2Hj0WRzVuMyqTqBvAeP54rHM+jOtw3GIlX6XxjBUE
knAkfESZIbDGMrBH4sIFWQP28xCViSGAmideS61nwAY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.2
Release Details
UpdatedSept. 13, 2026, 4:16 p.m.
Changelog

Security

  • Eine zyklische Teilprojekt-Hierarchie konnte den Server blockieren (Issue #199). SubProjectService::create()/update() prüften bei parent_id nur, ob das Ziel-Teilprojekt zum selben Projekt gehört, nicht aber, ob dadurch ein Zyklus entsteht (z. B. A → Parent B, B → Parent A). findDescendantIds() lief bei einem solchen Zyklus beim Löschen in eine Endlosschleife (CPU-Hang, unbegrenztes Speicherwachstum) — auslösbar für jede Person mit Bearbeitungsrecht am Projekt, nicht nur für Eigentümer oder Admins. Eine Zyklusprüfung analog zu TaskSchedulingService::assertValidDependencies() weist solche Änderungen jetzt ab (BadRequestException parent_cycle); zusätzlich bricht findDescendantIds() über ein visited-Set ab, falls Altdaten bereits einen Zyklus enthalten
  • Beim Anlegen einer Aufgabe ließen sich Abhängigkeiten zu Aufgaben fremder Projekte setzen (Issue #190). TaskService::create() reichte mitgesendete dependencies ungeprüft an TaskSchedulingService::addDependencies() weiter; eine predecessor_id aus einem nicht zugänglichen Projekt erzeugte so einen task_dependencies-Eintrag über die Projektgrenze hinweg. Der Create-Pfad ruft jetzt wie schon update() vorher assertValidDependencies() auf, analog zur IDOR-Prüfung in MilestoneService

Added

  • Projekte, Teilprojekte und Aufgaben einklappbar (Issue #197): Ein Dreieck vor jeder Zeile mit Unterobjekten (Projekt, Teilprojekt oder Aufgabe mit Meilenstein) blendet deren Nachfahren per Klick aus und wieder ein; ein Tooltip nennt den aktuellen Zustand. Weil Tabelle und Chart zeilengleich scrollen, wirkt das Einklappen auf beide Ansichten — buildRowModel() filtert die gemeinsame Zeilenliste, bevor buildFrappeRows()/buildTableRows() sie übernehmen. Vorgänger-Verweise auf ausgeblendete Zeilen werden aus den Chart-Balken entfernt, damit frappe-gantt beim Ziehen eines Balkens nicht auf einen fehlenden Balken zugreift. Der Zustand wird pro Nutzer über die bestehenden UI-Einstellungen gespeichert
  • Eigene Farbe für Teilprojekte und Aufgaben (Issue #196): Im Detaildialog von Teilprojekten und Aufgaben lässt sich jetzt — wie bisher nur beim Projekt — eine Farbe aus der Palette oder frei wählen. Ohne eigene Farbe („Geerbt") übernimmt das Objekt die Farbe des nächsten übergeordneten Teilprojekts bzw. des Projekts; Meilensteine erscheinen in der Farbe ihres übergeordneten Objekts. Balken, Summenklammern und Meilenstein-Rauten im Chart sowie der Farbpunkt vor jedem Eintrag der Tabellenansicht (bisher stets die Projektfarbe) verwenden diese effektive Farbe (src/gantt/colors.js); die dynamischen CSS-Regeln hängen dafür nicht mehr an der Projekt-id (gantt-color-<id>), sondern am Farbwert (gantt-fill-<hex>)
  • Die color-Spalte von Aufgaben und Teilprojekten gab es schon, sie war aber mit #0082c9 vorbelegt und wurde nie angezeigt. Die Migration Version1033Date20260913000000 setzt den Spalten-Default auf NULL (= erbt) und den Vorbelegungswert in den Bestandsdaten zurück. Objekte, die ihn trugen, sehen daher unverändert aus; Farben, die davon abweichen — etwa aus JSON-/CSV-Importen oder direkten API-Aufrufen, die bisher gespeichert, aber nie angezeigt wurden —, erscheinen nach dem Update erstmals im Chart. Die API liefert für Objekte ohne eigene Farbe jetzt color: null; PUT /tasks/{id} und PUT /subprojects/{id} akzeptieren color: null bzw. "" zum Zurücksetzen — bisher verwarf das array_filter im Controller einen solchen Wert

Changed

  • Dokumentation nach Sprache (DE/EN) und Zielgruppe organisiert (Issue #191): Entwickler-Doku liegt jetzt übersetzt unter docs/de/ und docs/en/ im Repository-Root (CONTRIBUTE.md, RELEASE.md, HOWTO_Release.md, sowie die vormals in src/detail/, tests/e2e/ und tests/fixtures/ liegenden READMEs als detail-module.md/e2e-tests.md/test-fixtures.md, mit kurzen Verweis-Stubs an den alten Pfaden), Nutzer-Doku unter nc_gantt/docs/de/ und nc_gantt/docs/en/ (FEATURES.md, HOWTO.md, INSTALL.md, LICENSE.md). Die Root-README.md ist die vormalige nc_gantt/README.md, ergänzt um ein Sprachen-/Themen-Inhaltsverzeichnis am Anfang; die alte Root-README (zehn Anwendungsfälle) ist darin aufgegangen. nc_gantt/CHANGELOG.md bleibt bewusst an seinem bisherigen Pfad und einsprachig, weil release.sh, scripts/validate-appinfo.js und tests/Unit/AppInfoXmlTest.php exakt diesen Pfad für die Versionsabschnitts-Prüfung voraussetzen und der historische Log als technisches Protokoll nicht rückwirkend übersetzt wurde; LICENSE.md führt den englischen AGPLv3-Text als maßgeblich, die deutsche Fassung ist als inoffizielle, nicht rechtsverbindliche Übersetzung gekennzeichnet. appinfo/info.xml verlinkt in allen sieben App-Store-Sprachen sowie in <documentation> (user/admin/developer) jetzt auf die neuen docs/<lang>/…-Pfade, mit Englisch als Fallback für Sprachen ohne eigene Doku (fr/es/it/nl/pt-br)
  • Deutsche Übersetzung der README (Issue #194): nc_gantt/docs/de/README.md übersetzt die Root-README.md vollständig (Anwendungsfälle, Funktionen, Installation, Architektur); beide Dateien verlinken oben in einer kleinen Sprachtabelle aufeinander
  • App-Store-Beschreibung ergänzt: Der Abschnitt „Gantt-Diagramm & Tabellenansicht“ in appinfo/info.xml nennt in allen sieben Sprachfassungen jetzt auch das Einklappen von Projekten, Teilprojekten und Aufgaben sowie die eigenen Farben für Teilprojekte und Aufgaben

Fixed

  • Ist-Kosten ließen sich im Aufgaben-Formular nicht speichern (Issue #198): Das Feld für actual_cost las das bei numberField() nicht vorhandene .value statt getValue(), sodass beim Speichern stets null statt des eingegebenen Werts an den Server ging
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureHezqeAXIywdFI8vq5gO4MON/W8MjpkNqrjO2PZgZFFAHkSovdh5IqDbaUoltzPgk
rPiZtmytaqM29hGS2Qqh+DyymRKK7hco5chJ7tcrNqZcDx3KC9UDH2u+GK8GzkS0
jMsvi603ajD3ApEAiZUH9f0eiCYVerg6+/vUh63LFYQuYb7zjyUrNzmNTRl55eeS
cSgDjEvM0kqbaD/FXtdWK0ZzCaOJcSpuCLorf5i7WJU2lQx0Wi0I6lFHYFBVzLBj
W+U8NMiFZE+Wy1YGGwz7QOY1o9fOmrxzwIchuLvLVPCT4j9Vk9YNByc2XREv++UJ
/l3q8EYLLASMKHkscessRS5izHRaTR3iwgHNrxvb8vaXjzu6+he45/LaeQC6pg1M
wsBgAdHTb7alK48/W4p8ex0W5x8l/XyGeBzcSXidxlFD4SKFE2LAGxRoDzIIPeOj
gC3MT8IvAuKU5/g2fruxOxa9Nv2HNh3V9zw4reGxf018TeKVESxE7oMYpn4aFwmK
aq8c+n7EVqGnY1imH+BwvL5EeKzjjBTKcb8Z8DHSxNx6fOG+8ExyJLL+/s9zUSXY
uB1EnmU6QTWH5Tb93gqG/JgVV/8MoERfXSNMi2VOAjsniaGHkagXKs830EwamrDs
ttDDE3UQ2EymEVIDa1v8WTKAxWtFC2sl4GXwuyOTUyA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.1
Release Details
UpdatedSept. 2, 2026, 6:15 p.m.
Changelog

Security

  • Ressourcen-, Gruppen- und Kapazitätsdaten fremder Projekte waren über geteilte Projekte einsehbar (Issue #183). ResourceMapper, ResourceGroupMapper und ResourceCapacityMapper::findVisibleToUser() gaben pauschal alle Datensätze eines Eigentümers frei, sobald irgendein Projekt dieses Eigentümers mit dem Nutzer geteilt war — unabhängig davon, ob die konkrete Ressource überhaupt in einem geteilten Projekt verwendet wird. Der neue zentrale ResourceVisibilityService prüft stattdessen, ob die Ressource tatsächlich in einer für den Nutzer sichtbaren Aufgabe verwendet wird, und wird von ResourceService, ResourceCapacityService, ResourceGroupService und TaskResourceAssignmentService genutzt. Damit lassen sich über eine geteilte Ressource weder die übrigen, nicht geteilten Ressourcen desselben Eigentümers einsehen noch eigenen Aufgaben zuordnen
  • GET /api/v1/resources/{id}/utilization gab Planungsdaten aus nicht zugänglichen Projekten preis (Issue #181). ResourceUtilizationService::findVisibleResource() prüfte nur, ob der Ressourcen-Eigentümer irgendein Projekt mit dem Nutzer geteilt hat; über die Auslastung waren dadurch Aufgaben- und Projekttitel samt Terminen aus fremden Projekten abgreifbar. Die Prüfung stellt jetzt projektbezogen fest, ob die Ressource in mindestens einem über AccessService::canViewOrOwn() sichtbaren Projekt verwendet wird; zusätzlich filtert collectPlannedHours() die Aufgaben auf genau diese Projekte, sodass auch bei erlaubtem Ressourcenzugriff nichts aus fremden Projekten durchsickert
  • Aufgaben und Teilprojekte ließen sich an fremde Projekte hängen (Issue #179). TaskService::create()/update() und SubProjectService::create()/update() prüften nur die Schreibberechtigung auf die eigene projectId, nicht aber, ob eine mitgesendete subproject_id bzw. parent_id überhaupt zu diesem Projekt gehört — mit Folgewirkung beim Löschen eines Teilprojekts (SubProjectService::deleteTasksForSubproject()) und im öffentlichen ICS-Feed (CalendarFeedService::buildIcs()), da beide über TaskMapper::findBySubProject() allein nach subproject_id filtern. Analog zum Schutz aus Issue #97 prüfen jetzt AccessService::assertTaskBelongsToProject() und assertSubprojectBelongsToProject() die Zugehörigkeit; deleteTasksForSubproject() und buildIcs() filtern die geladenen Aufgaben zusätzlich defensiv nach project_id, um vor dem Fix entstandene Altdaten abzudecken

Changed

  • Dokumentation aufgeräumt: Die Doku ist jetzt nach Zielgruppe getrennt — alles, was Nutzerinnen und Nutzer brauchen, liegt in nc_gantt/ und wird damit vom release.sh-Archiv mit ausgeliefert, das Repository-Root enthält nur noch Entwickler-Doku. Dafür sind FEATURES.md, INSTALL.md und die neue HOWTO.md nach nc_gantt/ gewandert; die Root-README.md beschreibt die zehn wichtigsten Anwendungsfälle des Werkzeugs, erklärt je Fall den gedachten Arbeitsweg und führt in einer Repository-Übersicht auf, welches Dokument für wen gedacht ist. Neu ist nc_gantt/HOWTO.md: eine Schritt-für-Schritt-Anleitung entlang eines durchgerechneten Beispielprojekts, die alle zehn Anwendungsfälle mit konkreten Feldwerten, Abhängigkeiten und Terminen durchspielt
  • Dabei korrigierte Verweise und Angaben: der Lizenz-Link in nc_gantt/README.md zeigte auf das nicht vorhandene LICENSES/AGPL-3.0-or-later.txt statt auf LICENSE.md; die Nextcloud-Anforderung stand mit „28–33" in README.md und „28–34" in FEATURES.md gegen die tatsächlichen 31–34 aus appinfo/info.xml; FEATURES.md nannte noch Version 1.5.0; INSTALL.md verwies auf ein dist/nc_gantt-<version>.zip im Repository, das es nicht gibt (dist/ ist nicht versioniert und release.sh baut .tar.gz), und in CONTRIBUTE.md trug ein Link auf RELEASE.md die Beschriftung release.sh. Alle relativen Links und Abschnitts-Anker der versionierten Markdown-Dateien lösen jetzt auf
  • Tests laufen künftig vor jedem Commit (Issue #185): setup-dev.sh installiert einen pre-commit-Hook, der Vitest und PHPUnit (über Docker) ausführt und den Commit bei Fehlern abbricht — genau die Stelle, an der die vier defekten Tests aus dem Ressourcen-Sichtbarkeits-Commit hängen geblieben wären. phpunit.xml schaltet zusätzlich failOnRisky und failOnWarning ein, damit ein Test ohne Assertion den Lauf rot färbt statt unauffällig grün zu bleiben, und CONTRIBUTE.md dokumentiert die Regel zu Mock-Defaults ohne with() in setUp()
  • Die Default-Stubs in setUp() von ResourceVisibilityServiceTest blockierten die spezifischeren Stubs der einzelnen Testmethoden — PHPUnit wertet für dieselbe Methode den zuerst registrierten Matcher aus, und der greift per any() bei jedem Aufruf. findByResourceId(), findByIds() und findByResourceIdExcluding() lieferten dadurch immer ein leeres Array, was vier Failures und zwei risky Tests verursachte. Die Defaults sind entbehrlich, weil PHPUnit-Mocks für den Rückgabetyp array ohnehin ein leeres Array liefern; der Produktivcode blieb unverändert
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerHm/UxN1WTCOMd9LklleINo7LKDOEAyn1zlsR5eTCaUmdGaN1m8Knqk/EKDYcj73
zWx7TXmT4cP91Ag7mCMwY/gRqJabF7TgeXiA7CeV7VE7/D/+apIatMWRT7zYCjkF
oxXKimNrtpDSKWNXKSQn6RfJaEHu5WnIoF4X9Nyx/wf1WoWoThyvWMSSW/3jdajV
WXCV4XQjzbTULw9T+Ini/e8vp6mXt5CrmCdyUDQQcj2t9PF9Hq3GvWT1VQlYA8Kf
IJuqnArSc4JdtJiJnozpOKSBkAbDPTrFytwUTVUO4XlrGIvyqHv14ZVmBM8UCMo6
yVNA9HEBW62/b8W+Z3BtCBTuBnkRk3Wka8qhMMbqOe8kl/1vQtTKQkaDdnXaShUs
18mfDHjLZhcIrJEwbtPvo9Xpf+IWEZeoiRzEqa30n70iZTkPEVv/GxNVmJ74XYH/
Xa/vMWI/sxtXKtKY60XBXXCEVMuyxR+vfIbJOpDCcFlKyWHiXPEdYh2Ac8Bs1hc7
bk+lolw8FEWISrFdUwYdOrfRr4CMW+h2CYzgZsDKKYrVn+hRUy5mVOL1QXZJbNJJ
wr9O/JrUbXAMGdhtLuRUwM4tsPgMF2+cTLMe+wuFzUjWBBNiqb8GKJP2K94hdurD
+VMd/p/TtU02M23ooysu2mft5RCmD0/QbP8yxEACbHg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.0
Release Details
UpdatedAug. 28, 2026, 2:34 p.m.
Changelog

Achtung: Diese Version setzt Nextcloud 31 oder neuer voraus. Die bisherige Untergrenze war Nextcloud 28. Mit dem Umstieg auf @nextcloud/vue 9 — unvermeidbar, weil Vue 2 seit dem 31.12.2023 End-of-Life ist und keine Sicherheitskorrekturen mehr bekommt — fällt die Unterstützung für Nextcloud 28, 29 und 30 weg. Installationen auf diesen Versionen bekommen das Update nicht angeboten und bleiben auf 1.5.4; sie werden von dort nicht weiter gepflegt. Wer die App weiter nutzen möchte, aktualisiert zuerst den Nextcloud-Server auf mindestens Version 31 (max-version bleibt unverändert 34).

Changed

  • Das Frontend läuft auf Vue 3.5 statt Vue 2.7 (Issue #150). Vue 2 ist seit dem 31.12.2023 End-of-Life und bekommt weder Sicherheits- noch Fehlerkorrekturen; die Nextcloud-Server-Generation, gegen die die App freigegeben ist, hat ihren Frontend-Stack längst auf Vue 3 umgestellt. Mit umgestellt sind @nextcloud/vue 8 → 9, @nextcloud/dialogs 6 → 7, Pinia 2 → 3 und @nextcloud/webpack-vue-config 5 → 7; die dreizehn Build-Pakete, die das Webpack-Preset ab Version 7 als peerDependencies erwartet (webpack, vue-loader, sass, babel-loader …), stehen jetzt explizit in den devDependencies. npm audit --omit=dev meldet damit 0 statt 14 Befunde — die vier an Vue 2 hängenden Advisories (vue, vue-template-compiler, vue-resize, vue2-datepicker) waren ohne diese Migration nicht auflösbar
  • appinfo/info.xml fordert jetzt Nextcloud 31 statt 28 (max-version bleibt 34). @nextcloud/vue 9 lässt die Unterstützung für Nextcloud vor 31 fallen; das ist die unvermeidbare Gegenleistung der Migration. Installationen auf Nextcloud 28–30 bleiben auf Version 1.5.4
  • Die drei Einstiegspunkte src/main.js, src/admin.js und src/dashboard.js erzeugen ihre Anwendung über createApp() statt new Vue(); Pinia wird als Plugin registriert (app.use(pinia)), das Weiterreichen der Store-Instanz an das Vanilla-Detailmodal per setActivePinia() bleibt unverändert. Die Vue.prototype-Zuweisungen im Dashboard-Einstiegspunkt sind entfallen — DashboardWidget.vue importiert t ohnehin selbst
  • Anpassungen in den 17 Komponenten an die API von @nextcloud/vue 9: Import-Pfade (@nextcloud/vue/components/NcButton statt @nextcloud/vue/dist/Components/NcButton.js), type="primary" → variant="primary" bei NcButton/NcActions, NcActionCheckbox und NcSelect auf modelValue (:checked/@update:checked bzw. :value/@input sind entfallen), :shown.sync → v-model:shown bei NcPopover. Dazu die Vue-3-Umstellungen im eigenen Code: beforeDestroy() → beforeUnmount() in GanttTable.vue, ResourceGrid.vue und GanttChart.vue (siehe Issue #149), this.$set()/this.$delete() durch direkte Zuweisung bzw. splice() ersetzt
  • css/main.css und der zugehörige Util::addStyle()-Aufruf im PageController sind entfallen. Die Datei enthielt einzig #content { margin-top: 0 !important } gegen eine Doppelmarge, die NcContent unter @nextcloud/vue 8 zusätzlich zum Nextcloud-Layout gesetzt hat. Unter Version 9 gibt es diese Marge nicht mehr, der Reset schob die App stattdessen um die Höhe der Kopfzeile nach oben — die Navigationsleiste lag dadurch unter dem Nextcloud-Header und war nicht mehr anklickbar. Der Mount-Punkt #nc-gantt-app bleibt als Wrapper im DOM stehen (Vue 3 rendert in das Element hinein, statt es wie Vue 2 zu ersetzen), nimmt aber nachgemessen exakt die Maße des Content-Bereichs an und braucht keine eigene Regel
  • ESLint nutzt das Preset @nextcloud/eslint-config/vue3. Es lässt vue/custom-event-name-casing auf dem Vue-3-Default (camelCase) laufen, während das Vue-2-Preset kebab-case erzwungen hat; da der Bestand durchgängig kebab-case verwendet — unter Vue 3 gleichermaßen gültig — setzt .eslintrc.js die Regel explizit auf kebab-case
  • Der <style>-Block in GanttChart.vue war mit 581 Zeilen ungescopt und wirkte damit auf die gesamte Nextcloud-Oberfläche statt nur auf die Komponente (Issue #151). Die 65 eigenen .nc-gantt-*/.nc-*-Regeln laufen jetzt über <style scoped>, mit :deep() dort, wo Overlays ihre Elemente per createElementNS() bzw. innerHTML außerhalb des Vue-Templates in .nc-gantt-container einhängen. Die Regeln, die auf frappe-gantt-eigene Klassennamen zugreifen (.gantt .bar & Co.), liegen gebündelt in src/vendor/frappe-gantt/theme.css direkt neben dem vendorten Quellcode der Bibliothek und werden von dort per index.js importiert; die beiden nicht unter .gantt verankerten Selektoren (.bar-wrapper.gantt-milestone .bar-label/.handle) sind dabei korrigiert. In App.vue laufen die drei Core-Überschreibungen (.app-navigation__content, #app-settings, .app-navigation .app-navigation-list) ebenfalls über :deep() in einem scoped Block. Abgesichert ist das durch vue/enforce-style-attribute aus eslint-plugin-vue, das ab sofort scoped für jeden <style>-Block in src/ erzwingt
  • TaskService bündelte auf 774 Zeilen drei Verantwortlichkeiten und brauchte dafür acht Konstruktor-Abhängigkeiten; der Schnitt trennt sie (Issue #153). TaskSchedulingService übernimmt Constraint-Propagation über EA/AA/EE/AE mit Lag, Kahn-Sortierung, Arbeitstagsrechnung, Zyklenprüfung und What-if-Simulation — CriticalPathService wird dort regulär injiziert, statt simulate() als Methodenparameter durchgereicht zu werden. TaskResourceAssignmentService übernimmt Auflösung und Persistierung der Ressourcenzuordnung inklusive MAX_TASK_RESOURCES und PT-Umrechnung. TaskService bleibt als CRUD-Fassade mit Berechtigungsprüfung, Validierung, Änderungsprotokoll und API-Aufbereitung: 311 statt 774 Zeilen, sieben statt acht Abhängigkeiten. Die Methodenrümpfe sind unverändert verschoben, es ändert sich keine Terminlogik. TaskServiceTest ist entlang derselben Grenzen aufgeteilt (TaskServiceTestCase als gemeinsame Basis, dazu TaskSchedulingServiceTest mit 39, TaskResourceAssignmentServiceTest mit 8 und TaskServiceTest mit 11 Fällen — exakt der bisherige Umfang), wobei die ausgelagerten Services weiterhin über ihren Aufrufer TaskService angetrieben werden, damit neben der Rechnung auch die Verdrahtung zwischen Fassade und Engine abgedeckt bleibt
  • Die 212 Zeilen CSS der Vanilla-Detaildialoge wurden zur Laufzeit als <style> in den <head> injiziert (src/detail/styles.js); sie liegen jetzt als src/detail/detail.css vor und werden von Webpack eingebunden (Issue #152). Der im Kopfkommentar genannte Grund — nicht von der Loader-Konfiguration abhängen zu wollen — trifft seit dem Vendoring nicht mehr zu, src/vendor/ importiert .css und .scss ebenfalls direkt. Dabei fiel auf, dass das Stylelint-Glob unquoted an die Shell ging und ohne globstar nur eine Verzeichnisebene traf: weder src/App.vue noch eine .css in src/vendor/ wurde je geprüft. Mit Quotes globbt Stylelint selbst; die dadurch aufgedeckten Findings in App.vue sind mitbehoben
  • Die PHP-Einrückung ist auf Tabs vereinheitlicht (Issue #170). nextcloud/coding-standard erwartet Tabs, die Codebasis nutzte durchgehend vier Leerzeichen — composer cs:check beanstandete dadurch 144 von 146 Dateien, der CI-Job wäre ab dem ersten Lauf dauerhaft rot und als Gate wertlos gewesen. Ursache der Drift war ein falscher Kommentar in .editorconfig, der dem Coding-Standard vier Leerzeichen unterstellte; der ist mitkorrigiert. Die Änderung ist rein mechanisch (php-cs-fixer fix): Einrückung, Klammern um einzeilige if-Rümpfe, sortierte use-Blöcke und einfache statt doppelte Anführungszeichen, wo keine Escape-Sequenz im Spiel ist. Der Commit steht in .git-blame-ignore-revs, damit git blame weiterhin die inhaltlich letzte Änderung einer Zeile zeigt

Added

  • appinfo/info.xml wird jetzt automatisch gegen das App-Store-Schema geprüft, statt den Fehler erst beim Upload zu erfahren: scripts/validate-appinfo.js (npm-Script validate:appinfo) validiert gegen die eingecheckte Schema-Kopie tests/fixtures/info.xsd — per xmllint, ersatzweise über lokales php oder das composer:2-Image — und prüft zusätzlich Semver-Format, Gleichstand mit package.json und den CHANGELOG-Abschnitt zur Version. release.sh ruft das als erstes Gate mit --online auf, lädt das Schema dabei frisch und meldet, wenn die Kopie veraltet ist
  • tests/Unit/AppInfoXmlTest.php führt dieselbe Schema-Validierung in der Testsuite aus (DOMDocument::schemaValidate()) und gleicht zusätzlich die Übersetzungen in l10n/ gegen die Store-Texte ab: gettext-Codes wie pt_BR werden als solche erkannt, und zu jeder übersetzten Sprache müssen <name>, <summary> und <description> in info.xml existieren
  • Die Stunden je Personentag sind konfigurierbar (Issue #169). Der Umrechnungsfaktor lag dreifach hartkodiert vor (TaskResourceAssignmentService, ResourceUtilizationService, ResourceGrid.vue) und wurde über gegenseitige Kommentarverweise von Hand konsistent gehalten — eine feste 8-Stunden-Annahme passt aber nicht zu jedem Arbeitszeitmodell. Der Wert liegt jetzt an genau einer Stelle: WorkTimeService liest ihn als App-Config-Wert hours_per_pt (Standard 8), validiert ihn gegen 1..24 und fällt bei unplausibel gespeicherten Werten auf den Standard zurück, damit die Auslastungsrechnung nicht durch 0 teilt; in ResourceUtilizationService entfällt damit auch die Konstante DEFAULT_CAPACITY_H, die denselben Vollzeit-Referenzwert doppelt hielt. Ins Frontend kommt der Faktor über den Initial State des PageController, sodass er schon beim ersten Rendern des Ressourcen-Rasters bereitsteht; GET/PUT /settings liefern und setzen ihn zusätzlich, wobei ein PUT ohne hours_per_pt den gespeicherten Wert unverändert lässt, damit ältere Clients ihn nicht auf den Standard zurücksetzen. Gepflegt wird er im Admin-Bereich neben Arbeitsbeginn und -ende
  • Die Vanilla-Detaildialoge sind für Screenreader und Tastatur verdrahtet (Issue #152): Die Dialoge verweisen über aria-labelledby auf ihre Überschrift, section() verknüpft Beschriftung und Bedienelement (bei Gruppen über role="group"), Knöpfe ohne Text bekommen einen Namen, Umschaltknöpfe melden ihren Zustand über aria-pressed, und die Datums-Plausibilitätsmeldung hängt per aria-describedby am Feld statt nur im title-Attribut des Icons zu stehen. Der Fokus beginnt beim Öffnen im Formular statt auf dem Schließen-Knopf und kehrt beim Schließen zum Auslöser zurück. Abgesichert durch payload.test.js (19 Fälle für Speicher- und Löschlogik) und accessibility.spec.js (6 E2E-Fälle) — für DOM-Code ist der E2E-Lauf die tragfähigere Absicherung als eine neue jsdom-Abhängigkeit. src/detail/README.md erklärt, warum das Modul außerhalb von Vue steht und welche Regeln daraus folgen; CONTRIBUTE.md verweist darauf
  • Die CI läuft. Der Workflow lag unter nc_gantt/.gitea/workflows/ci.yml und war eine reine GitHub-Actions-Datei; vier voneinander unabhängige Gründe verhinderten, dass davon jemals etwas ausgeführt wurde (Issue #170): Forgejo liest Workflows ausschließlich im Repo-Root, runs-on: ubuntu-latest passt auf kein Codeberg-Label (die gehosteten Runner heißen codeberg-tiny/-small/-medium), die Kurzform shivammathur/setup-php@v2 wird gegen Forgejos Action-Host aufgelöst, wo es die Action nicht gibt, und die Repo-Einheit „Actions" war deaktiviert. Die Datei liegt jetzt unter .forgejo/workflows/ci.yml, die App bleibt via defaults.run.working-directory erreichbar. Inhaltlich sind php-lint und php-tests zu einem Job zusammengefasst und laufen beide über die volle Matrix 8.1/8.2/8.3 (vorher lief PHPUnit nur auf 8.2, obwohl composer.json >=8.1 fordert), die Syntaxprüfung nutzt xargs statt find -exec (find liefert seinen eigenen Exitcode, nicht den von php -l — der Schritt konnte gar nicht fehlschlagen), und der JS-Job führt neu vitest mit 180 bislang nie automatisiert gelaufenen Tests aus, dazu validate:appinfo und l10n:check. paths-ignore hält die geteilten Codeberg-Runner von reinen Doku- und Screenshot-Änderungen frei

Fixed

  • Nach der Migration auf @nextcloud/dialogs 7 erschienen Statusmeldungen wie „Einstellungen gespeichert" als unformatierter Text oben links statt als Badge. Version 7 hat Toastify durch eigene Vue-Komponenten ersetzt, deren Styles nicht mehr im JS-Bundle stecken, sondern über den separaten Export @nextcloud/dialogs/style.css eingebunden werden müssen; ohne diesen Import lief der Toast-Container ungestylt im normalen Dokumentfluss mit. src/main.js importiert das Stylesheet jetzt. Die Toasts erscheinen dabei an der neuen Standardposition der Bibliothek unten links neben der Navigation statt wie früher oben zentriert — Version 7 bietet dafür keine Option mehr
  • testBuildIcsStripsCarriageReturnToPreventLineInjection prüfte das gesamte ICS-Dokument auf Freiheit von \r. RFC 5545 trennt Zeilen aber genau damit, und buildIcs() setzt sie korrekt als CRLF — der Test konnte nie grün sein und schlug auf main dauerhaft fehl. Der Schutz selbst greift; nur die Assertion war zu weit gefasst. Geprüft wird jetzt, was die Line-Injection tatsächlich ausschließt: kein einzelnes CR außerhalb der CRLF-Zeilentrenner, der eingeschleuste Titel erzeugt keine eigene ICS-Zeile, und er landet escaped im SUMMARY-Wert. Gegenprobe mit entfernter CR-Normalisierung in escapeText() schlägt weiterhin fehl, der Schutz bleibt also scharf getestet. Dass der Fehlschlag so lange unbemerkt blieb, lag daran, dass die Workflows nicht ausgeführt wurden (siehe Issue #170)
  • validate:appinfo brach im ersten CI-Lauf mit einer Meldung über einen nicht erreichbaren Docker-Socket ab (Issue #170). Die Validator-Kette in scripts/validate-appinfo.js probiert xmllint, dann php, dann docker und nimmt den erstbesten verfügbaren — „verfügbar" hieß dabei aber nur, dass das Binary existiert. Das Runner-Image bringt die docker-CLI mit, ohne dass ein Daemon erreichbar wäre, und hat weder xmllint noch php. isAvailable prüft jetzt, ob der Validator wirklich läuft (docker info statt --version), sodass die Kette korrekt durchfällt und bei gänzlich untauglicher Umgebung „Kein Schema-Validator gefunden" meldet statt eines Socket-Fehlers; zusätzlich installiert der JS-Job libxml2-utils, damit im CI immer xmllint gewinnt — der native XSD-Validator, ohne Daemon und ohne Netzwerk. Welcher Validator läuft, hing zuvor davon ab, was auf der Maschine zufällig installiert war
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegDK8JkRgxBUCFljhxHFzTzf37tjhwQ/k9E+DCDeVHxPECDOF0J3FbWmv2uqmm9R7
H8yyFfL/KFEDK3Jt24o2gujQZ9KlyBYJvrfwv//zvACtgGoErBUamME+FzFfK3x8
rRdXt1iXctmwutRvTNPj8uvfMJmo/vdbJRDGEihI/9wVdt4S+O3/OlpdjUg+QGFP
cMmlQwIAHAWrYSUllnN4GeryrFPGB7fLi/Nrhx0eAwQHUjIZHEb/s9ZxjmnYZd0c
8FJsgAUXfjowOIrDtWxr0P+HVj4c5qhGx4XEUFdfR0slriaL+zPbo2Li1wgsBA7r
+yoo8ocOJPaN3iCpRNL4z8q2sxEuWRt2Dx0I5ck6F7g+gpzDoTo5bFEvq4Eir0wN
lGHhyxcdEK653dTKOBYI07tCjJYU+9VxCbRzR7O+sVI412Tox5W5cYGl4WpN8aNH
00CX7PLaruaEjwuFdAvhY6SzsXA3RFmN3Dv9qFx3dl3HjoQ+OCqy89XzT3tbBN07
3aZO7OgyB26al2XdRFaaRQaVLqjJzUCz2B5nh+z9+L+QcBl3F9sE2sFZnkWwx3bp
MOdj2nCS11r3Z0utRI9uDEanys6HkqnpGed4WEGX9O+EfocqNg2b0wgyqjFNWbxI
n4uoeakhyWYOVHCUfzU8tn6kmU4b8n+GfpMjKPhBeRw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.4
Release Details
UpdatedAug. 27, 2026, 7:06 p.m.
Changelog

Fixed

  • appinfo/info.xml wurde vom Nextcloud App Store abgelehnt: der Sprachcode pt_BR in <name>, <summary> und <description> ist im Schema nicht enthalten — die Enumeration kennt nur BCP-47-Schreibweise in Kleinbuchstaben mit Bindestrich. Alle drei Vorkommen heißen jetzt pt-br
  • Die Kindelemente von <info> sind im Schema eine feste Sequenz; <settings> stand vor <dependencies> und wäre nach dem Sprachcode-Fix als nächster Validierungsfehler aufgeschlagen. Der Block steht jetzt an der vom Schema vorgesehenen Position hinter <dependencies>. xmllint --schema gegen https://apps.nextcloud.com/schema/apps/info.xsd läuft damit sauber durch
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureYbjiEkGYW1s8Xg9Jvrt4nNo/67zkrQDA16yfK2MzVa58R982qrLUupNbvUE/FscP
pFg9nlqxd81cpyH1ZhWOM7fMq98mPyjSUsJZ7xjYzNccF13xvDpBXFlfX4UyCknc
50Rmw9a8RxiCH0mEpH9zWjdanX194R8cUIMDY+BcymCgyBVRQub9yaL5iiHH3hNA
OOD+UOiSUNHns9R6sgZ+tF4OeoTufWR5//AfRmuL5qC5spyuP1iKr7FnzPar+/sS
pHKojfUyi0sAjlrThVZDo8fvVPqkkH4jgvVmNE41t1UOlVdajIRd2yhbHX0h0SSs
rmt4CQqNXAjLE/Wlq2lu9Viw4M2BhhPIfVb5YJFzaxjhB8RRy53rquXRL64f7vGa
agq0ri3eD+0ZWmMOlJmHaMv6AXYd0ZG8myb3PAyNC0NYXzrw2PAVL8jDfzmq2cNY
Q3rIh101vtGPytqanNS2VmtfZ8BX63rd4SLR4PJdRYrm3Wbqanx8WZa20eLgYF8S
M9xhxl5Jf8+TnYLwHi1sGm1ID2XJ4krfgOxiUlB8lGeSweUK1uueC0bmUDsrXyhJ
RMGST41TU1HqXx/3Eeh91JKq/Jbx15kPuxu7WNKyuSOj4vKbgFwBUSmHKDU6lvEZ
L/bnxZi965/4K3kvyJ8o2j5e4xUmJLeusi9Fg0yrRqE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.2
Release Details
UpdatedAug. 26, 2026, 5:18 p.m.
Changelog

Changed

  • Teilen-Dialog: das Eingabefeld für den einzuladenden Nutzer ist jetzt ein Suchfeld statt eines reinen Textfelds für die Benutzer-ID (Issue #144). Getippt wird nach Anzeigename, Benutzer-ID oder E-Mail-Adresse; ein neuer, admin-unabhängiger Endpunkt GET /api/v1/users/search (UserController::search(), IUserManager::search(), max. 20 Treffer, der eigene Account wird ausgeblendet) liefert dazu Anzeigename, Benutzer-ID und E-Mail als Vorschlagsliste. Gespeichert wird weiterhin die Benutzer-ID als principal_id – für Umgebungen mit LDAP/AD-Benutzerverwaltung war die zufällig generierte ID zuvor nicht ohne Umweg über die Benutzerverwaltung in Erfahrung zu bringen. Die Liste bereits geteilter Zugriffe zeigt ebenfalls den Anzeigenamen statt der rohen ID (ShareController::index() reichert die Antwort um display_name an, Fallback auf principal_id falls der Account inzwischen gelöscht wurde)
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureyLn/jlYbk3gIYfgIcjON0z467/xL9L6QNcmfgOa/4zhTJjLknXq7eu1UJddWWlLJ
NG2U9d/OEFO9wLHPYPGNiWgQMRIk+TGYlQVTXcZH3WpBxdJOm3XMnk5hq2kNB2FN
TJemqr/dxafGB2hTIgivPMXMnemdrtxWN1RAdzCxZIg9HwJWjG5JxdqHmURHwske
p/Ki/iZghKjrxbPIghbqtrvkCJRImbgVWY8AmUNUPLgotNIthiWFnkTDVhYWcreP
pWleWHxEn57UOtehff1aJa+hRI+8WVkx9iXArx0SThmOCYYgJ/HeG8dOOtmr9jSm
1nzETAL87YLxL/+qM6DyBIgNWhrq1oj9Q12bBm1+la477z6fpOgUlMjWkPEYRQi/
vhxirXwB4BZBYvaMEkNlN2U3+7BqgigGoaTcbN2rG/TP1JuOjdj9jEjMMRye0JbT
35XOoM/VPYGovrT9slHg8r3Wolyd8ioXVOu4IcMImvC5OrTzpKY7NLEc/S+cOKwz
ijOKcnoV0j++KxtQKKgEO96HJpEM0jteQVOGCoPbghjS3OFzkiB1bUVJAlu1jn54
38Q2zw8rZ0EMm1rEPmIrl72DIIXionbWKatREgy7QPwuL/WKZ2c+J1UIUTKck5eO
7M2VHh6P3FnIg/UsOCMu3U7ja0Y8vQ3YUo3tazLfob0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.1
Release Details
UpdatedAug. 25, 2026, 1:28 p.m.
Changelog

Added

  • Screenshot des gerenderten Gantt-Charts als PNG, SVG oder PDF (Issue #116): ein neuer „Screenshot"-Eintrag in der Chart-Toolbar öffnet ein Menü mit den drei Formaten, derselbe Export steht im Bearbeiten-Dialog des Projekts neben JSON und CSV bereit (dort mit vorgeschalteter Formatwahl statt Menü, openScreenshotFormatDialog()). Exportiert wird stets das gesamte Chart, nicht nur der sichtbare Scroll-Ausschnitt — frappe-gantt rendert die volle Breite und Höhe ohnehin als eine SVG. PNG und SVG klonen die aktuell gerenderte SVG und schreiben getComputedStyle() als Inline-Attribute (löst auch CSS-Variablen auf), sodass die Datei ohne das App-Stylesheet eigenständig darstellbar ist; sie folgen dem eingestellten UI-Theme, ein hinterlegtes Rechteck in --color-main-background deckt dabei auch die Fläche unterhalb der Aufgabenzeilen (Workload-Chart, Leerraum) ab, die sonst transparent bliebe. PNG rastert per Canvas mit doppelter Auflösung
  • Zeitraum-Auswahl für den PDF-/Druck-Export (Issue #116): vor dem Drucken fragt ein Dialog, welchen Zeitraum die PDF abdecken soll — gesamter Projektzeitraum (Vorbelegung), laufendes Kalenderjahr, „aktives Quartal" (Vormonat bis einschließlich übernächstem Monat) oder ein frei eingegebener Bereich. Der Vorschlag ist der tatsächlich von Balken belegte Zeitraum, nicht gantt_start/gantt_end — frappe-gantt legt darum großzügig Puffer, der andernfalls mitgedruckt würde. Umgesetzt wird der Zuschnitt als viewBox auf dem SVG-Klon; die dafür nötige Skala (Chart-Start, Pixel pro Millisekunde) schreibt GanttChart.vue nach jedem Render als data-Attribute an den Chart-Container, sodass auch Aufrufer ohne Zugriff auf die frappe-Instanz (Bearbeiten-Dialog) sie lesen können. Die Zeitraum-Logik steckt isoliert testbar in src/gantt/printRange.js (src/gantt/printRange.test.js)

Changed

  • Der PDF-Export druckt nicht mehr den Live-Container, sondern einen gestylten, selbst gerasterten Klon des Charts (Issue #116). Bisher wurde das Inline-SVG direkt gedruckt — wie fein die Browser-Engine das rastert, ist nicht steuerbar und fiel je nach Browser sichtbar grob aus. Jetzt wird der Klon mit 300 dpi bezogen auf die bedruckbare Fläche (A4 quer, 10 mm Rand → 277 × 190 mm) in ein Canvas gerastert und als Bild mit fest vorgegebener physischer Größe in den Druck gegeben; schlägt die Rasterung fehl, bleibt der SVG-Klon als Druckinhalt übrig und skaliert per CSS auf die Seitenbreite. Der Klon wird immer im hellen Theme berechnet (Papier ist weiß, ein dunkles UI-Theme ergäbe sonst dunkelgraue Flächen und helle Schrift) — dafür genügt ein ausgelagerter Container mit gesetztem data-theme-light, da Nextcloud die Stylesheets aller Theme-Varianten lädt und nur über dieses Attribut umschaltet. Das Ausblenden der übrigen Seite läuft über display: none statt visibility: hidden, sonst blieb die Fläche der App als Leerseiten erhalten; Nextclouds Hintergrundbild auf html/body wird für den Druck abgeschaltet. Aufgeräumt wird über afterprint mit Timeout-Fallback für Browser, die das Event nicht feuern
  • Der Toolbar-Eintrag heißt „Screenshot" (Icon MonitorScreenshot) statt „Export" (Icon ExportVariant) — „Export" ist im Projekt bereits für den Daten-Export nach JSON/CSV belegt, der Chart-Export ist demgegenüber eine reine Abbildung der aktuellen Darstellung
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturek6kpvF53IR8tnkOFXyoSPFPGlwVzO/RUTgI1p1ppRcFmoFPUyFeIokOqWA1OtD9q
wdo+qkQQPyJGph0Uwp+hwP9oGmeKWErnlv8HMdA5I5iaXIN7afGvdfxWfKgqWetw
tGmCF7l2IzMmT2Ol/3pKgCnFkXTDPCVeNINNbUdi7EjeRpbcbOGWt3+5UVwZgVTX
Z9/kEEPvjMV98s1zT5yBXcFvyqZA7bhhezmBkfzaLa0DAIczFJbhyFtR06WuS7bP
CWBCs/xvSRuCwo61oyXRAWLohz/2GsrMSa5jvPvnEG1XEHpGPVorCijAvsBm4sX5
yAZTrBm3zbNYObqVE2idxTH/eqP8xXtE38JFjpG5IahpeFNb9V2EbH1nrjfRKYcH
NVvXcXFREAmvorShKDE0ymFN+/BCjvU1vnJCnRD5RMsoSGUzXSVHPrF1emtusLHj
ltP22eYOCpraEpZheTkFftqvpbiQKVXsZ+8wY4dJnh5Ymi71loxZhggoH8s/pmp+
WbSx6MZwSPBqJ0HOJWckFiZXur2OQYOVLfXt8uPYNU/EZSoYDqm81GVMglQXxyM6
YDh0xXqwJ7Qft7k0O/3Fa5YLJGP4B4RvjU29N3/WDsfjuk7PeCSTPhRlTOfV/Fbn
jS83WVBavmyklhu0rNYuCYfdYvUxemIO3VuGmYzFXgI=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.0
Release Details
UpdatedAug. 24, 2026, 5:26 p.m.
Changelog

Added

  • Editierbare Vorgänger-Spalte in der Tabellenansicht, Teilschritt F von #117 (Issue #124): eine neue, über das Spaltenmenü einblendbare Spalte „Vorgänger“ zeigt die Abhängigkeiten einer Aufgabe in der aus MS Project bekannten Kurznotation (12EA+2d = Vorgänger Aufgabe 12, Ende-Anfang, zwei Arbeitstage Abstand; mehrere durch ; getrennt) und nimmt sie dort auch entgegen. Typ (EA/AA/EE/AE, englische Kürzel FS/SS/FF/SF werden beim Eingeben mit akzeptiert) und Abstand sind optional, 12 allein bedeutet also EA ohne Abstand. Geprüft wird beim Verlassen der Zelle: Syntax, Existenz der Aufgabe im selben Projekt, Selbstbezug, Dubletten und Kreisschluss über die gesamte Abhängigkeitskette – bei einem Fehler bleibt die Zelle im Bearbeitungsmodus und nennt den Grund im Klartext, die bisherigen Abhängigkeiten bleiben unangetastet. Gespeichert wird über dieselbe PUT /tasks/{id}-Route wie der Detail-Dialog (inkl. dep_mode), sodass der Server die Termine anwendet und durch die Nachfolgerkette propagiert; Balken, Pfeile und Typ-Labels im Chart zeichnen sich mit der Antwort neu. Bewusst ohne Undo-Toast, da ein Zurücknehmen nur der Termine (nicht der Abhängigkeit) beim nächsten Neuberechnen wieder überschrieben würde. Nur Aufgaben haben eine editierbare Zelle – Meilensteine hängen über task_id an genau einer Aufgabe, Projekte und Teilprojekte sind berechnete Strukturebenen. Notation und Prüfungen stecken isoliert testbar in src/gantt/dependencyNotation.js (src/gantt/dependencyNotation.test.js)
  • Zeitabstand (Lag/Lead) je Aufgaben-Abhängigkeit (Issue #124): neue Spalte lag_days in nc_gantt_task_deps (Version1032; der Name vermeidet das seit MySQL 8.0 reservierte Wort LAG und nennt die Einheit). Der Abstand zählt in Arbeitstagen über den Ressourcenkalender wie die übrige Terminberechnung, wirkt bei allen vier Abhängigkeitstypen auf deren Ankertag und darf negativ sein (Lead: der Nachfolger beginnt früher). Bestandsabhängigkeiten bekommen 0 und verhalten sich unverändert. TaskService::update() weist eine gesendete Vorgängerliste jetzt vollständig zurück (invalid_dependency, dependency_cycle), bevor die bisherigen Abhängigkeiten dafür gelöscht werden, statt Vorgänger aus fremden Projekten oder Zyklen zu speichern – letztere hätten die topologische Sortierung in recalculateProject() stillschweigend Aufgaben überspringen lassen

  • Konfigurierbare Spalten in der Tabellenansicht, Teilschritt E von #117 (Issue #122): ein Menü in der Tabellenkopfzeile blendet Spalten ein und aus, die Reihenfolge lässt sich per Drag & Drop der Kopfzellen ändern (barrierefreie Alternative: Strg+Pfeil links/rechts auf der fokussierten Kopfzelle), die Breiten per Ziehen am Spaltentrenner (Doppelklick setzt auf die Standardbreite zurück). Zusätzlich zu den bisherigen Spalten (Titel, Start, Ende, Fortschritt) stehen ID, Dauer (Kalendertage inkl. Start- und Endtag, bei Meilensteinen 0), Status (Issue #78, nur Aufgaben/Teilprojekte) und Ressource (Issue #103, zugeordnete Ressourcennamen einer Aufgabe) bereit – alle vier als reine Anzeigespalten, die das Inline-Editing und die Tastaturnavigation daher überspringen. Auswahl, Reihenfolge und Breiten werden pro Nutzer über den in Teilschritt B angelegten UI-Preferences-Endpunkt persistiert (neuer Key gantt_table_columns, kein Schema- oder Endpunktwechsel nötig); eine gespeicherte Konfiguration wird beim Laden validiert, sodass entfernte Spalten oder unplausible Breiten die Ansicht nicht beschädigen können. Die Tastaturnavigation folgt jetzt der sichtbaren Spaltenreihenfolge statt einer festen Liste. Reicht die Tabellenbreite für die gewählten Spalten nicht aus, scrollen Kopfzeile und Zeilen gemeinsam horizontal; die Obergrenze des Splitters steigt dafür von 560 auf 900 px. Die Spaltenlogik steckt isoliert testbar in src/gantt/tableColumns.js (src/gantt/tableColumns.test.js). Ein Sortieren der Zeilen nach Spaltenwert ist bewusst nicht enthalten – es kollidiert mit der Hierarchie Projekt → Teilprojekt → Aufgabe und der sort_order, die das Chart für dieselbe Zeilenreihenfolge braucht

  • Tastatur-Navigation in der Tabellenansicht, Teilschritt D von #117 (Issue #121): Tab/Shift+Tab springen zur nächsten bzw. vorigen editierbaren Zelle (überspringt Zeilen ohne Schreibrecht, springt am Zeilenende zur nächsten/vorigen Zeile), Enter speichert und springt in dieselbe Spalte der nächsten Zeile, Esc bricht ab und stellt den vorigen Wert wieder her. Pfeiltasten bewegen die Zellauswahl, wenn gerade keine Zelle im Editiermodus ist (innerhalb einer Zelle bleiben sie für Cursor-/Wertänderung nutzbar). Die reine Traversierungslogik steckt isoliert testbar in src/gantt/tableNav.js (src/gantt/tableNav.test.js). Die ausgewählte Zelle bekommt einen sichtbaren Fokusrahmen und wird per scrollIntoView() ins Sichtfeld gescrollt – über den bestehenden bidirektionalen Scroll-Sync (Issue #119) folgt das Gantt-Chart automatisch mit. Neue ARIA-Rollen (grid/row/columnheader/gridcell, aria-readonly je Zelle) machen die Tabelle für Screenreader als Grid erkennbar
  • Read-only Tabellenansicht links vom Zeitstrahl, Teilschritt B von #117 (Issue #119): neue Komponente GanttTable.vue zeigt dieselben Zeilen wie das Chart (Titel mit Einrückung nach depth, Startdatum, Enddatum, Fortschritt), gespeist aus buildRowModel()/buildTableRows() (siehe Teilschritt A, Issue #118) – Reihenfolge, Filter-Dimming, kritischer Pfad und Projektfarben bleiben so exakt konsistent zum Chart. Zeilenhöhen sind fest an frappe-gantt gekoppelt (40 px = bar_height 22 + padding 18, Kopfzeile 52 px = header_height). Ein Toolbar-Button blendet die Tabelle ein/aus, ein Splitter macht die Breite verstellbar (160–560 px), vertikales Scrollen ist bidirektional mit .nc-gantt-container synchronisiert. Sichtbarkeit und Breite werden pro Nutzer über einen neuen, generischen UI-Preferences-Endpunkt (GET/PUT /api/v1/ui-preferences, SettingsController über IConfig::get/setUserValue() als JSON-Blob) persistiert, damit Teilschritt E (Editieren) das Schema nur noch um weitere Keys erweitern muss, ohne Endpunkt oder Speicherformat anzupassen. Rein lesend – Editieren folgt in Teilschritt C
  • Projekt-Export/-Import (JSON, format_version: 2) kann jetzt optional die von den Aufgaben referenzierten Ressourcen inkl. Gruppe, Wochenmodell, Arbeitskalender+Feiertagen und Tageskapazitäten mitnehmen, statt wie bisher nur die rohe resource_id in eine Zieldatei zu schreiben, die dort ungeprüft übernommen wurde – auf einem System ohne identische Ressourcen-IDs ging die Zuordnung damit verloren oder landete schlimmstenfalls bei einer fremden Ressource (Issue #115). Vor dem JSON-Download entscheidet ein Options-Dialog, was mitexportiert wird (Ressourcen / Verfügbarkeiten / Kalender, gestaffelt); vor dem JSON-Import zeigt ein passender Dialog, wie viele Ressourcen die Datei enthält, und lässt auswählen, was übernommen wird sowie ob bei Namensgleichheit die vorhandene Ressource wiederverwendet (Default) oder immer neu angelegt wird. Referenzen werden ausschließlich über eine Mapping-Tabelle aufgelöst und nie roh durchgereicht; nicht auflösbare Referenzen werden verworfen (Regressionsschutz zu #112/#113). Neu angelegte Ressourcen/Gruppen/Kapazitäten gehören stets dem importierenden Nutzer. Wochenmodelle/Kalender/Feiertage sind weiterhin admin-only anlegbar – ohne Adminrechte werden nur namensgleiche vorhandene Einträge gemappt. CSV bleibt unverändert aufgabenzentriert mit Ressourcen nur als ID-Referenz; der volle Ressourcenumfang ist JSON-only. Alt-Exporte ohne resources-Schlüssel importieren unverändert wie bisher, ohne Options-Dialog
  • Inline-Editing der Basisfelder in der Tabellenansicht, Teilschritt C von #117 (Issue #120): die Zellen für Titel, Start, Ende und Fortschritt sind direkt editierbar und aktualisieren die Balken im Chart sofort. Gespeichert wird über die bestehenden Store-Actions (updateTask/updateSubproject/updateMilestone/updateProject) inkl. Terminpropagation und dem vorhandenen Undo-Toast für Aufgaben (analog Drag & Drop im Chart); ein lokaler pendingEdits-Overlay zeigt die Änderung optimistisch an und rollt sie bei einem fehlgeschlagenen Request mit Fehlermeldung zurück. Die Schreibrechte löst tableRows.js je Feld über store.canEditProject() auf (titleEditable/startEditable/endEditable/progressEditable) – Projekte und Teilprojekte bleiben bei Datum und Fortschritt read-only, da diese Werte aus den Aufgaben berechnet werden. Geprüft wird: Titel nicht leer, Enddatum nicht vor Startdatum (Meilensteine ausgenommen, deren Start- und Endspalte dasselbe milestone_date zeigen) und Fortschritt 0–100. Im Simulationsmodus rufen Datumsänderungen an Aufgaben simulateTaskChange() statt einer echten Speicherung auf
  • Ressourcen-Überbuchungswarnung (Issue #82): die Kapazitätsplanung war bisher reine Datenerfassung ohne Abgleich mit dem tatsächlich verplanten Aufwand. Der neue Endpunkt GET /api/v1/resources/{id}/utilization?from=&to=&group= (ResourceUtilizationService/ResourceUtilizationController) stellt den geplanten Aufwand aus den Aufgaben-Ressourcen-Zuordnungen – gleichmäßig auf die Arbeitstage der Aufgabe verteilt, über WorkCalendarService mit ressourcenspezifischen Arbeitstagen und Feiertagen – der erfassten Tageskapazität gegenüber. Mehrfach-Ressourcen-Zuordnungen (Issue #103) samt Alt-Aufgaben werden berücksichtigt, ohne gepflegte Tageskapazität gilt ein Fallback von 1 PT/Tag. Im Ressourcen-Raster färben sich die Zellen nach Auslastungsgrad (> 90 % gelb, > 100 % rot) und der Tooltip zeigt geplante Stunden gegen Kapazität samt Aufschlüsselung nach Projekt und Aufgabe; das Workload-Chart im Gantt markiert überbuchte Perioden jetzt rot. Übersetzungen für alle sechs Sprachen ergänzt
  • Nachgetragen – beide Funktionen wurden bereits mit 1.4.2 ausgeliefert, waren dort aber nicht im Changelog dokumentiert:
  • Einheitliches Statusmodell für Teilprojekte und Aufgaben (Issue #78): neues Feld status (not_started/in_progress/blocked/done) auf nc_gantt_subprojects und nc_gantt_tasks ergänzend zum numerischen Fortschritt, Bestandsdaten leitet Migration Version1030 daraus ab. Validierung per StatusValidator (Whitelist analog ColorValidator), Status-Dropdown in den Detailformularen, blockierte Balken im Gantt schraffiert und mit Warnsymbol, blocked_count je Projekt im Rollup sowie ein Badge in der Projektliste
  • Mehrere Ressourcen mit individuellem Aufwand je Aufgabe (Issue #103): bis zu vier Ressourcen je Aufgabe mit eigener Aufwandsschätzung in der Einheit der Ressource (neue Tabelle nc_gantt_task_resources, Migration Version1029, Backfill aus den vorhandenen resource_id/work-Werten). Das manuelle Work-Feld entfällt: TaskService::resolveResources() summiert die Einzel-Aufwände zur Gesamt-Work (1 PT = 8 h) und übernimmt die erste Zuordnung als primäre resource_id, die weiterhin für Arbeitskalender und CPM maßgeblich ist. Ressourcen-Filter sowie CSV-/JSON-Export und -Import berücksichtigen alle Zuordnungen statt nur der primären

Changed

  • buildFrappeRows() (src/gantt/frappeRows.js) mischte bisher Datenmodell und frappe-gantt-Formatierung: die Einrückungstiefe steckte als Leerzeichen im name, Zustände (Dimming, kritischer Pfad, Summenbalken) hingen in custom_class – eine geplante Tabellenansicht neben dem Gantt-Chart (Issue #117) hätte dieselbe Logik duplizieren müssen (Teilschritt A, Issue #118). Neue Funktion buildRowModel() (src/gantt/rowModel.js) liefert dieselbe Zeilenliste jetzt als neutrales Modell (id, type, entityId, depth, title, start, end, progress, parentId, editable, matched, critical), resolveMilestoneDate() wandert mit ins Modell. buildFrappeRows() ist nur noch ein dünner Adapter darüber, der name/custom_class/dependencies wie bisher erzeugt. Reines Refactoring ohne sichtbare Änderung, per Fixture-Vergleich gegen die alte Implementierung geprüft; neue Unit-Tests (src/gantt/rowModel.test.js, npm run test:unit, via neu eingeführtem vitest) decken Hierarchie, Einrückungstiefe, Meilenstein-Datumsauflösung und Filter-Dimming ab

Fixed

  • Abhängigkeitsfortschreibung bei Aufgaben lieferte für alle vier Abhängigkeitstypen (EA/AA/EE/AE) einen um einen Arbeitstag zu langen Nachfolger-Termin – bei EA zusätzlich mit falschem Anschlusstag, da der Nachfolger auf dem Endtag des Vorgängers statt am folgenden Arbeitstag begann und dadurch mit dem Vorgänger überlappte (Issue #114). Ursache war eine Off-by-one-Vermischung zwischen der inklusiven Arbeitstagszählung und der exklusiven Datumsfortschreibung in TaskService::applyConstraint()/computeMultiConstraint(). Betroffen sind sowohl das direkte Setzen einer Abhängigkeit als auch die Terminpropagation nach Drag & Drop und die What-if-Simulation. Verhaltensänderung: Bestehende Pläne mit EA-Abhängigkeiten verschieben sich beim nächsten Speichern/Neuberechnen um einen Arbeitstag nach vorne (auf den korrekten Anschlusstag); bei AA/EE/AE verkürzt sich der zuvor fälschlich verlängerte Nachfolger-Zeitraum um einen Arbeitstag, die ursprüngliche Dauer bleibt jetzt garantiert erhalten. Abhängige Meilensteine (Dauer 0) sind unverändert korrekt und von diesem Fix nicht betroffen

  • Security: jeder Nutzer mit Schreibrecht auf ein eigenes Projekt konnte jede in der Instanz existierende resource_id einer eigenen Aufgabe zuordnen – unabhängig vom Eigentümer und ohne jede Share-Beziehung (Cross-Tenant-IDOR, Issue #112). TaskService::resolveResources() lud die vom Client gesendeten IDs bisher nur über ResourceMapper::findByIds(), also als reine Existenzprüfung ohne Berechtigungscheck; da die IDs fortlaufende Integer sind, waren sie trivial zu erraten. Der Listen- wie der Legacy-Einzelfeld-Pfad in create()/update() filtern referenzierte IDs jetzt über ResourceMapper::findVisibleToUser(), nicht sichtbare IDs werden wie unbekannte verworfen (Liste) bzw. nicht übernommen (Einzelfeld, der bestehende Wert bleibt erhalten). Regressionstests decken beide Pfade für create() und update() ab

  • Feiertage ließen sich nicht in mehreren Regionskalendern führen: POST /admin/holidays lief in einen HTTP 500, sobald dasselbe Datum in einem zweiten Kalender angelegt wurde, weil der Unique-Index aus Version1006 allein auf holiday_date lag, obwohl Feiertage seit Version1013 über calendar_id kalenderbezogen sind. Version1031 verschiebt den Index auf (holiday_date, calendar_id); die Eindeutigkeit globaler Feiertage sichert weiterhin der Upsert in HolidayAdminController::create(). Betroffen war neben der Feiertagsverwaltung auch der Kalender-Import aus einem Projekt-Export, wo der Fehler still verschluckt wurde. Zweitens war GET /admin/holidays admin-only, während work-calendars und work-schedules lesend für alle offen sind – für normale Nutzer blieb der Store deshalb immer leer (keine Feiertage im Projekt-Export, keine Markierung arbeitsfreier Tage im Ressourcen-Raster); der Lese-Endpunkt zieht nach dem Muster der übrigen Stammdaten auf /api/v1/holidays um (NoAdminRequired), Anlegen und Löschen bleiben unter /api/v1/admin/holidays. Ergänzend werden Feiertage jetzt mit ihrem Kalender gelöscht (WorkCalendarController::destroy(), HolidayMapper::deleteByCalendar()) und Bestandswaisen bereits gelöschter Kalender in Version1031 aufgeräumt – über verweisende Ressourcen haben sie bis dahin weiter als arbeitsfreie Tage gewirkt
  • Die Installation der Tabelle nc_gantt_task_resources scheiterte auf Instanzen, deren Tabellenpräfix die von Nextcloud aus Oracle-Kompatibilität erzwungene 30-Zeichen-Grenze für Bezeichner reizt: der von Doctrine automatisch vergebene Primary-Key-Name (Tabellenname + _pkey) wird mit Präfix oc_ zu lang (Issue #132). Die Primärschlüssel-Definition bekommt – analog zu den bestehenden Migrationen für nc_gantt_resource_capacity und nc_gantt_resource_groups – einen expliziten kurzen Namen
  • Der absolut positionierte Toggle-Button der Nextcloud-App-Navigation ragte in den App-Content hinein und verdeckte dort den Titel des ersten Eintrags in der Projekt-Rollup-Leiste (Issue #134); der linke Innenabstand reserviert jetzt Platz für den Button
  • Ein leerer Balken in Höhe der Kopfzeile erschien oberhalb des App-Inhalts, weil der CSS-Reset für den Nextcloud-Content-Wrapper an main#content.app-nc_gantt gekoppelt war (Issue #126). Ändert sich das Markup zwischen Nextcloud-Versionen (Klasse oder Tag), griff der Reset nicht mehr und der von NcContent (#content-vue) selbst gesetzte margin-top: var(--header-height) addierte sich zum Rand des Cores. Der Reset hängt jetzt robust an #content mit !important, ohne Abhängigkeit vom app-spezifischen Klassennamen
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegO8Qmtdwg8LOBKr5G+mq4co374hsqqSN6oCIRV9atUx0KqogPxisJiIExkPoSIbu
e4EeIaUzg2alhZsKBLWOwR3Ab3fNm5vvd2DzJn6Hei7A70woz9m/LXkVlcx3vALW
bwolClUX1XCZOBVo/kSS5AOVwak3/huzT1iF9xH9BnpdzVuOjZ563LYwSayfb2Gq
SkWNfLZlKlDkTTF2PIszrNmaa89GmyqTbraxSP16/9nYdIj8ZU0zrhC3HtpihgAT
wya2lwnMf6I73HU0xxgT/+ITTmurx4eHwKWH80P4tjg/Y9mnHODIpAslbW3351mc
qdTHXIBW1MB1YGCQ+JPt11v1X2ccn29kEF9d1nkizKZ1+Du+LJQhPhwHiQxPWSwAUlTlQ4tj1lbe3+FJ1jsVTCqWbDUN02EoV7ZwtN15/HLJfM6w4drUi8ThJ1aLG3v6
cIwMJjYgWeepxR2gv4YgBf4u7txodm5bAWuzySk4rfAG1dBxmie/bfNnWmQ9LrYy
spwKc73gsPhg03ll+jp0b27fJfNF4IpOysz3i+r1ExVfeNIWWO91JPatO2pXZh4O
q0rczM8wYvctvcEkKQIL67HoIrKw4GVIXfqM9Oc8e/Ay7d/NPL+BjpCO6zvoFBY+
SRmjit15YmGW4EQwweQfKXuZ2YeySj+7OjhKHpKfvfg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.2
Release Details
UpdatedAug. 20, 2026, 4:41 a.m.
Changelog

Changed

  • release.sh fragt die Versionsnummer jetzt interaktiv ab, statt sie nur aus appinfo/info.xml zu lesen: zu Beginn werden die aktuelle Version und das letzte Release-Tag angezeigt und es wird gefragt, ob eine neue Nummer vergeben werden soll (Vorschlag: nächster Patch-Level). Die Eingabe wird auf das Format X.Y.Z, auf einen echten Versionssprung und auf ein noch nicht vergebenes Tag geprüft und dann nach appinfo/info.xml, package.json und package-lock.json geschrieben. Fehlt ein Changelog-Abschnitt zur neuen Version, warnt das Skript; anschließend bietet es an, den Bump als chore: Version X.Y.Z zu committen — nötig, weil der Tag-Name den Commit-Hash enthält. Für nicht-interaktive Läufe gibt es --version=X.Y.Z und --no-bump; ohne TTY wird nie gefragt, make release bleibt also unverändert nutzbar
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturepoVHZxteU3AR2cpRRV98jNfv4j7rWnLEd3kbpn5Dp4T1ubAI+0QlRf9jxi0BaYz1
+1dg+3Yt25uN6Slp1FHiw7kyqt5HJ60XgnOWo+Zt0YBHSnWqLDhF8zxPEnQCkF+v
nUvscpcoQY2bRiIBZhVWVgeAD592uvOA4gfRDrUyLVDKjOrzBu3ScbNvCqTqE9IC
vs8qZkRLIcaD6tKoSLT2pZqmwBoCdClvEw5D5v40WSvLq232BZnXpQ4ogG3QvJdL
mYO8HlPZKVOxANp0MGnWle4IFRMbMt22Ebpkjc18Q9Q4AIv/Sd3zTsc+uMONTM95
s0GXvLSm95o0UCFZ1CL7fCEq7NwlprjVrTeWGmlJNVjHJuIyRKHEz5DR12s0lAGm
/rKGWjoVP+Wvk3upRizR/5y2fpWoiwuDqCtz9gQN6+HHYLaqLvYqsAJ/SxmtLtP7
8B2rK61ClKowz4phbTZdrCaF8w3IWnFFBTFquIVFuN2jUoHvdCMtd0910sLAMY5M
Xs9nk0FVsP23zKXlQpOojq7ouCQagRhsnBJDLxWRR+NIq582f7SDgcrkBqxlGJX4
/Ql9V+2iFx61QANCTjdJIO3LbvbK60vGos+ency0GfvSDbPovDAVyJzt8ZUh5BNw
UXQ4XOHJZAagAr36rKttk1DQbZZ/2bUEq231c96m83g=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.1
Release Details
UpdatedAug. 17, 2026, 7:43 p.m.
Changelog

Fixed

  • Die Meilenstein-Raute hatte eine fest verdrahtete Farbe (#fbbf24) statt der Projektfarbe zu folgen, obwohl Balken für Aufgaben und Teilprojekte bereits korrekt eingefärbt wurden (Issue #102). Zusätzlich wurden die separaten – und wirkungslosen – Farbwähler bei Teilprojekt und Aufgabe entfernt, da deren Anzeige ohnehin immer der Projektfarbe folgt
  • „Meilenstein hinzufügen" im Drei-Punkte-Menü der Projektliste nutzte zwei aufeinanderfolgende window.prompt()-Dialoge (Name, dann Datum als Freitext) statt des regulären Detail-Dialogs (Issue #101). Der Klick emittiert jetzt add-milestone analog zu „Aufgabe hinzufügen" (Issue #91) und öffnet denselben Meilenstein-Dialog wie beim Bearbeiten, mit vorbelegtem Projekt
  • Im festen Datumsmodus des Aufgaben-Detaildialogs konnte ein Enddatum vor dem Startdatum eingegeben und gespeichert werden (Issue #100). Jetzt zieht ein über das Enddatum hinaus verschobenes Startdatum das Enddatum mit; ein zu frühes Enddatum erzeugt ein rotes Fehler-Icon am Feld, hält den Fokus dort und deaktiviert den Speichern-Button, bis ein gültiges Datum eingetragen ist (dateField() in fields.js mit neuer setError()-Methode)
  • Eine Aufgabe mit Abhängigkeit behielt trotzdem die manuell eingegebenen Start-/Enddatumsfelder, die beim Speichern durch die Abhängigkeitsberechnung überschrieben wurden – für Nutzer nicht nachvollziehbar (Issue #99). Der Aufgaben-Detaildialog hat jetzt denselben Fixed-date/Dependency-Umschalter wie Meilensteine (Issue #90): im Dependency-Modus ersetzt eine Dauer (Tage) die Datumsfelder, die Abhängigkeit bestimmt den Termin. Serverseitig wendet TaskService::create() neu hinzugefügte Abhängigkeiten sofort an (bisher nur update()), damit auch neu angelegte Aufgaben keinen die Abhängigkeit ignorierenden Platzhaltertermin behalten
  • Der Speichern-Button des Detail-Dialogs blieb nach dem Hinzufügen einer Abhängigkeit deaktiviert: die Gültigkeitsprüfung hing allein am input-Event, das bei einem <select> vor dem change-Event feuert – zu diesem Zeitpunkt war die Abhängigkeit noch nicht in der Liste. detailModal.js wertet jetzt zusätzlich change aus, der Abhängigkeits-Editor (fields.js) meldet jede Mutation (Hinzufügen, Entfernen, Typwechsel) und der Fixed-date/Dependency-Umschalter in taskForm.js den Moduswechsel über ein eigenes bubbelndes change-Event

Added

  • CONTRIBUTE.md und setup-dev.sh für neue Mitwirkende (Issue #98): Projekt-Leitlinien (FOSS, Datensparsamkeit, Security), VIBE Coding ausdrücklich erlaubt, sowie der komplette lokale Dev-Setup-Weg (Docker-only, Nextcloud + MariaDB unter localhost:8088, optionales E2E-Setup, Git-/Bot-Auth). setup-dev.sh automatisiert diese Schritte, konfigurierbar über --workdir/--no-e2e/--no-docker-install
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturetYcUvVtDEQrAakG32EQF1atSOmz91/8XudCUh60A9r/AxMv+AA5AX2rIvNYDkEn0
OuTmXA6Iz0DoVkdij11MPDhfLaUy0mXeLbwB5fj/xF/cFYcNeCFGKD3k2jyRwOTo
2n1sEp0r8sn0fitHzLfhcB/By0VJkZyhaO/NctP01XfcSQM7lGEOeY/uagtGaDTI
JnPLoQTHo0nUOv4y7jUMZuurEgBOc4yeUxTKv4A/m642hvNQIS0q6K9ON41JHKIO
UtI7DbDWSboVhct1Go3zoj5XcaAGG9ZiwSkJDt/BXT5tWH4SOnSn7mys0j1F6Y0g
vIJWjz6S2RlekwIrIxgyaeEVBOeQ4OM1ilJGWKXDyYJM/0IBxqnqRXDkAFlOopDo
xezlcFdWTkYL5OBKMFJhKuTy7dEQ9hxx5Nz4+vLbNV8gDJZBI+5yI5m8W+809WIH
iKUpIda1ShOblvYH6Ek5AzBdTmoFQ6T4f7iWT7j1i4uUP2DsgehjGVl8yDIT/wyx
nV9t7koXapu1WeXrunSa++mXsi3lceNpthdz5/cUEOe9yWvc1rrulRhiWzePga8H
zjVQYGAIPvwYkWa47U6Zh7HBsQaS+9yQ8NDnD49SS4jUWTGlwNqfM+q9fZ16/G9d
7l4chZUPPbBv+xWfdxmFw+MC5zdkAq27isHToTqnm+w=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.0
Release Details
UpdatedAug. 13, 2026, 6:41 a.m.
Changelog

Added

  • Meilensteine werden jetzt immer direkt unter ihrem übergeordneten Objekt angezeigt statt gesammelt am Projektende (Issue #90): Meilensteine lassen sich neu auch einer Aufgabe zuordnen (bisher nur Projekt/Teilprojekt, Spalte task_id war bereits vorhanden, es fehlte nur die UI), das Auswahlfeld im Detail-Dialog heißt entsprechend „Übergeordnetes Objekt" statt „Teilprojekt". Zusätzlich können Meilensteine ihr Datum jetzt statt eines fixen Werts aus einer Abhängigkeit zum übergeordneten Objekt beziehen (EA/AA/EE/AE, analog zu Aufgaben-Abhängigkeiten; neue Spalte dependency_type, Migration Version1028) – da ein Meilenstein keine Dauer hat, bestimmen EA/EE das Ende und AA/AE den Start des Zielobjekts. Die Berechnung übernimmt der neue MilestoneDateService (serverseitig live bei jedem Abruf sowie im ICS-Feed neu aufgelöst, nicht persistiert propagiert) sowie clientseitig in frappeRows.js für sofortige Reaktivität ohne Neuladen
  • Undo für Drag-&-Drop-Verschiebungen inkl. Propagationskette (Issue #85, Umsetzung der einstufigen Empfehlung: letzte eigene Operation, kein Redo, zunächst nur Drag & Drop): Migration Version1027Date20260810020000 ergänzt nc_gantt_change_events um op_id (UUID) – alle Change-Events einer zusammenhängenden Operation (Drag + gesamte Propagationskette B→C→D) erhalten dieselbe op_id, erzeugt in TaskService::update()/propagateDates() und über ChangeEventService::log() protokolliert (dieses Change-Tracking war bisher tote Scaffolding-Logik ohne Aufrufer). Neuer Endpoint POST /api/v1/changes/{opId}/revert (ChangeEventController::revert, ChangeEventService::revertOperation) wendet alle Change-Events der Operation invers an, in einer DB-Transaktion mit Rechteprüfung über AccessService::canEditOrOwn(). Konfliktschutz: Revert wird mit HTTP 409 abgelehnt, sobald der aktuelle Feldwert nicht mehr dem protokollierten new_value entspricht (jemand hat zwischenzeitlich erneut geändert); der Revert selbst erzeugt wiederum neue Change-Events, die Audit-Historie bleibt lückenlos. Frontend: nach jedem gespeicherten Drag zeigt GanttChart.vue einen Toast „N Termine verschoben — Rückgängig" (showUndo aus @nextcloud/dialogs, 10 s sichtbar) sowie ein globales Ctrl+Z für die letzte eigene Operation der Session (stores/projects.js: lastOperation, revertOperation(), undoLastOperation())
  • Kalender-Export als abonnierbarer ICS-Feed pro Projekt (Issue #83, Variante A aus dem Umsetzungsvorschlag): neue öffentliche Route GET /apps/nc_gantt/projects/{id}/calendar.ics?token=… (regulärer, unauthentifizierter CalendarFeedController, damit externe Clients wie Thunderbird, Nextcloud Kalender oder mobile Apps ohne Nextcloud-Login abonnieren können) liefert Meilensteine als ganztägige VEVENTs (◆-Präfix), den aus den Aufgaben abgeleiteten Projektzeitraum sowie optional Teilprojekt-Zeiträume (Schalter „Auch Teilprojekt-Zeiträume einschließen", standardmäßig aus, um den Feed bei großen Projekten nicht zu überladen). Zugriff erfolgt über einen pro Projekt generierten, 256-Bit-zufälligen Token in der URL (CalendarFeedService, neue Spalte calendar_token auf nc_gantt_projects, Migration Version1026), der über einen neuen „Kalender-Feed"-Bereich im Freigabe-Dialog angezeigt, kopiert und jederzeit widerrufen/neu generiert werden kann (CalendarTokenController, nur für Projekt-Admins/-Eigentümer). ICS-Generierung ohne neue Abhängigkeit, RFC-5545-konform (Escaping, Zeilenfaltung)
  • What-if-Simulationsmodus für Terminverschiebungen (Issue #84): neuer Endpunkt POST /api/v1/projects/{id}/simulate führt Terminpropagation und CPM rein im Speicher aus (kein DB-Schreibzugriff) – TaskService::simulate() nutzt die neu extrahierte, stateless propagateDatesCore()-Kernlogik, die auch der persistierende update()-Pfad verwendet, CriticalPathService::computeFromTasks() erlaubt CPM auf einem hypothetisch veränderten Task-Datensatz. Frontend: „Simulation"-Toggle in der Gantt-Toolbar mit Banner inkl. Projektende-Delta in Arbeitstagen, Geisterbalken für die Ist-Termine (analog Baseline-Overlay) sowie Verwerfen/Übernehmen. Drag & Drop ruft im Simulationsmodus simulate() statt einer echten Speicherung auf. Szenarien bleiben bewusst flüchtig (kein Speichern benannter Szenarien), „Übernehmen" wendet die Änderungen als einzelne Task-Updates an, Simulation läuft pro Projekt
  • Neuer Eintrag „Aufgabe hinzufügen" im Drei-Punkte-Menü der Projektliste (Issue #91): legt wie der „Neu"-Button eine Aufgabe an, belegt dabei aber das gewählte Projekt direkt vor, sodass der Projekt-Auswahlschritt in der Maske entfällt
  • Such-/Filterleiste für Projekte, Teilprojekte und Aufgaben (Issue #81, Stufe 1: rein clientseitig auf Basis der bereits geladenen Daten aktivierter Projekte): Freitextsuche im Titel, Risiko, „überfällig" (Ende < heute und Fortschritt < 100 %), „über Budget" (Ist-Kosten > geplantes Budget), Ressource und „ohne Deck-Verknüpfung". Neuer Pinia-Store stores/filters.js (Zustand pro Nutzer in localStorage persistiert) und reine Matching-Logik in gantt/filterMatch.js. Nicht passende Balken werden in GanttChart.vue gedimmt statt ausgeblendet (Abhängigkeitspfeile und Zeitkontext bleiben lesbar), Projekte/Teilprojekte gelten als Treffer, sobald ein Nachfahre passt. Dieselbe Filterleiste (components/FilterBar.vue) dimmt in ProjectList.vue nicht passende Projekte; für nicht aktivierte Projekte sind nur Titel-Treffer zuverlässig auswertbar, da deren Aufgaben/Teilprojekte nicht geladen sind

Changed

  • Freigabe für Nextcloud 34 (Issue #94): max-version in appinfo/info.xml von 33 auf 34 angehoben, unterstützter Bereich damit NC 28–34. Verifiziert gegen Nextcloud 34.0.2 mit PHP 8.5 und MariaDB 11.4 (148 Unit-Tests, 39 E2E-Tests)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerdYQI+Semnk0cWtKqUZinVDHk0KpxyqB5Ox4NBL9pyf5nrpK8xZpe9lBxc6zhDfW
8adLfVIwQtX+bI7S1O05xm0hwrUHQQ2ii6ADD8mlAxnZjdhKSCAhZ0NcaqT7Em0X
nD7yRP2B8zAAQMJNjzeBsLbzveNrXMoGzFrvm/jFxskUIUS01ykfWEg2C5+5/IAf
7jBAM8x2Qj92b8zyBFpLjfJQw9aTE/trtCsmZXQFmkJjGS0anrJc9dXKfqnHxknX
8jmLOv4Yq4Qtk3UF35HTiIGcA2W0UKfVyFb4XcrIMeSmLXXcagJWjeH5nB18UUNn
JyCgE/qY4v/SnrNQU92mIdMOQGuvYg7xbaz226lgNdwM2HuM9U8nc2juqJxAO+Wp
xs85yl82qIviZ5YY39HK371MZ/ufMsaVsVy3Nfn3PjqqkzGLmtAL6KJDcKHZWdDw
ugEPwO61DxnERZsNGeFpXfzCxjMpZc/qwUIfGw85idmU2vdedtrxHBkDGoqu1IP7
xZOO8uSYrzbXM6g1gs3WPBOY51CmZLs5pIQTN1tVl5ifGw4uOuzDBUCKiR5oA2MI
352HbA7wwWFgwoN9W5t4MRTowmaYpM8AB+nh9f1A5q9S8SelfMpXOqGew9VZb2jd
AuiJK/LEPjRgs5s3/FfC4Sb1MSsXDr1N1jziaF4MomM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 33

Gantt Projects 2.3.0
Release Details
UpdatedSept. 30, 2026, 11:45 a.m.
Changelog

Added

  • Aufgaben und Teilprojekte per Drag & Drop umsortieren (Issue #299): Vor jeder Aufgabe und jedem Teilprojekt steht in der Tabelle ein Ziehgriff (☰, ganz links, unabhängig von der Einrückung); eine blaue Linie zeigt die Einfügeposition. Verschoben wird nur innerhalb desselben Elternobjekts und unter Zeilen desselben Typs, sonst zeigt der Mauszeiger „nicht erlaubt“ und ein Ablegen bricht ab. Neuer Endpunkt PUT /api/v1/projects/{projectId}/row-order (kind, id, before_id) mit RowOrderService: vergibt die sort_order aller Geschwister fortlaufend neu (auch bei gleichen Werten), Schreibrecht nötig. Bewusst nicht Teil: Griff für Meilensteine (sie haben keine sort_order, stehen vor den Aufgaben ihres Elternobjekts) und Projekte, Umhängen in ein anderes Teilprojekt; der Zeiger ist der Standard-„nicht erlaubt“-Zeiger des Browsers. Doku (FEATURES.md, de/en) ergänzt. Neue PHPUnit-Tests (RowOrderServiceTest), Vitest-Tests (rowDrag.test.js) und der E2E-Test row-reorder.spec.js

Fixed

  • Klick auf das Deck-Abweichungssymbol öffnet den Dialog wieder beim Bereich „Abweichungen von Deck“ (jetzt im Reiter „Verbindungen“): focusDeckSection wurde im Dialog-Kontext nicht mehr weitergereicht. E2E-Tests für die Reiter angepasst
  • Tabelle und Gantt-Chart scrollen vertikal zuverlässig gemeinsam (Issue #292): Tabellenzeilen und Balken standen schon ganz oben um 9 px versetzt (Tabellenkopf 52 px, erste Chart-Zeile erst bei header_height + padding / 2 = 61 px), und weil nur das Chart den Ressourcen-Bereich enthält, konnte es weiter scrollen als die Tabelle – die Tabelle blieb am Ende hängen, das Chart zeigte einen anderen Ausschnitt oder sprang zurück. Der Tabellenkopf ist jetzt so hoch wie der Chart-Kopf bis zur ersten Zeile, ein Platzhalter unter den Tabellenzeilen gleicht Ressourcen-Bereich und Scrollleisten aus (per ResizeObserver laufend nachgeführt), sodass beide Seiten denselben maximalen scrollTop haben. Der Sync überträgt nur noch bei tatsächlicher Abweichung statt über ein synchron zurückgesetztes syncing-Flag, das asynchrone Scroll-Events nicht abfangen konnte. Geometrie in src/gantt/tableScrollSync.js (Vitest), neuer E2E-Test table-scroll-sync.spec.js

Added

  • Deck-Karten schon beim Anlegen einer Aufgabe verknüpfen oder neu anlegen (Issue #306): Der Deck-Bereich im Reiter „Verbindungen“ war bei neuen Aufgaben gesperrt („Speichere zuerst die Aufgabe“). Jetzt lassen sich Karten auswählen und „+“ vormerken; beim Speichern verknüpft bzw. legt die App sie an (deckCardsSection().commit()). Schlägt eine Deck-Aktion fehl, bleibt die Aufgabe gespeichert und ein Hinweis nennt die Zahl der Fehlschläge. Board und Kartenliste des Pickers folgen dem gewählten Projekt und übergeordneten Objekt (auch beim projektlosen Anlegen über „Aufgabe“). Der Termin-Abgleichsdialog entfällt dabei; Abweichungen zeigt danach der Bereich „Abweichungen von Deck“
  • Reiter „Anhänge“ in allen Detaildialogen (Projekt, Teilprojekt, Aufgabe, Meilenstein; vor „Verbindungen“) mit den Bereichen Dateien und Notizen: beliebig viele Dateien (Nextcloud-Dateiauswahldialog mit eigenen Ordnern) und Notizen (Auswahl aus der App Notes im Stil der Deck-Kartenauswahl) verknüpfen. Der Server teilt jede angehängte Datei automatisch mit allen Personen, die Zugriff auf das Projekt haben (Betrachter lesen, Bearbeiter/Admins dürfen ändern) und gleicht die Freigaben nach jeder Änderung an Anhängen, Projektfreigaben und Löschungen ab; entfernt werden nur die von der App angelegten Freigaben (nc_gantt_attach_shares). Die Freigabe gilt je Datei, nicht je Objekt: Sie bleibt bestehen, solange die Datei an mindestens einem Objekt der App hängt. Doku ergänzt in FEATURES.md (Abschnitt „Anhänge: Dateien und Notizen“, de/en), READMEs, HOWTO (Schritt 10) und Store-Beschreibung (info.xml). Neue Tabellen nc_gantt_attachments und nc_gantt_attach_shares (Migration Version1044), Endpunkte GET/POST /attachments, DELETE /attachments/{id}, AttachmentService. Die Dateiauswahl hängt sich in den modalen Detaildialog ein (sonst läge sie unsichtbar dahinter), und nachgeladene Bundle-Teile (Dateiauswahl, Datumsformate) nutzen den tatsächlichen Installationsort der App als Adresse (src/publicPath.js; die Vorgabe /apps/nc_gantt/js/ griff z. B. unter custom_apps nicht). Neue PHPUnit- (AttachmentServiceTest) und Vitest-Tests (attachments.test.js) sowie der E2E-Test attachments.spec.js
  • Aufgaben können von Meilensteinen abhängen (Issue #308): Vorgänger m-<id> neben t-<id> (keine Migration), in allen vier Typen mit positivem und negativem Abstand. Ein Meilenstein zählt als Punkt mit Start = Ende = seinem Datum; ein fester verschiebt sich nie und zieht seine abhängigen Aufgaben samt Nachfolgern mit, ein abgeleiteter folgt weiter seinem Ziel und gibt dessen Verschiebung an die Aufgaben dahinter weiter. TaskSchedulingService (Prüfung, Termine, Propagation, Neuberechnung) kennt Meilensteine als Knoten; die Zyklenprüfung berücksichtigt die Kanten Zielaufgaben → abgeleiteter Meilenstein und läuft auch beim Speichern eines Meilensteins (dependency_cycle); der Dialog sperrt solche Meilensteine als Vorgänger bzw. Ziele mit Hinweis. Im kritischen Pfad sind Meilensteine Knoten: ein fester ersetzt das Projektende als spätestes Ende für die Aufgaben, die über eine Abhängigkeit vor ihm liegen; ein negativer Puffer verhindert das Speichern mit dem Hinweis auf die betroffene Aufgabe (negative_float:<Titel>, bereits negativer Puffer wird nicht erneut gemeldet). Beim Löschen eines Meilensteins (auch über Aufgabe/Teilprojekt) entfallen die Abhängigkeiten auf ihn, ProjectDuplicationService und JSON-Import/-Export münzen m-<id> auf die neuen Meilensteine um. Oberfläche: Auswahl M<id> <Titel> im Abhängigkeits-Editor, Kurznotation M3EA-2d in der Vorgänger-Spalte, Pfeile von und zu Meilenstein-Balken, Nummerierung M<id> im Diagramm, Glossar und FEATURES.md (de/en) nachgezogen. Bekannte Vereinfachungen: die Pufferrechnung zählt den Meilenstein-Termin in Arbeitstagen des globalen Kalenders ab dem frühesten Aufgabenbeginn, und ein Meilenstein-Termin verschiebt keine Deck-Karten (Deck-Abgleich läuft nur bei Änderungen an Aufgaben). Neue PHPUnit-Tests (TaskSchedulingMilestoneTest, CriticalPathMilestoneTest, ergänzt: Meilenstein-, Duplizierungs-, Lösch- und Datums-Tests), Vitest-Tests und der E2E-Test milestone-dependency.spec.js
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureS4LN+K40y1DKrwQo02FOHkiugxuFllfcAsVp7IBoUthbKYnNdZtSmzBH+ZxY0V8C
U954MnYEijFZD14jMuW2wkvMomspSCRGfCfEHytRCLfNwmsK9AV5UfxutjQBe6Y/
ATjKYnhOEPrE2iXLOZ+P73VbC8GBPY6W21P1APsy0Af+XUoFycIdTnLJUU5QJgWY
+K/N7nUfXyqVA17kCO+/Zhw6w5wl5O7ZIMezWbNn3ApvOeIC8AE/CwJ/JccrQgE9
F4A1RTqFZcK0hCrluGelanyva6ygwAmYPoJtkR+CeiJr7vFdSSq6HJe0lG0y9SAm
RNFnDuEzt6uFqSl9EqK6ykVp7ns0k6npvld3gSqGN0Frq4dwGjxSjcRkpPGAYT7M
IH6YJDVlNbVdF/wRJtEmOvO1duI4uzBhjX3PIFX1LBoFR0p5OsKuMIAXGEupbBi4
BV9Zu5578h5VQzZt32gFnsQcGVsvAn2nayCCbcQx/WUHSW57NmBdktF4l+7tme/p
vL+hzncZ2sdDbhDrrDq2G167QwkAJgpGJFQR+34YpGrY3DDhj767+Q/bMHwm4zmk
VYm+g4MAf49P2CKyqwVB1E9SGe3MgTK2rDRnibhVIlgVEDvih1Uf/E5wigpBJerf
E1DvLXTLEtteko53AjIpT/GWpiiD9md4QpCUUdfGMnU=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.2.0
Release Details
UpdatedSept. 28, 2026, 7:36 a.m.
Changelog

Added

  • Implizite Kopfzahl je Aufgabe (Issue #252, Schritt C aus #249; R11): Der Aufgabendialog nennt unter jeder Zuordnung mit Aufwand im Klartext, wie viele Personen gleichzeitig nötig sind („Erfordert im Schnitt 2,0 × Java-Entwickler gleichzeitig“) — Aufwand ÷ Arbeitstage des Balkens ÷ Tageskapazität einer vollen Kraft, je Zuordnung, live bei Änderung von Aufwand, Start, Ende oder Dauer. Gezählt wird nach dem globalen Kalender (Mo–Fr abzüglich globaler Feiertage) für Rollen und Personen gleich; Personenkalender zählen nicht (Entscheidung in #252, daher entfällt das Akzeptanzkriterium „Teilzeitkalender“). Ab der Warnschwelle (globale App-Einstellung headcount_warn_threshold, Standard 3,0, gültig 0,5–100, im Admin-Bereich unter „Warnen ab“; GET/PUT /settings liefern und setzen headcount_threshold, ein PUT ohne den Wert lässt ihn unverändert, Initial State für das erste Rendern) erscheint dort zusätzlich die Konsequenz („mit weniger Personen dauert die Aufgabe entsprechend länger“), und die Tabelle zeigt vor dem Titel ein Personen-Symbol mit Tooltip — ein Hinweis, keine Blockade. Aufgaben ohne Aufwand oder ohne Arbeitstage und Zuordnungen in anderen Einheiten als PT/h zeigen nichts. Die Rechnung liegt als reine Funktion in src/utils/headcount.js, die Schwelle in WorkTimeService. Nicht Teil: Hinweis am Balken im Diagramm, Prüfung der tatsächlichen Verfügbarkeit (Schritt B, #251). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue Vitest-Tests (headcount.test.js, tableRows.test.js, settings.test.js) und PHPUnit-Tests (WorkTimeServiceTest)
  • Kapazitätsprüfung über Rollen hinweg: Engpass-Teilmenge statt Einzelrolle, Alarm über ein gleitendes 5-Tage-Fenster (Issue #251, Schritt B aus #249; R6, R10): Rollenkapazität ist nicht additiv — bei zwei Java-Entwicklern, von denen einer zusätzlich IT-Architekt ist, besteht „2× Java + 1× Architekt“ die Einzelprüfung (2 ≤ 2, 1 ≤ 1), ist aber nicht zu leisten. Neuer UI- und datenbankfreier Rechenkern: CapacityFlow löst je Fenster ein Flussproblem (Bedarfsknoten = Rollen und direkt zugewiesene Personen im selben Fluss, Kante wo die Person die Rolle ausfüllt, Personenkapazität zur Senke; Dinic in CapacityFlowGraph) und liest die Engpass-Teilmenge aus dem Min-Cut, zerlegt in unabhängige Engpässe samt den Personen mit Mehrfachrolle als Ursache; CapacityWindowCheck legt darüber das gleitende Fenster von 5 Arbeitstagen (fest, nicht konfigurierbar, Feiertage verkürzen es nicht): rot = irgendein Fenster fließt nicht (nicht durch Verschieben lösbar), gelb = ein einzelner Tag fließt nicht, aber alle Fenster um ihn (Tagesspitze, umsortierbar); Tage, an denen niemand des Knotens arbeitet, zählen nicht als Spitze (Teilzeit Mo–Do erzeugt keine Meldung); überlappende Verstöße mit gemeinsamen Rollen werden zu einem Zeitraum zusammengefasst. CapacityCheckService trägt Aufgaben der für den Betrachter sichtbaren Projekte (Lücke bewusst akzeptiert, wie in #181), Rollen und Personenkapazität zusammen — Bedarf auf dem globalen Kalender, Kapazität aus den Personenkalendern (ResourceUtilizationService::personCapacityFn() ist dafür öffentlich), Material ausgeschlossen, Rolle ohne Personen meldet rot — und liefert GET /api/v1/capacity-check (Rollen, direkte Personen, Zeitraum, Fehlbetrag in Stunden, Ursache, betroffene Aufgaben). Die Tabelle zeigt vor dem Titel eine kleine Person in Gelb oder Rot mit Tooltip („Java-Entwickler + IT-Architekt zusammen überbucht (KW 42). Fehlbetrag: 5 PT. Ursache: Mehrfachrollen von …“), keine Benachrichtigungen; der Store capacityCheck lädt bei Änderungen an Aufgaben, Rollen, Kapazität oder Feiertagen neu. Ressourcen-Raster und Workload-Chart färben nach demselben Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder über 90 % im Fenster, Tooltip mit Reserve), die Rollenzeile übernimmt das Urteil der Prüfung. Nicht Teil: Sequenzierungsvorschlag (#282), Kostenminimierung der Zuordnung (entfällt), Projektkalender (#284). Bekannte Grenzen (in FEATURES.md): nur sichtbare Projekte, Aufgaben kürzer als das Fenster werden verdünnt (fängt die implizite Kopfzahl aus #252 auf). Neue Texte in allen Übersetzungen; FEATURES.md/HOWTO.md (de/en) und die Integrationsmatrix sind nachgezogen, der Hinweis auf die naive Rechnung aus #250 ist entfernt. Neue PHPUnit-Tests (CapacityFlowTest, CapacityWindowCheckTest, CapacityCheckServiceTest; u. a. Prüffall, Kette über drei Rollen, Bedarf exakt auf Kapazität, Rolle ohne Personen, Laufzeit 30 Personen/10 Rollen/1 Jahr), Vitest-Tests (capacityCheck.test.js, workloadModel.test.js, tableRows.test.js) und der E2E-Test capacity-check.spec.js

  • Projekt duplizieren bzw. als Vorlage für ein neues Projekt verwenden (Issue #270): Die Aktion Duplizieren im Menü des Projekts in der Navigation (auch mit reinem Leserecht) und im Projektdialog legt ein neues Projekt der ausführenden Person an. Der Dialog fragt Titel („Kopie von …“), neuen Starttermin (Vorschlag: bisheriger Projektstart = frühester Aufgabenbeginn) und die Ressourcenzuordnungen (alle / nur Rollen / keine). Alle Termine — Aufgaben, Meilensteine, Planungszeiträume von Projekt und Teilprojekten — verschieben sich um die Differenz in Arbeitstagen des globalen Kalenders, ein Start auf einem freien Tag rückt auf den nächsten Arbeitstag, bei unverändertem Start bleiben die Termine unberührt. Übernommen werden Teilprojekte samt Hierarchie, Aufgaben, Abhängigkeiten mit Typ und Abstand, Meilensteine mit Zuordnung und Abhängigkeitstyp (abgeleitete folgen dem kopierten Termin), Zuordnungen samt Aufwand (nicht sichtbare Ressourcen entfallen, der Aufwand wird bei weggelassenen Zuordnungen neu gebildet), Farben, Beschreibungen und Projektregeln; zurückgesetzt werden Fortschritt (Modus „Deck“ wird „manuell“), Status, Ist-Kosten, Baselines, Änderungsprotokoll, Freigaben, ICS-Token, Deck-Board, Deck-Listen und Kartenverknüpfungen (Deck-Abgleich „Aus“). Neuer ProjectDuplicationService legt alles in einer Transaktion über die Mapper an (kein Änderungsprotokoll, keine Benachrichtigungen, kein Deck-Zugriff); GET /projects/{id}/duplicate liefert Start und Umfang für den Dialog, POST /projects/{id}/duplicate (title, start_date, resources) die Kopie (201). Nicht Teil: eigene Vorlagenverwaltung (Projekte als „Vorlage“ markieren, Galerie beim Anlegen). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue PHPUnit-Tests (ProjectDuplicationServiceTest), Vitest-Tests (duplicate.test.js, ioDialogs.duplicate.test.js) und der E2E-Test project-duplicate.spec.js

Changed

  • Rollenzeile im Ressourcen-Raster ohne „naiv additiv“ (#251): GET /resources/{id}/utilization liefert für Rollen nur noch capacity_derived; capacity_naive und multi_role_member_ids entfallen, ebenso der Hinweis „zu optimistisch“ im Raster (ersetzt durch den Verweis auf die Prüfung über alle Rollen). Das Workload-Chart unter dem Diagramm färbt Perioden nicht mehr je Periode rot, sondern nach dem 5-Tage-Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder knapp)

Fixed

  • Server-Zeitzone der Arbeitszeiten aus default_timezone statt immer UTC (Issue #294): Nextcloud setzt die PHP-Zeitzone in lib/base.php fest auf UTC, deshalb zeigte der Admin-Bereich unter „Arbeitszeiten“ immer „UTC (+00:00)“ und Arbeitsbeginn/-ende wurden als UTC ausgelegt — neue Termine im Detaildialog/Import und die Deck-Fälligkeiten lagen dadurch 1–2 Stunden daneben. Neuer ServerTimeZoneService liest default_timezone aus der config.php (fehlt der Wert oder ist er ungültig, bleibt es bei UTC); GET/PUT /settings liefern dessen Namen und aktuellen Versatz, der Deck-Terminabgleich legt work_start/work_end in dieser Zeitzone aus. Neue PHPUnit-Tests (ServerTimeZoneServiceTest, DeckDateSyncServiceTest)
  • Schrift im Balken passte nicht zur Balkenfarbe (Issue #287): .bar-label stand in theme.css fest auf Weiß, unabhängig von Balkenfarbe und Theme — auf hellen und mittleren Farben, darunter die Standardfarbe #0082c9 (Kontrast 4,2:1), war der Titel im Balken im hellen Theme schwer lesbar, während der Titel außerhalb des Balkens (.big) bereits die Textfarbe des Themes bekam. injectProjectColors() setzt jetzt je Farbklasse die Schriftfarbe, die nach WCAG den höheren Kontrast liefert (labelTextColor() in colors.js: Weiß oder #111111). Die Balkenfarbe ist in beiden Themes dieselbe, die Schrift deshalb ebenfalls. Bei Projekt- und Teilprojektzeilen (Summenbalken, nur eine auf 18 % abgeschwächte Klammerfläche darunter) war die vorgesehene Textfarbe des Themes wirkungslos: .gantt-summary-bar .bar-label und .gantt .bar-label hatten gleiche Spezifität und beide !important, die spätere gewann — der Titel blieb im hellen Theme weiß. Die Regel ist jetzt spezifischer (.gantt .bar-wrapper.gantt-summary-bar .bar-label); Meilenstein und Titel neben dem Balken (.big) behalten ihre Regeln. Neue Vitest-Tests in colors.test.js
  • JSON-Export ohne Datenbank-IDs der Abhängigkeiten (Issue #289): Je Abhängigkeit enthält der Export nur noch predecessor_id, dependency_type und lag_days; id und task_id (Datenbank-IDs des Quellsystems, im Zielsystem bedeutungslos) entfallen. Der Import bleibt kompatibel, ältere Exporte mit diesen Feldern werden weiter gelesen. Der E2E-Test zum Round-Trip aus #269 war seit seiner Einführung rot (erwartete exakt drei Felder je Abhängigkeit und übersah den Import-Optionsdialog) und ist korrigiert.

Security

  • Benutzersuche beachtet die Enumeration-Einstellungen von Nextcloud und leakt keine E-Mail-Adressen mehr (Issue #285): GET /users/search rief IUserManager::search() direkt auf und lieferte jedem angemeldeten Nutzer zu jedem Treffer UID, Anzeigenamen und E-Mail-Adresse — mit kurzen Suchbegriffen ließen sich so alle Konten der Instanz auslesen, auch wenn die Admins das Auflisten abgeschaltet oder auf Gruppen/Telefonbuch beschränkt hatten. Die Suche läuft jetzt über die Collaborators-API (ISearch::search() mit IShare::TYPE_USER, ohne Lookup-Server) wie der Teilen-Dialog im Kern und beachtet damit shareapi_allow_share_dialog_user_enumeration, shareapi_restrict_user_enumeration_to_group/_to_phone/_full_match* und shareapi_only_share_with_group_members; exakte Treffer stehen vorn. subname stammt ausschließlich aus deren Ergebnis, die App lädt keine E-Mail-Adresse mehr selbst nach. Das eigene Konto (nur mit include_self, #204) ergänzt der Controller selbst, wenn Benutzer-ID, Anzeigename oder E-Mail den Suchbegriff enthalten, weil die Collaborators-API es nie liefert. Neue PHPUnit-Tests in UserControllerTest (u. a. keine Fremdkonten bei abgeschalteter Enumeration)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureesXfyynRSXx6Sc+EbEb+n8jhv9KklmBcayRslKKo0dpavjZ1x7CE/oUKrOppOVm7
YsCst1sFoTz/sPZ4qveDz/erE0STxAi2UMyiHHUglJ3JmcCJKtF5byki45BAm02V
PV7dn+EYQP8bd4jWy96mQa66eGzuuAsgR/mA7S1UC9SvcKq6UF6A0LJpJqMyInaR
ycEg6H78WtrIOKAuB5WyaEo20IRsXIechdfyl39Xz1bl1IextE3DPey0OaV6CzfF
6YQeMho+ja2iqh6JKfAzq2zKgTDBLo/bcgk8Gy/b4djBX5KES2w96x535UG7a6JM
fYEg7QdRUqOGgXoCTRSwIAGtqX8bq9sc7058+5NqO9xYOiGDzfv6dJ94x6+KXpd4
eN1N1P0+MfL9TKP27NQLsy46vXPU36w4cSijoMmxyrYGUbVVudrb8XANUvv/jZc+
d/yNvPzhYRDHvoGJ3gn5exgVpQW2M7qQMOBVDYDkROo1UqKv1k3da9s14VMHtX6y
PyhIdtt6Qi7Lu0cMykFH0rYQYoe84Ot3Xb547ldyNCCDAeta4vTczDbA5dTrdstM
HCrbbSl4iF/Lu7zeVhlxnWnQyuC5HjUg0vVuikxkEyYScruNJw4prkXD04XHOMyG
1HOBUrG2tiFl60B8XGyibo5+fc1ioQFwe2iIjdLOW3M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.1.0
Release Details
UpdatedSept. 25, 2026, 6:24 a.m.
Changelog

Changed

  • Detaildialog in abgesetzte Bereiche gegliedert (Issue #263): Die Formulare für Projekt, Teilprojekt, Aufgabe und Meilenstein fassen ihre Felder jetzt in Bereiche mit Rahmen, farbig hinterlegtem Kopf, Symbol und Überschrift zusammen (z. B. Grundinformationen, Darstellung, Terminplanung, Budget und Kosten, Ressourcen, Risikostufe, Fortschritt und Status, Deck, Erinnerungen, Baselines, Export und Import). Die Bereiche heben sich vor einem dezent getönten Dialoghintergrund ab, der Dialogkopf zeigt ein Symbol der Objektart, Feldbeschriftungen stehen in normaler statt Versal-Schrift, und die Risikostufen tragen ihre Ampelfarbe auch ungewählt. Die Farbtöne sind aus den Nextcloud-Farbvariablen gemischt und funktionieren im hellen wie im dunklen Design. Die Trennlinien zwischen den einzelnen Feldern aus dem ersten Anlauf entfallen dafür. Symbole kommen aus @mdi/js, das jetzt direkte Abhängigkeit ist
  • Neue App-Store-Screenshots zur überarbeiteten Oberfläche: Die zehn verlinkten Bilder in appinfo/info.xml sind neu aufgenommen (Projektstruktur, Abhängigkeiten im gegliederten Detaildialog, Tabelle, kritischer Pfad, Ressourcen, Kalender und Feiertage, Abweichungsbericht, Kennzahlen/EVM, Simulation, Mehrprojektansicht mit Filter). Sie liegen jetzt unter screenshots/appstore/ statt screenshots/usecases/, damit der App Store nicht auf zwischengespeicherte alte Bilder unter den bisherigen URLs zurückgreift; die übrigen, veralteten Bilder aus screenshots/ sind entfernt. Das Showcase-Skript scrollt dafür in UC 2 den Abschnitt „Abhängigkeiten“ des Dialogs ins Bild und in UC 5 das Ressourcenraster ganz nach links
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureZQ2bK6Wi8ifFLdOZU9fKEitqGU2/V3SC8sQadWpViyGbPp9TFzXz/m+6VOgxRM0K
88XrfFLwkUN5CrQ8Mzj8rzUUKAp0ANWZVMC43yWzABpywSQ+FosMPTPoPyS7dymn
eKXUnbqfSE3yebLbOtpQaK9VIMVXfvIpMQuIaSnnSX/Mi3ofY4oN3eyBy9NT1uH+
icAEBU4EFZbByJe3tF+IdKP81OX0kFydqeuiSgWJph7yRxP+ucBioO+BsjGB1ZGh
2kiCYjlYoZPbgc4WSoWfe3WGY/cUiqr/6sPEkdWfYHrG6c/tUuNhM1E1pmBXlwIK
S3IzZVklRWFkFcTHdYLAg5QKWTN2+m0sCpPkMkZQn2NYXQNQ5oAPKNoDTMBmW+zG
B72pYfEAs5TQFGcn1M6k6e4IdVjnLtM6AdmyeMdu9y8hKUH8en1RT/MVbXZlFN7O
fZoXKZSwpdocdqMZOqaT3qoUTFUZbmOBzcQQ3xeYQ4H2UvIp4PlyPkXeiFEHKY93
hZWcmtTHthlbPGhtnAutXA7zCzruiXisJXCNaf7ZM+xSbtW3GeLqRMwkLImyxydK
1C+BekGHiJaWI32PyqZH1mwR+V1qgqLLcD3yb+gKOBOUSBW4pdMnNpmvnO7y8KBg
jw8MQf3e5b0JVVoksbULqjqvCOm2ug60NIZYCfp59BA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.1
Release Details
UpdatedSept. 24, 2026, 7:49 a.m.
Changelog

Changed

  • App-Store-Beschreibung und README neu gefasst: Zusammenfassung und Beschreibung (info.xml, Deutsch und Englisch) stellen jetzt das professionelle Projektmanagement mit der Deck-Integration als Alleinstellungsmerkmal in den Vordergrund, die Funktionsliste ist kompakter, und ein eigener Abschnitt erklärt die Datensouveränität (alle Daten bleiben auf der eigenen Instanz). Das englische README und docs/de/README.md übernehmen den Wortlaut. In den übrigen Sprachen entfällt an zwei Emojis der Variation Selector
  • Toolbar-Schalter einheitlich: Die segmentierten Schalter der Toolbars (Zeitskala und Darstellung im Diagramm, Zeitskala im Ressourcenraster) und der Ansichts-Umschalter der App teilen sich jetzt dieselbe Optik: eckige Segmente ohne eigenen Rand und ohne eigene Rundung, sodass Hover- und Aktiv-Flächen bis zur Kante reichen und kein Randstreifen mehr stehen bleibt; die Rundung kommt allein vom Container. Die gemeinsame Regel liegt in der neuen segmented-switch.css statt in mehreren nahezu identischen Blöcken

Fixed

  • Leerzustand im Diagramm nicht mittig: Der Leerzustand des Diagramms (GanttChart.vue) steht jetzt mittig, und sein Icon ist optisch zentriert (der viewBox war seitlich versetzt)
  • Stylelint-Fehler in ResourceGrid.vue: fehlende Leerzeile vor einer Regel (rule-empty-line-before), die den JS-Job der CI rot färbte
  • JSON-Import brach bei Abhängigkeiten ab, Meilensteine fehlten im Export (Issue #269): importJson() übergab predecessor_id-Werte unverändert an store.createTask() – die alten IDs des Quellprojekts bestanden den IDOR-Schutz aus #190 nicht (Import in ein anderes Projekt/eine andere Instanz brach mit invalid_dependency ab und hinterließ ein halb angelegtes Projekt) bzw. zeigten beim Import ins selbe Projekt auf die falschen (alten) Aufgaben. Aufgaben werden jetzt zunächst ohne Abhängigkeiten angelegt und diese erst danach mit auf die importierten Aufgaben umgemünzten predecessor_id-Werten nachgetragen – unabhängig davon, in welcher Reihenfolge Vorgänger und Nachfolger in der Datei stehen. exportJson() nimmt außerdem Meilensteine samt ihrer Zuordnung zu Teilprojekt/Aufgabe und ihres dependency_type mit; der Import schreibt diese Bezüge ebenfalls auf die neuen IDs um
  • Balken mit Abhängigkeiten ziehen schrieb doppelt nach Deck (Issue #267): frappe-gantt zieht beim Verschieben/Resizen eines Balkens dessen Abhängigkeits-Balken in der Vorschau um denselben Pixel-Versatz mit und feuerte on_date_change für jeden von ihnen einzeln – der Server berechnet die Kette beim Speichern des tatsächlich gezogenen Balkens aber bereits selbst (propagated). Der zweite, nur aus der Vorschau abgeleitete Save überschrieb die korrekt propagierten Termine mit dem naiven Pixel-Versatz und erzeugte bei aktiver Deck-Anbindung eine doppelte Rückfrage bzw. Erfolgsmeldung. GanttChart.vue verarbeitet on_date_change jetzt nur noch für den Balken, auf dem die Ziehbewegung tatsächlich begann
  • Picker für eine zweite Deck-Karte konnte dauerhaft deaktiviert bleiben (Issue #267): Der Deck-Bereich im Aufgabendialog rendert einmal sofort (Ladezustand) und ein zweites Mal, sobald die Verknüpfungen geladen sind – traf eine Auswahl im Picker genau in dieses Fenster, ersetzte der zweite Render Select und „Add“-Button durch neue, wieder deaktivierte Knoten und die Auswahl ging verloren. Der Bereich rendert jetzt erst, nachdem die Verknüpfungen geladen sind
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureblE0iU/iUXgQIlpljnjDbdufoMyB2l1S9iGos0PQ3DDhQU9wFB13iopDY4YfCjrW
Y2bVw//uxXAO1S4wzURGPTAEgXGI3NM4A2JaHsxMREiJoU6ANjCSOnt45f9yvgNC
se02XpRKzbTtIUXTIGsH2KUe3grZ7hHg95x4riQhhrLhsDESZMZANpO60O52/AMM
gb4yZiCTbUUr1IzM/IowbHWJcghd6RG8KG+EwONJpRqJVjZhcqhNgcM9k/nKyCxY
tw6o7MIlwQmOUzh6kZXxl8p4auRpiHcGrruiWzJwEb6WSmH/XFTdGwP/ZV8lNnoj
KrR2e+yaKiPMR7UQLrtE8yUe1LGmghJejF/cDO3uLEOGD87gZfHjqXAJOrHI+zyx
FcT65clu8MUG2Tk1PQammIkYty0sPaHKwB/woLOlEBWX8LqrAK9ylOD7NHTdArhJ
TibyfWC9UI1f/iyBYu0xCEq11T23DtrFcpun7ELMNUiQ2z260/PykzEK6WtEhRVd
hgLyzf3YoFSqRNOM5IfewWlGt9YdyM4J3bLIYP01GY4MFV90y9M7/RcaehOG3U/5
DFnQswUXRr9ivk4S7dFt7J0WqltzLDqbIwtXi5f6TEqahrYD1Y46W4JXArdhOK+P
FE4VR6KQrLzKQ9PQhTbrfqCLj0tewWnxc8g6OgIGGk4=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.0
Release Details
UpdatedSept. 23, 2026, 9 a.m.
Changelog

Major-Release mit zwei Schwerpunkten. Die Einzelheiten stehen in den Einträgen darunter.

  • Komplett überarbeitete Deck-Schnittstelle (Zielbild #234, Issues #209–#211, #235–#241, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit geworden. Pro Projekt gibt es eine Stufe für den Deck-Abgleich: Aus (Standard), Nur lesen, Schreiben mit Rückfrage oder Schreiben ohne Rückfrage. Eine Aufgabe kann mehrere Karten haben, und Karten lassen sich direkt aus der Aufgabe anlegen. Termine werden mit Konfliktregel nach Deck geschrieben. Fortschritt und Status kommen sofort aus Deck. Abweichungen zwischen Plan und Deck erscheinen im Überblick, lassen sich annehmen und filtern. Beim Löschen können verknüpfte Karten archiviert werden, doppelte Erinnerungen werden unterdrückt, und ein occ-Befehl gleicht einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich am Verhalten nichts; Bestandsprojekte starten höchstens auf Nur lesen.
  • Neuer Umgang mit Planungsdaten für Projekte und Teilprojekte (Issue #229): Bisher ergaben sich Start und Ende von Projekten und Teilprojekten ausschließlich aus ihren Aufgaben. Jetzt lässt sich zusätzlich ein eigener Planungszeitraum setzen, im Detaildialog oder direkt in der Tabelle. Gezeigt wird er statt des berechneten Zeitraums, auch bei noch leeren Teilprojekten. Laufen die Aufgaben über den Rahmen hinaus, markiert ihn ein rotes Ausrufezeichen. Ampel, kritischer Pfad, Filter und Rollups rechnen weiter mit dem tatsächlich verplanten Inhalt. Ohne gesetzten Planungszeitraum bleibt alles wie bisher.
  • Warum eine neue Hauptversion: Die API-Felder haben sich teilweise geändert. deck_card_id an Aufgaben ist nur noch ein berechnetes Lesefeld, Karten werden über /tasks/{id}/deck-links verknüpft (siehe „Changed“). Löschen über DELETE liefert jetzt einen Körper mit deck. Beim Update laufen die Migrationen Version1039–Version1043 automatisch; vorhandene Kartenverknüpfungen werden übernommen.

Added

  • Detail-Dialog per Klick aus der Tabelle öffnen (Issue #264): Bislang öffnete der Detail-Dialog nur über einen Klick auf den Balken im Gantt-Diagramm, was bei größeren Projekten erst Scrollen zum richtigen Monat verlangte. Die Titel-Spalte der Tabelle hat jetzt einen geteilten Klickbereich: Bei Aufgaben und Meilensteinen öffnet ein Klick auf den Platz vor dem Bezeichnungs-Rahmen (Zusammenklapp-Pfeil, Farbpunkt, Abweichungssymbol/-zähler) den Detail-Dialog, während der Rahmen selbst wie bisher die Inline-Bearbeitung der Bezeichnung öffnet. Bei Projekten und Teilprojekten öffnet der gesamte bisherige Rahmen den Detail-Dialog; die Inline-Bearbeitung der Bezeichnung entfällt dort, da beide Objekttypen ohnehin nur das Titelfeld inline editierbar hatten. Der Rahmen ist jetzt als sichtbarer Rand um die Bezeichnung dargestellt (GanttTable.vue)
  • Deck-Anbindung abgeschlossen (Stufen 1–3 aus #234, Issues #209–#211, #235–#240, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit mit Deck geworden. Je Projekt lässt sich eine von vier Stufen wählen — Aus, Nur lesen (Fortschritt und Status kommen sofort aus Deck), Schreiben mit Rückfrage und Schreiben ohne Rückfrage (beide erlauben zusätzlich, Karten aus einer Aufgabe anzulegen und ihre Termine sowie einmalig die Zuständigen nachzuführen). Wo Deck und Plan auseinanderlaufen — Karte gelöscht/archiviert, Termin oder Zuständigkeit von Hand geändert, Karte überfällig, ziehende Person ohne die geforderte Rolle —, zeigt die App das an, statt es aufzulösen; angenommen wird je Aufgabe. Doppelte Erinnerungen zwischen Gantt und Deck werden unterdrückt, ein occ-Befehl gleicht bei Bedarf alles einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich nichts am bisherigen Verhalten. Die folgenden Einträge beschreiben die einzelnen Schritte im Detail; Endstand siehe FEATURES.md Kapitel 16 und die Integrationsmatrix, HOWTO.md und README („Optionale Abhängigkeit zu Deck“)
  • Deck-Fundament: Adapter, Verknüpfungstabelle (1:n) und Opt-in-Stufen je Projekt (Issue #209, Stufe 1, Schritte 1–2 aus #234): Die Migration Version1039 legt nc_gantt_deck_links an (eine Zeile je Karte mit Aufgabe, Board, zuletzt gesehenem Kartenstand, zuletzt geschriebenen Terminen, angenommenem Stand je Abweichungsart und Zeitpunkt des Abgleichs; eindeutiger Index auf der Karten-ID) und übernimmt vorhandene deck_card_id-Werte. Die Spalte deck_card_id bleibt lesbar, ist veraltet und wird bis zum Rückbau mit #237 aus der Tabelle befüllt. Das Löschen einer Aufgabe entfernt ihre Verknüpfungen, die Karte bleibt. Neue API: GET/POST /tasks/{id}/deck-links, DELETE /tasks/{id}/deck-links/{cardId}, GET /deck-links/occupied?project_id=|board_id= (belegte Karten → Aufgabe)
  • DeckGateway als einzige Andockstelle zu Deck (Deck-Services erst im Aufruf aus dem Container, isAvailable(), supportsStartDate(), readCard(), permissions(), Ergebnisobjekt DeckResult statt Exception, nur mit angemeldetem Benutzer, Schreibmethoden nur in den beiden Schreibstufen — ihre Umsetzung folgt mit #210, #211, #237, #238)
  • Opt-in-Stufe „Deck-Abgleich“ je Projekt mit vier Stufen (Aus — Standard, Nur lesen, Schreiben mit Rückfrage, Schreiben ohne Rückfrage); serverseitig beantwortet DeckSyncService getrennt „darf lesen“, „darf schreiben“, „muss fragen“. Projektdialog mit Vorschlag „Nur lesen“ beim Verknüpfen eines Boards, Hinweis beim Umschalten auf eine Schreibstufe und ausgegraut mit sichtbarem Hinweistext ohne Deck. Bestandsprojekte mit Board und Aufgabe im Fortschrittsmodus „aus Deck“ starten auf Nur lesen, alle übrigen auf Aus; nie auf einer Schreibstufe. Der clientseitige Abgleich aus #221 prüft die Stufe
  • Abweichungsdienst (DeckDeviationService, DeckDeviationProvider) mit einheitlichem „Annehmen“ (GET /tasks/{id}/deck-deviations, POST /tasks/{id}/deck-deviations/accept) und Bereich „Abweichungen zu Deck“ im Aufgabendialog; die Abweichungsarten liefern die Folgetickets
  • Vertragstest gegen Deck (make test-deck-contract, tests/deck-contract/, CI-Job Deck-Vertragstest) und README-Abschnitt „Optionale Abhängigkeit zu Deck“; FEATURES.md (de/en) um Stufen und mehrere Karten ergänzt
  • Deck im E2E-Setup optional: setup-dev.sh --no-deck, tests/e2e/helpers/deck.js (Tests ohne Deck überspringen sich selbst), tests/e2e/specs/deck-foundation.spec.js
  • Deck-Rückmeldung per Event-Listener: Fortschritt und Status sofort übernehmen (Issue #235, Stufe 1, Schritt 3 aus #234): DeckCardListener ist für CardCreatedEvent, CardUpdatedEvent und CardDeletedEvent per Klassenname registriert und wertet nur getWebhookSerializable()['card'] aus; er wirft nie. DeckCardSyncService aktualisiert ab der Stufe Nur lesen den zuletzt gesehenen Kartenstand an der Verknüpfung (erledigt, archiviert, gelöscht, Fälligkeit, Start, Zuweisungen, Checkliste) und berechnet für Aufgaben im Modus „aus Deck“ Fortschritt (Mittelwert je Karte: erledigt = 100 %, sonst Checklistenanteil; archiviert zählt als erledigt, gelöscht zählt nicht mit; ohne mitzählende Karte bleibt alles stehen) und Status (done / in_progress / not_started, blocked bleibt). Die Formel DeckProgress in PHP spiegelt cardProgress(); beide prüfen tests/fixtures/deck-progress-cases.json. Neue Abweichungsarten „Karte archiviert“ / „Karte gelöscht“ über den Abweichungsdienst; Übernahmen stehen mit Operations-ID deck:… im Änderungsprotokoll. Der clientseitige Abgleich beim Öffnen bleibt als Fallback und setzt den Status nach denselben Regeln (PUT /tasks/{id}/progress/sync akzeptiert done). Der Aufgabendialog weist im Modus „aus Deck“ neben dem Statusfeld darauf hin, dass der Status Deck folgt. Vertragstest, README-Abschnitt und FEATURES.md (de/en) sind nachgezogen
  • occ nc_gantt:deck:sync --user=<uid> [--project=<id>] [--dry-run]: einmaliger Vollabgleich aus Deck (Issue #236, Stufe 1, Schritt 4 aus #234): Der Befehl setzt den Kontext des Kontos per setVolatileActiveUser, liest jede verknüpfte Karte, die das Konto in Deck sehen darf, und übernimmt sie über dieselbe Logik wie der Listener aus #235 (DeckCardSyncService::applyStates(), gemeinsamer Weg statt zweiter Implementierung). Nur Projekte ab Nur lesen; nach Deck wird nie geschrieben. Ausgabe je Projekt: geprüfte und geänderte Aufgaben, übersprungene Karten (keine Berechtigung / nicht gefunden); Rückgabewert ≠ 0 nur bei Fehlern, auch wenn Deck fehlt. --dry-run speichert nichts. Registriert in appinfo/info.xml (<commands>); FEATURES.md (de/en) und README ergänzt
  • Mehrere Deck-Karten je Aufgabe: Picker, „+“ zum Anlegen und Rolle der Deck-Listen je Projekt (Issue #237, Stufe 2, Schritt 5 aus #234; UC1 „Übergabe“, O1, O2, O8, O14): Der Aufgabendialog hat den Bereich „Deck-Karten“ (für bestehende Aufgaben) mit der Liste der verknüpften Karten (Titel, Liste, erledigt, „In Deck öffnen“, „Lösen“ — das Lösen ändert die Karte nie), dem Picker (Karten an einer anderen Aufgabe sind gesperrt und nennen sie) und „+“, das über den Server und DeckGateway::createCard() eine Karte mit Titel und Beschreibung der Aufgabe samt Rücklink „Geplant in Gantt-Projekte: <URL>“ (Deeplink ?project=&task=), Fälligkeit = Ende, ab Deck 1.18 Start = Start anlegt; „+“ gibt es erst ab „Schreiben mit Rückfrage“ und mit Bearbeitungsrecht am Board (sonst gesperrt mit Hinweis auf Grund und Stufe) und braucht keine zweite Rückfrage. DeckGateway::setDates() ist umgesetzt (Fälligkeit, ab Deck 1.18 Start; Ergebnisobjekt statt Exception; nur in einer Schreibstufe; prüft nach dem Schreiben, ob Deck die Termine übernommen hat) — #210 baut darauf auf. Beim Verknüpfen einer Karte mit abweichendem Datum fragt der Dialog sofort: Plan in Deck durchsetzen (nur in einer Schreibstufe, selbst die Bestätigung), Plan anpassen oder Abweichung stehen lassen; eine datumslose Karte bekommt die Planlage ohne Rückfrage, aber nur ab einer Schreibstufe. Der Stand wird als „zuletzt gesehen“ bzw. „zuletzt von Gantt geschrieben“ an der Verknüpfung gespeichert. Projekteinstellung „Rolle der Deck-Listen“ (deck_list_role: subproject — Standard, storage, none) mit „?“-Hilfe; das Listenfeld am Teilprojekt entfällt bei none, gespeicherte Listen-IDs bleiben; ein Wechsel ändert keine Verknüpfung. Ohne Liste am Teilprojekt nutzt „+“ die Liste des nächsten übergeordneten Teilprojekts, sonst die erste Liste des Boards. Neue API: POST /tasks/{id}/deck-cards, POST /tasks/{id}/deck-links mit optionalem date_action. Der Fortschritt „aus Deck“ im Dialog bildet bei mehreren Karten den Mittelwert. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kapitel 16 und Integrationsmatrix), HOWTO.md und README (Deck-Vertrag) sind nachgezogen; Vertragstest um CardService::create()/update(), StackMapper::findAll() und den Startparameter ergänzt

  • Termine der Aufgabe in die verknüpften Deck-Karten schreiben, mit Konfliktregel und gebündelter Rückfrage (Issue #210, Stufe 2, Schritt 6 aus #234; UC3 „Umplanung“, O4, O8, O9, O11, O12): Ab einer Schreibstufe folgen alle verknüpften Karten den Terminen der Aufgabe — Fälligkeit = Ende zum Arbeitstagsende (Einstellung „Arbeitszeit“), ab Deck 1.18 zusätzlich Start — bei jedem Weg der Terminänderung (Dialog, Tabelle, Ziehen im Diagramm, Propagation über Abhängigkeiten inklusive Karten fremder Aufgaben, „Termine neu berechnen“, Undo). Der neue DeckDateSyncService schreibt synchron im Request nach dem Speichern über DeckGateway::setDates(), fehlertolerant (Deck-Fehler brechen das Speichern nie ab); das Frontend zeigt danach „3 Deck-Karten aktualisiert“, aufklappbar mit Liste, auch fehlgeschlagene Karten. Konfliktregel: Geschrieben wird nur, wenn die Karte noch den zuletzt von Gantt geschriebenen Wert trägt (ohne geschriebenen Wert: wenn sie kein Datum trägt); sonst bleibt sie unangetastet und der Abweichungsdienst zeigt „Fälligkeit/Start in Deck abweichend“ mit Deck- und Plan-Datum. Aktionen im Aufgabendialog: Plan in Deck durchsetzen (POST /tasks/{id}/deck-deviations/enforce, ohne Rückfrage), Plan anpassen (Deck-Datum wird Ende der Aufgabe, gespeichert mit dem Dialog) und Annehmen. Rückfrage in „Schreiben mit Rückfrage“: POST /projects/{id}/deck-preview rechnet die Propagation ohne Speichern; ein Dialog listet einmal gebündelt alle betroffenen Karten mit altem und neuem Wert, Karten fremder Aufgaben hervorgehoben, „Abbrechen“ verwirft die gesamte Änderung, kein „Nicht mehr fragen“; ohne Bestätigung (deck_confirmed) wird nicht geschrieben. Bei „Schreiben ohne Rückfrage“ entfällt der Dialog, bei „Aus“ und „Nur lesen“ wird nichts geschrieben. Rechte: Ohne Bearbeitungsrecht am Board wird die Karte als „Deck-Abgleich ausstehend (keine Berechtigung)“ markiert und beim nächsten Speichern durch eine Person mit Rechten mitgeschrieben (Migration Version1041, Spalte sync_pending); eine in Deck gelöschte oder nicht auffindbare Karte bleibt verknüpft und erscheint als Abweichung „Karte gelöscht“. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kapitel 16) und HOWTO.md sind nachgezogen, der Hinweis „Terminverschiebungen im Gantt ändern derzeit keine Deck-Karten“ ist ersetzt. Neue PHPUnit-Tests (DeckDateSyncServiceTest, TaskDeckSyncTest, DeckDateDeviationProviderTest, DeckDeviationActionServiceTest), Vitest-Tests (deckWrite.test.js, deckWriteDialogs.test.js, deckDeviations.test.js, projects.test.js) und der E2E-Test deck-date-sync.spec.js

  • Verknüpfte Deck-Karten beim Löschen auf Rückfrage archivieren (Issue #238, Stufe 2, Schritt 7 aus #234; O10, O11, O12): Der Löschdialog einer Aufgabe mit Karten fragt „Verknüpfte Deck-Karte(n) ebenfalls archivieren?“ mit der Kartenzahl, Standard „Nein“; Teilprojekt und Projekt stellen eine Sammelrückfrage mit der Gesamtzahl. Gefragt wird nur in den beiden Schreibstufen und nur für Karten an Boards mit Bearbeitungsrecht (DeckGateway::permissions()); für die übrigen sagt der Dialog „N Karten bleiben unverändert“, der Löschdialog ist die Bestätigung (keine zweite Rückfrage). DeckGateway::archive() ist umgesetzt (CardService::archive(), Erfolg erst nach erneutem Lesen); DeckDeletionService plant vor dem Löschen (die Kaskade entfernt die Verknüpfungen) und archiviert synchron danach, fehlertolerant: scheitert Deck, ist die Aufgabe trotzdem gelöscht und die Meldung nennt die nicht archivierten Karten, sonst die archivierten mit Link. Neu: GET /tasks|subprojects|projects/{id}/deck-delete-preview; DELETE nimmt archive_deck_cards an und liefert jetzt deck (archived, failed) statt eines leeren Körpers. Verknüpfung lösen ändert die Karte nie; ein Undo für das Löschen gibt es nicht. PHPUnit mit gemocktem Gateway, Vitest, E2E deck-archive-on-delete.spec.js; Vertragstest um CardService::archive(id) ergänzt; FEATURES.md (de/en) und README ergänzt
  • Rollen als Ressourcentyp: Aufgaben fordern Rollen, Personen tragen die Kapazität (Issue #250, Schritt A aus #249; R2–R5): nc_gantt_resources.type (person — Standard für alle bestehenden Zeilen —, role, material) und die n:m-Tabelle nc_gantt_resource_role (Person ↔ Rolle, eindeutige Kombination; Migration Version1042, dazu nc_gantt_projects.roles_only_assignment). Eine Rolle ist Zuweisungsziel ohne Konto, Arbeitszeitmodell, Kalender und eigene Kapazität (Kapazitätswerte an Rollen werden nicht angelegt); TaskResource und ResourceGroup bleiben unverändert, Rollen sind eine zur Gruppe orthogonale Achse. Rollen werden in der Ressourcenverwaltung über „Neue Rolle“ angelegt, umbenannt und gelöscht und getrennt in der Navigation geführt; die Zuordnung Person ↔ Rolle ist von beiden Seiten pflegbar (role_ids an der Person, person_ids an der Rolle, ohne Admin-Rechte). Löschen einer Rolle mit Aufgaben fragt nach und nennt sie (GET /resources/{id}/usage), Löschen einer Person entfernt ihre Zuordnungen, die Rollen bleiben. Im Aufgabendialog lassen sich Rollen und Personen gemischt mit Aufwand zuordnen, die Auswahl kennzeichnet „(Rolle)“/„(Person)“ (ohne Rollen unverändert); keine Migration bestehender Zuordnungen. Projekteinstellung „Nur Rollenzuweisung zulassen“ (Standard aus) verhindert nur die Neuzuweisung von Personen. ResourceUtilizationService leitet die Kapazität einer Rolle je Tag aus der Summe ihrer Personen ab (Urlaub/Teilzeit senkt alle ihre Rollen) — bewusst naiv additiv, das Ressourcen-Raster weist auf die Mehrfachzählung hin (capacity_naive); Personen- und Rollenzuordnungen laufen durch dieselbe Auswertung, das Workload-Chart summiert Rollen aus ihren Personen. Sichtbarkeit und Rechte folgen den bestehenden Ressourcenregeln. Nicht Teil: Konflikterkennung über Rollen (Schritt B), Kopfzahl (Schritt C), Rollen-Level, Historisierung, Deck-Schnittstelle. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 11 und Integrationsmatrix) und HOWTO.md sind nachgezogen. Neue PHPUnit-Tests (ResourceServiceTest, ResourceUtilizationServiceTest, TaskResourceAssignmentServiceTest, ResourceRolesMigrationTest), Vitest-Tests (resourceType.test.js, resourceDelete.test.js, resources.test.js) und der E2E-Test resource-roles.spec.js
  • Zuständigkeit bei der Kartenanlage übernehmen, danach Abweichungen anzeigen (Issue #211, Stufe 3, Schritt 8 aus #234; O7): „+“ im Aufgabendialog weist der neuen Karte alle Nextcloud-Konten zu, die mit den Ressourcen der Aufgabe verknüpft sind (DeckGateway::assign() über AssignmentService::assignUser(), jedes Konto einzeln). Der Dialog unterscheidet beim Überspringen Person ohne Konto (Lücke: „2 Ressourcen ohne Nextcloud-Konto wurden nicht zugewiesen“), Rolle (#250; sachliche Auskunft, die Karte bleibt absichtlich frei zum Ziehen, nicht als „nicht zugewiesen“ gezählt) und Konto ohne Zugriff auf das Board (übersprungen und genannt; Gantt teilt das Board nicht). Scheitert die Zuweisung, bleibt die Karte angelegt und verknüpft. Danach gibt es keine laufende Synchronisation. Neue Abweichungsart assignees („Zuständige abweichend“, DeckAssigneeDeviationProvider über den Abweichungsdienst aus #209): Menge der zugewiesenen Benutzer der Karte gegen Menge der verknüpften Konten der Ressourcen, in beide Richtungen, mit Annehmen (Hinweis kehrt erst bei erneuter Änderung zurück); Gruppen/Teams und Rollen erzeugen keine Abweichung (Gruppen erscheinen als „auch zugewiesen: …“ an der Karte, bei gemischter Zuordnung zählt nur der Personenanteil). Der Kartenstand trennt jetzt assignees (Benutzer) und assigned_groups. Neue Antwortfelder assignment bei POST /tasks/{id}/deck-cards; Vertragstest und README um AssignmentService::assignUser() und BadRequestException ergänzt. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kap. 16) und HOWTO.md nachgezogen. PHPUnit (DeckAssigneeDeviationProviderTest, DeckAssigneeResolverTest, DeckAssigneesTest, DeckLinkServiceTest, DeckGatewayTest), Vitest (deckAssignees.test.js, deckCards.test.js, deckDeviations.test.js) und E2E deck-assignment.spec.js
  • Abweichungen zwischen Plan und Deck im Überblick anzeigen, annehmen und filtern (Issue #239, Stufe 3, Schritt 9 aus #234; UC4 „Abweichungen erkennen“, Leitprinzip 8): Zwei neue Abweichungsarten über den gemeinsamen Abweichungsdienst aus #209 — overdue (DeckOverdueDeviationProvider: Fälligkeit vor heute, nicht erledigt, nicht archiviert/gelöscht, ausschließlich aus dem zuletzt gesehenen Kartenstand, kein Deck-Zugriff nötig) und role (DeckRoleDeviationProvider, setzt #250 voraus: eine Karte einer Aufgabe mit Rollenzuweisung ist einer Person zugewiesen, die keiner der geforderten Rollen zugeordnet ist — ermittelt über ResourceRoleMapper::findPersonIdsOfRoles(); ohne Rollenzuweisung an der Aufgabe greift die Art nicht, bei gemischter Zuordnung läuft sie unabhängig neben assignees). Neu DeckDeviationService::kindsByTask() fasst die offenen Arten je Aufgabe eines Projekts in einem Rutsch zusammen (ein Durchlauf über alle Verknüpfungen statt eine Anfrage je Aufgabe); TaskService hängt daraus das berechnete Lesefeld deck_deviation_kinds an jede Aufgabe aus GET /projects/{id}/tasks, RollupService liefert zusätzlich deck_deviation_count (Summe über das Projekt) im bestehenden Rollup-Endpunkt. Im Frontend: Warnsymbol an der Aufgabe in Tabelle (GanttTable.vue, echtes <button>) und Diagramm (gantt/overlays/deckDeviationOverlay.js, fokussierbares SVG mit eigenem Klick-/Tastatur-Handler statt des abgeschalteten Bibliotheks-Popups) mit Tooltip/aria-label über die betroffenen Arten (utils/deckDeviationKinds.js); ein Klick öffnet den Aufgabendialog gescrollt und fokussiert beim Bereich „Abweichungen zu Deck“ (detailModal.js, neue Option focusDeckSection, Ereigniskette open-deck-deviations/edit-task-deck). Zähler an Teilprojekt (rekursiv über verschachtelte Teilprojekte) und Projekt in der Tabelle sowie, über den Rollup, in der Projektliste und im Projekt-Rollup-Balken. Neuer Filter „Deck weicht ab“ (filters.js, filterMatch.js, FilterBar.vue) wirkt wie die übrigen Filter auf Tabelle und Diagramm. Für „Karte gelöscht/archiviert“ gibt es im Aufgabendialog zusätzlich die Aktion Verknüpfung lösen (DELETE /tasks/{id}/deck-links/{cardId}, ändert die Karte nie); nach Annehmen, Durchsetzen oder Verknüpfung lösen lädt der Dialog die Aufgabenliste des Projekts neu (onDeviationsChanged), damit Symbol und Zähler ohne Neuladen der Seite mitziehen. Wie immer gilt: Abweichungen nur ab der Projektstufe „Nur lesen“, Annehmen nur mit Bearbeitungsrecht. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, neuer Abschnitt in Kap. 16 und Filterliste in Kap. 5) und HOWTO.md (de/en) nachgezogen. PHPUnit (DeckOverdueDeviationProviderTest, DeckRoleDeviationProviderTest, DeckCardStateDeviationProviderTest — bislang fehlend zu #235 —, erweiterte DeckDeviationServiceTest/RollupServiceTest), Vitest (deckDeviationKinds.test.js, erweiterte filterMatch.test.js/tableRows.test.js/deckDeviations.test.js) und E2E deck-deviations-overview.spec.js
  • Doppelte Signale vermeiden: Gantt-Erinnerungen und Tasks-Einträge bei Aufgaben mit Deck-Karte unterdrücken (Issue #240, Stufe 3, Schritt 10 aus #234; O13): Neuer DeckSuppressionService beantwortet für Aufgaben mit mindestens einer verknüpften, nicht archivierten Karte (nach dem zuletzt gesehenen Kartenstand aus #235) in einem Projekt ab Stufe „Nur lesen“, ob NC-Gantt für diese Aufgabe still bleiben soll — DueReminderService, EventNotificationService::notifyAssigned() und TaskCalendarSyncService fragen dort statt die Regel je einmal selbst nachzubilden, statt selbst zu unterdrücken. Unterdrückt werden die Fälligkeits-Erinnerung der Aufgabe, die Zuweisungsmeldung und ihr Eintrag in der Tasks-App; nicht unterdrückt bleiben notifyRescheduled() (Deck kennt keine Kettenverschiebung) und Meilenstein-Erinnerungen (Meilensteine haben keine Karte). Persönliche Einstellung „Gantt-Benachrichtigungen auch für Aufgaben mit Deck-Karte erhalten“ im Abschnitt „Gantt-Projekte“ der Nextcloud-Benutzereinstellungen (PUT /api/v1/settings/reminder-deck-override), Standard aus — die Unterdrückung ist also aktiv; gilt gleichermaßen für Benachrichtigungen und Tasks-App-Einträge. Wird die Unterdrückung wirksam oder aufgehoben, räumt bzw. holt der jeweils nächste Lauf von DueReminderService/TaskCalendarSyncService die betroffenen Erinnerungen bzw. To-dos ab — über denselben Soll-/Ist-Abgleich, der schon das Stummschalten trägt, ohne eigenen Sonderfall. Neue Texte in l10n/de.json/de.js; FEATURES.md (de/en, Integrationsmatrix, Kap. 14, 18 und 20) nachgezogen. Neue PHPUnit-Tests (DeckSuppressionServiceTest, erweiterte DueReminderServiceTest/EventNotificationServiceTest/TaskCalendarSyncServiceTest) und Vitest (stores/settings.test.js)
  • Optionaler Planungszeitraum für Projekte und Teilprojekte (Issue #229): Projekte und Teilprojekte können neben dem aus den Aufgaben berechneten Zeitraum einen eigenen, bewusst gesetzten Rahmen haben (plan_start/plan_end, Migration Version1043; leer für alle Bestandsprojekte, die sich damit unverändert verhalten). Eingabe im Detaildialog (Abschnitt „Planungszeitraum“) und per Inline-Editing in den Spalten Start/Ende der Tabelle, die dafür erstmals auch bei Projekt- und Teilprojektzeilen editierbar sind. Start und Ende sind nur gemeinsam gesetzt: ein leeres Gegenfeld übernimmt den eingegebenen Wert, ein vorhandener wird nie überschrieben, das Leeren eines Felds entfernt den Zeitraum; Start ≤ Ende und Schreibrecht werden serverseitig geprüft (PlanningPeriod, Fehlercodes invalid_plan_date, plan_start_after_end). Ist ein Planungszeitraum gesetzt, zeigen Balken, Tabelle (Start/Ende/Dauer), PDF/Druck und ICS-Feed ihn anstelle des berechneten Zeitraums — auch ein Teilprojekt ohne Aufgaben wird so sichtbar; ein Elternobjekt ohne eigenen Planungszeitraum umfasst in der Darstellung die Zeiträume seiner Kind-Teilprojekte. Liegen die Aufgaben nicht vollständig im Rahmen oder ragt ein Kind-Teilprojekt mit Planungszeitraum heraus, markiert ein rotes Ausrufezeichen die Zeile in Tabelle und Diagramm (planWarningOverlay.js), der Tooltip nennt die Ursache; der Rahmen passt sich nie selbst an und verschiebt keine Aufgaben. Ampel, Rollups, kritischer Pfad, Meilensteine, Filter, Fortschritt und Budget rechnen weiter mit dem berechneten Zeitraum; Baselines und Deck bleiben unberührt. Änderungen stehen mit Operations-ID im Änderungsprotokoll und lassen sich per Toast rückgängig machen (revertOperation() kennt dafür jetzt auch Projekte und liefert entity_types); der JSON-Export/-Import führt den Planungszeitraum mit. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 1, 3, 4, 10, 14) und HOWTO.md (de/en) nachgezogen. PHPUnit (PlanningPeriodTest, erweiterte ProjectServiceTest/SubProjectServiceTest/ChangeEventServiceTest/CalendarFeedServiceTest), Vitest (planningPeriod.test.js, erweiterte rowModel.test.js/tableRows.test.js/io.test.js) und E2E planning-period.spec.js
  • Rich-Vorschau für Links auf Projekte und Aufgaben (Issue #241, Stufe 4, Schritt 12 aus #234; O14): Eigener GanttReferenceProvider (OCP\Collaboration\Reference, ohne jede Deck-Abhängigkeit) erkennt Links der Form ?project=<id> bzw. ?project=<id>&task=<id>, mit und ohne index.php, und löst nur auf, wenn die anfragende Person laut AccessService mindestens Leserecht auf das Projekt hat — sonst bleibt der Link ein nackter Link. Die Vorschau einer Aufgabe zeigt Titel, Projekt/Teilprojekt, Start–Ende, Fortschritt, Status und Zuständige, die eines Projekts Titel, Zeitraum, Rollup-Fortschritt und den nächsten Meilenstein; ein eigenes Vue-Widget (registerWidget aus @nextcloud/vue, geladen nur beim RenderReferenceEvent) rendert das im Nextcloud-Look mit Dark-Mode. Cache-Präfix je Projekt bzw. Aufgabe und Cache-Schlüssel je Benutzer (ReferenceCachePrefix); TaskService, ProjectService und MilestoneService verwerfen den betroffenen Cache über IReferenceManager::invalidateCache() bei jeder Änderung, damit die Vorschau nicht bis zu einer Stunde veraltet bleibt. FEATURES.md (de/en, neues Kapitel 23 und Integrationsmatrix) nachgezogen. Neue PHPUnit-Tests (GanttReferenceProviderTest, TaskServiceReferenceCacheTest, ProjectServiceReferenceCacheTest, MilestoneServiceReferenceCacheTest) und E2E-Test reference-preview.spec.js (prüft über den Core-Endpunkt references/resolve, denselben Weg, den Text, Notizen und Talk nutzen). Nicht Teil: ein Smart-Picker-Eintrag zum Einfügen einer Gantt-Aufgabe per Suche (geplanter Folgeschritt zusammen mit der Talk-Integration)

Changed

  • Aufgaben-API: deck_card_id ist nur noch ein berechnetes Lesefeld (Issue #237): Die Migration Version1040 entfernt die Spalte nc_gantt_tasks.deck_card_id (Werte, die noch nicht in nc_gantt_deck_links stehen, werden vorher übernommen) und ergänzt nc_gantt_projects.deck_list_role. Die API liefert deck_card_id weiter — als erste Verknüpfung der Aufgabe —, damit bestehende Nutzer nicht brechen; geschrieben wird nur noch über die Verknüpfungs-Endpunkte, ein mitgesendetes deck_card_id bei POST/PUT auf Aufgaben wird ignoriert. Der Aufgabendialog und alle Frontend-Pfade lesen die Verknüpfungen aus der Tabelle

Fixed

  • Rollenkapazität im Ressourcen-Raster stimmte nicht mit den Personenzeilen überein (Issue #260): Drei unabhängige Fehler wirkten zusammen. Ein Options-API-Watcher auf store.activeIds (Getter-Pfad, nicht deep) feuerte nie, weil toggle() das Array per push()/splice() mutierte statt neu zuzuweisen — eine nachträglich in der Seitenleiste aktivierte Ressource blieb bis zum Moduswechsel ohne Auslastungsfarbe, Tooltip oder abgeleiteten Rollenwert. ResourceUtilizationService und die Personenzeile der Tabelle (getVal()) lasen unterschiedliche Kapazitätsquellen: die Rolle über die 8h/Tag-Pauschale von WorkTimeService::getHoursPerPt() für jeden ungepflegten Tag, die Person über den Mittelwert der gepflegten Tage hochgerechnet auf die ganze Periode. Beide laufen jetzt über dieselbe Tagesfunktion (personCapacityFn() im Backend, resourceCapacity.js im Frontend) — die Pauschale greift nur noch, wenn für eine Person überhaupt keine Kapazität gepflegt ist, nicht mehr für einzelne Lücken innerhalb eines gepflegten Verlaufs, und ist dann auch in der Personenzeile sichtbar. Ein gepflegter Tageswert auf einem Feiertag zählte bisher voll statt 0h, sowohl in der Auslastungsberechnung als auch in resources.js::capacityOf() (Überbuchungsprüfung/Auslastungsgraph im Gantt-Diagramm) — beide prüfen jetzt Arbeitszeitmodell und Feiertagskalender der Ressource unabhängig vom gepflegten Wert. „Wert bis Monats-/Jahresende kopieren“ (doFill()) legt keine Kapazitätssätze mehr auf Feiertagen oder arbeitsfreien Tagen an. Eine Rollenspalte gilt im Tagesmodus als arbeitsfrei, wenn keine ihrer Personen an diesem Tag arbeitet. Neue PHPUnit-Tests (ResourceUtilizationServiceTest), Vitest (resourceCapacity.test.js, erweiterte resources.test.js) und E2E-Test (resource-roles.spec.js)
  • Deck-Vertragstest schlug falsch an (Issue #256): Für CardService::create()/update() prüfte er die Parameter positional, obwohl NC-Gantt sie über DeckGateway::invokeNamed() nach Namen aufruft. Ab Deck 1.18 steht startdate hinter dem von NC-Gantt nicht genutzten done, der CI-Job Deck-Vertragstest wurde dadurch rot, ohne dass etwas kaputt war. Die beiden Methoden stehen in contract.php jetzt unter named: geprüft wird, dass es die Parameternamen gibt und dass NC-Gantt jeden Pflichtparameter der Deck-Methode kennt — die Reihenfolge in Deck ist gleichgültig, neue Parameter dazwischen (zuletzt color) brechen den Vertrag nicht mehr. Positional aufgerufene Methoden bleiben unverändert
  • Startdatum einer Deck-Karte wurde nie gelesen (DeckGateway::cardState()): Deck-Entities stellen ihre Getter über __call bereit, method_exists() war daher immer falsch; jetzt is_callable()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturekJ6Xo6R3EOShOn1PWZI+CpEgV34eRXUZy4CNPkmb+e7uqUL4Tu/tWsqGpu15PCRr
8HR2enQnsTmTk2fkKQUKBqbcfvO3vfylEzNRxU7YuOtyGW6sFAdLSKK8MDnq9XxH
1A91Bmumu7dxP+nh3u5EyGswDoH84tXcxVH0BHEr22ny4fo1/irWKT5RDcumVhyQ
SZSqcwL1iEsYzmmFZJoyqQu7Cfy6IbZXko4OJJGYWLTBvu3CBfDpQFoCg0GaMevf
SccjJXjY6+00oW3OGGn1JH3vz1dMm7l2vD7QfT04AoeFNxJkVcwFo9mTapZijalO
3kgLc7GOLXG6I7hgmqyRBHuUfJAskET9WUl9Ya92UC2Fa8NGwiBFkkQnzy8BGzEp
lbsQ/cDrFw9/U8c1RIfs3T0uOsjtSDeKIAaKQz/SJYAAYdv3SJvqWZxoIt6p04Vk
4fBM02pxBrMH+rNR+X2frwCD3+q4zBtRSxfZNWzz0kHuo9vt714yVLh5dVynwPjP
+6/5b6eE9BsobfL6M6HyZdAzZYIBkPZbfm11TA6nDT5WwUD5r+KBUJDT2hTChAVS
n7pHjc3PQ6wWgVNjvJqmtV8mvH+qvWnLFGGFd5ttHMYjfB555hcStB2UF5cAzkji
2hhP2aeeujkdcPkZ4XETBfQpkbNHaIPUxiEgUHz2oEs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.5
Release Details
UpdatedSept. 21, 2026, 8:05 a.m.
Changelog

Changed

  • Freigabe für Nextcloud 35: max-version in appinfo/info.xml von 34 auf 35 angehoben, unterstützter Bereich damit NC 31–35; die Angabe in der App-Store-Beschreibung (alle sieben Sprachen), README.md, FEATURES.md und INSTALL.md ist nachgezogen. Verifiziert gegen Nextcloud 35.0.0 mit PHP 8.5 und MariaDB 11.4 (302 Unit-Tests im Frontend, 431 PHPUnit-Tests, 98 E2E-Tests) – am Code der App war dafür keine Anpassung nötig. Die lokale Dev-Umgebung (setup-dev.sh) setzt jetzt auf nextcloud:35-apache und installiert Deck mit, weil die Fortschritts- und Karten-Integration von nc_gantt darauf aufsetzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureOtLYluq0hTNN8+vQY8AK0ALLooVxvUUxymkOjs0nAtdES78R6Sii2b9n1POirXvO
EwnIrLzKWwiT05WSV2dsrWliGU+hBhXmvUO0RviY4jmBpu9+mexpLgoHP5dUwIna
OwAN1TgJg0TOod9/bLSQts6qhr8ONk/giqNpMjId1dvQX+hymeQXFchZHk8ohVx4
4d2BaKizT6HQweBNOxxR+FGd6juzsfw4sC9ujASI6IzxPKhH9piiDFniKaERJuGo
iUvDbljcRA4YVRgM003aD9ytwdzdJ4Z3+JfI4L+LBuwaUsXFyQsC3q8bbaZWSj+w
VZX536GBNNwb0RE/nKkgGQ8WLmwG+dosiL26+Wh+a6lX2EWLFZaw9L9iwGFYG92I
iZmFDOnIpqSicG8PmCTV9YSg5u90fxPg53CaVlv6/eCxqY2mUADyErpBSHSDc1tn
g/JM58znHYQF3PfmpFc3Vh901bBytBPTkUY3p0LF1JKoH7tnZ75ar4fTORoF8nEO
Hfr50lB+bWYhEdj1LCvD9yHsRfGcaj5BZ5m6NGvFzvAf/DStAgzyF2jyz/0+YvnM
8XF+LLYY9TktnVyxHNbwjaoE32sHc/le6ABbWSLdI/R/pYWul1EVPKagxYlAQIS1
0IEJbQarL1SKdaQrVfxtxafKvVVsPLcV6DgEBzeem14=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.4
Release Details
UpdatedSept. 21, 2026, 6:44 a.m.
Changelog

Fixed

  • Fortschritt „aus Deck“ war nicht mehr erreichbar (Issue #221): Seit der Umstellung der Detail-Dialoge auf <dialog> speicherte der Aufgabendialog fest progress_mode: 'manual' – eine Aufgabe, die per API auf deck stand, wurde beim Speichern still zurückgesetzt, und fetchDeckProgress(), syncTaskProgress() und PUT /tasks/{id}/progress/sync hatten keinen Aufrufer mehr. Der Modus ist wiederhergestellt, jetzt mit der verknüpften Karte als Grundlage statt des ganzen Boards: Unter „Fortschritt“ schaltet der Dialog zwischen Manuell und Aus Deck um (neues Feld src/detail/forms/taskProgress.js). Eine erledigte Karte zählt 100 %, sonst der Anteil der abgehakten Punkte ihrer Markdown-Checkliste (cardProgress() in src/utils/deckProgress.js); der Dialog zeigt die Grundlage an und lädt die Karte per „Abgleichen“ neu. Beim Aktivieren eines Projekts übernimmt die neue Store-Aktion syncDeckProgress() den Wert für alle Aufgaben im Modus Aus Deck über den vorhandenen Endpunkt – nur geänderte Werte und nur mit Bearbeitungsrecht, ohne das Laden des Diagramms aufzuhalten. Ist Deck nicht erreichbar oder die Karte verschwunden, bleiben Modus und letzter Wert erhalten. Backend und Datenmodell bleiben unverändert. FEATURES.md (Übersicht und Kapitel 16), HOWTO.md und die App-Store-Beschreibung beschreiben den Modus wieder; neue Texte in allen Übersetzungen. Neue Unit-Tests (deckProgress.test.js, taskProgress.test.js, projects.test.js, TaskServiceTest) und E2E-Test tests/e2e/specs/deck-progress.spec.js, der die Deck-API nachbildet
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturedkpQCpaBGHbxMN8d8IvmDWANDU90LVl0Tp5u2TiH7QHbEz7fovU1pmVNKkGAF6VB
eTFeVCkt+Nq8ZdpfnmfC3ZF4wEO/wxd/fPDjm2pZM6WXb03HGpA7/J49KTifC/mj
KY68+niFgI7cjdxaIkI3KZPsWSnhsIZnTQA+HVaQxNRSIuXMV21NYkVQNZaCwZ1y
DmqesNT1/A9457BNtdk8tYpTF1IF9K98fgIB68MmJ3guBEF1jdh1eHqsp9Zhluxv
MgZNf5wNcBTdLT+9z98jxDoSZGx9+7U57ACA54Kd08f3TZLey+nBSIjjSusgyFYl
WDyYjimUfFMlNNktHMkinpvw0qTlu62cIG4yqO1Kd0mzJ4+ueUrwBFuvRIMscVjy
pXuaC2Try+5ZBw8pUl6XDbhPslzvfKI1uO8nUachDNWJR1ULS6HdX8PsWRTA3PFq
NdETu9H9tcLeheaEiGqNQbPEy/2A8TEWqsMcdzl3iX/W3tQJfj1O+KVMOPKC+Kek
rrZiNfEd4KtVgVH0YLU5xb3XyXDFG0q2Q1b3gJBsoKcn2Wr0D7ijK0FLb3U97RDj
tBlHHoy7gVE1JnXvO/Z8oau5KvytSVffwIbdxKAG6oW5Pi92+HTN4J0icMz3+NIr
LHSTqB4XsIdKGjWdKxMj7KOo2FqWwZR0rq0QuFWxWSY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.3
Release Details
UpdatedSept. 18, 2026, 8:33 p.m.
Changelog

Changed

  • Angleichung an die Nextcloud-Entwicklerstandards (Issue #227): App-Werte (Arbeitszeit, Stunden je PT, Erinnerungen) werden über die typisierten Getter und Setter von IAppConfig statt über das seit NC 29 veraltete IConfig::getAppValue()/setAppValue() gelesen und geschrieben; vorhandene Werte werden beim nächsten Speichern typisiert, Benutzerwerte bleiben bei IConfig (IUserConfig gibt es erst ab NC 32). Die Controller bekommen die Benutzer-ID wie im Developer Manual vorgesehen als ?string $userId per Dependency Injection, statt sie in jeder Methode aus der Session zu lesen. npm run lint läuft ohne Warnungen: require-jsdoc gilt nur noch für exportierte Funktionen, die leeren JSDoc-Hüllen an internen Funktionen sind entfernt, exportierte Funktionen vollständig dokumentiert. <style>-Blöcke und detail.css sind wie der übrige Code mit Tabs eingerückt (reine Whitespace-Änderung, .editorconfig hält das für CSS fest)

Fixed

  • Feste Signalfarben ignorierten Dark Mode und Kontrast-Themes (Issue #227): Chart-Overlays, Popups, Tabelle, Dialoge, Rollup, Report und Dashboard-Widget nutzten feste Rot-, Gelb- und Grüntöne. Sie kommen jetzt aus dem Nextcloud-Theming: Text über --color-error-text/-warning-text/-success-text, Flächen und Linien über --color-element-error usw. (ab NC 32) mit Rückfall auf --color-error für NC 31, Tönungen per color-mix()
  • Teilprojekte standen auf einer Ebene mit ihrem Projekt (Issue #230): In Tabelle und Chart-Beschriftung waren Teilprojekte der obersten Ebene nicht eingerückt, während direkte Aufgaben und Meilensteine des Projekts eine Stufe tiefer standen – Projekt und Teilprojekt ließen sich so nur schwer unterscheiden, und in der Übersicht mehrerer Projekte verschwamm die Grenze zwischen ihnen. buildRowModel() reiht sie jetzt mit Tiefe 1 ein; alle darunterliegenden Ebenen rücken entsprechend eine Stufe nach
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturewwr3SUZOKRGnM7uwZfWOLH4FzXNvzjqMOcqVDH3mlte3y8BbmOzAeE5tvPajrvld
xLoe/OaOJ6tLpE8e/7MRaKiB10CuP0sFMA0j5nzOLw7sDPpTpqAV7xVRBbtZhio6
ZWQ0HjSdRU2JGnlP+MFPi7b/c+fSYVz/M9/q/b79Wyf1x2cDRhUJymN8tmFeD8f0
VVL0kXoMiX8oIJVHj0FrMdEWj+nz8pMPLF17bXluzu5REBHi4YLAnjWdO1YYkTOL
QNxQ/b3lAKZG4jvHXTADGlwbGh3iMjidMSjhpnKt2GsDylHxGozgBFb+rCDiSr9Y
PTM6dC/Nmyy4O3/p8Sbs/ZBpqb8wgO51k7Qd6ltXLU+n7eiSoHQyXL+uQihGA2dg
tBbHeKhcPPR5gz2/cSSqkeEyVkesmg015dLkbdStJoq5zR6s7fGYB3ZoUjqoqR2O
5cVF8Jf4k/swhXM7fMQ2zjoZj8yKePguCx7CqppbUFvr5DXltbHh0KDWVVrSgfXM
/zjL9lqQQXrrgTT16oXJfmq1dajsd1e6qwP/OqRY304W9HceyRCh/jQu0oUoQcSz
+fKenL7yWw3IB2UCGHdiXqzGqyGcGdVL0eVPvZhkzpqoDrr82mjPkXshmM/oF//U
942f6SK2u3YG+bKx/n1Up3ZX90WhZxNsZdW+eDqCbmY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.2
Release Details
UpdatedSept. 17, 2026, 1:01 p.m.
Changelog

Changed

  • PHPUnit-Stubs auf Nextcloud 31 angehoben (Issue #219): nextcloud/ocp steht in composer.json/composer.lock jetzt auf dev-stable31 statt dev-stable28 und damit auf der kleinsten unterstützten Nextcloud-Version; psr/log zieht dabei von 1.1 auf 3.0 nach. OCP-Klassen ab NC 29 sind in Unit-Tests damit verfügbar, die Suite lief ohne Anpassungen an Mocks durch. Nachgeholt ist der Unit-Test für das Mapping ParameterOutOfRangeException → 400 invalid_<parameter> in ExceptionMiddlewareTest (bisher nur per E2E abgedeckt). Entfallen ist die Laufzeitprüfung im Notifier, die nur wegen der alten Stubs nötig war: Er wirft jetzt direkt OCP\Notification\UnknownNotificationException (seit NC 30). Das Release-Artefakt enthält die Stubs nicht
  • Wartbarkeit im Frontend (Issue #226): Der tasks-Watcher in GanttChart.vue ruft refreshOverlays() nicht mehr zusätzlich auf – renderGantt() plant das selbst ein, Workload-Diagramm, Baselines, Klammern, Fortschritt und Meilensteine wurden bei jeder Datenänderung doppelt gezeichnet. Das Dashboard-Widget beendet sein Aktualisierungsintervall beim Unmount. Das Entpacken von API-Antworten (data.ocs?.data ?? data, 61 Stellen) übernimmt der Helfer unwrap() in src/api.js. AdminHolidays.vue arbeitet über die Stores für Einstellungen, Kalender und Feiertage (holidays.js hat dafür load() und remove() bekommen), PersonalReminders.vue über den Settings-Store (neu: remindersMuted, setRemindersMuted(); die persönliche Einstellungsseite nutzt dafür jetzt Pinia), ShareDialog.vue über api() aus src/api.js statt einer eigenen Kopie. Der Projekt-Store spiegelt die Lösch-Kaskade des Backends: Beim Löschen eines Teilprojekts verschwinden auch Unter-Teilprojekte, deren Aufgaben und die Meilensteine, beim Löschen eines Projekts dessen Teilprojekte, Aufgaben und Meilensteine, und beim Löschen von Aufgaben die Abhängigkeiten anderer Aufgaben darauf. Kleinkram: keine Überschattung der Übersetzungsfunktion t mehr, ungenutzte catch-Variable entfernt. Der E2E-Test aus #223 wartet vor dem Ziehen auf eine stabile Balkenposition, weil die nachladende Tabelle das Diagramm sonst unter dem Mauszeiger verschob
  • Wartbarkeit im Backend (Issue #226): TaskService::create() und update() laufen in einer Transaktion (TTransactional::atomic()): Speichern, Ressourcen-Zuordnungen, Abhängigkeiten, Constraints, Propagation und Change-Events gelingen gemeinsam oder gar nicht, Benachrichtigungen gehen erst nach dem Commit hinaus. Die drei identischen Parser in TaskController sind zu parseJsonArray() zusammengelegt. Die seit dem 3-Ebenen-Modell ungenutzte Tabelle nc_gantt_sp_deps entfernt Migration Version1038Date20260917000000, Mapper und Entity sind gelöscht. Milestone hat nur noch einen Docblock (die @method-Annotationen des ersten waren für IDE und statische Analyse unsichtbar), Holiday die fehlenden Annotationen und keinen toten DateTime-Zweig mehr. WorkCalendarController und WorkScheduleController verzichten beim Ändern und Löschen auf die Eigentümerprüfung, die für die ohnehin nur Nextcloud-Admins zugänglichen Endpunkte immer zutraf. ExceptionMiddleware unterscheidet reguläre Controller: Eine ApiException wird dort zu einer JSONResponse mit Status und Meldung statt zu einer OCSException ohne OCS-Kontext, andere Exceptions behandelt Nextcloud selbst. POST /admin/holidays lehnt ein Datum außerhalb von Y-m-d bzw. einen nicht existierenden Kalendertag mit 400 invalid_date ab und einen unbekannten Kalender mit 400 invalid_calendar_id
  • Statische Analyse in der CI (Issue #226): PHPStan Level 5 prüft lib/ gegen die OCP-Stubs (phpstan.neon, composer phpstan) und läuft in jedem PHP-Job der CI nach dem Coding-Standard. Die Klassen der dav-App und von Sabre in DavTaskCalendarBackend sind ausgenommen, weil es dafür keine Stubs gibt. Behoben sind dabei die übrigen Befunde: HolidayMapper deklariert seinen Entity-Typ, ApiException ihre möglichen Status, ChangeEventService speichert Aufgaben und Teilprojekte typgerecht, Rohdaten des Clients sind nicht mehr als bereits geprüfte Strukturen annotiert, TaskCalendarSyncService erkennt neue Zuordnungszeilen ohne Vergleich einer nie leeren ID, und die Zyklusprüfung der Teilprojekt-Hierarchie greift nicht mehr auf ein fehlendes Array-Element zu (PHP-Warnung bei Altdaten mit verwaistem parent_id). Das Release-Archiv enthält phpstan.neon nicht

Fixed

  • Abhängigkeitsmodus „Pflicht“ griff beim Ziehen und in der Tabelle nicht (Issue #223): Der Server erzwingt die Abhängigkeiten der geänderten Aufgabe nur, wenn der Client dep_mode: 'strict' mitsendet. Das taten nur der Detail-Dialog und die Spalte „Vorgänger“, nicht aber Drag&Drop im Chart und die Start-/Endzellen der Tabelle – ausgerechnet die häufigsten Wege, einen Termin zu ändern. Eine Aufgabe ließ sich so vor das Ende ihres Vorgängers schieben und wurde dort gespeichert. store.updateTask() ergänzt den eingestellten Modus jetzt selbst, sobald start_date oder end_date im Payload stehen; ein explizit übergebener dep_mode hat Vorrang. Das gilt damit auch für „Simulation übernehmen“
  • Fehlgeschlagenes Speichern nach Drag&Drop blieb ohne Rückmeldung (Issue #223): Lehnte der Server eine Verschiebung ab (fehlende Rechte, ungültiges Datum, Netzwerkfehler), blieb der Balken an der neuen Position stehen, obwohl der alte Termin galt, und der Fehler landete nur in der Browser-Konsole. GanttChart.vue zeigt jetzt „Speichern fehlgeschlagen“ und setzt den Chart auf den Stand des Stores zurück, der Balken springt zurück – auch für Meilensteine und im Simulationsmodus. Neue Unit-Tests src/stores/projects.test.js, neuer E2E-Test tests/e2e/specs/dependency-mode.spec.js
  • Löschen hinterließ verwaiste Datensätze (Issue #224): Die Lösch-Kaskade einer Aufgabe war beim Löschen von Aufgaben, Teilprojekten und Projekten jeweils von Hand geschrieben und dabei auseinandergelaufen. Alle drei Wege nutzen jetzt den neuen TaskDeletionService (Abhängigkeiten in beide Richtungen, Meilensteine an der Aufgabe, Ressourcen-Zuordnungen). Dadurch räumen Teilprojekt- und Projektlöschung auch die Ressourcen-Zuordnungen ab, die bisher stehen blieben. Die Teilprojektlöschung entfernt zusätzlich die Meilensteine direkt an den gelöschten Teilprojekten (auf das Projekt eingeschränkt, vgl. Issue #179), die Projektlöschung Baselines samt Einträgen. Das Löschen einer Ressource entfernt ihre Zuordnungen und Kapazitätseinträge; war sie die primäre Ressource einer Aufgabe, rückt wie beim Speichern die nächste Zuordnung nach, sonst wird resource_id null. Den Aufwand (work) der Aufgaben lässt es bewusst unverändert: Die Arbeit fällt mit der Ressource nicht weg, sondern ist neu zu verteilen, und eine Neuberechnung senkte stillschweigend Plankosten und Fortschrittsgewichte. Aufgaben- und Ressourcenlöschung laufen jetzt wie die übrigen Löschwege in einer Transaktion. Bereits vorhandene Waisen werden nicht nachträglich bereinigt. Entfallen sind die dadurch ungenutzten TaskMapper::deleteByProject() und TaskSchedulingService::deleteDependencies()
  • Gelöschte Nextcloud-Konten wurden nicht aufgeräumt (Issue #225): Die App reagierte nicht auf das Löschen eines Kontos. Freigaben an das Konto blieben stehen, und ein später gleichnamig angelegtes Konto erbte darüber den Zugriff auf die Projekte – ebenso das Eigentum an den Projekten des gelöschten Kontos. Der neue UserDeletedListener (registriert für OCP\User\Events\UserDeletedEvent) delegiert an UserDeletionService, der in einer Transaktion die Freigaben an das Konto löscht, die Verknüpfung von Ressourcen mit dem Konto (uid, Issue #203) aufhebt und Projekte, Ressourcen, Ressourcengruppen, Kapazitäten, Wochenmodelle und Kalender des Kontos erhält, aber auf einen Platzhalter-Eigentümer #deleted-<zufällig> überträgt. # ist in Nextcloud-Benutzer-IDs nicht zulässig, der Platzhalter kann also nie einem Konto gehören; er ist je Löschung neu und für alle Tabellen derselben Löschung gleich, sodass Projekte und Ressourcen des Kontos zusammengehörig bleiben. Verwalten können diese Daten danach Nextcloud-Administratoren und Inhaber einer Admin-Freigabe. Historie (change_events.changed_by, baselines.created_by) bleibt unverändert; Erinnerungen und Tasks-Synchronisation gleichen sich wie bisher selbst ab. Neuer E2E-Test tests/e2e/specs/user-deletion.spec.js
  • Drag&Drop öffnete zusätzlich den Bearbeiten-Dialog: Nach dem Ziehen eines Balkens löst der Browser einen Klick auf den Balken aus, und GanttChart.vue öffnete daraufhin den Detail-Dialog – nach jeder Verschiebung musste man ihn wieder schließen. _onBarClick() ignoriert den Klick jetzt, wenn sich die Maus zwischen Drücken und Loslassen um mehr als 4 Pixel bewegt hat. Der E2E-Test zum Verschieben per Drag prüft, dass der Dialog geschlossen bleibt
  • Glossar beschrieb noch Abhängigkeiten zwischen Teilprojekten: Seit dem 3-Ebenen-Modell hängen Abhängigkeiten, kritischer Pfad und Puffer an Aufgaben, und der Abweichungsbericht rechnet über die Aufgaben der Baseline. Das Glossar sprach dagegen von Teilprojekten, die voneinander abhängen, eigene Termine und eigenen Aufwand haben. Korrigiert sind die Einträge Teilprojekt, Kritischer Pfad, Puffer, Abhängigkeit, Gesamte Terminabweichung und Ø Fortschritt (samt Formel) – auf Englisch und in allen Übersetzungen (de, es, fr, it, nl, pt_BR)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureKOPH7uJRRB6f+Iqi2oeNidpUdRwDrpvn6N99cSmWLf+W+QrWztXKyDbTLOq71mBy
cXEkYbN+4KnMt0Zie40fLjvXqI8sWgouPHQdobRBCvq60TUpHVT1umUSAWrP7KDJ
XMyCnbKt6jqpTmUusE7sMGkZwBB1SEdx5tAilm2gu67wzR5WQakpZXXsp3Au+GyM
sxPgqD5bgRMcKdRMF+VXIF5gZTsJrNgal9J3SZZ1m8KGMArFPm+dEJaxAF+z0NxX
NTNCKR/tF2y20xgc2Rt4KLNOg5TTrRLB931vwkoM0eLUjFavTXjFAMAoVndvX5yb
37sRCzJX2tzekoch1/e6BWVbo5PH5YJmP5at2lWmrhDcWHrzFxTQZ2ZiY6GYISAO
b/oKUbvmmTs5VFToXRWAQASOC9afDEbKdWes+zoglIP3euOwok+2VD4rXZtVUNlU
fZ/FwsIYt8OB9UF7pQWKQSma8OPsZNnsBaoJVbFinieAwgKs35XtYHeTUWfsXAsL
7GaRK2AJ4jkV4uu35/mHQOjm2DM0ZfkC4OpUI3qXhLM70YTn3BIC0Y3k1P8ZmG4J
RtMSXNmqjBDMO+A+qclcRULziVStXCSlkDVAIw3I37JdrGYlB+avZ9yTX3EsNR0C
gVjxSTao2fhfLKp6o1tQO0+ImF1XqRIxPE1vAWS0hOE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.0
Release Details
UpdatedSept. 16, 2026, 1:50 p.m.
Changelog

Added

  • Ressource optional mit einem Nextcloud-Benutzerkonto verknüpfen (Issue #203, Datenmodell + API): Ressourcen haben ein neues, optionales Feld uid (Migration Version1034Date20260915000000, Spalte nc_gantt_resources.uid, nullable und indiziert). POST /resources und PUT /resources/{id} nehmen es entgegen, GET /resources liefert es mit; uid: null bzw. "" hebt die Verknüpfung auf. Ein nicht existierendes Konto lehnt ResourceService über IUserManager::userExists() mit 400 user_not_found ab; je Eigentümer der Ressource darf ein Konto nur einer Ressource zugeordnet sein, sonst 409 uid_already_assigned (verhindert später doppelte Benachrichtigungen). Ressourcen ohne Konto — Maschinen, externe Dienstleister, Sammelrollen — bleiben uneingeschränkt nutzbar. Der JSON-Export enthält uid je Ressource; beim Import wird eine im Zielsystem unbekannte oder dort bereits vergebene uid verworfen, die Ressource ohne Verknüpfung angelegt und nach dem Import ein Hinweis mit den betroffenen Konten angezeigt, statt abzubrechen. Die Auswahl des Kontos in der Oberfläche siehe Issue #204
  • Nextcloud-Benutzer in der Ressourcenverwaltung auswählen (Issue #204): Der Ressourcen-Dialog hat ein optionales Feld „Nextcloud-Benutzer“ mit Typeahead-Suche über die vorhandene Benutzersuche (GET /users/search, keine Vollliste); geleert bleibt bzw. wird die Ressource ohne Konto. Die Auswahl übernimmt den Anzeigenamen als Vorschlag ins Namensfeld, solange dort noch nichts Eigenes steht — ein leeres Feld oder der unveränderte vorige Vorschlag wird ersetzt, ein selbst eingegebener Name bleibt. Lehnt der Server die Verknüpfung ab (400 user_not_found/invalid_uid, 409 uid_already_assigned), erscheint die Meldung direkt am Feld statt als allgemeiner Speicherfehler; beim Bearbeiten wird die uid nur bei einer Änderung mitgesendet, damit ein inzwischen gelöschtes Konto das Speichern anderer Felder nicht blockiert. Verknüpfte Ressourcen zeigen in der Ressourcenliste, im Kapazitätsraster und in der Ressourcen-Zuordnung des Aufgabendialogs Avatar und Anzeigenamen des Kontos (Ressourcenname, bei Abweichung ergänzt um den Anzeigenamen in Klammern); nicht verknüpfte bleiben unverändert. Dafür liefert die Ressourcen-API zusätzlich das berechnete Feld uid_display_name (bei gelöschtem Konto null, die Oberfläche zeigt dann die uid), und GET /users/search nimmt mit include_self=1 auch das eigene Konto auf, damit man sich selbst als Ressource verplanen kann — der Teilen-Dialog schließt es weiterhin aus. Das „+“-Menü im Kapazitätsraster schließt sich jetzt nach „Neue Ressourcengruppe“/„Neue Ressource“ (close-after-click); bisher blieb es über der Ansicht offen und entzog dem ersten Feld der Sidebar beim Schließen den Fokus
  • „Meine Aufgaben“: Filter, Endpunkt und Dashboard-Widget (Issue #205): Auf Basis der Verknüpfung aus #203 sieht man projektübergreifend, was einem selbst zugewiesen ist. Neuer Endpunkt GET /api/v1/my-tasks (optional include_done, limit 1–500) liefert die Aufgaben, deren primäre oder zusätzliche Ressource (Issue #103) mit dem eigenen Konto verknüpft ist — gleich, wem die Ressource gehört —, sortiert nach Ende, dann Start. Offen heißt Fortschritt < 100 % und Status nicht „erledigt“; overdue folgt der Kennzahl „kritisch“ des Projekt-Widgets. Die Verknüpfung allein gewährt keinen Zugriff: MyTasksService prüft je Projekt AccessService::canViewOrOwn(). has_linked_resource unterscheidet „keine Ressource verknüpft“ von „nichts zu tun“. Das neue Dashboard-Widget „Meine Gantt-Aufgaben“ (nc_gantt_my_tasks) ist ein API-Widget (IAPIWidgetV2, IButtonWidget, IIconWidget, IReloadableWidget), das Dashboard rendert es ohne eigenes Bundle: die nächsten fälligen und überfälligen Aufgaben mit Projekt und Fälligkeit, überfällige mit rotem Overlay-Symbol, jeder Eintrag ein Direktlink. Ohne verknüpfte Ressource zeigt es einen erklärenden Leerzustand mit Setup-Button zur Ressourcenverwaltung. Direktlinks ?project=<id>&task=<id> aktivieren das Projekt und öffnen den Aufgabendialog, ?view=resources öffnet die Ressourcenverwaltung; die Parameter verschwinden danach aus der URL. In der Filterleiste dimmt der neue Filter „Mir zugewiesen“ in Chart, Tabelle und Projektliste alles ohne verknüpfte Ressource; nicht aktivierte Projekte mit zugewiesenen Aufgaben erkennt die Projektliste über den Endpunkt. Ist keine Ressource mit dem Konto verknüpft, erklärt ein Hinweis im Filter das und führt zur Ressourcenverwaltung
  • Fälligkeits-Erinnerungen als Nextcloud-Benachrichtigung (Issue #206): Ein Hintergrund-Job (DueReminderJob, appinfo/info.xml) meldet einmal täglich jede offene Aufgabe und jeden Meilenstein, die heute fällig sind, in den nächsten Tagen fällig werden oder überfällig sind. Der Job läuft stündlich und meldet höchstens einmal je Tag, sobald die Serverzeit die eingestellte Stunde erreicht hat (reminder_hour, Standard 6 Uhr); die Vorlaufzeit steuert reminder_lead_days (Standard 1 Tag) — die Oberfläche zu beidem kommt mit Issue #207. Empfänger sind die über Issue #203 verknüpften Konten der zugeordneten Ressourcen (primäre wie zusätzliche, Issue #103); ist keine Ressource verknüpft, erhält der Projekteigentümer die Meldung, damit die Funktion auch ohne gepflegte Verknüpfungen nützt. Ein Meilenstein erbt die Empfänger seiner Aufgabe. Wie bei „Meine Aufgaben“ gewährt die Verknüpfung allein keinen Zugriff: wer das Projekt nicht sehen darf, wird nicht benachrichtigt. Der übersetzte Notifier verlinkt direkt auf das Element (?project=<id>&task=<id>, beim Meilenstein auf das Projekt) und kommt ohne Datenbankzugriff aus — alles Nötige steht in den Parametern der Benachrichtigung. Gegen Dubletten führt die App über die neue Tabelle nc_gantt_reminders (Migration Version1035Date20260916000000, eindeutig über Element und Empfänger) selbst Buch: je Element, Empfänger und Anlass höchstens eine offene Benachrichtigung. Jeder Lauf gleicht Soll und Ist ab — wechselt der Anlass („wird bald fällig“ → „überfällig“), ersetzt der Job die Benachrichtigung; ist das Element erledigt, verschoben oder gelöscht, zieht er sie zurück. Ohne aktivierte Notifications-App tut der Job nichts und protokolliert keine Fehler
  • Erinnerungen konfigurierbar: Vorlaufzeit, Umfang, Abschalten (Issue #207): Jedes Projekt legt selbst fest, ob es erinnert, wie weit im Voraus (0–14 Arbeitstage; ohne eigenen Wert gilt die Vorlaufzeit der Instanz), worüber (Meilensteine und Aufgaben oder nur Meilensteine) und ob auch an Überfälliges erinnert wird. Der Detaildialog des Projekts hat dafür den Abschnitt „Erinnerungen"; die Felder liegen in nc_gantt_projects (Migration Version1036Date20260916100000), PUT /projects/{id} nimmt reminders_enabled, reminder_lead_days, reminder_scope und reminder_overdue entgegen, GET /projects liefert sie mit, unzulässige Werte weist die API mit 400 invalid_reminder_lead_days bzw. invalid_reminder_scope ab. Die Vorlaufzeit zählt jetzt Arbeitstage statt Kalendertage — dieselben Wochenmodelle und Feiertage wie die Terminplanung —, sodass eine montags fällige Aufgabe bei einem Vorlauf von einem Tag am Freitag davor gemeldet wird und nicht am arbeitsfreien Sonntag. In den persönlichen Nextcloud-Einstellungen (Abschnitt „Gantt-Projekte") lässt sich die Erinnerung der App für das eigene Konto stummschalten (PUT /settings/reminder-mute); bereits zugestellte Benachrichtigungen zieht der nächste Lauf dann zurück. Die Vorbelegung aller neuen Felder entspricht dem Verhalten aus Issue #206

  • Ereignis-Benachrichtigungen: Zuweisung und Terminverschiebung (Issue #208): Zusätzlich zum Tageslauf aus Issue #206 meldet die App Änderungen in dem Moment, in dem sie passieren. Wer über eine Ressourcenzuordnung neu für eine Aufgabe zuständig wird, bekommt „Aufgabe … wurde dir zugewiesen"; ändert sich ein Start- oder Enddatum, geht „Der Termin der Aufgabe … hat sich verschoben" an die Zuständigen — auch dann, wenn die Verschiebung erst über die Propagationskette (TaskSchedulingService) oder über „Termine neu berechnen" entstanden ist. Verschiebt eine Aktion eine ganze Kette, erhält jede Person eine Meldung, die die angefasste Aufgabe benennt und die Zahl der weiteren für sie betroffenen Elemente nennt, statt einer Flut von Einzelmeldungen. Wer die Änderung auslöst, hört nichts davon; stummgeschaltete Konten und Projekte mit abgeschalteten Erinnerungen (Issue #207) bleiben still, und wer das Projekt nicht sehen darf, wird nicht benachrichtigt. Anders als die Fälligkeits-Erinnerungen fallen die Ereignis-Meldungen nicht auf den Projekteigentümer zurück: gemeldet wird nur, wer über eine verknüpfte Ressource (Issue #203, #103) tatsächlich zuständig ist. Die Meldungen tragen den eigenen Objekttyp task_event, damit der Tageslauf sie beim Aufräumen seiner eigenen Erinnerungen zum selben Element nicht mit zurückzieht; je Aufgabe und Empfänger bleibt höchstens eine offene Ereignis-Meldung stehen — die neue ersetzt die vorige. Die Empfängerermittlung samt Stummschaltung liegt dafür jetzt im gemeinsamen NotificationRecipientService, den sich DueReminderService und der neue EventNotificationService teilen. Die persönliche Einstellung heißt entsprechend „Benachrichtigungen" und schaltet beides stumm

Fixed

  • Parameter außerhalb des Wertebereichs ergaben 500 statt 400 (gefunden bei Issue #205): Nextclouds Dispatcher prüft Integer-Parameter mit @param int<min, max> — und limit ohne Angabe auf 1–500 — noch vor dem Controller. Die ParameterOutOfRangeException machte die ExceptionMiddleware bisher zu 500 internal_error samt Fehlerprotokoll; jetzt antwortet die API mit 400 invalid_<parameter>
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureHgTu9/xKCVwgEQ94i1KF1Wc1ucKK+FK0W47ldxlBDNCRKgbhuvGpV7QKdpXiYR5G
Y18Tdot2H1tVhxwYqNPF4/dRt2zuaXzudAIrMrZ70Rxhxo15HNIcFulblH97Td76
HpBO3VxAlBeB6bSQS+YnC3HRC0koDMVJu7VtabL0duzWYZap0539AhczP2rXiAnN
+PxyU8dubrSLOljWGiL6jExiK7Eg6L1luFujNp4MnI8/cBqlBobjRgGmXF5SVfF0
kIcBN8T3Dcpg1w8fuGe68YnaR0xv3ML63zn1nBFb+5zkANv8Yb08dMIQdaNmkCEq
SNSWazd81/HbHDyLEUTK1fpDol9Hn8fv1+4u3MuvCzOcVZjxrvDozAyLLGQX7Y/Y
bup5FbA/WUYXwW6H4XyltYjW60+ivbTsbKflYn6lrbdevFb+z7s0niDgRzSGXIc3
jFh+7NpYzrCVnawKHxJjnyQGrjUNuuozbeAgUh2MaU0D7hh2LuZlLJ7pf5hZTvO+
q1yqdLMiSnmHIFeBh35qH7wywsUmkQYViopegvbRt0kz+ksyHmaxgOE9TOp6JoIc
kivnQLil1Tl0J0gK8w3Nk8q2Hj0WRzVuMyqTqBvAeP54rHM+jOtw3GIlX6XxjBUE
knAkfESZIbDGMrBH4sIFWQP28xCViSGAmideS61nwAY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.2
Release Details
UpdatedSept. 13, 2026, 4:16 p.m.
Changelog

Security

  • Eine zyklische Teilprojekt-Hierarchie konnte den Server blockieren (Issue #199). SubProjectService::create()/update() prüften bei parent_id nur, ob das Ziel-Teilprojekt zum selben Projekt gehört, nicht aber, ob dadurch ein Zyklus entsteht (z. B. A → Parent B, B → Parent A). findDescendantIds() lief bei einem solchen Zyklus beim Löschen in eine Endlosschleife (CPU-Hang, unbegrenztes Speicherwachstum) — auslösbar für jede Person mit Bearbeitungsrecht am Projekt, nicht nur für Eigentümer oder Admins. Eine Zyklusprüfung analog zu TaskSchedulingService::assertValidDependencies() weist solche Änderungen jetzt ab (BadRequestException parent_cycle); zusätzlich bricht findDescendantIds() über ein visited-Set ab, falls Altdaten bereits einen Zyklus enthalten
  • Beim Anlegen einer Aufgabe ließen sich Abhängigkeiten zu Aufgaben fremder Projekte setzen (Issue #190). TaskService::create() reichte mitgesendete dependencies ungeprüft an TaskSchedulingService::addDependencies() weiter; eine predecessor_id aus einem nicht zugänglichen Projekt erzeugte so einen task_dependencies-Eintrag über die Projektgrenze hinweg. Der Create-Pfad ruft jetzt wie schon update() vorher assertValidDependencies() auf, analog zur IDOR-Prüfung in MilestoneService

Added

  • Projekte, Teilprojekte und Aufgaben einklappbar (Issue #197): Ein Dreieck vor jeder Zeile mit Unterobjekten (Projekt, Teilprojekt oder Aufgabe mit Meilenstein) blendet deren Nachfahren per Klick aus und wieder ein; ein Tooltip nennt den aktuellen Zustand. Weil Tabelle und Chart zeilengleich scrollen, wirkt das Einklappen auf beide Ansichten — buildRowModel() filtert die gemeinsame Zeilenliste, bevor buildFrappeRows()/buildTableRows() sie übernehmen. Vorgänger-Verweise auf ausgeblendete Zeilen werden aus den Chart-Balken entfernt, damit frappe-gantt beim Ziehen eines Balkens nicht auf einen fehlenden Balken zugreift. Der Zustand wird pro Nutzer über die bestehenden UI-Einstellungen gespeichert
  • Eigene Farbe für Teilprojekte und Aufgaben (Issue #196): Im Detaildialog von Teilprojekten und Aufgaben lässt sich jetzt — wie bisher nur beim Projekt — eine Farbe aus der Palette oder frei wählen. Ohne eigene Farbe („Geerbt") übernimmt das Objekt die Farbe des nächsten übergeordneten Teilprojekts bzw. des Projekts; Meilensteine erscheinen in der Farbe ihres übergeordneten Objekts. Balken, Summenklammern und Meilenstein-Rauten im Chart sowie der Farbpunkt vor jedem Eintrag der Tabellenansicht (bisher stets die Projektfarbe) verwenden diese effektive Farbe (src/gantt/colors.js); die dynamischen CSS-Regeln hängen dafür nicht mehr an der Projekt-id (gantt-color-<id>), sondern am Farbwert (gantt-fill-<hex>)
  • Die color-Spalte von Aufgaben und Teilprojekten gab es schon, sie war aber mit #0082c9 vorbelegt und wurde nie angezeigt. Die Migration Version1033Date20260913000000 setzt den Spalten-Default auf NULL (= erbt) und den Vorbelegungswert in den Bestandsdaten zurück. Objekte, die ihn trugen, sehen daher unverändert aus; Farben, die davon abweichen — etwa aus JSON-/CSV-Importen oder direkten API-Aufrufen, die bisher gespeichert, aber nie angezeigt wurden —, erscheinen nach dem Update erstmals im Chart. Die API liefert für Objekte ohne eigene Farbe jetzt color: null; PUT /tasks/{id} und PUT /subprojects/{id} akzeptieren color: null bzw. "" zum Zurücksetzen — bisher verwarf das array_filter im Controller einen solchen Wert

Changed

  • Dokumentation nach Sprache (DE/EN) und Zielgruppe organisiert (Issue #191): Entwickler-Doku liegt jetzt übersetzt unter docs/de/ und docs/en/ im Repository-Root (CONTRIBUTE.md, RELEASE.md, HOWTO_Release.md, sowie die vormals in src/detail/, tests/e2e/ und tests/fixtures/ liegenden READMEs als detail-module.md/e2e-tests.md/test-fixtures.md, mit kurzen Verweis-Stubs an den alten Pfaden), Nutzer-Doku unter nc_gantt/docs/de/ und nc_gantt/docs/en/ (FEATURES.md, HOWTO.md, INSTALL.md, LICENSE.md). Die Root-README.md ist die vormalige nc_gantt/README.md, ergänzt um ein Sprachen-/Themen-Inhaltsverzeichnis am Anfang; die alte Root-README (zehn Anwendungsfälle) ist darin aufgegangen. nc_gantt/CHANGELOG.md bleibt bewusst an seinem bisherigen Pfad und einsprachig, weil release.sh, scripts/validate-appinfo.js und tests/Unit/AppInfoXmlTest.php exakt diesen Pfad für die Versionsabschnitts-Prüfung voraussetzen und der historische Log als technisches Protokoll nicht rückwirkend übersetzt wurde; LICENSE.md führt den englischen AGPLv3-Text als maßgeblich, die deutsche Fassung ist als inoffizielle, nicht rechtsverbindliche Übersetzung gekennzeichnet. appinfo/info.xml verlinkt in allen sieben App-Store-Sprachen sowie in <documentation> (user/admin/developer) jetzt auf die neuen docs/<lang>/…-Pfade, mit Englisch als Fallback für Sprachen ohne eigene Doku (fr/es/it/nl/pt-br)
  • Deutsche Übersetzung der README (Issue #194): nc_gantt/docs/de/README.md übersetzt die Root-README.md vollständig (Anwendungsfälle, Funktionen, Installation, Architektur); beide Dateien verlinken oben in einer kleinen Sprachtabelle aufeinander
  • App-Store-Beschreibung ergänzt: Der Abschnitt „Gantt-Diagramm & Tabellenansicht“ in appinfo/info.xml nennt in allen sieben Sprachfassungen jetzt auch das Einklappen von Projekten, Teilprojekten und Aufgaben sowie die eigenen Farben für Teilprojekte und Aufgaben

Fixed

  • Ist-Kosten ließen sich im Aufgaben-Formular nicht speichern (Issue #198): Das Feld für actual_cost las das bei numberField() nicht vorhandene .value statt getValue(), sodass beim Speichern stets null statt des eingegebenen Werts an den Server ging
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureHezqeAXIywdFI8vq5gO4MON/W8MjpkNqrjO2PZgZFFAHkSovdh5IqDbaUoltzPgk
rPiZtmytaqM29hGS2Qqh+DyymRKK7hco5chJ7tcrNqZcDx3KC9UDH2u+GK8GzkS0
jMsvi603ajD3ApEAiZUH9f0eiCYVerg6+/vUh63LFYQuYb7zjyUrNzmNTRl55eeS
cSgDjEvM0kqbaD/FXtdWK0ZzCaOJcSpuCLorf5i7WJU2lQx0Wi0I6lFHYFBVzLBj
W+U8NMiFZE+Wy1YGGwz7QOY1o9fOmrxzwIchuLvLVPCT4j9Vk9YNByc2XREv++UJ
/l3q8EYLLASMKHkscessRS5izHRaTR3iwgHNrxvb8vaXjzu6+he45/LaeQC6pg1M
wsBgAdHTb7alK48/W4p8ex0W5x8l/XyGeBzcSXidxlFD4SKFE2LAGxRoDzIIPeOj
gC3MT8IvAuKU5/g2fruxOxa9Nv2HNh3V9zw4reGxf018TeKVESxE7oMYpn4aFwmK
aq8c+n7EVqGnY1imH+BwvL5EeKzjjBTKcb8Z8DHSxNx6fOG+8ExyJLL+/s9zUSXY
uB1EnmU6QTWH5Tb93gqG/JgVV/8MoERfXSNMi2VOAjsniaGHkagXKs830EwamrDs
ttDDE3UQ2EymEVIDa1v8WTKAxWtFC2sl4GXwuyOTUyA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.1
Release Details
UpdatedSept. 2, 2026, 6:15 p.m.
Changelog

Security

  • Ressourcen-, Gruppen- und Kapazitätsdaten fremder Projekte waren über geteilte Projekte einsehbar (Issue #183). ResourceMapper, ResourceGroupMapper und ResourceCapacityMapper::findVisibleToUser() gaben pauschal alle Datensätze eines Eigentümers frei, sobald irgendein Projekt dieses Eigentümers mit dem Nutzer geteilt war — unabhängig davon, ob die konkrete Ressource überhaupt in einem geteilten Projekt verwendet wird. Der neue zentrale ResourceVisibilityService prüft stattdessen, ob die Ressource tatsächlich in einer für den Nutzer sichtbaren Aufgabe verwendet wird, und wird von ResourceService, ResourceCapacityService, ResourceGroupService und TaskResourceAssignmentService genutzt. Damit lassen sich über eine geteilte Ressource weder die übrigen, nicht geteilten Ressourcen desselben Eigentümers einsehen noch eigenen Aufgaben zuordnen
  • GET /api/v1/resources/{id}/utilization gab Planungsdaten aus nicht zugänglichen Projekten preis (Issue #181). ResourceUtilizationService::findVisibleResource() prüfte nur, ob der Ressourcen-Eigentümer irgendein Projekt mit dem Nutzer geteilt hat; über die Auslastung waren dadurch Aufgaben- und Projekttitel samt Terminen aus fremden Projekten abgreifbar. Die Prüfung stellt jetzt projektbezogen fest, ob die Ressource in mindestens einem über AccessService::canViewOrOwn() sichtbaren Projekt verwendet wird; zusätzlich filtert collectPlannedHours() die Aufgaben auf genau diese Projekte, sodass auch bei erlaubtem Ressourcenzugriff nichts aus fremden Projekten durchsickert
  • Aufgaben und Teilprojekte ließen sich an fremde Projekte hängen (Issue #179). TaskService::create()/update() und SubProjectService::create()/update() prüften nur die Schreibberechtigung auf die eigene projectId, nicht aber, ob eine mitgesendete subproject_id bzw. parent_id überhaupt zu diesem Projekt gehört — mit Folgewirkung beim Löschen eines Teilprojekts (SubProjectService::deleteTasksForSubproject()) und im öffentlichen ICS-Feed (CalendarFeedService::buildIcs()), da beide über TaskMapper::findBySubProject() allein nach subproject_id filtern. Analog zum Schutz aus Issue #97 prüfen jetzt AccessService::assertTaskBelongsToProject() und assertSubprojectBelongsToProject() die Zugehörigkeit; deleteTasksForSubproject() und buildIcs() filtern die geladenen Aufgaben zusätzlich defensiv nach project_id, um vor dem Fix entstandene Altdaten abzudecken

Changed

  • Dokumentation aufgeräumt: Die Doku ist jetzt nach Zielgruppe getrennt — alles, was Nutzerinnen und Nutzer brauchen, liegt in nc_gantt/ und wird damit vom release.sh-Archiv mit ausgeliefert, das Repository-Root enthält nur noch Entwickler-Doku. Dafür sind FEATURES.md, INSTALL.md und die neue HOWTO.md nach nc_gantt/ gewandert; die Root-README.md beschreibt die zehn wichtigsten Anwendungsfälle des Werkzeugs, erklärt je Fall den gedachten Arbeitsweg und führt in einer Repository-Übersicht auf, welches Dokument für wen gedacht ist. Neu ist nc_gantt/HOWTO.md: eine Schritt-für-Schritt-Anleitung entlang eines durchgerechneten Beispielprojekts, die alle zehn Anwendungsfälle mit konkreten Feldwerten, Abhängigkeiten und Terminen durchspielt
  • Dabei korrigierte Verweise und Angaben: der Lizenz-Link in nc_gantt/README.md zeigte auf das nicht vorhandene LICENSES/AGPL-3.0-or-later.txt statt auf LICENSE.md; die Nextcloud-Anforderung stand mit „28–33" in README.md und „28–34" in FEATURES.md gegen die tatsächlichen 31–34 aus appinfo/info.xml; FEATURES.md nannte noch Version 1.5.0; INSTALL.md verwies auf ein dist/nc_gantt-<version>.zip im Repository, das es nicht gibt (dist/ ist nicht versioniert und release.sh baut .tar.gz), und in CONTRIBUTE.md trug ein Link auf RELEASE.md die Beschriftung release.sh. Alle relativen Links und Abschnitts-Anker der versionierten Markdown-Dateien lösen jetzt auf
  • Tests laufen künftig vor jedem Commit (Issue #185): setup-dev.sh installiert einen pre-commit-Hook, der Vitest und PHPUnit (über Docker) ausführt und den Commit bei Fehlern abbricht — genau die Stelle, an der die vier defekten Tests aus dem Ressourcen-Sichtbarkeits-Commit hängen geblieben wären. phpunit.xml schaltet zusätzlich failOnRisky und failOnWarning ein, damit ein Test ohne Assertion den Lauf rot färbt statt unauffällig grün zu bleiben, und CONTRIBUTE.md dokumentiert die Regel zu Mock-Defaults ohne with() in setUp()
  • Die Default-Stubs in setUp() von ResourceVisibilityServiceTest blockierten die spezifischeren Stubs der einzelnen Testmethoden — PHPUnit wertet für dieselbe Methode den zuerst registrierten Matcher aus, und der greift per any() bei jedem Aufruf. findByResourceId(), findByIds() und findByResourceIdExcluding() lieferten dadurch immer ein leeres Array, was vier Failures und zwei risky Tests verursachte. Die Defaults sind entbehrlich, weil PHPUnit-Mocks für den Rückgabetyp array ohnehin ein leeres Array liefern; der Produktivcode blieb unverändert
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerHm/UxN1WTCOMd9LklleINo7LKDOEAyn1zlsR5eTCaUmdGaN1m8Knqk/EKDYcj73
zWx7TXmT4cP91Ag7mCMwY/gRqJabF7TgeXiA7CeV7VE7/D/+apIatMWRT7zYCjkF
oxXKimNrtpDSKWNXKSQn6RfJaEHu5WnIoF4X9Nyx/wf1WoWoThyvWMSSW/3jdajV
WXCV4XQjzbTULw9T+Ini/e8vp6mXt5CrmCdyUDQQcj2t9PF9Hq3GvWT1VQlYA8Kf
IJuqnArSc4JdtJiJnozpOKSBkAbDPTrFytwUTVUO4XlrGIvyqHv14ZVmBM8UCMo6
yVNA9HEBW62/b8W+Z3BtCBTuBnkRk3Wka8qhMMbqOe8kl/1vQtTKQkaDdnXaShUs
18mfDHjLZhcIrJEwbtPvo9Xpf+IWEZeoiRzEqa30n70iZTkPEVv/GxNVmJ74XYH/
Xa/vMWI/sxtXKtKY60XBXXCEVMuyxR+vfIbJOpDCcFlKyWHiXPEdYh2Ac8Bs1hc7
bk+lolw8FEWISrFdUwYdOrfRr4CMW+h2CYzgZsDKKYrVn+hRUy5mVOL1QXZJbNJJ
wr9O/JrUbXAMGdhtLuRUwM4tsPgMF2+cTLMe+wuFzUjWBBNiqb8GKJP2K94hdurD
+VMd/p/TtU02M23ooysu2mft5RCmD0/QbP8yxEACbHg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.0
Release Details
UpdatedAug. 28, 2026, 2:34 p.m.
Changelog

Achtung: Diese Version setzt Nextcloud 31 oder neuer voraus. Die bisherige Untergrenze war Nextcloud 28. Mit dem Umstieg auf @nextcloud/vue 9 — unvermeidbar, weil Vue 2 seit dem 31.12.2023 End-of-Life ist und keine Sicherheitskorrekturen mehr bekommt — fällt die Unterstützung für Nextcloud 28, 29 und 30 weg. Installationen auf diesen Versionen bekommen das Update nicht angeboten und bleiben auf 1.5.4; sie werden von dort nicht weiter gepflegt. Wer die App weiter nutzen möchte, aktualisiert zuerst den Nextcloud-Server auf mindestens Version 31 (max-version bleibt unverändert 34).

Changed

  • Das Frontend läuft auf Vue 3.5 statt Vue 2.7 (Issue #150). Vue 2 ist seit dem 31.12.2023 End-of-Life und bekommt weder Sicherheits- noch Fehlerkorrekturen; die Nextcloud-Server-Generation, gegen die die App freigegeben ist, hat ihren Frontend-Stack längst auf Vue 3 umgestellt. Mit umgestellt sind @nextcloud/vue 8 → 9, @nextcloud/dialogs 6 → 7, Pinia 2 → 3 und @nextcloud/webpack-vue-config 5 → 7; die dreizehn Build-Pakete, die das Webpack-Preset ab Version 7 als peerDependencies erwartet (webpack, vue-loader, sass, babel-loader …), stehen jetzt explizit in den devDependencies. npm audit --omit=dev meldet damit 0 statt 14 Befunde — die vier an Vue 2 hängenden Advisories (vue, vue-template-compiler, vue-resize, vue2-datepicker) waren ohne diese Migration nicht auflösbar
  • appinfo/info.xml fordert jetzt Nextcloud 31 statt 28 (max-version bleibt 34). @nextcloud/vue 9 lässt die Unterstützung für Nextcloud vor 31 fallen; das ist die unvermeidbare Gegenleistung der Migration. Installationen auf Nextcloud 28–30 bleiben auf Version 1.5.4
  • Die drei Einstiegspunkte src/main.js, src/admin.js und src/dashboard.js erzeugen ihre Anwendung über createApp() statt new Vue(); Pinia wird als Plugin registriert (app.use(pinia)), das Weiterreichen der Store-Instanz an das Vanilla-Detailmodal per setActivePinia() bleibt unverändert. Die Vue.prototype-Zuweisungen im Dashboard-Einstiegspunkt sind entfallen — DashboardWidget.vue importiert t ohnehin selbst
  • Anpassungen in den 17 Komponenten an die API von @nextcloud/vue 9: Import-Pfade (@nextcloud/vue/components/NcButton statt @nextcloud/vue/dist/Components/NcButton.js), type="primary" → variant="primary" bei NcButton/NcActions, NcActionCheckbox und NcSelect auf modelValue (:checked/@update:checked bzw. :value/@input sind entfallen), :shown.sync → v-model:shown bei NcPopover. Dazu die Vue-3-Umstellungen im eigenen Code: beforeDestroy() → beforeUnmount() in GanttTable.vue, ResourceGrid.vue und GanttChart.vue (siehe Issue #149), this.$set()/this.$delete() durch direkte Zuweisung bzw. splice() ersetzt
  • css/main.css und der zugehörige Util::addStyle()-Aufruf im PageController sind entfallen. Die Datei enthielt einzig #content { margin-top: 0 !important } gegen eine Doppelmarge, die NcContent unter @nextcloud/vue 8 zusätzlich zum Nextcloud-Layout gesetzt hat. Unter Version 9 gibt es diese Marge nicht mehr, der Reset schob die App stattdessen um die Höhe der Kopfzeile nach oben — die Navigationsleiste lag dadurch unter dem Nextcloud-Header und war nicht mehr anklickbar. Der Mount-Punkt #nc-gantt-app bleibt als Wrapper im DOM stehen (Vue 3 rendert in das Element hinein, statt es wie Vue 2 zu ersetzen), nimmt aber nachgemessen exakt die Maße des Content-Bereichs an und braucht keine eigene Regel
  • ESLint nutzt das Preset @nextcloud/eslint-config/vue3. Es lässt vue/custom-event-name-casing auf dem Vue-3-Default (camelCase) laufen, während das Vue-2-Preset kebab-case erzwungen hat; da der Bestand durchgängig kebab-case verwendet — unter Vue 3 gleichermaßen gültig — setzt .eslintrc.js die Regel explizit auf kebab-case
  • Der <style>-Block in GanttChart.vue war mit 581 Zeilen ungescopt und wirkte damit auf die gesamte Nextcloud-Oberfläche statt nur auf die Komponente (Issue #151). Die 65 eigenen .nc-gantt-*/.nc-*-Regeln laufen jetzt über <style scoped>, mit :deep() dort, wo Overlays ihre Elemente per createElementNS() bzw. innerHTML außerhalb des Vue-Templates in .nc-gantt-container einhängen. Die Regeln, die auf frappe-gantt-eigene Klassennamen zugreifen (.gantt .bar & Co.), liegen gebündelt in src/vendor/frappe-gantt/theme.css direkt neben dem vendorten Quellcode der Bibliothek und werden von dort per index.js importiert; die beiden nicht unter .gantt verankerten Selektoren (.bar-wrapper.gantt-milestone .bar-label/.handle) sind dabei korrigiert. In App.vue laufen die drei Core-Überschreibungen (.app-navigation__content, #app-settings, .app-navigation .app-navigation-list) ebenfalls über :deep() in einem scoped Block. Abgesichert ist das durch vue/enforce-style-attribute aus eslint-plugin-vue, das ab sofort scoped für jeden <style>-Block in src/ erzwingt
  • TaskService bündelte auf 774 Zeilen drei Verantwortlichkeiten und brauchte dafür acht Konstruktor-Abhängigkeiten; der Schnitt trennt sie (Issue #153). TaskSchedulingService übernimmt Constraint-Propagation über EA/AA/EE/AE mit Lag, Kahn-Sortierung, Arbeitstagsrechnung, Zyklenprüfung und What-if-Simulation — CriticalPathService wird dort regulär injiziert, statt simulate() als Methodenparameter durchgereicht zu werden. TaskResourceAssignmentService übernimmt Auflösung und Persistierung der Ressourcenzuordnung inklusive MAX_TASK_RESOURCES und PT-Umrechnung. TaskService bleibt als CRUD-Fassade mit Berechtigungsprüfung, Validierung, Änderungsprotokoll und API-Aufbereitung: 311 statt 774 Zeilen, sieben statt acht Abhängigkeiten. Die Methodenrümpfe sind unverändert verschoben, es ändert sich keine Terminlogik. TaskServiceTest ist entlang derselben Grenzen aufgeteilt (TaskServiceTestCase als gemeinsame Basis, dazu TaskSchedulingServiceTest mit 39, TaskResourceAssignmentServiceTest mit 8 und TaskServiceTest mit 11 Fällen — exakt der bisherige Umfang), wobei die ausgelagerten Services weiterhin über ihren Aufrufer TaskService angetrieben werden, damit neben der Rechnung auch die Verdrahtung zwischen Fassade und Engine abgedeckt bleibt
  • Die 212 Zeilen CSS der Vanilla-Detaildialoge wurden zur Laufzeit als <style> in den <head> injiziert (src/detail/styles.js); sie liegen jetzt als src/detail/detail.css vor und werden von Webpack eingebunden (Issue #152). Der im Kopfkommentar genannte Grund — nicht von der Loader-Konfiguration abhängen zu wollen — trifft seit dem Vendoring nicht mehr zu, src/vendor/ importiert .css und .scss ebenfalls direkt. Dabei fiel auf, dass das Stylelint-Glob unquoted an die Shell ging und ohne globstar nur eine Verzeichnisebene traf: weder src/App.vue noch eine .css in src/vendor/ wurde je geprüft. Mit Quotes globbt Stylelint selbst; die dadurch aufgedeckten Findings in App.vue sind mitbehoben
  • Die PHP-Einrückung ist auf Tabs vereinheitlicht (Issue #170). nextcloud/coding-standard erwartet Tabs, die Codebasis nutzte durchgehend vier Leerzeichen — composer cs:check beanstandete dadurch 144 von 146 Dateien, der CI-Job wäre ab dem ersten Lauf dauerhaft rot und als Gate wertlos gewesen. Ursache der Drift war ein falscher Kommentar in .editorconfig, der dem Coding-Standard vier Leerzeichen unterstellte; der ist mitkorrigiert. Die Änderung ist rein mechanisch (php-cs-fixer fix): Einrückung, Klammern um einzeilige if-Rümpfe, sortierte use-Blöcke und einfache statt doppelte Anführungszeichen, wo keine Escape-Sequenz im Spiel ist. Der Commit steht in .git-blame-ignore-revs, damit git blame weiterhin die inhaltlich letzte Änderung einer Zeile zeigt

Added

  • appinfo/info.xml wird jetzt automatisch gegen das App-Store-Schema geprüft, statt den Fehler erst beim Upload zu erfahren: scripts/validate-appinfo.js (npm-Script validate:appinfo) validiert gegen die eingecheckte Schema-Kopie tests/fixtures/info.xsd — per xmllint, ersatzweise über lokales php oder das composer:2-Image — und prüft zusätzlich Semver-Format, Gleichstand mit package.json und den CHANGELOG-Abschnitt zur Version. release.sh ruft das als erstes Gate mit --online auf, lädt das Schema dabei frisch und meldet, wenn die Kopie veraltet ist
  • tests/Unit/AppInfoXmlTest.php führt dieselbe Schema-Validierung in der Testsuite aus (DOMDocument::schemaValidate()) und gleicht zusätzlich die Übersetzungen in l10n/ gegen die Store-Texte ab: gettext-Codes wie pt_BR werden als solche erkannt, und zu jeder übersetzten Sprache müssen <name>, <summary> und <description> in info.xml existieren
  • Die Stunden je Personentag sind konfigurierbar (Issue #169). Der Umrechnungsfaktor lag dreifach hartkodiert vor (TaskResourceAssignmentService, ResourceUtilizationService, ResourceGrid.vue) und wurde über gegenseitige Kommentarverweise von Hand konsistent gehalten — eine feste 8-Stunden-Annahme passt aber nicht zu jedem Arbeitszeitmodell. Der Wert liegt jetzt an genau einer Stelle: WorkTimeService liest ihn als App-Config-Wert hours_per_pt (Standard 8), validiert ihn gegen 1..24 und fällt bei unplausibel gespeicherten Werten auf den Standard zurück, damit die Auslastungsrechnung nicht durch 0 teilt; in ResourceUtilizationService entfällt damit auch die Konstante DEFAULT_CAPACITY_H, die denselben Vollzeit-Referenzwert doppelt hielt. Ins Frontend kommt der Faktor über den Initial State des PageController, sodass er schon beim ersten Rendern des Ressourcen-Rasters bereitsteht; GET/PUT /settings liefern und setzen ihn zusätzlich, wobei ein PUT ohne hours_per_pt den gespeicherten Wert unverändert lässt, damit ältere Clients ihn nicht auf den Standard zurücksetzen. Gepflegt wird er im Admin-Bereich neben Arbeitsbeginn und -ende
  • Die Vanilla-Detaildialoge sind für Screenreader und Tastatur verdrahtet (Issue #152): Die Dialoge verweisen über aria-labelledby auf ihre Überschrift, section() verknüpft Beschriftung und Bedienelement (bei Gruppen über role="group"), Knöpfe ohne Text bekommen einen Namen, Umschaltknöpfe melden ihren Zustand über aria-pressed, und die Datums-Plausibilitätsmeldung hängt per aria-describedby am Feld statt nur im title-Attribut des Icons zu stehen. Der Fokus beginnt beim Öffnen im Formular statt auf dem Schließen-Knopf und kehrt beim Schließen zum Auslöser zurück. Abgesichert durch payload.test.js (19 Fälle für Speicher- und Löschlogik) und accessibility.spec.js (6 E2E-Fälle) — für DOM-Code ist der E2E-Lauf die tragfähigere Absicherung als eine neue jsdom-Abhängigkeit. src/detail/README.md erklärt, warum das Modul außerhalb von Vue steht und welche Regeln daraus folgen; CONTRIBUTE.md verweist darauf
  • Die CI läuft. Der Workflow lag unter nc_gantt/.gitea/workflows/ci.yml und war eine reine GitHub-Actions-Datei; vier voneinander unabhängige Gründe verhinderten, dass davon jemals etwas ausgeführt wurde (Issue #170): Forgejo liest Workflows ausschließlich im Repo-Root, runs-on: ubuntu-latest passt auf kein Codeberg-Label (die gehosteten Runner heißen codeberg-tiny/-small/-medium), die Kurzform shivammathur/setup-php@v2 wird gegen Forgejos Action-Host aufgelöst, wo es die Action nicht gibt, und die Repo-Einheit „Actions" war deaktiviert. Die Datei liegt jetzt unter .forgejo/workflows/ci.yml, die App bleibt via defaults.run.working-directory erreichbar. Inhaltlich sind php-lint und php-tests zu einem Job zusammengefasst und laufen beide über die volle Matrix 8.1/8.2/8.3 (vorher lief PHPUnit nur auf 8.2, obwohl composer.json >=8.1 fordert), die Syntaxprüfung nutzt xargs statt find -exec (find liefert seinen eigenen Exitcode, nicht den von php -l — der Schritt konnte gar nicht fehlschlagen), und der JS-Job führt neu vitest mit 180 bislang nie automatisiert gelaufenen Tests aus, dazu validate:appinfo und l10n:check. paths-ignore hält die geteilten Codeberg-Runner von reinen Doku- und Screenshot-Änderungen frei

Fixed

  • Nach der Migration auf @nextcloud/dialogs 7 erschienen Statusmeldungen wie „Einstellungen gespeichert" als unformatierter Text oben links statt als Badge. Version 7 hat Toastify durch eigene Vue-Komponenten ersetzt, deren Styles nicht mehr im JS-Bundle stecken, sondern über den separaten Export @nextcloud/dialogs/style.css eingebunden werden müssen; ohne diesen Import lief der Toast-Container ungestylt im normalen Dokumentfluss mit. src/main.js importiert das Stylesheet jetzt. Die Toasts erscheinen dabei an der neuen Standardposition der Bibliothek unten links neben der Navigation statt wie früher oben zentriert — Version 7 bietet dafür keine Option mehr
  • testBuildIcsStripsCarriageReturnToPreventLineInjection prüfte das gesamte ICS-Dokument auf Freiheit von \r. RFC 5545 trennt Zeilen aber genau damit, und buildIcs() setzt sie korrekt als CRLF — der Test konnte nie grün sein und schlug auf main dauerhaft fehl. Der Schutz selbst greift; nur die Assertion war zu weit gefasst. Geprüft wird jetzt, was die Line-Injection tatsächlich ausschließt: kein einzelnes CR außerhalb der CRLF-Zeilentrenner, der eingeschleuste Titel erzeugt keine eigene ICS-Zeile, und er landet escaped im SUMMARY-Wert. Gegenprobe mit entfernter CR-Normalisierung in escapeText() schlägt weiterhin fehl, der Schutz bleibt also scharf getestet. Dass der Fehlschlag so lange unbemerkt blieb, lag daran, dass die Workflows nicht ausgeführt wurden (siehe Issue #170)
  • validate:appinfo brach im ersten CI-Lauf mit einer Meldung über einen nicht erreichbaren Docker-Socket ab (Issue #170). Die Validator-Kette in scripts/validate-appinfo.js probiert xmllint, dann php, dann docker und nimmt den erstbesten verfügbaren — „verfügbar" hieß dabei aber nur, dass das Binary existiert. Das Runner-Image bringt die docker-CLI mit, ohne dass ein Daemon erreichbar wäre, und hat weder xmllint noch php. isAvailable prüft jetzt, ob der Validator wirklich läuft (docker info statt --version), sodass die Kette korrekt durchfällt und bei gänzlich untauglicher Umgebung „Kein Schema-Validator gefunden" meldet statt eines Socket-Fehlers; zusätzlich installiert der JS-Job libxml2-utils, damit im CI immer xmllint gewinnt — der native XSD-Validator, ohne Daemon und ohne Netzwerk. Welcher Validator läuft, hing zuvor davon ab, was auf der Maschine zufällig installiert war
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegDK8JkRgxBUCFljhxHFzTzf37tjhwQ/k9E+DCDeVHxPECDOF0J3FbWmv2uqmm9R7
H8yyFfL/KFEDK3Jt24o2gujQZ9KlyBYJvrfwv//zvACtgGoErBUamME+FzFfK3x8
rRdXt1iXctmwutRvTNPj8uvfMJmo/vdbJRDGEihI/9wVdt4S+O3/OlpdjUg+QGFP
cMmlQwIAHAWrYSUllnN4GeryrFPGB7fLi/Nrhx0eAwQHUjIZHEb/s9ZxjmnYZd0c
8FJsgAUXfjowOIrDtWxr0P+HVj4c5qhGx4XEUFdfR0slriaL+zPbo2Li1wgsBA7r
+yoo8ocOJPaN3iCpRNL4z8q2sxEuWRt2Dx0I5ck6F7g+gpzDoTo5bFEvq4Eir0wN
lGHhyxcdEK653dTKOBYI07tCjJYU+9VxCbRzR7O+sVI412Tox5W5cYGl4WpN8aNH
00CX7PLaruaEjwuFdAvhY6SzsXA3RFmN3Dv9qFx3dl3HjoQ+OCqy89XzT3tbBN07
3aZO7OgyB26al2XdRFaaRQaVLqjJzUCz2B5nh+z9+L+QcBl3F9sE2sFZnkWwx3bp
MOdj2nCS11r3Z0utRI9uDEanys6HkqnpGed4WEGX9O+EfocqNg2b0wgyqjFNWbxI
n4uoeakhyWYOVHCUfzU8tn6kmU4b8n+GfpMjKPhBeRw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.4
Release Details
UpdatedAug. 27, 2026, 7:06 p.m.
Changelog

Fixed

  • appinfo/info.xml wurde vom Nextcloud App Store abgelehnt: der Sprachcode pt_BR in <name>, <summary> und <description> ist im Schema nicht enthalten — die Enumeration kennt nur BCP-47-Schreibweise in Kleinbuchstaben mit Bindestrich. Alle drei Vorkommen heißen jetzt pt-br
  • Die Kindelemente von <info> sind im Schema eine feste Sequenz; <settings> stand vor <dependencies> und wäre nach dem Sprachcode-Fix als nächster Validierungsfehler aufgeschlagen. Der Block steht jetzt an der vom Schema vorgesehenen Position hinter <dependencies>. xmllint --schema gegen https://apps.nextcloud.com/schema/apps/info.xsd läuft damit sauber durch
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureYbjiEkGYW1s8Xg9Jvrt4nNo/67zkrQDA16yfK2MzVa58R982qrLUupNbvUE/FscP
pFg9nlqxd81cpyH1ZhWOM7fMq98mPyjSUsJZ7xjYzNccF13xvDpBXFlfX4UyCknc
50Rmw9a8RxiCH0mEpH9zWjdanX194R8cUIMDY+BcymCgyBVRQub9yaL5iiHH3hNA
OOD+UOiSUNHns9R6sgZ+tF4OeoTufWR5//AfRmuL5qC5spyuP1iKr7FnzPar+/sS
pHKojfUyi0sAjlrThVZDo8fvVPqkkH4jgvVmNE41t1UOlVdajIRd2yhbHX0h0SSs
rmt4CQqNXAjLE/Wlq2lu9Viw4M2BhhPIfVb5YJFzaxjhB8RRy53rquXRL64f7vGa
agq0ri3eD+0ZWmMOlJmHaMv6AXYd0ZG8myb3PAyNC0NYXzrw2PAVL8jDfzmq2cNY
Q3rIh101vtGPytqanNS2VmtfZ8BX63rd4SLR4PJdRYrm3Wbqanx8WZa20eLgYF8S
M9xhxl5Jf8+TnYLwHi1sGm1ID2XJ4krfgOxiUlB8lGeSweUK1uueC0bmUDsrXyhJ
RMGST41TU1HqXx/3Eeh91JKq/Jbx15kPuxu7WNKyuSOj4vKbgFwBUSmHKDU6lvEZ
L/bnxZi965/4K3kvyJ8o2j5e4xUmJLeusi9Fg0yrRqE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.2
Release Details
UpdatedAug. 26, 2026, 5:18 p.m.
Changelog

Changed

  • Teilen-Dialog: das Eingabefeld für den einzuladenden Nutzer ist jetzt ein Suchfeld statt eines reinen Textfelds für die Benutzer-ID (Issue #144). Getippt wird nach Anzeigename, Benutzer-ID oder E-Mail-Adresse; ein neuer, admin-unabhängiger Endpunkt GET /api/v1/users/search (UserController::search(), IUserManager::search(), max. 20 Treffer, der eigene Account wird ausgeblendet) liefert dazu Anzeigename, Benutzer-ID und E-Mail als Vorschlagsliste. Gespeichert wird weiterhin die Benutzer-ID als principal_id – für Umgebungen mit LDAP/AD-Benutzerverwaltung war die zufällig generierte ID zuvor nicht ohne Umweg über die Benutzerverwaltung in Erfahrung zu bringen. Die Liste bereits geteilter Zugriffe zeigt ebenfalls den Anzeigenamen statt der rohen ID (ShareController::index() reichert die Antwort um display_name an, Fallback auf principal_id falls der Account inzwischen gelöscht wurde)
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureyLn/jlYbk3gIYfgIcjON0z467/xL9L6QNcmfgOa/4zhTJjLknXq7eu1UJddWWlLJ
NG2U9d/OEFO9wLHPYPGNiWgQMRIk+TGYlQVTXcZH3WpBxdJOm3XMnk5hq2kNB2FN
TJemqr/dxafGB2hTIgivPMXMnemdrtxWN1RAdzCxZIg9HwJWjG5JxdqHmURHwske
p/Ki/iZghKjrxbPIghbqtrvkCJRImbgVWY8AmUNUPLgotNIthiWFnkTDVhYWcreP
pWleWHxEn57UOtehff1aJa+hRI+8WVkx9iXArx0SThmOCYYgJ/HeG8dOOtmr9jSm
1nzETAL87YLxL/+qM6DyBIgNWhrq1oj9Q12bBm1+la477z6fpOgUlMjWkPEYRQi/
vhxirXwB4BZBYvaMEkNlN2U3+7BqgigGoaTcbN2rG/TP1JuOjdj9jEjMMRye0JbT
35XOoM/VPYGovrT9slHg8r3Wolyd8ioXVOu4IcMImvC5OrTzpKY7NLEc/S+cOKwz
ijOKcnoV0j++KxtQKKgEO96HJpEM0jteQVOGCoPbghjS3OFzkiB1bUVJAlu1jn54
38Q2zw8rZ0EMm1rEPmIrl72DIIXionbWKatREgy7QPwuL/WKZ2c+J1UIUTKck5eO
7M2VHh6P3FnIg/UsOCMu3U7ja0Y8vQ3YUo3tazLfob0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.1
Release Details
UpdatedAug. 25, 2026, 1:28 p.m.
Changelog

Added

  • Screenshot des gerenderten Gantt-Charts als PNG, SVG oder PDF (Issue #116): ein neuer „Screenshot"-Eintrag in der Chart-Toolbar öffnet ein Menü mit den drei Formaten, derselbe Export steht im Bearbeiten-Dialog des Projekts neben JSON und CSV bereit (dort mit vorgeschalteter Formatwahl statt Menü, openScreenshotFormatDialog()). Exportiert wird stets das gesamte Chart, nicht nur der sichtbare Scroll-Ausschnitt — frappe-gantt rendert die volle Breite und Höhe ohnehin als eine SVG. PNG und SVG klonen die aktuell gerenderte SVG und schreiben getComputedStyle() als Inline-Attribute (löst auch CSS-Variablen auf), sodass die Datei ohne das App-Stylesheet eigenständig darstellbar ist; sie folgen dem eingestellten UI-Theme, ein hinterlegtes Rechteck in --color-main-background deckt dabei auch die Fläche unterhalb der Aufgabenzeilen (Workload-Chart, Leerraum) ab, die sonst transparent bliebe. PNG rastert per Canvas mit doppelter Auflösung
  • Zeitraum-Auswahl für den PDF-/Druck-Export (Issue #116): vor dem Drucken fragt ein Dialog, welchen Zeitraum die PDF abdecken soll — gesamter Projektzeitraum (Vorbelegung), laufendes Kalenderjahr, „aktives Quartal" (Vormonat bis einschließlich übernächstem Monat) oder ein frei eingegebener Bereich. Der Vorschlag ist der tatsächlich von Balken belegte Zeitraum, nicht gantt_start/gantt_end — frappe-gantt legt darum großzügig Puffer, der andernfalls mitgedruckt würde. Umgesetzt wird der Zuschnitt als viewBox auf dem SVG-Klon; die dafür nötige Skala (Chart-Start, Pixel pro Millisekunde) schreibt GanttChart.vue nach jedem Render als data-Attribute an den Chart-Container, sodass auch Aufrufer ohne Zugriff auf die frappe-Instanz (Bearbeiten-Dialog) sie lesen können. Die Zeitraum-Logik steckt isoliert testbar in src/gantt/printRange.js (src/gantt/printRange.test.js)

Changed

  • Der PDF-Export druckt nicht mehr den Live-Container, sondern einen gestylten, selbst gerasterten Klon des Charts (Issue #116). Bisher wurde das Inline-SVG direkt gedruckt — wie fein die Browser-Engine das rastert, ist nicht steuerbar und fiel je nach Browser sichtbar grob aus. Jetzt wird der Klon mit 300 dpi bezogen auf die bedruckbare Fläche (A4 quer, 10 mm Rand → 277 × 190 mm) in ein Canvas gerastert und als Bild mit fest vorgegebener physischer Größe in den Druck gegeben; schlägt die Rasterung fehl, bleibt der SVG-Klon als Druckinhalt übrig und skaliert per CSS auf die Seitenbreite. Der Klon wird immer im hellen Theme berechnet (Papier ist weiß, ein dunkles UI-Theme ergäbe sonst dunkelgraue Flächen und helle Schrift) — dafür genügt ein ausgelagerter Container mit gesetztem data-theme-light, da Nextcloud die Stylesheets aller Theme-Varianten lädt und nur über dieses Attribut umschaltet. Das Ausblenden der übrigen Seite läuft über display: none statt visibility: hidden, sonst blieb die Fläche der App als Leerseiten erhalten; Nextclouds Hintergrundbild auf html/body wird für den Druck abgeschaltet. Aufgeräumt wird über afterprint mit Timeout-Fallback für Browser, die das Event nicht feuern
  • Der Toolbar-Eintrag heißt „Screenshot" (Icon MonitorScreenshot) statt „Export" (Icon ExportVariant) — „Export" ist im Projekt bereits für den Daten-Export nach JSON/CSV belegt, der Chart-Export ist demgegenüber eine reine Abbildung der aktuellen Darstellung
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturek6kpvF53IR8tnkOFXyoSPFPGlwVzO/RUTgI1p1ppRcFmoFPUyFeIokOqWA1OtD9q
wdo+qkQQPyJGph0Uwp+hwP9oGmeKWErnlv8HMdA5I5iaXIN7afGvdfxWfKgqWetw
tGmCF7l2IzMmT2Ol/3pKgCnFkXTDPCVeNINNbUdi7EjeRpbcbOGWt3+5UVwZgVTX
Z9/kEEPvjMV98s1zT5yBXcFvyqZA7bhhezmBkfzaLa0DAIczFJbhyFtR06WuS7bP
CWBCs/xvSRuCwo61oyXRAWLohz/2GsrMSa5jvPvnEG1XEHpGPVorCijAvsBm4sX5
yAZTrBm3zbNYObqVE2idxTH/eqP8xXtE38JFjpG5IahpeFNb9V2EbH1nrjfRKYcH
NVvXcXFREAmvorShKDE0ymFN+/BCjvU1vnJCnRD5RMsoSGUzXSVHPrF1emtusLHj
ltP22eYOCpraEpZheTkFftqvpbiQKVXsZ+8wY4dJnh5Ymi71loxZhggoH8s/pmp+
WbSx6MZwSPBqJ0HOJWckFiZXur2OQYOVLfXt8uPYNU/EZSoYDqm81GVMglQXxyM6
YDh0xXqwJ7Qft7k0O/3Fa5YLJGP4B4RvjU29N3/WDsfjuk7PeCSTPhRlTOfV/Fbn
jS83WVBavmyklhu0rNYuCYfdYvUxemIO3VuGmYzFXgI=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.0
Release Details
UpdatedAug. 24, 2026, 5:26 p.m.
Changelog

Added

  • Editierbare Vorgänger-Spalte in der Tabellenansicht, Teilschritt F von #117 (Issue #124): eine neue, über das Spaltenmenü einblendbare Spalte „Vorgänger“ zeigt die Abhängigkeiten einer Aufgabe in der aus MS Project bekannten Kurznotation (12EA+2d = Vorgänger Aufgabe 12, Ende-Anfang, zwei Arbeitstage Abstand; mehrere durch ; getrennt) und nimmt sie dort auch entgegen. Typ (EA/AA/EE/AE, englische Kürzel FS/SS/FF/SF werden beim Eingeben mit akzeptiert) und Abstand sind optional, 12 allein bedeutet also EA ohne Abstand. Geprüft wird beim Verlassen der Zelle: Syntax, Existenz der Aufgabe im selben Projekt, Selbstbezug, Dubletten und Kreisschluss über die gesamte Abhängigkeitskette – bei einem Fehler bleibt die Zelle im Bearbeitungsmodus und nennt den Grund im Klartext, die bisherigen Abhängigkeiten bleiben unangetastet. Gespeichert wird über dieselbe PUT /tasks/{id}-Route wie der Detail-Dialog (inkl. dep_mode), sodass der Server die Termine anwendet und durch die Nachfolgerkette propagiert; Balken, Pfeile und Typ-Labels im Chart zeichnen sich mit der Antwort neu. Bewusst ohne Undo-Toast, da ein Zurücknehmen nur der Termine (nicht der Abhängigkeit) beim nächsten Neuberechnen wieder überschrieben würde. Nur Aufgaben haben eine editierbare Zelle – Meilensteine hängen über task_id an genau einer Aufgabe, Projekte und Teilprojekte sind berechnete Strukturebenen. Notation und Prüfungen stecken isoliert testbar in src/gantt/dependencyNotation.js (src/gantt/dependencyNotation.test.js)
  • Zeitabstand (Lag/Lead) je Aufgaben-Abhängigkeit (Issue #124): neue Spalte lag_days in nc_gantt_task_deps (Version1032; der Name vermeidet das seit MySQL 8.0 reservierte Wort LAG und nennt die Einheit). Der Abstand zählt in Arbeitstagen über den Ressourcenkalender wie die übrige Terminberechnung, wirkt bei allen vier Abhängigkeitstypen auf deren Ankertag und darf negativ sein (Lead: der Nachfolger beginnt früher). Bestandsabhängigkeiten bekommen 0 und verhalten sich unverändert. TaskService::update() weist eine gesendete Vorgängerliste jetzt vollständig zurück (invalid_dependency, dependency_cycle), bevor die bisherigen Abhängigkeiten dafür gelöscht werden, statt Vorgänger aus fremden Projekten oder Zyklen zu speichern – letztere hätten die topologische Sortierung in recalculateProject() stillschweigend Aufgaben überspringen lassen

  • Konfigurierbare Spalten in der Tabellenansicht, Teilschritt E von #117 (Issue #122): ein Menü in der Tabellenkopfzeile blendet Spalten ein und aus, die Reihenfolge lässt sich per Drag & Drop der Kopfzellen ändern (barrierefreie Alternative: Strg+Pfeil links/rechts auf der fokussierten Kopfzelle), die Breiten per Ziehen am Spaltentrenner (Doppelklick setzt auf die Standardbreite zurück). Zusätzlich zu den bisherigen Spalten (Titel, Start, Ende, Fortschritt) stehen ID, Dauer (Kalendertage inkl. Start- und Endtag, bei Meilensteinen 0), Status (Issue #78, nur Aufgaben/Teilprojekte) und Ressource (Issue #103, zugeordnete Ressourcennamen einer Aufgabe) bereit – alle vier als reine Anzeigespalten, die das Inline-Editing und die Tastaturnavigation daher überspringen. Auswahl, Reihenfolge und Breiten werden pro Nutzer über den in Teilschritt B angelegten UI-Preferences-Endpunkt persistiert (neuer Key gantt_table_columns, kein Schema- oder Endpunktwechsel nötig); eine gespeicherte Konfiguration wird beim Laden validiert, sodass entfernte Spalten oder unplausible Breiten die Ansicht nicht beschädigen können. Die Tastaturnavigation folgt jetzt der sichtbaren Spaltenreihenfolge statt einer festen Liste. Reicht die Tabellenbreite für die gewählten Spalten nicht aus, scrollen Kopfzeile und Zeilen gemeinsam horizontal; die Obergrenze des Splitters steigt dafür von 560 auf 900 px. Die Spaltenlogik steckt isoliert testbar in src/gantt/tableColumns.js (src/gantt/tableColumns.test.js). Ein Sortieren der Zeilen nach Spaltenwert ist bewusst nicht enthalten – es kollidiert mit der Hierarchie Projekt → Teilprojekt → Aufgabe und der sort_order, die das Chart für dieselbe Zeilenreihenfolge braucht

  • Tastatur-Navigation in der Tabellenansicht, Teilschritt D von #117 (Issue #121): Tab/Shift+Tab springen zur nächsten bzw. vorigen editierbaren Zelle (überspringt Zeilen ohne Schreibrecht, springt am Zeilenende zur nächsten/vorigen Zeile), Enter speichert und springt in dieselbe Spalte der nächsten Zeile, Esc bricht ab und stellt den vorigen Wert wieder her. Pfeiltasten bewegen die Zellauswahl, wenn gerade keine Zelle im Editiermodus ist (innerhalb einer Zelle bleiben sie für Cursor-/Wertänderung nutzbar). Die reine Traversierungslogik steckt isoliert testbar in src/gantt/tableNav.js (src/gantt/tableNav.test.js). Die ausgewählte Zelle bekommt einen sichtbaren Fokusrahmen und wird per scrollIntoView() ins Sichtfeld gescrollt – über den bestehenden bidirektionalen Scroll-Sync (Issue #119) folgt das Gantt-Chart automatisch mit. Neue ARIA-Rollen (grid/row/columnheader/gridcell, aria-readonly je Zelle) machen die Tabelle für Screenreader als Grid erkennbar
  • Read-only Tabellenansicht links vom Zeitstrahl, Teilschritt B von #117 (Issue #119): neue Komponente GanttTable.vue zeigt dieselben Zeilen wie das Chart (Titel mit Einrückung nach depth, Startdatum, Enddatum, Fortschritt), gespeist aus buildRowModel()/buildTableRows() (siehe Teilschritt A, Issue #118) – Reihenfolge, Filter-Dimming, kritischer Pfad und Projektfarben bleiben so exakt konsistent zum Chart. Zeilenhöhen sind fest an frappe-gantt gekoppelt (40 px = bar_height 22 + padding 18, Kopfzeile 52 px = header_height). Ein Toolbar-Button blendet die Tabelle ein/aus, ein Splitter macht die Breite verstellbar (160–560 px), vertikales Scrollen ist bidirektional mit .nc-gantt-container synchronisiert. Sichtbarkeit und Breite werden pro Nutzer über einen neuen, generischen UI-Preferences-Endpunkt (GET/PUT /api/v1/ui-preferences, SettingsController über IConfig::get/setUserValue() als JSON-Blob) persistiert, damit Teilschritt E (Editieren) das Schema nur noch um weitere Keys erweitern muss, ohne Endpunkt oder Speicherformat anzupassen. Rein lesend – Editieren folgt in Teilschritt C
  • Projekt-Export/-Import (JSON, format_version: 2) kann jetzt optional die von den Aufgaben referenzierten Ressourcen inkl. Gruppe, Wochenmodell, Arbeitskalender+Feiertagen und Tageskapazitäten mitnehmen, statt wie bisher nur die rohe resource_id in eine Zieldatei zu schreiben, die dort ungeprüft übernommen wurde – auf einem System ohne identische Ressourcen-IDs ging die Zuordnung damit verloren oder landete schlimmstenfalls bei einer fremden Ressource (Issue #115). Vor dem JSON-Download entscheidet ein Options-Dialog, was mitexportiert wird (Ressourcen / Verfügbarkeiten / Kalender, gestaffelt); vor dem JSON-Import zeigt ein passender Dialog, wie viele Ressourcen die Datei enthält, und lässt auswählen, was übernommen wird sowie ob bei Namensgleichheit die vorhandene Ressource wiederverwendet (Default) oder immer neu angelegt wird. Referenzen werden ausschließlich über eine Mapping-Tabelle aufgelöst und nie roh durchgereicht; nicht auflösbare Referenzen werden verworfen (Regressionsschutz zu #112/#113). Neu angelegte Ressourcen/Gruppen/Kapazitäten gehören stets dem importierenden Nutzer. Wochenmodelle/Kalender/Feiertage sind weiterhin admin-only anlegbar – ohne Adminrechte werden nur namensgleiche vorhandene Einträge gemappt. CSV bleibt unverändert aufgabenzentriert mit Ressourcen nur als ID-Referenz; der volle Ressourcenumfang ist JSON-only. Alt-Exporte ohne resources-Schlüssel importieren unverändert wie bisher, ohne Options-Dialog
  • Inline-Editing der Basisfelder in der Tabellenansicht, Teilschritt C von #117 (Issue #120): die Zellen für Titel, Start, Ende und Fortschritt sind direkt editierbar und aktualisieren die Balken im Chart sofort. Gespeichert wird über die bestehenden Store-Actions (updateTask/updateSubproject/updateMilestone/updateProject) inkl. Terminpropagation und dem vorhandenen Undo-Toast für Aufgaben (analog Drag & Drop im Chart); ein lokaler pendingEdits-Overlay zeigt die Änderung optimistisch an und rollt sie bei einem fehlgeschlagenen Request mit Fehlermeldung zurück. Die Schreibrechte löst tableRows.js je Feld über store.canEditProject() auf (titleEditable/startEditable/endEditable/progressEditable) – Projekte und Teilprojekte bleiben bei Datum und Fortschritt read-only, da diese Werte aus den Aufgaben berechnet werden. Geprüft wird: Titel nicht leer, Enddatum nicht vor Startdatum (Meilensteine ausgenommen, deren Start- und Endspalte dasselbe milestone_date zeigen) und Fortschritt 0–100. Im Simulationsmodus rufen Datumsänderungen an Aufgaben simulateTaskChange() statt einer echten Speicherung auf
  • Ressourcen-Überbuchungswarnung (Issue #82): die Kapazitätsplanung war bisher reine Datenerfassung ohne Abgleich mit dem tatsächlich verplanten Aufwand. Der neue Endpunkt GET /api/v1/resources/{id}/utilization?from=&to=&group= (ResourceUtilizationService/ResourceUtilizationController) stellt den geplanten Aufwand aus den Aufgaben-Ressourcen-Zuordnungen – gleichmäßig auf die Arbeitstage der Aufgabe verteilt, über WorkCalendarService mit ressourcenspezifischen Arbeitstagen und Feiertagen – der erfassten Tageskapazität gegenüber. Mehrfach-Ressourcen-Zuordnungen (Issue #103) samt Alt-Aufgaben werden berücksichtigt, ohne gepflegte Tageskapazität gilt ein Fallback von 1 PT/Tag. Im Ressourcen-Raster färben sich die Zellen nach Auslastungsgrad (> 90 % gelb, > 100 % rot) und der Tooltip zeigt geplante Stunden gegen Kapazität samt Aufschlüsselung nach Projekt und Aufgabe; das Workload-Chart im Gantt markiert überbuchte Perioden jetzt rot. Übersetzungen für alle sechs Sprachen ergänzt
  • Nachgetragen – beide Funktionen wurden bereits mit 1.4.2 ausgeliefert, waren dort aber nicht im Changelog dokumentiert:
  • Einheitliches Statusmodell für Teilprojekte und Aufgaben (Issue #78): neues Feld status (not_started/in_progress/blocked/done) auf nc_gantt_subprojects und nc_gantt_tasks ergänzend zum numerischen Fortschritt, Bestandsdaten leitet Migration Version1030 daraus ab. Validierung per StatusValidator (Whitelist analog ColorValidator), Status-Dropdown in den Detailformularen, blockierte Balken im Gantt schraffiert und mit Warnsymbol, blocked_count je Projekt im Rollup sowie ein Badge in der Projektliste
  • Mehrere Ressourcen mit individuellem Aufwand je Aufgabe (Issue #103): bis zu vier Ressourcen je Aufgabe mit eigener Aufwandsschätzung in der Einheit der Ressource (neue Tabelle nc_gantt_task_resources, Migration Version1029, Backfill aus den vorhandenen resource_id/work-Werten). Das manuelle Work-Feld entfällt: TaskService::resolveResources() summiert die Einzel-Aufwände zur Gesamt-Work (1 PT = 8 h) und übernimmt die erste Zuordnung als primäre resource_id, die weiterhin für Arbeitskalender und CPM maßgeblich ist. Ressourcen-Filter sowie CSV-/JSON-Export und -Import berücksichtigen alle Zuordnungen statt nur der primären

Changed

  • buildFrappeRows() (src/gantt/frappeRows.js) mischte bisher Datenmodell und frappe-gantt-Formatierung: die Einrückungstiefe steckte als Leerzeichen im name, Zustände (Dimming, kritischer Pfad, Summenbalken) hingen in custom_class – eine geplante Tabellenansicht neben dem Gantt-Chart (Issue #117) hätte dieselbe Logik duplizieren müssen (Teilschritt A, Issue #118). Neue Funktion buildRowModel() (src/gantt/rowModel.js) liefert dieselbe Zeilenliste jetzt als neutrales Modell (id, type, entityId, depth, title, start, end, progress, parentId, editable, matched, critical), resolveMilestoneDate() wandert mit ins Modell. buildFrappeRows() ist nur noch ein dünner Adapter darüber, der name/custom_class/dependencies wie bisher erzeugt. Reines Refactoring ohne sichtbare Änderung, per Fixture-Vergleich gegen die alte Implementierung geprüft; neue Unit-Tests (src/gantt/rowModel.test.js, npm run test:unit, via neu eingeführtem vitest) decken Hierarchie, Einrückungstiefe, Meilenstein-Datumsauflösung und Filter-Dimming ab

Fixed

  • Abhängigkeitsfortschreibung bei Aufgaben lieferte für alle vier Abhängigkeitstypen (EA/AA/EE/AE) einen um einen Arbeitstag zu langen Nachfolger-Termin – bei EA zusätzlich mit falschem Anschlusstag, da der Nachfolger auf dem Endtag des Vorgängers statt am folgenden Arbeitstag begann und dadurch mit dem Vorgänger überlappte (Issue #114). Ursache war eine Off-by-one-Vermischung zwischen der inklusiven Arbeitstagszählung und der exklusiven Datumsfortschreibung in TaskService::applyConstraint()/computeMultiConstraint(). Betroffen sind sowohl das direkte Setzen einer Abhängigkeit als auch die Terminpropagation nach Drag & Drop und die What-if-Simulation. Verhaltensänderung: Bestehende Pläne mit EA-Abhängigkeiten verschieben sich beim nächsten Speichern/Neuberechnen um einen Arbeitstag nach vorne (auf den korrekten Anschlusstag); bei AA/EE/AE verkürzt sich der zuvor fälschlich verlängerte Nachfolger-Zeitraum um einen Arbeitstag, die ursprüngliche Dauer bleibt jetzt garantiert erhalten. Abhängige Meilensteine (Dauer 0) sind unverändert korrekt und von diesem Fix nicht betroffen

  • Security: jeder Nutzer mit Schreibrecht auf ein eigenes Projekt konnte jede in der Instanz existierende resource_id einer eigenen Aufgabe zuordnen – unabhängig vom Eigentümer und ohne jede Share-Beziehung (Cross-Tenant-IDOR, Issue #112). TaskService::resolveResources() lud die vom Client gesendeten IDs bisher nur über ResourceMapper::findByIds(), also als reine Existenzprüfung ohne Berechtigungscheck; da die IDs fortlaufende Integer sind, waren sie trivial zu erraten. Der Listen- wie der Legacy-Einzelfeld-Pfad in create()/update() filtern referenzierte IDs jetzt über ResourceMapper::findVisibleToUser(), nicht sichtbare IDs werden wie unbekannte verworfen (Liste) bzw. nicht übernommen (Einzelfeld, der bestehende Wert bleibt erhalten). Regressionstests decken beide Pfade für create() und update() ab

  • Feiertage ließen sich nicht in mehreren Regionskalendern führen: POST /admin/holidays lief in einen HTTP 500, sobald dasselbe Datum in einem zweiten Kalender angelegt wurde, weil der Unique-Index aus Version1006 allein auf holiday_date lag, obwohl Feiertage seit Version1013 über calendar_id kalenderbezogen sind. Version1031 verschiebt den Index auf (holiday_date, calendar_id); die Eindeutigkeit globaler Feiertage sichert weiterhin der Upsert in HolidayAdminController::create(). Betroffen war neben der Feiertagsverwaltung auch der Kalender-Import aus einem Projekt-Export, wo der Fehler still verschluckt wurde. Zweitens war GET /admin/holidays admin-only, während work-calendars und work-schedules lesend für alle offen sind – für normale Nutzer blieb der Store deshalb immer leer (keine Feiertage im Projekt-Export, keine Markierung arbeitsfreier Tage im Ressourcen-Raster); der Lese-Endpunkt zieht nach dem Muster der übrigen Stammdaten auf /api/v1/holidays um (NoAdminRequired), Anlegen und Löschen bleiben unter /api/v1/admin/holidays. Ergänzend werden Feiertage jetzt mit ihrem Kalender gelöscht (WorkCalendarController::destroy(), HolidayMapper::deleteByCalendar()) und Bestandswaisen bereits gelöschter Kalender in Version1031 aufgeräumt – über verweisende Ressourcen haben sie bis dahin weiter als arbeitsfreie Tage gewirkt
  • Die Installation der Tabelle nc_gantt_task_resources scheiterte auf Instanzen, deren Tabellenpräfix die von Nextcloud aus Oracle-Kompatibilität erzwungene 30-Zeichen-Grenze für Bezeichner reizt: der von Doctrine automatisch vergebene Primary-Key-Name (Tabellenname + _pkey) wird mit Präfix oc_ zu lang (Issue #132). Die Primärschlüssel-Definition bekommt – analog zu den bestehenden Migrationen für nc_gantt_resource_capacity und nc_gantt_resource_groups – einen expliziten kurzen Namen
  • Der absolut positionierte Toggle-Button der Nextcloud-App-Navigation ragte in den App-Content hinein und verdeckte dort den Titel des ersten Eintrags in der Projekt-Rollup-Leiste (Issue #134); der linke Innenabstand reserviert jetzt Platz für den Button
  • Ein leerer Balken in Höhe der Kopfzeile erschien oberhalb des App-Inhalts, weil der CSS-Reset für den Nextcloud-Content-Wrapper an main#content.app-nc_gantt gekoppelt war (Issue #126). Ändert sich das Markup zwischen Nextcloud-Versionen (Klasse oder Tag), griff der Reset nicht mehr und der von NcContent (#content-vue) selbst gesetzte margin-top: var(--header-height) addierte sich zum Rand des Cores. Der Reset hängt jetzt robust an #content mit !important, ohne Abhängigkeit vom app-spezifischen Klassennamen
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegO8Qmtdwg8LOBKr5G+mq4co374hsqqSN6oCIRV9atUx0KqogPxisJiIExkPoSIbu
e4EeIaUzg2alhZsKBLWOwR3Ab3fNm5vvd2DzJn6Hei7A70woz9m/LXkVlcx3vALW
bwolClUX1XCZOBVo/kSS5AOVwak3/huzT1iF9xH9BnpdzVuOjZ563LYwSayfb2Gq
SkWNfLZlKlDkTTF2PIszrNmaa89GmyqTbraxSP16/9nYdIj8ZU0zrhC3HtpihgAT
wya2lwnMf6I73HU0xxgT/+ITTmurx4eHwKWH80P4tjg/Y9mnHODIpAslbW3351mc
qdTHXIBW1MB1YGCQ+JPt11v1X2ccn29kEF9d1nkizKZ1+Du+LJQhPhwHiQxPWSwAUlTlQ4tj1lbe3+FJ1jsVTCqWbDUN02EoV7ZwtN15/HLJfM6w4drUi8ThJ1aLG3v6
cIwMJjYgWeepxR2gv4YgBf4u7txodm5bAWuzySk4rfAG1dBxmie/bfNnWmQ9LrYy
spwKc73gsPhg03ll+jp0b27fJfNF4IpOysz3i+r1ExVfeNIWWO91JPatO2pXZh4O
q0rczM8wYvctvcEkKQIL67HoIrKw4GVIXfqM9Oc8e/Ay7d/NPL+BjpCO6zvoFBY+
SRmjit15YmGW4EQwweQfKXuZ2YeySj+7OjhKHpKfvfg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.2
Release Details
UpdatedAug. 20, 2026, 4:41 a.m.
Changelog

Changed

  • release.sh fragt die Versionsnummer jetzt interaktiv ab, statt sie nur aus appinfo/info.xml zu lesen: zu Beginn werden die aktuelle Version und das letzte Release-Tag angezeigt und es wird gefragt, ob eine neue Nummer vergeben werden soll (Vorschlag: nächster Patch-Level). Die Eingabe wird auf das Format X.Y.Z, auf einen echten Versionssprung und auf ein noch nicht vergebenes Tag geprüft und dann nach appinfo/info.xml, package.json und package-lock.json geschrieben. Fehlt ein Changelog-Abschnitt zur neuen Version, warnt das Skript; anschließend bietet es an, den Bump als chore: Version X.Y.Z zu committen — nötig, weil der Tag-Name den Commit-Hash enthält. Für nicht-interaktive Läufe gibt es --version=X.Y.Z und --no-bump; ohne TTY wird nie gefragt, make release bleibt also unverändert nutzbar
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturepoVHZxteU3AR2cpRRV98jNfv4j7rWnLEd3kbpn5Dp4T1ubAI+0QlRf9jxi0BaYz1
+1dg+3Yt25uN6Slp1FHiw7kyqt5HJ60XgnOWo+Zt0YBHSnWqLDhF8zxPEnQCkF+v
nUvscpcoQY2bRiIBZhVWVgeAD592uvOA4gfRDrUyLVDKjOrzBu3ScbNvCqTqE9IC
vs8qZkRLIcaD6tKoSLT2pZqmwBoCdClvEw5D5v40WSvLq232BZnXpQ4ogG3QvJdL
mYO8HlPZKVOxANp0MGnWle4IFRMbMt22Ebpkjc18Q9Q4AIv/Sd3zTsc+uMONTM95
s0GXvLSm95o0UCFZ1CL7fCEq7NwlprjVrTeWGmlJNVjHJuIyRKHEz5DR12s0lAGm
/rKGWjoVP+Wvk3upRizR/5y2fpWoiwuDqCtz9gQN6+HHYLaqLvYqsAJ/SxmtLtP7
8B2rK61ClKowz4phbTZdrCaF8w3IWnFFBTFquIVFuN2jUoHvdCMtd0910sLAMY5M
Xs9nk0FVsP23zKXlQpOojq7ouCQagRhsnBJDLxWRR+NIq582f7SDgcrkBqxlGJX4
/Ql9V+2iFx61QANCTjdJIO3LbvbK60vGos+ency0GfvSDbPovDAVyJzt8ZUh5BNw
UXQ4XOHJZAagAr36rKttk1DQbZZ/2bUEq231c96m83g=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.1
Release Details
UpdatedAug. 17, 2026, 7:43 p.m.
Changelog

Fixed

  • Die Meilenstein-Raute hatte eine fest verdrahtete Farbe (#fbbf24) statt der Projektfarbe zu folgen, obwohl Balken für Aufgaben und Teilprojekte bereits korrekt eingefärbt wurden (Issue #102). Zusätzlich wurden die separaten – und wirkungslosen – Farbwähler bei Teilprojekt und Aufgabe entfernt, da deren Anzeige ohnehin immer der Projektfarbe folgt
  • „Meilenstein hinzufügen" im Drei-Punkte-Menü der Projektliste nutzte zwei aufeinanderfolgende window.prompt()-Dialoge (Name, dann Datum als Freitext) statt des regulären Detail-Dialogs (Issue #101). Der Klick emittiert jetzt add-milestone analog zu „Aufgabe hinzufügen" (Issue #91) und öffnet denselben Meilenstein-Dialog wie beim Bearbeiten, mit vorbelegtem Projekt
  • Im festen Datumsmodus des Aufgaben-Detaildialogs konnte ein Enddatum vor dem Startdatum eingegeben und gespeichert werden (Issue #100). Jetzt zieht ein über das Enddatum hinaus verschobenes Startdatum das Enddatum mit; ein zu frühes Enddatum erzeugt ein rotes Fehler-Icon am Feld, hält den Fokus dort und deaktiviert den Speichern-Button, bis ein gültiges Datum eingetragen ist (dateField() in fields.js mit neuer setError()-Methode)
  • Eine Aufgabe mit Abhängigkeit behielt trotzdem die manuell eingegebenen Start-/Enddatumsfelder, die beim Speichern durch die Abhängigkeitsberechnung überschrieben wurden – für Nutzer nicht nachvollziehbar (Issue #99). Der Aufgaben-Detaildialog hat jetzt denselben Fixed-date/Dependency-Umschalter wie Meilensteine (Issue #90): im Dependency-Modus ersetzt eine Dauer (Tage) die Datumsfelder, die Abhängigkeit bestimmt den Termin. Serverseitig wendet TaskService::create() neu hinzugefügte Abhängigkeiten sofort an (bisher nur update()), damit auch neu angelegte Aufgaben keinen die Abhängigkeit ignorierenden Platzhaltertermin behalten
  • Der Speichern-Button des Detail-Dialogs blieb nach dem Hinzufügen einer Abhängigkeit deaktiviert: die Gültigkeitsprüfung hing allein am input-Event, das bei einem <select> vor dem change-Event feuert – zu diesem Zeitpunkt war die Abhängigkeit noch nicht in der Liste. detailModal.js wertet jetzt zusätzlich change aus, der Abhängigkeits-Editor (fields.js) meldet jede Mutation (Hinzufügen, Entfernen, Typwechsel) und der Fixed-date/Dependency-Umschalter in taskForm.js den Moduswechsel über ein eigenes bubbelndes change-Event

Added

  • CONTRIBUTE.md und setup-dev.sh für neue Mitwirkende (Issue #98): Projekt-Leitlinien (FOSS, Datensparsamkeit, Security), VIBE Coding ausdrücklich erlaubt, sowie der komplette lokale Dev-Setup-Weg (Docker-only, Nextcloud + MariaDB unter localhost:8088, optionales E2E-Setup, Git-/Bot-Auth). setup-dev.sh automatisiert diese Schritte, konfigurierbar über --workdir/--no-e2e/--no-docker-install
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturetYcUvVtDEQrAakG32EQF1atSOmz91/8XudCUh60A9r/AxMv+AA5AX2rIvNYDkEn0
OuTmXA6Iz0DoVkdij11MPDhfLaUy0mXeLbwB5fj/xF/cFYcNeCFGKD3k2jyRwOTo
2n1sEp0r8sn0fitHzLfhcB/By0VJkZyhaO/NctP01XfcSQM7lGEOeY/uagtGaDTI
JnPLoQTHo0nUOv4y7jUMZuurEgBOc4yeUxTKv4A/m642hvNQIS0q6K9ON41JHKIO
UtI7DbDWSboVhct1Go3zoj5XcaAGG9ZiwSkJDt/BXT5tWH4SOnSn7mys0j1F6Y0g
vIJWjz6S2RlekwIrIxgyaeEVBOeQ4OM1ilJGWKXDyYJM/0IBxqnqRXDkAFlOopDo
xezlcFdWTkYL5OBKMFJhKuTy7dEQ9hxx5Nz4+vLbNV8gDJZBI+5yI5m8W+809WIH
iKUpIda1ShOblvYH6Ek5AzBdTmoFQ6T4f7iWT7j1i4uUP2DsgehjGVl8yDIT/wyx
nV9t7koXapu1WeXrunSa++mXsi3lceNpthdz5/cUEOe9yWvc1rrulRhiWzePga8H
zjVQYGAIPvwYkWa47U6Zh7HBsQaS+9yQ8NDnD49SS4jUWTGlwNqfM+q9fZ16/G9d
7l4chZUPPbBv+xWfdxmFw+MC5zdkAq27isHToTqnm+w=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.0
Release Details
UpdatedAug. 13, 2026, 6:41 a.m.
Changelog

Added

  • Meilensteine werden jetzt immer direkt unter ihrem übergeordneten Objekt angezeigt statt gesammelt am Projektende (Issue #90): Meilensteine lassen sich neu auch einer Aufgabe zuordnen (bisher nur Projekt/Teilprojekt, Spalte task_id war bereits vorhanden, es fehlte nur die UI), das Auswahlfeld im Detail-Dialog heißt entsprechend „Übergeordnetes Objekt" statt „Teilprojekt". Zusätzlich können Meilensteine ihr Datum jetzt statt eines fixen Werts aus einer Abhängigkeit zum übergeordneten Objekt beziehen (EA/AA/EE/AE, analog zu Aufgaben-Abhängigkeiten; neue Spalte dependency_type, Migration Version1028) – da ein Meilenstein keine Dauer hat, bestimmen EA/EE das Ende und AA/AE den Start des Zielobjekts. Die Berechnung übernimmt der neue MilestoneDateService (serverseitig live bei jedem Abruf sowie im ICS-Feed neu aufgelöst, nicht persistiert propagiert) sowie clientseitig in frappeRows.js für sofortige Reaktivität ohne Neuladen
  • Undo für Drag-&-Drop-Verschiebungen inkl. Propagationskette (Issue #85, Umsetzung der einstufigen Empfehlung: letzte eigene Operation, kein Redo, zunächst nur Drag & Drop): Migration Version1027Date20260810020000 ergänzt nc_gantt_change_events um op_id (UUID) – alle Change-Events einer zusammenhängenden Operation (Drag + gesamte Propagationskette B→C→D) erhalten dieselbe op_id, erzeugt in TaskService::update()/propagateDates() und über ChangeEventService::log() protokolliert (dieses Change-Tracking war bisher tote Scaffolding-Logik ohne Aufrufer). Neuer Endpoint POST /api/v1/changes/{opId}/revert (ChangeEventController::revert, ChangeEventService::revertOperation) wendet alle Change-Events der Operation invers an, in einer DB-Transaktion mit Rechteprüfung über AccessService::canEditOrOwn(). Konfliktschutz: Revert wird mit HTTP 409 abgelehnt, sobald der aktuelle Feldwert nicht mehr dem protokollierten new_value entspricht (jemand hat zwischenzeitlich erneut geändert); der Revert selbst erzeugt wiederum neue Change-Events, die Audit-Historie bleibt lückenlos. Frontend: nach jedem gespeicherten Drag zeigt GanttChart.vue einen Toast „N Termine verschoben — Rückgängig" (showUndo aus @nextcloud/dialogs, 10 s sichtbar) sowie ein globales Ctrl+Z für die letzte eigene Operation der Session (stores/projects.js: lastOperation, revertOperation(), undoLastOperation())
  • Kalender-Export als abonnierbarer ICS-Feed pro Projekt (Issue #83, Variante A aus dem Umsetzungsvorschlag): neue öffentliche Route GET /apps/nc_gantt/projects/{id}/calendar.ics?token=… (regulärer, unauthentifizierter CalendarFeedController, damit externe Clients wie Thunderbird, Nextcloud Kalender oder mobile Apps ohne Nextcloud-Login abonnieren können) liefert Meilensteine als ganztägige VEVENTs (◆-Präfix), den aus den Aufgaben abgeleiteten Projektzeitraum sowie optional Teilprojekt-Zeiträume (Schalter „Auch Teilprojekt-Zeiträume einschließen", standardmäßig aus, um den Feed bei großen Projekten nicht zu überladen). Zugriff erfolgt über einen pro Projekt generierten, 256-Bit-zufälligen Token in der URL (CalendarFeedService, neue Spalte calendar_token auf nc_gantt_projects, Migration Version1026), der über einen neuen „Kalender-Feed"-Bereich im Freigabe-Dialog angezeigt, kopiert und jederzeit widerrufen/neu generiert werden kann (CalendarTokenController, nur für Projekt-Admins/-Eigentümer). ICS-Generierung ohne neue Abhängigkeit, RFC-5545-konform (Escaping, Zeilenfaltung)
  • What-if-Simulationsmodus für Terminverschiebungen (Issue #84): neuer Endpunkt POST /api/v1/projects/{id}/simulate führt Terminpropagation und CPM rein im Speicher aus (kein DB-Schreibzugriff) – TaskService::simulate() nutzt die neu extrahierte, stateless propagateDatesCore()-Kernlogik, die auch der persistierende update()-Pfad verwendet, CriticalPathService::computeFromTasks() erlaubt CPM auf einem hypothetisch veränderten Task-Datensatz. Frontend: „Simulation"-Toggle in der Gantt-Toolbar mit Banner inkl. Projektende-Delta in Arbeitstagen, Geisterbalken für die Ist-Termine (analog Baseline-Overlay) sowie Verwerfen/Übernehmen. Drag & Drop ruft im Simulationsmodus simulate() statt einer echten Speicherung auf. Szenarien bleiben bewusst flüchtig (kein Speichern benannter Szenarien), „Übernehmen" wendet die Änderungen als einzelne Task-Updates an, Simulation läuft pro Projekt
  • Neuer Eintrag „Aufgabe hinzufügen" im Drei-Punkte-Menü der Projektliste (Issue #91): legt wie der „Neu"-Button eine Aufgabe an, belegt dabei aber das gewählte Projekt direkt vor, sodass der Projekt-Auswahlschritt in der Maske entfällt
  • Such-/Filterleiste für Projekte, Teilprojekte und Aufgaben (Issue #81, Stufe 1: rein clientseitig auf Basis der bereits geladenen Daten aktivierter Projekte): Freitextsuche im Titel, Risiko, „überfällig" (Ende < heute und Fortschritt < 100 %), „über Budget" (Ist-Kosten > geplantes Budget), Ressource und „ohne Deck-Verknüpfung". Neuer Pinia-Store stores/filters.js (Zustand pro Nutzer in localStorage persistiert) und reine Matching-Logik in gantt/filterMatch.js. Nicht passende Balken werden in GanttChart.vue gedimmt statt ausgeblendet (Abhängigkeitspfeile und Zeitkontext bleiben lesbar), Projekte/Teilprojekte gelten als Treffer, sobald ein Nachfahre passt. Dieselbe Filterleiste (components/FilterBar.vue) dimmt in ProjectList.vue nicht passende Projekte; für nicht aktivierte Projekte sind nur Titel-Treffer zuverlässig auswertbar, da deren Aufgaben/Teilprojekte nicht geladen sind

Changed

  • Freigabe für Nextcloud 34 (Issue #94): max-version in appinfo/info.xml von 33 auf 34 angehoben, unterstützter Bereich damit NC 28–34. Verifiziert gegen Nextcloud 34.0.2 mit PHP 8.5 und MariaDB 11.4 (148 Unit-Tests, 39 E2E-Tests)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerdYQI+Semnk0cWtKqUZinVDHk0KpxyqB5Ox4NBL9pyf5nrpK8xZpe9lBxc6zhDfW
8adLfVIwQtX+bI7S1O05xm0hwrUHQQ2ii6ADD8mlAxnZjdhKSCAhZ0NcaqT7Em0X
nD7yRP2B8zAAQMJNjzeBsLbzveNrXMoGzFrvm/jFxskUIUS01ykfWEg2C5+5/IAf
7jBAM8x2Qj92b8zyBFpLjfJQw9aTE/trtCsmZXQFmkJjGS0anrJc9dXKfqnHxknX
8jmLOv4Yq4Qtk3UF35HTiIGcA2W0UKfVyFb4XcrIMeSmLXXcagJWjeH5nB18UUNn
JyCgE/qY4v/SnrNQU92mIdMOQGuvYg7xbaz226lgNdwM2HuM9U8nc2juqJxAO+Wp
xs85yl82qIviZ5YY39HK371MZ/ufMsaVsVy3Nfn3PjqqkzGLmtAL6KJDcKHZWdDw
ugEPwO61DxnERZsNGeFpXfzCxjMpZc/qwUIfGw85idmU2vdedtrxHBkDGoqu1IP7
xZOO8uSYrzbXM6g1gs3WPBOY51CmZLs5pIQTN1tVl5ifGw4uOuzDBUCKiR5oA2MI
352HbA7wwWFgwoN9W5t4MRTowmaYpM8AB+nh9f1A5q9S8SelfMpXOqGew9VZb2jd
AuiJK/LEPjRgs5s3/FfC4Sb1MSsXDr1N1jziaF4MomM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.3.1
Release Details
UpdatedAug. 6, 2026, 12:57 p.m.
Changelog

Added

  • Regressionstests für die Rollentrennung Viewer/Editor/Admin: ForbiddenException-Tests für TaskService::create/update/delete/syncProgress und ProjectService::updateProject/deleteProject (inkl. „Editor darf nicht löschen") sowie erstmalige Testabdeckung für SubProjectService und MilestoneService

Fixed

  • CSV-/Formel-Injection im Aufgaben-Export (detail/io.js exportCsv): Task-Felder wie Titel oder Beschreibung, die mit =, +, - oder @ beginnen, wurden unverändert übernommen und von Excel/LibreOffice/Google Sheets als Formel ausgewertet. Die Absicherung aus ReportView.vue liegt jetzt in einer gemeinsamen Utility (src/utils/csv.js, escCsvCell) und wird an beiden Export-Stellen verwendet
  • Viewer sahen im Frontend dieselbe Oberfläche wie Editor/Admin (Add Task/Subproject/Milestone, Share, Delete Project, Drag/Resize im Gantt-Chart, volle Bearbeitungsformulare), obwohl das Backend Schreibversuche ohnehin korrekt ablehnte – kein Least-Privilege an der UI. ProjectService::getAccessibleProjects()/getProject()/createProject()/updateProject() liefern jetzt das effektive permission-Feld (AccessService::getEffectivePermission()) mit; ProjectList.vue, GanttChart.vue (inkl. Drag/Resize-Sperre je Projekt) und das Detail-Modal (detail/detailModal.js) blenden Schreib-Aktionen für Viewer aus bzw. schalten Formulare read-only
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureBUyDeI9y7iKD1wQJLHrW6IFq0zuXOJQ1Zv/x+0RryIIQ0fAGAUjwQyLDYLYpRzPg
6ITDPfY9bftVlwkB8w9LqZiFETkH8mgc6l5XB7F6NKzFrdaSnNw21CIVJjWyHpkR
mxDq+vQepZd+YtB9jMz39kbFk9BNHrpKIwIXXZVqEqj1U54NmT+5XnT892m9mY6o
RWQNf3Mn5WcH3vmoDi/UAOgDaO30GFCRJePryurVpK7stkub8K6fYsWIkS5NeJlP
YHdb4nBlkhUzQFjo0ZDSkCSiWjMdyCrtEanX6m6Ffhr+j0jpd+J9soMSjrzdzt4T
5mETo8xc61IQIVFWMD6fZ3/oCmbx2TFDZsJjRpFKxoPA08AkdC7xjxHoaKYZHvi4
+tzowxPzMfyjQ/hEj2dwq22doM2lXK1Y13VvV8kkaomrAQjB97gf+ODrYZNDgNvc
2ijU08B4cAB0HbEveDpDRjCJj8D5OwCMafRMa4Ul/nm0+RiBFOhIjDYdVAnCp7Ql
IEc4UQZM9WYLQlouwGPk83POpbqkbc8vKAucGBXvHAbxNpUSOGbssMYFGU/e9pOg
+oT2gcJrC3MYPGIWnS8iLn75PK1H063KFJv5yqC+h/rqwX/FxyTzKvofJi68GpLs
WtG3y67W8Ju4y1I9iQ9raJxxhFUJrStibGy9gc7L0DM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.3.0
Release Details
UpdatedJuly 9, 2026, 5:23 a.m.
Changelog

Fixed

  • Baseline-Toggle im Gantt-Toolbar zeigte nie Baseline-Balken und PUT baseline/active warf einen SQL-Fehler: baseline_id auf nc_gantt_projects wurde mit der 3-Ebenen-Migration versehentlich zusammen mit den tatsächlich berechneten Spalten entfernt (Migration Version1025 stellt die Spalte wieder her). BaselineService::create() aktiviert eine frisch eingefrorene Baseline jetzt automatisch fürs Projekt; renderBaselineBars() (GanttChart.vue) und baselineOverlay.js lesen Baseline-Items dafür über task_id statt des seit der Umstellung auf das 3-Ebenen-Modell nicht mehr existierenden subproject_id-Felds
  • OCS-API-Fehlerantworten (400/404/500 …) lieferten für alle OCSController-Endpunkte einen leeren text/html-Body statt des erwarteten JSON-Envelopes mit message; ExceptionMiddleware wirft jetzt passende OCP\AppFramework\OCS\OCSException-Subklassen statt einer rohen DataResponse, die Nextcloud-Core nur für 401/403 automatisch formatiert. Betroffene Controller (ProjectController, ReportController, ShareController, ChangeEventController, WorkScheduleController, WorkCalendarController, HolidayAdminController, SettingsController) wurden zusätzlich auf die bestehende ApiException-Hierarchie umgestellt

Removed

  • Nie ausgewertete Gruppen-Freigabe-Option entfernt: Share::TYPE_GROUP, principal_type-Parameter in ShareController sowie die DB-Spalte principal_type (Migration Version1024) wurden gestrichen; Freigaben sind jetzt ausschließlich benutzerbezogen

Changed

  • Dashboard-Summary (DashboardController::summary) lädt Baselines und Aufgaben-KPIs jetzt über gruppierte SQL-Aggregat-Queries statt pro Projekt einzeln (N+1-Fix, gilt auch für den Admin-Fall mit allen Instanz-Projekten)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureNJfbRJEXzKGipUjUW9tUKayHoY6ltSU0ZeCQXcDx6XVDY/naLK0dVY9ciofXU7oy
40sJIOsjxcUZbalDwqncPn0vjGoqmk3BFUT7KotEpLLf8/ZmIjdHEbQbJHzEpdQV
y5YedjqRLKB7/Ul35JWVFxY8eutCsYkonfof7v6AGMGB/RjQ7GDB6jWunEEwqUGV
3Y1isvdq5N3oo9NDY/QR0C2C7jiMnB9Kon0KzZ2yp0oF5euOpA/qrtmqxG3L9PoZ
DW1ppwAuARWtRDu4tcDAAQAkF1Mrg5YyYgRoKm7uVazlKEP7DxX56g/g4RS2otEE
rKsA/ER9yYiHnNyGcZuK9Jkfe2Ua6/R7gzJilRvHZ7CBPkhY+XNPcrjlsHbCJEJJ
cX/b0B56p7FyAFy6XjMY9PYFhxy/ubHE7Sk8ixX88uxgM4vf/sSFQaN84CcG/qqA
Nq/OEytBjZP82TsFFYBRcPKEPskF4ejjhNVC4bJpiuCIpo51nIxjeFGtDpeQiDG7
0OWc/GCvsz5W3eG7ShmQsRf0DoivL8tjgDUlMWO9boTpUy2Vnm1rDMKu6nFGKZ8o
TWTZR6u4zGZW1OBERdlU5NQmqxqXrsuGlT6r6DuQnWsWiscjXOyIFeHz+EOKhQOk
wUuN6xN8QES1CDFB27PYV0Sz2KhYhM78GKLCc9B10Ic=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.2.0
Release Details
UpdatedJuly 5, 2026, 9:21 a.m.
Changelog

Added

  • Löschen-Buttons im Detail-Modal für Teilprojekt, Aufgabe und Meilenstein
  • Deutsche App-Beschreibung in info.xml ergänzt

Fixed

  • Baseline-Datum und Kommentar im Report werden untereinander angezeigt
  • Dashboard-Widget zeigt Baseline-Datum und KPI-Werte korrekt an
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureXuYTZMxTlGNJVrGvU/GcIjSlBTYreXdvF5FilCmsi+l7MBvFlyeW1DsnxAsao3pu
HyhvaKXRnkraVQNT2ULgX4BmjUnhsELROrK03gtY88Odzqzh7RIXYkNkRcy92ara
56HIP8BBRD3m1c8pYPfpZfLAzkuurAzVqbJ6qbxw1AebdlPCFEq2g6C+IOcTon0T
q9ZD2I2XJmWd42EXXqva+NMrcx1jfjmHWVtl7xHKhwPndFCtHPMMAdoDS0Xpbh1V
3Ns4h/DphDpC8/ZYZ5sbpMCBekHw4VTZDGxNAuXJ5DMPhkqg2MxVplkS19PuBHLx
r74sgsz611Gjg55gjKOqCb5ZZEvqZTrwKTEYIbu8HicWtq/HxKRZS3FZN9MThTTX
Hesv65vsq9fH0UhEM6MVJjE5Phnw8Wb5ivtAiaUA+NzPrDfrHZAsSfR9NFL3rhFC
7wOmnXtzK40NjmqyMWJu/UP9UVbz3SeEiHiQO9GwBc2krzmXzcmLZFgEjty4CGoJ
SzXAA1QGpUGO4n0b3Y/5zSY4UxogGWybW+mOs27KDLytI915wNYGvBtcdAKCj6Ws
1uVA98npwMid/WSJJ04BXHX8LbowrDeTezSFJyGh+u/KnhlzkA7fqEuu3nQGT5rA
a9qeMmd5kmLBIVcG9RVVmw1kc0AK0qIqZDmlk9dWGJs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1
Release Details
UpdatedMay 27, 2026, 6:43 p.m.
Changelog

Added

  • Glossar-Button in der Navigation (über den Einstellungen) mit interaktivem Nachschlagewerk
  • Dreispaltige Glossar-Tabelle: Begriff/Abkürzung, Bedeutung/Englisch, Erklärung mit Berechnungsformeln
  • 30 Einträge in 6 Abschnitten: Allgemein, Terminplanung, Baseline & Berichtswesen, Earned Value Management, Ressourcen & Kapazität, Budget

Fixed

  • Fehlende deutsche Übersetzungen für englische UI-Strings ergänzt (Add dependency, Baseline, Dependencies, Remove, Work, No projects yet, Open Gantt Projects, u.a.)
  • Leere Übersetzung für Deck-Fortschritt-Hinweis befüllt
  • de.js vollständig mit de.json synchronisiert (war ~70 Einträge veraltet)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF1RHJx15J/N8tWNrDlAX1MFE/9tsct4PViEcBDeo8U/V2qJ/tezL7Xk0BVmYxjWw
pB+J685mcHjikh93/aZUqG8C6y0mR3TTJfaa7dLCIRMc1034x6Xj9Nd7Q9YVH4yi
RLTcRhrG2pN1Iqc23kwId995GJXHnCBEyuQuzo9m1VtA7GoFa8W2nqhXVT94khDS
8ZHQrixDfv63d4GfA5N2x4+phaiyObdskvCmVSuKrmouYvCGpPevtKG5YxzOrHid
aL48Kod0YgOCAD8nAqOhEmioP0m+wP2e/NC4Z5svSKT3vdlE67W1M68ipQQfnhPI
txui+2X8zFvoe/M5sE3DaXbLnOVNGQ7YRpyXFI5GsQmQ0cPSSO/94Mw15hLZfwMf
TDOrrvzf+Vu05sUTZ6CFh4FrSl4e5Q6Xjv6uH4BZtyOpPxl+ajgW4OkZvQDbt4KC
8aTZodMcDpXlRMDdBSxT1bT9tD05KUKmEZYtpxHiARrHDuQQCRSuxtjitQD9lbla
CMxKrBiO0YgdUb3FuczYkWEH+y8gW5egU6TDMnjAVbuUecGuGZN1Cc6tarcFWRu2
+V7Qn+aNoXEwP5e/0TM4Ig8ETAYseZRiTPHxeWiSg3RnU3HuT6kPVWw8LERACDT4
6F4WU9t2+4kESMLMjeV0/2UBQSykR/42Idsg0PtvpyQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1 (nightly)
Release Details
UpdatedMay 27, 2026, 5:24 p.m.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF4W7QJB8rVJ2xARdnhUbEouzRZMKcowz/ToDwr0oVFrVUbInFvHAMqXFTOVD1a5N
kDTb0pOhv+hCStTh70bN+aapjbdtp7SdQ3SbP5VoXBKLhUii41OqN/i4yM7itdSs
M8d5PVo9TJYVnfaij2xwWghMqZEyzxgOw4Q98dYIysf2BxsjjCtxGEMo/pISS90G
OE2RaVEFbwUcvu1XKszLP5X/k/onOchengURYVuzHP7VgVuomZpPSZ9VEIRKITyL
GpgSDrdqm5AXdQc7l/2OqprW6G+MxsuVzChbyPGslIYYXYsP9HLc/FfwVxaCJjh2
Nsiqfd+dMwnf7f9yrrBuZjbNwNa5x++IynrwaU01jVISkl9pgkbUYRaPSMTGFDAd
lsEEqlgs9KdzGoTFjDXNdTwDOsUMiu/ag8tPo7YxsxLFbVGa7laRAUhlGUT9QGPr
IOAAWvKqivsYnU5iwTCY/5HGza3SDfZc/e5upPfB+lTQTYijp1aAYRBAsoOLmuga
f0aP45pnfnYBJkT1kMsthjbGMdlL46wLiNHKRct9OlFwx74IRIyZdcs5Q3pC0aNN
DSLvsCSmY8/9PtTLzcOJKXTqqDVupCqq82bUlGRn7NR6xsinzv74MuOVnGY21tBm
iB5ceYpgiEukUz6YtS2quIIiMTnYmM+HBFnkXgBV0/Y=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32

Nextcloud 32

Gantt Projects 2.3.0
Release Details
UpdatedSept. 30, 2026, 11:45 a.m.
Changelog

Added

  • Aufgaben und Teilprojekte per Drag & Drop umsortieren (Issue #299): Vor jeder Aufgabe und jedem Teilprojekt steht in der Tabelle ein Ziehgriff (☰, ganz links, unabhängig von der Einrückung); eine blaue Linie zeigt die Einfügeposition. Verschoben wird nur innerhalb desselben Elternobjekts und unter Zeilen desselben Typs, sonst zeigt der Mauszeiger „nicht erlaubt“ und ein Ablegen bricht ab. Neuer Endpunkt PUT /api/v1/projects/{projectId}/row-order (kind, id, before_id) mit RowOrderService: vergibt die sort_order aller Geschwister fortlaufend neu (auch bei gleichen Werten), Schreibrecht nötig. Bewusst nicht Teil: Griff für Meilensteine (sie haben keine sort_order, stehen vor den Aufgaben ihres Elternobjekts) und Projekte, Umhängen in ein anderes Teilprojekt; der Zeiger ist der Standard-„nicht erlaubt“-Zeiger des Browsers. Doku (FEATURES.md, de/en) ergänzt. Neue PHPUnit-Tests (RowOrderServiceTest), Vitest-Tests (rowDrag.test.js) und der E2E-Test row-reorder.spec.js

Fixed

  • Klick auf das Deck-Abweichungssymbol öffnet den Dialog wieder beim Bereich „Abweichungen von Deck“ (jetzt im Reiter „Verbindungen“): focusDeckSection wurde im Dialog-Kontext nicht mehr weitergereicht. E2E-Tests für die Reiter angepasst
  • Tabelle und Gantt-Chart scrollen vertikal zuverlässig gemeinsam (Issue #292): Tabellenzeilen und Balken standen schon ganz oben um 9 px versetzt (Tabellenkopf 52 px, erste Chart-Zeile erst bei header_height + padding / 2 = 61 px), und weil nur das Chart den Ressourcen-Bereich enthält, konnte es weiter scrollen als die Tabelle – die Tabelle blieb am Ende hängen, das Chart zeigte einen anderen Ausschnitt oder sprang zurück. Der Tabellenkopf ist jetzt so hoch wie der Chart-Kopf bis zur ersten Zeile, ein Platzhalter unter den Tabellenzeilen gleicht Ressourcen-Bereich und Scrollleisten aus (per ResizeObserver laufend nachgeführt), sodass beide Seiten denselben maximalen scrollTop haben. Der Sync überträgt nur noch bei tatsächlicher Abweichung statt über ein synchron zurückgesetztes syncing-Flag, das asynchrone Scroll-Events nicht abfangen konnte. Geometrie in src/gantt/tableScrollSync.js (Vitest), neuer E2E-Test table-scroll-sync.spec.js

Added

  • Deck-Karten schon beim Anlegen einer Aufgabe verknüpfen oder neu anlegen (Issue #306): Der Deck-Bereich im Reiter „Verbindungen“ war bei neuen Aufgaben gesperrt („Speichere zuerst die Aufgabe“). Jetzt lassen sich Karten auswählen und „+“ vormerken; beim Speichern verknüpft bzw. legt die App sie an (deckCardsSection().commit()). Schlägt eine Deck-Aktion fehl, bleibt die Aufgabe gespeichert und ein Hinweis nennt die Zahl der Fehlschläge. Board und Kartenliste des Pickers folgen dem gewählten Projekt und übergeordneten Objekt (auch beim projektlosen Anlegen über „Aufgabe“). Der Termin-Abgleichsdialog entfällt dabei; Abweichungen zeigt danach der Bereich „Abweichungen von Deck“
  • Reiter „Anhänge“ in allen Detaildialogen (Projekt, Teilprojekt, Aufgabe, Meilenstein; vor „Verbindungen“) mit den Bereichen Dateien und Notizen: beliebig viele Dateien (Nextcloud-Dateiauswahldialog mit eigenen Ordnern) und Notizen (Auswahl aus der App Notes im Stil der Deck-Kartenauswahl) verknüpfen. Der Server teilt jede angehängte Datei automatisch mit allen Personen, die Zugriff auf das Projekt haben (Betrachter lesen, Bearbeiter/Admins dürfen ändern) und gleicht die Freigaben nach jeder Änderung an Anhängen, Projektfreigaben und Löschungen ab; entfernt werden nur die von der App angelegten Freigaben (nc_gantt_attach_shares). Die Freigabe gilt je Datei, nicht je Objekt: Sie bleibt bestehen, solange die Datei an mindestens einem Objekt der App hängt. Doku ergänzt in FEATURES.md (Abschnitt „Anhänge: Dateien und Notizen“, de/en), READMEs, HOWTO (Schritt 10) und Store-Beschreibung (info.xml). Neue Tabellen nc_gantt_attachments und nc_gantt_attach_shares (Migration Version1044), Endpunkte GET/POST /attachments, DELETE /attachments/{id}, AttachmentService. Die Dateiauswahl hängt sich in den modalen Detaildialog ein (sonst läge sie unsichtbar dahinter), und nachgeladene Bundle-Teile (Dateiauswahl, Datumsformate) nutzen den tatsächlichen Installationsort der App als Adresse (src/publicPath.js; die Vorgabe /apps/nc_gantt/js/ griff z. B. unter custom_apps nicht). Neue PHPUnit- (AttachmentServiceTest) und Vitest-Tests (attachments.test.js) sowie der E2E-Test attachments.spec.js
  • Aufgaben können von Meilensteinen abhängen (Issue #308): Vorgänger m-<id> neben t-<id> (keine Migration), in allen vier Typen mit positivem und negativem Abstand. Ein Meilenstein zählt als Punkt mit Start = Ende = seinem Datum; ein fester verschiebt sich nie und zieht seine abhängigen Aufgaben samt Nachfolgern mit, ein abgeleiteter folgt weiter seinem Ziel und gibt dessen Verschiebung an die Aufgaben dahinter weiter. TaskSchedulingService (Prüfung, Termine, Propagation, Neuberechnung) kennt Meilensteine als Knoten; die Zyklenprüfung berücksichtigt die Kanten Zielaufgaben → abgeleiteter Meilenstein und läuft auch beim Speichern eines Meilensteins (dependency_cycle); der Dialog sperrt solche Meilensteine als Vorgänger bzw. Ziele mit Hinweis. Im kritischen Pfad sind Meilensteine Knoten: ein fester ersetzt das Projektende als spätestes Ende für die Aufgaben, die über eine Abhängigkeit vor ihm liegen; ein negativer Puffer verhindert das Speichern mit dem Hinweis auf die betroffene Aufgabe (negative_float:<Titel>, bereits negativer Puffer wird nicht erneut gemeldet). Beim Löschen eines Meilensteins (auch über Aufgabe/Teilprojekt) entfallen die Abhängigkeiten auf ihn, ProjectDuplicationService und JSON-Import/-Export münzen m-<id> auf die neuen Meilensteine um. Oberfläche: Auswahl M<id> <Titel> im Abhängigkeits-Editor, Kurznotation M3EA-2d in der Vorgänger-Spalte, Pfeile von und zu Meilenstein-Balken, Nummerierung M<id> im Diagramm, Glossar und FEATURES.md (de/en) nachgezogen. Bekannte Vereinfachungen: die Pufferrechnung zählt den Meilenstein-Termin in Arbeitstagen des globalen Kalenders ab dem frühesten Aufgabenbeginn, und ein Meilenstein-Termin verschiebt keine Deck-Karten (Deck-Abgleich läuft nur bei Änderungen an Aufgaben). Neue PHPUnit-Tests (TaskSchedulingMilestoneTest, CriticalPathMilestoneTest, ergänzt: Meilenstein-, Duplizierungs-, Lösch- und Datums-Tests), Vitest-Tests und der E2E-Test milestone-dependency.spec.js
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureS4LN+K40y1DKrwQo02FOHkiugxuFllfcAsVp7IBoUthbKYnNdZtSmzBH+ZxY0V8C
U954MnYEijFZD14jMuW2wkvMomspSCRGfCfEHytRCLfNwmsK9AV5UfxutjQBe6Y/
ATjKYnhOEPrE2iXLOZ+P73VbC8GBPY6W21P1APsy0Af+XUoFycIdTnLJUU5QJgWY
+K/N7nUfXyqVA17kCO+/Zhw6w5wl5O7ZIMezWbNn3ApvOeIC8AE/CwJ/JccrQgE9
F4A1RTqFZcK0hCrluGelanyva6ygwAmYPoJtkR+CeiJr7vFdSSq6HJe0lG0y9SAm
RNFnDuEzt6uFqSl9EqK6ykVp7ns0k6npvld3gSqGN0Frq4dwGjxSjcRkpPGAYT7M
IH6YJDVlNbVdF/wRJtEmOvO1duI4uzBhjX3PIFX1LBoFR0p5OsKuMIAXGEupbBi4
BV9Zu5578h5VQzZt32gFnsQcGVsvAn2nayCCbcQx/WUHSW57NmBdktF4l+7tme/p
vL+hzncZ2sdDbhDrrDq2G167QwkAJgpGJFQR+34YpGrY3DDhj767+Q/bMHwm4zmk
VYm+g4MAf49P2CKyqwVB1E9SGe3MgTK2rDRnibhVIlgVEDvih1Uf/E5wigpBJerf
E1DvLXTLEtteko53AjIpT/GWpiiD9md4QpCUUdfGMnU=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.2.0
Release Details
UpdatedSept. 28, 2026, 7:36 a.m.
Changelog

Added

  • Implizite Kopfzahl je Aufgabe (Issue #252, Schritt C aus #249; R11): Der Aufgabendialog nennt unter jeder Zuordnung mit Aufwand im Klartext, wie viele Personen gleichzeitig nötig sind („Erfordert im Schnitt 2,0 × Java-Entwickler gleichzeitig“) — Aufwand ÷ Arbeitstage des Balkens ÷ Tageskapazität einer vollen Kraft, je Zuordnung, live bei Änderung von Aufwand, Start, Ende oder Dauer. Gezählt wird nach dem globalen Kalender (Mo–Fr abzüglich globaler Feiertage) für Rollen und Personen gleich; Personenkalender zählen nicht (Entscheidung in #252, daher entfällt das Akzeptanzkriterium „Teilzeitkalender“). Ab der Warnschwelle (globale App-Einstellung headcount_warn_threshold, Standard 3,0, gültig 0,5–100, im Admin-Bereich unter „Warnen ab“; GET/PUT /settings liefern und setzen headcount_threshold, ein PUT ohne den Wert lässt ihn unverändert, Initial State für das erste Rendern) erscheint dort zusätzlich die Konsequenz („mit weniger Personen dauert die Aufgabe entsprechend länger“), und die Tabelle zeigt vor dem Titel ein Personen-Symbol mit Tooltip — ein Hinweis, keine Blockade. Aufgaben ohne Aufwand oder ohne Arbeitstage und Zuordnungen in anderen Einheiten als PT/h zeigen nichts. Die Rechnung liegt als reine Funktion in src/utils/headcount.js, die Schwelle in WorkTimeService. Nicht Teil: Hinweis am Balken im Diagramm, Prüfung der tatsächlichen Verfügbarkeit (Schritt B, #251). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue Vitest-Tests (headcount.test.js, tableRows.test.js, settings.test.js) und PHPUnit-Tests (WorkTimeServiceTest)
  • Kapazitätsprüfung über Rollen hinweg: Engpass-Teilmenge statt Einzelrolle, Alarm über ein gleitendes 5-Tage-Fenster (Issue #251, Schritt B aus #249; R6, R10): Rollenkapazität ist nicht additiv — bei zwei Java-Entwicklern, von denen einer zusätzlich IT-Architekt ist, besteht „2× Java + 1× Architekt“ die Einzelprüfung (2 ≤ 2, 1 ≤ 1), ist aber nicht zu leisten. Neuer UI- und datenbankfreier Rechenkern: CapacityFlow löst je Fenster ein Flussproblem (Bedarfsknoten = Rollen und direkt zugewiesene Personen im selben Fluss, Kante wo die Person die Rolle ausfüllt, Personenkapazität zur Senke; Dinic in CapacityFlowGraph) und liest die Engpass-Teilmenge aus dem Min-Cut, zerlegt in unabhängige Engpässe samt den Personen mit Mehrfachrolle als Ursache; CapacityWindowCheck legt darüber das gleitende Fenster von 5 Arbeitstagen (fest, nicht konfigurierbar, Feiertage verkürzen es nicht): rot = irgendein Fenster fließt nicht (nicht durch Verschieben lösbar), gelb = ein einzelner Tag fließt nicht, aber alle Fenster um ihn (Tagesspitze, umsortierbar); Tage, an denen niemand des Knotens arbeitet, zählen nicht als Spitze (Teilzeit Mo–Do erzeugt keine Meldung); überlappende Verstöße mit gemeinsamen Rollen werden zu einem Zeitraum zusammengefasst. CapacityCheckService trägt Aufgaben der für den Betrachter sichtbaren Projekte (Lücke bewusst akzeptiert, wie in #181), Rollen und Personenkapazität zusammen — Bedarf auf dem globalen Kalender, Kapazität aus den Personenkalendern (ResourceUtilizationService::personCapacityFn() ist dafür öffentlich), Material ausgeschlossen, Rolle ohne Personen meldet rot — und liefert GET /api/v1/capacity-check (Rollen, direkte Personen, Zeitraum, Fehlbetrag in Stunden, Ursache, betroffene Aufgaben). Die Tabelle zeigt vor dem Titel eine kleine Person in Gelb oder Rot mit Tooltip („Java-Entwickler + IT-Architekt zusammen überbucht (KW 42). Fehlbetrag: 5 PT. Ursache: Mehrfachrollen von …“), keine Benachrichtigungen; der Store capacityCheck lädt bei Änderungen an Aufgaben, Rollen, Kapazität oder Feiertagen neu. Ressourcen-Raster und Workload-Chart färben nach demselben Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder über 90 % im Fenster, Tooltip mit Reserve), die Rollenzeile übernimmt das Urteil der Prüfung. Nicht Teil: Sequenzierungsvorschlag (#282), Kostenminimierung der Zuordnung (entfällt), Projektkalender (#284). Bekannte Grenzen (in FEATURES.md): nur sichtbare Projekte, Aufgaben kürzer als das Fenster werden verdünnt (fängt die implizite Kopfzahl aus #252 auf). Neue Texte in allen Übersetzungen; FEATURES.md/HOWTO.md (de/en) und die Integrationsmatrix sind nachgezogen, der Hinweis auf die naive Rechnung aus #250 ist entfernt. Neue PHPUnit-Tests (CapacityFlowTest, CapacityWindowCheckTest, CapacityCheckServiceTest; u. a. Prüffall, Kette über drei Rollen, Bedarf exakt auf Kapazität, Rolle ohne Personen, Laufzeit 30 Personen/10 Rollen/1 Jahr), Vitest-Tests (capacityCheck.test.js, workloadModel.test.js, tableRows.test.js) und der E2E-Test capacity-check.spec.js

  • Projekt duplizieren bzw. als Vorlage für ein neues Projekt verwenden (Issue #270): Die Aktion Duplizieren im Menü des Projekts in der Navigation (auch mit reinem Leserecht) und im Projektdialog legt ein neues Projekt der ausführenden Person an. Der Dialog fragt Titel („Kopie von …“), neuen Starttermin (Vorschlag: bisheriger Projektstart = frühester Aufgabenbeginn) und die Ressourcenzuordnungen (alle / nur Rollen / keine). Alle Termine — Aufgaben, Meilensteine, Planungszeiträume von Projekt und Teilprojekten — verschieben sich um die Differenz in Arbeitstagen des globalen Kalenders, ein Start auf einem freien Tag rückt auf den nächsten Arbeitstag, bei unverändertem Start bleiben die Termine unberührt. Übernommen werden Teilprojekte samt Hierarchie, Aufgaben, Abhängigkeiten mit Typ und Abstand, Meilensteine mit Zuordnung und Abhängigkeitstyp (abgeleitete folgen dem kopierten Termin), Zuordnungen samt Aufwand (nicht sichtbare Ressourcen entfallen, der Aufwand wird bei weggelassenen Zuordnungen neu gebildet), Farben, Beschreibungen und Projektregeln; zurückgesetzt werden Fortschritt (Modus „Deck“ wird „manuell“), Status, Ist-Kosten, Baselines, Änderungsprotokoll, Freigaben, ICS-Token, Deck-Board, Deck-Listen und Kartenverknüpfungen (Deck-Abgleich „Aus“). Neuer ProjectDuplicationService legt alles in einer Transaktion über die Mapper an (kein Änderungsprotokoll, keine Benachrichtigungen, kein Deck-Zugriff); GET /projects/{id}/duplicate liefert Start und Umfang für den Dialog, POST /projects/{id}/duplicate (title, start_date, resources) die Kopie (201). Nicht Teil: eigene Vorlagenverwaltung (Projekte als „Vorlage“ markieren, Galerie beim Anlegen). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue PHPUnit-Tests (ProjectDuplicationServiceTest), Vitest-Tests (duplicate.test.js, ioDialogs.duplicate.test.js) und der E2E-Test project-duplicate.spec.js

Changed

  • Rollenzeile im Ressourcen-Raster ohne „naiv additiv“ (#251): GET /resources/{id}/utilization liefert für Rollen nur noch capacity_derived; capacity_naive und multi_role_member_ids entfallen, ebenso der Hinweis „zu optimistisch“ im Raster (ersetzt durch den Verweis auf die Prüfung über alle Rollen). Das Workload-Chart unter dem Diagramm färbt Perioden nicht mehr je Periode rot, sondern nach dem 5-Tage-Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder knapp)

Fixed

  • Server-Zeitzone der Arbeitszeiten aus default_timezone statt immer UTC (Issue #294): Nextcloud setzt die PHP-Zeitzone in lib/base.php fest auf UTC, deshalb zeigte der Admin-Bereich unter „Arbeitszeiten“ immer „UTC (+00:00)“ und Arbeitsbeginn/-ende wurden als UTC ausgelegt — neue Termine im Detaildialog/Import und die Deck-Fälligkeiten lagen dadurch 1–2 Stunden daneben. Neuer ServerTimeZoneService liest default_timezone aus der config.php (fehlt der Wert oder ist er ungültig, bleibt es bei UTC); GET/PUT /settings liefern dessen Namen und aktuellen Versatz, der Deck-Terminabgleich legt work_start/work_end in dieser Zeitzone aus. Neue PHPUnit-Tests (ServerTimeZoneServiceTest, DeckDateSyncServiceTest)
  • Schrift im Balken passte nicht zur Balkenfarbe (Issue #287): .bar-label stand in theme.css fest auf Weiß, unabhängig von Balkenfarbe und Theme — auf hellen und mittleren Farben, darunter die Standardfarbe #0082c9 (Kontrast 4,2:1), war der Titel im Balken im hellen Theme schwer lesbar, während der Titel außerhalb des Balkens (.big) bereits die Textfarbe des Themes bekam. injectProjectColors() setzt jetzt je Farbklasse die Schriftfarbe, die nach WCAG den höheren Kontrast liefert (labelTextColor() in colors.js: Weiß oder #111111). Die Balkenfarbe ist in beiden Themes dieselbe, die Schrift deshalb ebenfalls. Bei Projekt- und Teilprojektzeilen (Summenbalken, nur eine auf 18 % abgeschwächte Klammerfläche darunter) war die vorgesehene Textfarbe des Themes wirkungslos: .gantt-summary-bar .bar-label und .gantt .bar-label hatten gleiche Spezifität und beide !important, die spätere gewann — der Titel blieb im hellen Theme weiß. Die Regel ist jetzt spezifischer (.gantt .bar-wrapper.gantt-summary-bar .bar-label); Meilenstein und Titel neben dem Balken (.big) behalten ihre Regeln. Neue Vitest-Tests in colors.test.js
  • JSON-Export ohne Datenbank-IDs der Abhängigkeiten (Issue #289): Je Abhängigkeit enthält der Export nur noch predecessor_id, dependency_type und lag_days; id und task_id (Datenbank-IDs des Quellsystems, im Zielsystem bedeutungslos) entfallen. Der Import bleibt kompatibel, ältere Exporte mit diesen Feldern werden weiter gelesen. Der E2E-Test zum Round-Trip aus #269 war seit seiner Einführung rot (erwartete exakt drei Felder je Abhängigkeit und übersah den Import-Optionsdialog) und ist korrigiert.

Security

  • Benutzersuche beachtet die Enumeration-Einstellungen von Nextcloud und leakt keine E-Mail-Adressen mehr (Issue #285): GET /users/search rief IUserManager::search() direkt auf und lieferte jedem angemeldeten Nutzer zu jedem Treffer UID, Anzeigenamen und E-Mail-Adresse — mit kurzen Suchbegriffen ließen sich so alle Konten der Instanz auslesen, auch wenn die Admins das Auflisten abgeschaltet oder auf Gruppen/Telefonbuch beschränkt hatten. Die Suche läuft jetzt über die Collaborators-API (ISearch::search() mit IShare::TYPE_USER, ohne Lookup-Server) wie der Teilen-Dialog im Kern und beachtet damit shareapi_allow_share_dialog_user_enumeration, shareapi_restrict_user_enumeration_to_group/_to_phone/_full_match* und shareapi_only_share_with_group_members; exakte Treffer stehen vorn. subname stammt ausschließlich aus deren Ergebnis, die App lädt keine E-Mail-Adresse mehr selbst nach. Das eigene Konto (nur mit include_self, #204) ergänzt der Controller selbst, wenn Benutzer-ID, Anzeigename oder E-Mail den Suchbegriff enthalten, weil die Collaborators-API es nie liefert. Neue PHPUnit-Tests in UserControllerTest (u. a. keine Fremdkonten bei abgeschalteter Enumeration)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureesXfyynRSXx6Sc+EbEb+n8jhv9KklmBcayRslKKo0dpavjZ1x7CE/oUKrOppOVm7
YsCst1sFoTz/sPZ4qveDz/erE0STxAi2UMyiHHUglJ3JmcCJKtF5byki45BAm02V
PV7dn+EYQP8bd4jWy96mQa66eGzuuAsgR/mA7S1UC9SvcKq6UF6A0LJpJqMyInaR
ycEg6H78WtrIOKAuB5WyaEo20IRsXIechdfyl39Xz1bl1IextE3DPey0OaV6CzfF
6YQeMho+ja2iqh6JKfAzq2zKgTDBLo/bcgk8Gy/b4djBX5KES2w96x535UG7a6JM
fYEg7QdRUqOGgXoCTRSwIAGtqX8bq9sc7058+5NqO9xYOiGDzfv6dJ94x6+KXpd4
eN1N1P0+MfL9TKP27NQLsy46vXPU36w4cSijoMmxyrYGUbVVudrb8XANUvv/jZc+
d/yNvPzhYRDHvoGJ3gn5exgVpQW2M7qQMOBVDYDkROo1UqKv1k3da9s14VMHtX6y
PyhIdtt6Qi7Lu0cMykFH0rYQYoe84Ot3Xb547ldyNCCDAeta4vTczDbA5dTrdstM
HCrbbSl4iF/Lu7zeVhlxnWnQyuC5HjUg0vVuikxkEyYScruNJw4prkXD04XHOMyG
1HOBUrG2tiFl60B8XGyibo5+fc1ioQFwe2iIjdLOW3M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.1.0
Release Details
UpdatedSept. 25, 2026, 6:24 a.m.
Changelog

Changed

  • Detaildialog in abgesetzte Bereiche gegliedert (Issue #263): Die Formulare für Projekt, Teilprojekt, Aufgabe und Meilenstein fassen ihre Felder jetzt in Bereiche mit Rahmen, farbig hinterlegtem Kopf, Symbol und Überschrift zusammen (z. B. Grundinformationen, Darstellung, Terminplanung, Budget und Kosten, Ressourcen, Risikostufe, Fortschritt und Status, Deck, Erinnerungen, Baselines, Export und Import). Die Bereiche heben sich vor einem dezent getönten Dialoghintergrund ab, der Dialogkopf zeigt ein Symbol der Objektart, Feldbeschriftungen stehen in normaler statt Versal-Schrift, und die Risikostufen tragen ihre Ampelfarbe auch ungewählt. Die Farbtöne sind aus den Nextcloud-Farbvariablen gemischt und funktionieren im hellen wie im dunklen Design. Die Trennlinien zwischen den einzelnen Feldern aus dem ersten Anlauf entfallen dafür. Symbole kommen aus @mdi/js, das jetzt direkte Abhängigkeit ist
  • Neue App-Store-Screenshots zur überarbeiteten Oberfläche: Die zehn verlinkten Bilder in appinfo/info.xml sind neu aufgenommen (Projektstruktur, Abhängigkeiten im gegliederten Detaildialog, Tabelle, kritischer Pfad, Ressourcen, Kalender und Feiertage, Abweichungsbericht, Kennzahlen/EVM, Simulation, Mehrprojektansicht mit Filter). Sie liegen jetzt unter screenshots/appstore/ statt screenshots/usecases/, damit der App Store nicht auf zwischengespeicherte alte Bilder unter den bisherigen URLs zurückgreift; die übrigen, veralteten Bilder aus screenshots/ sind entfernt. Das Showcase-Skript scrollt dafür in UC 2 den Abschnitt „Abhängigkeiten“ des Dialogs ins Bild und in UC 5 das Ressourcenraster ganz nach links
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureZQ2bK6Wi8ifFLdOZU9fKEitqGU2/V3SC8sQadWpViyGbPp9TFzXz/m+6VOgxRM0K
88XrfFLwkUN5CrQ8Mzj8rzUUKAp0ANWZVMC43yWzABpywSQ+FosMPTPoPyS7dymn
eKXUnbqfSE3yebLbOtpQaK9VIMVXfvIpMQuIaSnnSX/Mi3ofY4oN3eyBy9NT1uH+
icAEBU4EFZbByJe3tF+IdKP81OX0kFydqeuiSgWJph7yRxP+ucBioO+BsjGB1ZGh
2kiCYjlYoZPbgc4WSoWfe3WGY/cUiqr/6sPEkdWfYHrG6c/tUuNhM1E1pmBXlwIK
S3IzZVklRWFkFcTHdYLAg5QKWTN2+m0sCpPkMkZQn2NYXQNQ5oAPKNoDTMBmW+zG
B72pYfEAs5TQFGcn1M6k6e4IdVjnLtM6AdmyeMdu9y8hKUH8en1RT/MVbXZlFN7O
fZoXKZSwpdocdqMZOqaT3qoUTFUZbmOBzcQQ3xeYQ4H2UvIp4PlyPkXeiFEHKY93
hZWcmtTHthlbPGhtnAutXA7zCzruiXisJXCNaf7ZM+xSbtW3GeLqRMwkLImyxydK
1C+BekGHiJaWI32PyqZH1mwR+V1qgqLLcD3yb+gKOBOUSBW4pdMnNpmvnO7y8KBg
jw8MQf3e5b0JVVoksbULqjqvCOm2ug60NIZYCfp59BA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.1
Release Details
UpdatedSept. 24, 2026, 7:49 a.m.
Changelog

Changed

  • App-Store-Beschreibung und README neu gefasst: Zusammenfassung und Beschreibung (info.xml, Deutsch und Englisch) stellen jetzt das professionelle Projektmanagement mit der Deck-Integration als Alleinstellungsmerkmal in den Vordergrund, die Funktionsliste ist kompakter, und ein eigener Abschnitt erklärt die Datensouveränität (alle Daten bleiben auf der eigenen Instanz). Das englische README und docs/de/README.md übernehmen den Wortlaut. In den übrigen Sprachen entfällt an zwei Emojis der Variation Selector
  • Toolbar-Schalter einheitlich: Die segmentierten Schalter der Toolbars (Zeitskala und Darstellung im Diagramm, Zeitskala im Ressourcenraster) und der Ansichts-Umschalter der App teilen sich jetzt dieselbe Optik: eckige Segmente ohne eigenen Rand und ohne eigene Rundung, sodass Hover- und Aktiv-Flächen bis zur Kante reichen und kein Randstreifen mehr stehen bleibt; die Rundung kommt allein vom Container. Die gemeinsame Regel liegt in der neuen segmented-switch.css statt in mehreren nahezu identischen Blöcken

Fixed

  • Leerzustand im Diagramm nicht mittig: Der Leerzustand des Diagramms (GanttChart.vue) steht jetzt mittig, und sein Icon ist optisch zentriert (der viewBox war seitlich versetzt)
  • Stylelint-Fehler in ResourceGrid.vue: fehlende Leerzeile vor einer Regel (rule-empty-line-before), die den JS-Job der CI rot färbte
  • JSON-Import brach bei Abhängigkeiten ab, Meilensteine fehlten im Export (Issue #269): importJson() übergab predecessor_id-Werte unverändert an store.createTask() – die alten IDs des Quellprojekts bestanden den IDOR-Schutz aus #190 nicht (Import in ein anderes Projekt/eine andere Instanz brach mit invalid_dependency ab und hinterließ ein halb angelegtes Projekt) bzw. zeigten beim Import ins selbe Projekt auf die falschen (alten) Aufgaben. Aufgaben werden jetzt zunächst ohne Abhängigkeiten angelegt und diese erst danach mit auf die importierten Aufgaben umgemünzten predecessor_id-Werten nachgetragen – unabhängig davon, in welcher Reihenfolge Vorgänger und Nachfolger in der Datei stehen. exportJson() nimmt außerdem Meilensteine samt ihrer Zuordnung zu Teilprojekt/Aufgabe und ihres dependency_type mit; der Import schreibt diese Bezüge ebenfalls auf die neuen IDs um
  • Balken mit Abhängigkeiten ziehen schrieb doppelt nach Deck (Issue #267): frappe-gantt zieht beim Verschieben/Resizen eines Balkens dessen Abhängigkeits-Balken in der Vorschau um denselben Pixel-Versatz mit und feuerte on_date_change für jeden von ihnen einzeln – der Server berechnet die Kette beim Speichern des tatsächlich gezogenen Balkens aber bereits selbst (propagated). Der zweite, nur aus der Vorschau abgeleitete Save überschrieb die korrekt propagierten Termine mit dem naiven Pixel-Versatz und erzeugte bei aktiver Deck-Anbindung eine doppelte Rückfrage bzw. Erfolgsmeldung. GanttChart.vue verarbeitet on_date_change jetzt nur noch für den Balken, auf dem die Ziehbewegung tatsächlich begann
  • Picker für eine zweite Deck-Karte konnte dauerhaft deaktiviert bleiben (Issue #267): Der Deck-Bereich im Aufgabendialog rendert einmal sofort (Ladezustand) und ein zweites Mal, sobald die Verknüpfungen geladen sind – traf eine Auswahl im Picker genau in dieses Fenster, ersetzte der zweite Render Select und „Add“-Button durch neue, wieder deaktivierte Knoten und die Auswahl ging verloren. Der Bereich rendert jetzt erst, nachdem die Verknüpfungen geladen sind
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureblE0iU/iUXgQIlpljnjDbdufoMyB2l1S9iGos0PQ3DDhQU9wFB13iopDY4YfCjrW
Y2bVw//uxXAO1S4wzURGPTAEgXGI3NM4A2JaHsxMREiJoU6ANjCSOnt45f9yvgNC
se02XpRKzbTtIUXTIGsH2KUe3grZ7hHg95x4riQhhrLhsDESZMZANpO60O52/AMM
gb4yZiCTbUUr1IzM/IowbHWJcghd6RG8KG+EwONJpRqJVjZhcqhNgcM9k/nKyCxY
tw6o7MIlwQmOUzh6kZXxl8p4auRpiHcGrruiWzJwEb6WSmH/XFTdGwP/ZV8lNnoj
KrR2e+yaKiPMR7UQLrtE8yUe1LGmghJejF/cDO3uLEOGD87gZfHjqXAJOrHI+zyx
FcT65clu8MUG2Tk1PQammIkYty0sPaHKwB/woLOlEBWX8LqrAK9ylOD7NHTdArhJ
TibyfWC9UI1f/iyBYu0xCEq11T23DtrFcpun7ELMNUiQ2z260/PykzEK6WtEhRVd
hgLyzf3YoFSqRNOM5IfewWlGt9YdyM4J3bLIYP01GY4MFV90y9M7/RcaehOG3U/5
DFnQswUXRr9ivk4S7dFt7J0WqltzLDqbIwtXi5f6TEqahrYD1Y46W4JXArdhOK+P
FE4VR6KQrLzKQ9PQhTbrfqCLj0tewWnxc8g6OgIGGk4=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.0
Release Details
UpdatedSept. 23, 2026, 9 a.m.
Changelog

Major-Release mit zwei Schwerpunkten. Die Einzelheiten stehen in den Einträgen darunter.

  • Komplett überarbeitete Deck-Schnittstelle (Zielbild #234, Issues #209–#211, #235–#241, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit geworden. Pro Projekt gibt es eine Stufe für den Deck-Abgleich: Aus (Standard), Nur lesen, Schreiben mit Rückfrage oder Schreiben ohne Rückfrage. Eine Aufgabe kann mehrere Karten haben, und Karten lassen sich direkt aus der Aufgabe anlegen. Termine werden mit Konfliktregel nach Deck geschrieben. Fortschritt und Status kommen sofort aus Deck. Abweichungen zwischen Plan und Deck erscheinen im Überblick, lassen sich annehmen und filtern. Beim Löschen können verknüpfte Karten archiviert werden, doppelte Erinnerungen werden unterdrückt, und ein occ-Befehl gleicht einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich am Verhalten nichts; Bestandsprojekte starten höchstens auf Nur lesen.
  • Neuer Umgang mit Planungsdaten für Projekte und Teilprojekte (Issue #229): Bisher ergaben sich Start und Ende von Projekten und Teilprojekten ausschließlich aus ihren Aufgaben. Jetzt lässt sich zusätzlich ein eigener Planungszeitraum setzen, im Detaildialog oder direkt in der Tabelle. Gezeigt wird er statt des berechneten Zeitraums, auch bei noch leeren Teilprojekten. Laufen die Aufgaben über den Rahmen hinaus, markiert ihn ein rotes Ausrufezeichen. Ampel, kritischer Pfad, Filter und Rollups rechnen weiter mit dem tatsächlich verplanten Inhalt. Ohne gesetzten Planungszeitraum bleibt alles wie bisher.
  • Warum eine neue Hauptversion: Die API-Felder haben sich teilweise geändert. deck_card_id an Aufgaben ist nur noch ein berechnetes Lesefeld, Karten werden über /tasks/{id}/deck-links verknüpft (siehe „Changed“). Löschen über DELETE liefert jetzt einen Körper mit deck. Beim Update laufen die Migrationen Version1039–Version1043 automatisch; vorhandene Kartenverknüpfungen werden übernommen.

Added

  • Detail-Dialog per Klick aus der Tabelle öffnen (Issue #264): Bislang öffnete der Detail-Dialog nur über einen Klick auf den Balken im Gantt-Diagramm, was bei größeren Projekten erst Scrollen zum richtigen Monat verlangte. Die Titel-Spalte der Tabelle hat jetzt einen geteilten Klickbereich: Bei Aufgaben und Meilensteinen öffnet ein Klick auf den Platz vor dem Bezeichnungs-Rahmen (Zusammenklapp-Pfeil, Farbpunkt, Abweichungssymbol/-zähler) den Detail-Dialog, während der Rahmen selbst wie bisher die Inline-Bearbeitung der Bezeichnung öffnet. Bei Projekten und Teilprojekten öffnet der gesamte bisherige Rahmen den Detail-Dialog; die Inline-Bearbeitung der Bezeichnung entfällt dort, da beide Objekttypen ohnehin nur das Titelfeld inline editierbar hatten. Der Rahmen ist jetzt als sichtbarer Rand um die Bezeichnung dargestellt (GanttTable.vue)
  • Deck-Anbindung abgeschlossen (Stufen 1–3 aus #234, Issues #209–#211, #235–#240, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit mit Deck geworden. Je Projekt lässt sich eine von vier Stufen wählen — Aus, Nur lesen (Fortschritt und Status kommen sofort aus Deck), Schreiben mit Rückfrage und Schreiben ohne Rückfrage (beide erlauben zusätzlich, Karten aus einer Aufgabe anzulegen und ihre Termine sowie einmalig die Zuständigen nachzuführen). Wo Deck und Plan auseinanderlaufen — Karte gelöscht/archiviert, Termin oder Zuständigkeit von Hand geändert, Karte überfällig, ziehende Person ohne die geforderte Rolle —, zeigt die App das an, statt es aufzulösen; angenommen wird je Aufgabe. Doppelte Erinnerungen zwischen Gantt und Deck werden unterdrückt, ein occ-Befehl gleicht bei Bedarf alles einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich nichts am bisherigen Verhalten. Die folgenden Einträge beschreiben die einzelnen Schritte im Detail; Endstand siehe FEATURES.md Kapitel 16 und die Integrationsmatrix, HOWTO.md und README („Optionale Abhängigkeit zu Deck“)
  • Deck-Fundament: Adapter, Verknüpfungstabelle (1:n) und Opt-in-Stufen je Projekt (Issue #209, Stufe 1, Schritte 1–2 aus #234): Die Migration Version1039 legt nc_gantt_deck_links an (eine Zeile je Karte mit Aufgabe, Board, zuletzt gesehenem Kartenstand, zuletzt geschriebenen Terminen, angenommenem Stand je Abweichungsart und Zeitpunkt des Abgleichs; eindeutiger Index auf der Karten-ID) und übernimmt vorhandene deck_card_id-Werte. Die Spalte deck_card_id bleibt lesbar, ist veraltet und wird bis zum Rückbau mit #237 aus der Tabelle befüllt. Das Löschen einer Aufgabe entfernt ihre Verknüpfungen, die Karte bleibt. Neue API: GET/POST /tasks/{id}/deck-links, DELETE /tasks/{id}/deck-links/{cardId}, GET /deck-links/occupied?project_id=|board_id= (belegte Karten → Aufgabe)
  • DeckGateway als einzige Andockstelle zu Deck (Deck-Services erst im Aufruf aus dem Container, isAvailable(), supportsStartDate(), readCard(), permissions(), Ergebnisobjekt DeckResult statt Exception, nur mit angemeldetem Benutzer, Schreibmethoden nur in den beiden Schreibstufen — ihre Umsetzung folgt mit #210, #211, #237, #238)
  • Opt-in-Stufe „Deck-Abgleich“ je Projekt mit vier Stufen (Aus — Standard, Nur lesen, Schreiben mit Rückfrage, Schreiben ohne Rückfrage); serverseitig beantwortet DeckSyncService getrennt „darf lesen“, „darf schreiben“, „muss fragen“. Projektdialog mit Vorschlag „Nur lesen“ beim Verknüpfen eines Boards, Hinweis beim Umschalten auf eine Schreibstufe und ausgegraut mit sichtbarem Hinweistext ohne Deck. Bestandsprojekte mit Board und Aufgabe im Fortschrittsmodus „aus Deck“ starten auf Nur lesen, alle übrigen auf Aus; nie auf einer Schreibstufe. Der clientseitige Abgleich aus #221 prüft die Stufe
  • Abweichungsdienst (DeckDeviationService, DeckDeviationProvider) mit einheitlichem „Annehmen“ (GET /tasks/{id}/deck-deviations, POST /tasks/{id}/deck-deviations/accept) und Bereich „Abweichungen zu Deck“ im Aufgabendialog; die Abweichungsarten liefern die Folgetickets
  • Vertragstest gegen Deck (make test-deck-contract, tests/deck-contract/, CI-Job Deck-Vertragstest) und README-Abschnitt „Optionale Abhängigkeit zu Deck“; FEATURES.md (de/en) um Stufen und mehrere Karten ergänzt
  • Deck im E2E-Setup optional: setup-dev.sh --no-deck, tests/e2e/helpers/deck.js (Tests ohne Deck überspringen sich selbst), tests/e2e/specs/deck-foundation.spec.js
  • Deck-Rückmeldung per Event-Listener: Fortschritt und Status sofort übernehmen (Issue #235, Stufe 1, Schritt 3 aus #234): DeckCardListener ist für CardCreatedEvent, CardUpdatedEvent und CardDeletedEvent per Klassenname registriert und wertet nur getWebhookSerializable()['card'] aus; er wirft nie. DeckCardSyncService aktualisiert ab der Stufe Nur lesen den zuletzt gesehenen Kartenstand an der Verknüpfung (erledigt, archiviert, gelöscht, Fälligkeit, Start, Zuweisungen, Checkliste) und berechnet für Aufgaben im Modus „aus Deck“ Fortschritt (Mittelwert je Karte: erledigt = 100 %, sonst Checklistenanteil; archiviert zählt als erledigt, gelöscht zählt nicht mit; ohne mitzählende Karte bleibt alles stehen) und Status (done / in_progress / not_started, blocked bleibt). Die Formel DeckProgress in PHP spiegelt cardProgress(); beide prüfen tests/fixtures/deck-progress-cases.json. Neue Abweichungsarten „Karte archiviert“ / „Karte gelöscht“ über den Abweichungsdienst; Übernahmen stehen mit Operations-ID deck:… im Änderungsprotokoll. Der clientseitige Abgleich beim Öffnen bleibt als Fallback und setzt den Status nach denselben Regeln (PUT /tasks/{id}/progress/sync akzeptiert done). Der Aufgabendialog weist im Modus „aus Deck“ neben dem Statusfeld darauf hin, dass der Status Deck folgt. Vertragstest, README-Abschnitt und FEATURES.md (de/en) sind nachgezogen
  • occ nc_gantt:deck:sync --user=<uid> [--project=<id>] [--dry-run]: einmaliger Vollabgleich aus Deck (Issue #236, Stufe 1, Schritt 4 aus #234): Der Befehl setzt den Kontext des Kontos per setVolatileActiveUser, liest jede verknüpfte Karte, die das Konto in Deck sehen darf, und übernimmt sie über dieselbe Logik wie der Listener aus #235 (DeckCardSyncService::applyStates(), gemeinsamer Weg statt zweiter Implementierung). Nur Projekte ab Nur lesen; nach Deck wird nie geschrieben. Ausgabe je Projekt: geprüfte und geänderte Aufgaben, übersprungene Karten (keine Berechtigung / nicht gefunden); Rückgabewert ≠ 0 nur bei Fehlern, auch wenn Deck fehlt. --dry-run speichert nichts. Registriert in appinfo/info.xml (<commands>); FEATURES.md (de/en) und README ergänzt
  • Mehrere Deck-Karten je Aufgabe: Picker, „+“ zum Anlegen und Rolle der Deck-Listen je Projekt (Issue #237, Stufe 2, Schritt 5 aus #234; UC1 „Übergabe“, O1, O2, O8, O14): Der Aufgabendialog hat den Bereich „Deck-Karten“ (für bestehende Aufgaben) mit der Liste der verknüpften Karten (Titel, Liste, erledigt, „In Deck öffnen“, „Lösen“ — das Lösen ändert die Karte nie), dem Picker (Karten an einer anderen Aufgabe sind gesperrt und nennen sie) und „+“, das über den Server und DeckGateway::createCard() eine Karte mit Titel und Beschreibung der Aufgabe samt Rücklink „Geplant in Gantt-Projekte: <URL>“ (Deeplink ?project=&task=), Fälligkeit = Ende, ab Deck 1.18 Start = Start anlegt; „+“ gibt es erst ab „Schreiben mit Rückfrage“ und mit Bearbeitungsrecht am Board (sonst gesperrt mit Hinweis auf Grund und Stufe) und braucht keine zweite Rückfrage. DeckGateway::setDates() ist umgesetzt (Fälligkeit, ab Deck 1.18 Start; Ergebnisobjekt statt Exception; nur in einer Schreibstufe; prüft nach dem Schreiben, ob Deck die Termine übernommen hat) — #210 baut darauf auf. Beim Verknüpfen einer Karte mit abweichendem Datum fragt der Dialog sofort: Plan in Deck durchsetzen (nur in einer Schreibstufe, selbst die Bestätigung), Plan anpassen oder Abweichung stehen lassen; eine datumslose Karte bekommt die Planlage ohne Rückfrage, aber nur ab einer Schreibstufe. Der Stand wird als „zuletzt gesehen“ bzw. „zuletzt von Gantt geschrieben“ an der Verknüpfung gespeichert. Projekteinstellung „Rolle der Deck-Listen“ (deck_list_role: subproject — Standard, storage, none) mit „?“-Hilfe; das Listenfeld am Teilprojekt entfällt bei none, gespeicherte Listen-IDs bleiben; ein Wechsel ändert keine Verknüpfung. Ohne Liste am Teilprojekt nutzt „+“ die Liste des nächsten übergeordneten Teilprojekts, sonst die erste Liste des Boards. Neue API: POST /tasks/{id}/deck-cards, POST /tasks/{id}/deck-links mit optionalem date_action. Der Fortschritt „aus Deck“ im Dialog bildet bei mehreren Karten den Mittelwert. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kapitel 16 und Integrationsmatrix), HOWTO.md und README (Deck-Vertrag) sind nachgezogen; Vertragstest um CardService::create()/update(), StackMapper::findAll() und den Startparameter ergänzt

  • Termine der Aufgabe in die verknüpften Deck-Karten schreiben, mit Konfliktregel und gebündelter Rückfrage (Issue #210, Stufe 2, Schritt 6 aus #234; UC3 „Umplanung“, O4, O8, O9, O11, O12): Ab einer Schreibstufe folgen alle verknüpften Karten den Terminen der Aufgabe — Fälligkeit = Ende zum Arbeitstagsende (Einstellung „Arbeitszeit“), ab Deck 1.18 zusätzlich Start — bei jedem Weg der Terminänderung (Dialog, Tabelle, Ziehen im Diagramm, Propagation über Abhängigkeiten inklusive Karten fremder Aufgaben, „Termine neu berechnen“, Undo). Der neue DeckDateSyncService schreibt synchron im Request nach dem Speichern über DeckGateway::setDates(), fehlertolerant (Deck-Fehler brechen das Speichern nie ab); das Frontend zeigt danach „3 Deck-Karten aktualisiert“, aufklappbar mit Liste, auch fehlgeschlagene Karten. Konfliktregel: Geschrieben wird nur, wenn die Karte noch den zuletzt von Gantt geschriebenen Wert trägt (ohne geschriebenen Wert: wenn sie kein Datum trägt); sonst bleibt sie unangetastet und der Abweichungsdienst zeigt „Fälligkeit/Start in Deck abweichend“ mit Deck- und Plan-Datum. Aktionen im Aufgabendialog: Plan in Deck durchsetzen (POST /tasks/{id}/deck-deviations/enforce, ohne Rückfrage), Plan anpassen (Deck-Datum wird Ende der Aufgabe, gespeichert mit dem Dialog) und Annehmen. Rückfrage in „Schreiben mit Rückfrage“: POST /projects/{id}/deck-preview rechnet die Propagation ohne Speichern; ein Dialog listet einmal gebündelt alle betroffenen Karten mit altem und neuem Wert, Karten fremder Aufgaben hervorgehoben, „Abbrechen“ verwirft die gesamte Änderung, kein „Nicht mehr fragen“; ohne Bestätigung (deck_confirmed) wird nicht geschrieben. Bei „Schreiben ohne Rückfrage“ entfällt der Dialog, bei „Aus“ und „Nur lesen“ wird nichts geschrieben. Rechte: Ohne Bearbeitungsrecht am Board wird die Karte als „Deck-Abgleich ausstehend (keine Berechtigung)“ markiert und beim nächsten Speichern durch eine Person mit Rechten mitgeschrieben (Migration Version1041, Spalte sync_pending); eine in Deck gelöschte oder nicht auffindbare Karte bleibt verknüpft und erscheint als Abweichung „Karte gelöscht“. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kapitel 16) und HOWTO.md sind nachgezogen, der Hinweis „Terminverschiebungen im Gantt ändern derzeit keine Deck-Karten“ ist ersetzt. Neue PHPUnit-Tests (DeckDateSyncServiceTest, TaskDeckSyncTest, DeckDateDeviationProviderTest, DeckDeviationActionServiceTest), Vitest-Tests (deckWrite.test.js, deckWriteDialogs.test.js, deckDeviations.test.js, projects.test.js) und der E2E-Test deck-date-sync.spec.js

  • Verknüpfte Deck-Karten beim Löschen auf Rückfrage archivieren (Issue #238, Stufe 2, Schritt 7 aus #234; O10, O11, O12): Der Löschdialog einer Aufgabe mit Karten fragt „Verknüpfte Deck-Karte(n) ebenfalls archivieren?“ mit der Kartenzahl, Standard „Nein“; Teilprojekt und Projekt stellen eine Sammelrückfrage mit der Gesamtzahl. Gefragt wird nur in den beiden Schreibstufen und nur für Karten an Boards mit Bearbeitungsrecht (DeckGateway::permissions()); für die übrigen sagt der Dialog „N Karten bleiben unverändert“, der Löschdialog ist die Bestätigung (keine zweite Rückfrage). DeckGateway::archive() ist umgesetzt (CardService::archive(), Erfolg erst nach erneutem Lesen); DeckDeletionService plant vor dem Löschen (die Kaskade entfernt die Verknüpfungen) und archiviert synchron danach, fehlertolerant: scheitert Deck, ist die Aufgabe trotzdem gelöscht und die Meldung nennt die nicht archivierten Karten, sonst die archivierten mit Link. Neu: GET /tasks|subprojects|projects/{id}/deck-delete-preview; DELETE nimmt archive_deck_cards an und liefert jetzt deck (archived, failed) statt eines leeren Körpers. Verknüpfung lösen ändert die Karte nie; ein Undo für das Löschen gibt es nicht. PHPUnit mit gemocktem Gateway, Vitest, E2E deck-archive-on-delete.spec.js; Vertragstest um CardService::archive(id) ergänzt; FEATURES.md (de/en) und README ergänzt
  • Rollen als Ressourcentyp: Aufgaben fordern Rollen, Personen tragen die Kapazität (Issue #250, Schritt A aus #249; R2–R5): nc_gantt_resources.type (person — Standard für alle bestehenden Zeilen —, role, material) und die n:m-Tabelle nc_gantt_resource_role (Person ↔ Rolle, eindeutige Kombination; Migration Version1042, dazu nc_gantt_projects.roles_only_assignment). Eine Rolle ist Zuweisungsziel ohne Konto, Arbeitszeitmodell, Kalender und eigene Kapazität (Kapazitätswerte an Rollen werden nicht angelegt); TaskResource und ResourceGroup bleiben unverändert, Rollen sind eine zur Gruppe orthogonale Achse. Rollen werden in der Ressourcenverwaltung über „Neue Rolle“ angelegt, umbenannt und gelöscht und getrennt in der Navigation geführt; die Zuordnung Person ↔ Rolle ist von beiden Seiten pflegbar (role_ids an der Person, person_ids an der Rolle, ohne Admin-Rechte). Löschen einer Rolle mit Aufgaben fragt nach und nennt sie (GET /resources/{id}/usage), Löschen einer Person entfernt ihre Zuordnungen, die Rollen bleiben. Im Aufgabendialog lassen sich Rollen und Personen gemischt mit Aufwand zuordnen, die Auswahl kennzeichnet „(Rolle)“/„(Person)“ (ohne Rollen unverändert); keine Migration bestehender Zuordnungen. Projekteinstellung „Nur Rollenzuweisung zulassen“ (Standard aus) verhindert nur die Neuzuweisung von Personen. ResourceUtilizationService leitet die Kapazität einer Rolle je Tag aus der Summe ihrer Personen ab (Urlaub/Teilzeit senkt alle ihre Rollen) — bewusst naiv additiv, das Ressourcen-Raster weist auf die Mehrfachzählung hin (capacity_naive); Personen- und Rollenzuordnungen laufen durch dieselbe Auswertung, das Workload-Chart summiert Rollen aus ihren Personen. Sichtbarkeit und Rechte folgen den bestehenden Ressourcenregeln. Nicht Teil: Konflikterkennung über Rollen (Schritt B), Kopfzahl (Schritt C), Rollen-Level, Historisierung, Deck-Schnittstelle. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 11 und Integrationsmatrix) und HOWTO.md sind nachgezogen. Neue PHPUnit-Tests (ResourceServiceTest, ResourceUtilizationServiceTest, TaskResourceAssignmentServiceTest, ResourceRolesMigrationTest), Vitest-Tests (resourceType.test.js, resourceDelete.test.js, resources.test.js) und der E2E-Test resource-roles.spec.js
  • Zuständigkeit bei der Kartenanlage übernehmen, danach Abweichungen anzeigen (Issue #211, Stufe 3, Schritt 8 aus #234; O7): „+“ im Aufgabendialog weist der neuen Karte alle Nextcloud-Konten zu, die mit den Ressourcen der Aufgabe verknüpft sind (DeckGateway::assign() über AssignmentService::assignUser(), jedes Konto einzeln). Der Dialog unterscheidet beim Überspringen Person ohne Konto (Lücke: „2 Ressourcen ohne Nextcloud-Konto wurden nicht zugewiesen“), Rolle (#250; sachliche Auskunft, die Karte bleibt absichtlich frei zum Ziehen, nicht als „nicht zugewiesen“ gezählt) und Konto ohne Zugriff auf das Board (übersprungen und genannt; Gantt teilt das Board nicht). Scheitert die Zuweisung, bleibt die Karte angelegt und verknüpft. Danach gibt es keine laufende Synchronisation. Neue Abweichungsart assignees („Zuständige abweichend“, DeckAssigneeDeviationProvider über den Abweichungsdienst aus #209): Menge der zugewiesenen Benutzer der Karte gegen Menge der verknüpften Konten der Ressourcen, in beide Richtungen, mit Annehmen (Hinweis kehrt erst bei erneuter Änderung zurück); Gruppen/Teams und Rollen erzeugen keine Abweichung (Gruppen erscheinen als „auch zugewiesen: …“ an der Karte, bei gemischter Zuordnung zählt nur der Personenanteil). Der Kartenstand trennt jetzt assignees (Benutzer) und assigned_groups. Neue Antwortfelder assignment bei POST /tasks/{id}/deck-cards; Vertragstest und README um AssignmentService::assignUser() und BadRequestException ergänzt. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kap. 16) und HOWTO.md nachgezogen. PHPUnit (DeckAssigneeDeviationProviderTest, DeckAssigneeResolverTest, DeckAssigneesTest, DeckLinkServiceTest, DeckGatewayTest), Vitest (deckAssignees.test.js, deckCards.test.js, deckDeviations.test.js) und E2E deck-assignment.spec.js
  • Abweichungen zwischen Plan und Deck im Überblick anzeigen, annehmen und filtern (Issue #239, Stufe 3, Schritt 9 aus #234; UC4 „Abweichungen erkennen“, Leitprinzip 8): Zwei neue Abweichungsarten über den gemeinsamen Abweichungsdienst aus #209 — overdue (DeckOverdueDeviationProvider: Fälligkeit vor heute, nicht erledigt, nicht archiviert/gelöscht, ausschließlich aus dem zuletzt gesehenen Kartenstand, kein Deck-Zugriff nötig) und role (DeckRoleDeviationProvider, setzt #250 voraus: eine Karte einer Aufgabe mit Rollenzuweisung ist einer Person zugewiesen, die keiner der geforderten Rollen zugeordnet ist — ermittelt über ResourceRoleMapper::findPersonIdsOfRoles(); ohne Rollenzuweisung an der Aufgabe greift die Art nicht, bei gemischter Zuordnung läuft sie unabhängig neben assignees). Neu DeckDeviationService::kindsByTask() fasst die offenen Arten je Aufgabe eines Projekts in einem Rutsch zusammen (ein Durchlauf über alle Verknüpfungen statt eine Anfrage je Aufgabe); TaskService hängt daraus das berechnete Lesefeld deck_deviation_kinds an jede Aufgabe aus GET /projects/{id}/tasks, RollupService liefert zusätzlich deck_deviation_count (Summe über das Projekt) im bestehenden Rollup-Endpunkt. Im Frontend: Warnsymbol an der Aufgabe in Tabelle (GanttTable.vue, echtes <button>) und Diagramm (gantt/overlays/deckDeviationOverlay.js, fokussierbares SVG mit eigenem Klick-/Tastatur-Handler statt des abgeschalteten Bibliotheks-Popups) mit Tooltip/aria-label über die betroffenen Arten (utils/deckDeviationKinds.js); ein Klick öffnet den Aufgabendialog gescrollt und fokussiert beim Bereich „Abweichungen zu Deck“ (detailModal.js, neue Option focusDeckSection, Ereigniskette open-deck-deviations/edit-task-deck). Zähler an Teilprojekt (rekursiv über verschachtelte Teilprojekte) und Projekt in der Tabelle sowie, über den Rollup, in der Projektliste und im Projekt-Rollup-Balken. Neuer Filter „Deck weicht ab“ (filters.js, filterMatch.js, FilterBar.vue) wirkt wie die übrigen Filter auf Tabelle und Diagramm. Für „Karte gelöscht/archiviert“ gibt es im Aufgabendialog zusätzlich die Aktion Verknüpfung lösen (DELETE /tasks/{id}/deck-links/{cardId}, ändert die Karte nie); nach Annehmen, Durchsetzen oder Verknüpfung lösen lädt der Dialog die Aufgabenliste des Projekts neu (onDeviationsChanged), damit Symbol und Zähler ohne Neuladen der Seite mitziehen. Wie immer gilt: Abweichungen nur ab der Projektstufe „Nur lesen“, Annehmen nur mit Bearbeitungsrecht. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, neuer Abschnitt in Kap. 16 und Filterliste in Kap. 5) und HOWTO.md (de/en) nachgezogen. PHPUnit (DeckOverdueDeviationProviderTest, DeckRoleDeviationProviderTest, DeckCardStateDeviationProviderTest — bislang fehlend zu #235 —, erweiterte DeckDeviationServiceTest/RollupServiceTest), Vitest (deckDeviationKinds.test.js, erweiterte filterMatch.test.js/tableRows.test.js/deckDeviations.test.js) und E2E deck-deviations-overview.spec.js
  • Doppelte Signale vermeiden: Gantt-Erinnerungen und Tasks-Einträge bei Aufgaben mit Deck-Karte unterdrücken (Issue #240, Stufe 3, Schritt 10 aus #234; O13): Neuer DeckSuppressionService beantwortet für Aufgaben mit mindestens einer verknüpften, nicht archivierten Karte (nach dem zuletzt gesehenen Kartenstand aus #235) in einem Projekt ab Stufe „Nur lesen“, ob NC-Gantt für diese Aufgabe still bleiben soll — DueReminderService, EventNotificationService::notifyAssigned() und TaskCalendarSyncService fragen dort statt die Regel je einmal selbst nachzubilden, statt selbst zu unterdrücken. Unterdrückt werden die Fälligkeits-Erinnerung der Aufgabe, die Zuweisungsmeldung und ihr Eintrag in der Tasks-App; nicht unterdrückt bleiben notifyRescheduled() (Deck kennt keine Kettenverschiebung) und Meilenstein-Erinnerungen (Meilensteine haben keine Karte). Persönliche Einstellung „Gantt-Benachrichtigungen auch für Aufgaben mit Deck-Karte erhalten“ im Abschnitt „Gantt-Projekte“ der Nextcloud-Benutzereinstellungen (PUT /api/v1/settings/reminder-deck-override), Standard aus — die Unterdrückung ist also aktiv; gilt gleichermaßen für Benachrichtigungen und Tasks-App-Einträge. Wird die Unterdrückung wirksam oder aufgehoben, räumt bzw. holt der jeweils nächste Lauf von DueReminderService/TaskCalendarSyncService die betroffenen Erinnerungen bzw. To-dos ab — über denselben Soll-/Ist-Abgleich, der schon das Stummschalten trägt, ohne eigenen Sonderfall. Neue Texte in l10n/de.json/de.js; FEATURES.md (de/en, Integrationsmatrix, Kap. 14, 18 und 20) nachgezogen. Neue PHPUnit-Tests (DeckSuppressionServiceTest, erweiterte DueReminderServiceTest/EventNotificationServiceTest/TaskCalendarSyncServiceTest) und Vitest (stores/settings.test.js)
  • Optionaler Planungszeitraum für Projekte und Teilprojekte (Issue #229): Projekte und Teilprojekte können neben dem aus den Aufgaben berechneten Zeitraum einen eigenen, bewusst gesetzten Rahmen haben (plan_start/plan_end, Migration Version1043; leer für alle Bestandsprojekte, die sich damit unverändert verhalten). Eingabe im Detaildialog (Abschnitt „Planungszeitraum“) und per Inline-Editing in den Spalten Start/Ende der Tabelle, die dafür erstmals auch bei Projekt- und Teilprojektzeilen editierbar sind. Start und Ende sind nur gemeinsam gesetzt: ein leeres Gegenfeld übernimmt den eingegebenen Wert, ein vorhandener wird nie überschrieben, das Leeren eines Felds entfernt den Zeitraum; Start ≤ Ende und Schreibrecht werden serverseitig geprüft (PlanningPeriod, Fehlercodes invalid_plan_date, plan_start_after_end). Ist ein Planungszeitraum gesetzt, zeigen Balken, Tabelle (Start/Ende/Dauer), PDF/Druck und ICS-Feed ihn anstelle des berechneten Zeitraums — auch ein Teilprojekt ohne Aufgaben wird so sichtbar; ein Elternobjekt ohne eigenen Planungszeitraum umfasst in der Darstellung die Zeiträume seiner Kind-Teilprojekte. Liegen die Aufgaben nicht vollständig im Rahmen oder ragt ein Kind-Teilprojekt mit Planungszeitraum heraus, markiert ein rotes Ausrufezeichen die Zeile in Tabelle und Diagramm (planWarningOverlay.js), der Tooltip nennt die Ursache; der Rahmen passt sich nie selbst an und verschiebt keine Aufgaben. Ampel, Rollups, kritischer Pfad, Meilensteine, Filter, Fortschritt und Budget rechnen weiter mit dem berechneten Zeitraum; Baselines und Deck bleiben unberührt. Änderungen stehen mit Operations-ID im Änderungsprotokoll und lassen sich per Toast rückgängig machen (revertOperation() kennt dafür jetzt auch Projekte und liefert entity_types); der JSON-Export/-Import führt den Planungszeitraum mit. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 1, 3, 4, 10, 14) und HOWTO.md (de/en) nachgezogen. PHPUnit (PlanningPeriodTest, erweiterte ProjectServiceTest/SubProjectServiceTest/ChangeEventServiceTest/CalendarFeedServiceTest), Vitest (planningPeriod.test.js, erweiterte rowModel.test.js/tableRows.test.js/io.test.js) und E2E planning-period.spec.js
  • Rich-Vorschau für Links auf Projekte und Aufgaben (Issue #241, Stufe 4, Schritt 12 aus #234; O14): Eigener GanttReferenceProvider (OCP\Collaboration\Reference, ohne jede Deck-Abhängigkeit) erkennt Links der Form ?project=<id> bzw. ?project=<id>&task=<id>, mit und ohne index.php, und löst nur auf, wenn die anfragende Person laut AccessService mindestens Leserecht auf das Projekt hat — sonst bleibt der Link ein nackter Link. Die Vorschau einer Aufgabe zeigt Titel, Projekt/Teilprojekt, Start–Ende, Fortschritt, Status und Zuständige, die eines Projekts Titel, Zeitraum, Rollup-Fortschritt und den nächsten Meilenstein; ein eigenes Vue-Widget (registerWidget aus @nextcloud/vue, geladen nur beim RenderReferenceEvent) rendert das im Nextcloud-Look mit Dark-Mode. Cache-Präfix je Projekt bzw. Aufgabe und Cache-Schlüssel je Benutzer (ReferenceCachePrefix); TaskService, ProjectService und MilestoneService verwerfen den betroffenen Cache über IReferenceManager::invalidateCache() bei jeder Änderung, damit die Vorschau nicht bis zu einer Stunde veraltet bleibt. FEATURES.md (de/en, neues Kapitel 23 und Integrationsmatrix) nachgezogen. Neue PHPUnit-Tests (GanttReferenceProviderTest, TaskServiceReferenceCacheTest, ProjectServiceReferenceCacheTest, MilestoneServiceReferenceCacheTest) und E2E-Test reference-preview.spec.js (prüft über den Core-Endpunkt references/resolve, denselben Weg, den Text, Notizen und Talk nutzen). Nicht Teil: ein Smart-Picker-Eintrag zum Einfügen einer Gantt-Aufgabe per Suche (geplanter Folgeschritt zusammen mit der Talk-Integration)

Changed

  • Aufgaben-API: deck_card_id ist nur noch ein berechnetes Lesefeld (Issue #237): Die Migration Version1040 entfernt die Spalte nc_gantt_tasks.deck_card_id (Werte, die noch nicht in nc_gantt_deck_links stehen, werden vorher übernommen) und ergänzt nc_gantt_projects.deck_list_role. Die API liefert deck_card_id weiter — als erste Verknüpfung der Aufgabe —, damit bestehende Nutzer nicht brechen; geschrieben wird nur noch über die Verknüpfungs-Endpunkte, ein mitgesendetes deck_card_id bei POST/PUT auf Aufgaben wird ignoriert. Der Aufgabendialog und alle Frontend-Pfade lesen die Verknüpfungen aus der Tabelle

Fixed

  • Rollenkapazität im Ressourcen-Raster stimmte nicht mit den Personenzeilen überein (Issue #260): Drei unabhängige Fehler wirkten zusammen. Ein Options-API-Watcher auf store.activeIds (Getter-Pfad, nicht deep) feuerte nie, weil toggle() das Array per push()/splice() mutierte statt neu zuzuweisen — eine nachträglich in der Seitenleiste aktivierte Ressource blieb bis zum Moduswechsel ohne Auslastungsfarbe, Tooltip oder abgeleiteten Rollenwert. ResourceUtilizationService und die Personenzeile der Tabelle (getVal()) lasen unterschiedliche Kapazitätsquellen: die Rolle über die 8h/Tag-Pauschale von WorkTimeService::getHoursPerPt() für jeden ungepflegten Tag, die Person über den Mittelwert der gepflegten Tage hochgerechnet auf die ganze Periode. Beide laufen jetzt über dieselbe Tagesfunktion (personCapacityFn() im Backend, resourceCapacity.js im Frontend) — die Pauschale greift nur noch, wenn für eine Person überhaupt keine Kapazität gepflegt ist, nicht mehr für einzelne Lücken innerhalb eines gepflegten Verlaufs, und ist dann auch in der Personenzeile sichtbar. Ein gepflegter Tageswert auf einem Feiertag zählte bisher voll statt 0h, sowohl in der Auslastungsberechnung als auch in resources.js::capacityOf() (Überbuchungsprüfung/Auslastungsgraph im Gantt-Diagramm) — beide prüfen jetzt Arbeitszeitmodell und Feiertagskalender der Ressource unabhängig vom gepflegten Wert. „Wert bis Monats-/Jahresende kopieren“ (doFill()) legt keine Kapazitätssätze mehr auf Feiertagen oder arbeitsfreien Tagen an. Eine Rollenspalte gilt im Tagesmodus als arbeitsfrei, wenn keine ihrer Personen an diesem Tag arbeitet. Neue PHPUnit-Tests (ResourceUtilizationServiceTest), Vitest (resourceCapacity.test.js, erweiterte resources.test.js) und E2E-Test (resource-roles.spec.js)
  • Deck-Vertragstest schlug falsch an (Issue #256): Für CardService::create()/update() prüfte er die Parameter positional, obwohl NC-Gantt sie über DeckGateway::invokeNamed() nach Namen aufruft. Ab Deck 1.18 steht startdate hinter dem von NC-Gantt nicht genutzten done, der CI-Job Deck-Vertragstest wurde dadurch rot, ohne dass etwas kaputt war. Die beiden Methoden stehen in contract.php jetzt unter named: geprüft wird, dass es die Parameternamen gibt und dass NC-Gantt jeden Pflichtparameter der Deck-Methode kennt — die Reihenfolge in Deck ist gleichgültig, neue Parameter dazwischen (zuletzt color) brechen den Vertrag nicht mehr. Positional aufgerufene Methoden bleiben unverändert
  • Startdatum einer Deck-Karte wurde nie gelesen (DeckGateway::cardState()): Deck-Entities stellen ihre Getter über __call bereit, method_exists() war daher immer falsch; jetzt is_callable()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturekJ6Xo6R3EOShOn1PWZI+CpEgV34eRXUZy4CNPkmb+e7uqUL4Tu/tWsqGpu15PCRr
8HR2enQnsTmTk2fkKQUKBqbcfvO3vfylEzNRxU7YuOtyGW6sFAdLSKK8MDnq9XxH
1A91Bmumu7dxP+nh3u5EyGswDoH84tXcxVH0BHEr22ny4fo1/irWKT5RDcumVhyQ
SZSqcwL1iEsYzmmFZJoyqQu7Cfy6IbZXko4OJJGYWLTBvu3CBfDpQFoCg0GaMevf
SccjJXjY6+00oW3OGGn1JH3vz1dMm7l2vD7QfT04AoeFNxJkVcwFo9mTapZijalO
3kgLc7GOLXG6I7hgmqyRBHuUfJAskET9WUl9Ya92UC2Fa8NGwiBFkkQnzy8BGzEp
lbsQ/cDrFw9/U8c1RIfs3T0uOsjtSDeKIAaKQz/SJYAAYdv3SJvqWZxoIt6p04Vk
4fBM02pxBrMH+rNR+X2frwCD3+q4zBtRSxfZNWzz0kHuo9vt714yVLh5dVynwPjP
+6/5b6eE9BsobfL6M6HyZdAzZYIBkPZbfm11TA6nDT5WwUD5r+KBUJDT2hTChAVS
n7pHjc3PQ6wWgVNjvJqmtV8mvH+qvWnLFGGFd5ttHMYjfB555hcStB2UF5cAzkji
2hhP2aeeujkdcPkZ4XETBfQpkbNHaIPUxiEgUHz2oEs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.5
Release Details
UpdatedSept. 21, 2026, 8:05 a.m.
Changelog

Changed

  • Freigabe für Nextcloud 35: max-version in appinfo/info.xml von 34 auf 35 angehoben, unterstützter Bereich damit NC 31–35; die Angabe in der App-Store-Beschreibung (alle sieben Sprachen), README.md, FEATURES.md und INSTALL.md ist nachgezogen. Verifiziert gegen Nextcloud 35.0.0 mit PHP 8.5 und MariaDB 11.4 (302 Unit-Tests im Frontend, 431 PHPUnit-Tests, 98 E2E-Tests) – am Code der App war dafür keine Anpassung nötig. Die lokale Dev-Umgebung (setup-dev.sh) setzt jetzt auf nextcloud:35-apache und installiert Deck mit, weil die Fortschritts- und Karten-Integration von nc_gantt darauf aufsetzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureOtLYluq0hTNN8+vQY8AK0ALLooVxvUUxymkOjs0nAtdES78R6Sii2b9n1POirXvO
EwnIrLzKWwiT05WSV2dsrWliGU+hBhXmvUO0RviY4jmBpu9+mexpLgoHP5dUwIna
OwAN1TgJg0TOod9/bLSQts6qhr8ONk/giqNpMjId1dvQX+hymeQXFchZHk8ohVx4
4d2BaKizT6HQweBNOxxR+FGd6juzsfw4sC9ujASI6IzxPKhH9piiDFniKaERJuGo
iUvDbljcRA4YVRgM003aD9ytwdzdJ4Z3+JfI4L+LBuwaUsXFyQsC3q8bbaZWSj+w
VZX536GBNNwb0RE/nKkgGQ8WLmwG+dosiL26+Wh+a6lX2EWLFZaw9L9iwGFYG92I
iZmFDOnIpqSicG8PmCTV9YSg5u90fxPg53CaVlv6/eCxqY2mUADyErpBSHSDc1tn
g/JM58znHYQF3PfmpFc3Vh901bBytBPTkUY3p0LF1JKoH7tnZ75ar4fTORoF8nEO
Hfr50lB+bWYhEdj1LCvD9yHsRfGcaj5BZ5m6NGvFzvAf/DStAgzyF2jyz/0+YvnM
8XF+LLYY9TktnVyxHNbwjaoE32sHc/le6ABbWSLdI/R/pYWul1EVPKagxYlAQIS1
0IEJbQarL1SKdaQrVfxtxafKvVVsPLcV6DgEBzeem14=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.4
Release Details
UpdatedSept. 21, 2026, 6:44 a.m.
Changelog

Fixed

  • Fortschritt „aus Deck“ war nicht mehr erreichbar (Issue #221): Seit der Umstellung der Detail-Dialoge auf <dialog> speicherte der Aufgabendialog fest progress_mode: 'manual' – eine Aufgabe, die per API auf deck stand, wurde beim Speichern still zurückgesetzt, und fetchDeckProgress(), syncTaskProgress() und PUT /tasks/{id}/progress/sync hatten keinen Aufrufer mehr. Der Modus ist wiederhergestellt, jetzt mit der verknüpften Karte als Grundlage statt des ganzen Boards: Unter „Fortschritt“ schaltet der Dialog zwischen Manuell und Aus Deck um (neues Feld src/detail/forms/taskProgress.js). Eine erledigte Karte zählt 100 %, sonst der Anteil der abgehakten Punkte ihrer Markdown-Checkliste (cardProgress() in src/utils/deckProgress.js); der Dialog zeigt die Grundlage an und lädt die Karte per „Abgleichen“ neu. Beim Aktivieren eines Projekts übernimmt die neue Store-Aktion syncDeckProgress() den Wert für alle Aufgaben im Modus Aus Deck über den vorhandenen Endpunkt – nur geänderte Werte und nur mit Bearbeitungsrecht, ohne das Laden des Diagramms aufzuhalten. Ist Deck nicht erreichbar oder die Karte verschwunden, bleiben Modus und letzter Wert erhalten. Backend und Datenmodell bleiben unverändert. FEATURES.md (Übersicht und Kapitel 16), HOWTO.md und die App-Store-Beschreibung beschreiben den Modus wieder; neue Texte in allen Übersetzungen. Neue Unit-Tests (deckProgress.test.js, taskProgress.test.js, projects.test.js, TaskServiceTest) und E2E-Test tests/e2e/specs/deck-progress.spec.js, der die Deck-API nachbildet
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturedkpQCpaBGHbxMN8d8IvmDWANDU90LVl0Tp5u2TiH7QHbEz7fovU1pmVNKkGAF6VB
eTFeVCkt+Nq8ZdpfnmfC3ZF4wEO/wxd/fPDjm2pZM6WXb03HGpA7/J49KTifC/mj
KY68+niFgI7cjdxaIkI3KZPsWSnhsIZnTQA+HVaQxNRSIuXMV21NYkVQNZaCwZ1y
DmqesNT1/A9457BNtdk8tYpTF1IF9K98fgIB68MmJ3guBEF1jdh1eHqsp9Zhluxv
MgZNf5wNcBTdLT+9z98jxDoSZGx9+7U57ACA54Kd08f3TZLey+nBSIjjSusgyFYl
WDyYjimUfFMlNNktHMkinpvw0qTlu62cIG4yqO1Kd0mzJ4+ueUrwBFuvRIMscVjy
pXuaC2Try+5ZBw8pUl6XDbhPslzvfKI1uO8nUachDNWJR1ULS6HdX8PsWRTA3PFq
NdETu9H9tcLeheaEiGqNQbPEy/2A8TEWqsMcdzl3iX/W3tQJfj1O+KVMOPKC+Kek
rrZiNfEd4KtVgVH0YLU5xb3XyXDFG0q2Q1b3gJBsoKcn2Wr0D7ijK0FLb3U97RDj
tBlHHoy7gVE1JnXvO/Z8oau5KvytSVffwIbdxKAG6oW5Pi92+HTN4J0icMz3+NIr
LHSTqB4XsIdKGjWdKxMj7KOo2FqWwZR0rq0QuFWxWSY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.3
Release Details
UpdatedSept. 18, 2026, 8:33 p.m.
Changelog

Changed

  • Angleichung an die Nextcloud-Entwicklerstandards (Issue #227): App-Werte (Arbeitszeit, Stunden je PT, Erinnerungen) werden über die typisierten Getter und Setter von IAppConfig statt über das seit NC 29 veraltete IConfig::getAppValue()/setAppValue() gelesen und geschrieben; vorhandene Werte werden beim nächsten Speichern typisiert, Benutzerwerte bleiben bei IConfig (IUserConfig gibt es erst ab NC 32). Die Controller bekommen die Benutzer-ID wie im Developer Manual vorgesehen als ?string $userId per Dependency Injection, statt sie in jeder Methode aus der Session zu lesen. npm run lint läuft ohne Warnungen: require-jsdoc gilt nur noch für exportierte Funktionen, die leeren JSDoc-Hüllen an internen Funktionen sind entfernt, exportierte Funktionen vollständig dokumentiert. <style>-Blöcke und detail.css sind wie der übrige Code mit Tabs eingerückt (reine Whitespace-Änderung, .editorconfig hält das für CSS fest)

Fixed

  • Feste Signalfarben ignorierten Dark Mode und Kontrast-Themes (Issue #227): Chart-Overlays, Popups, Tabelle, Dialoge, Rollup, Report und Dashboard-Widget nutzten feste Rot-, Gelb- und Grüntöne. Sie kommen jetzt aus dem Nextcloud-Theming: Text über --color-error-text/-warning-text/-success-text, Flächen und Linien über --color-element-error usw. (ab NC 32) mit Rückfall auf --color-error für NC 31, Tönungen per color-mix()
  • Teilprojekte standen auf einer Ebene mit ihrem Projekt (Issue #230): In Tabelle und Chart-Beschriftung waren Teilprojekte der obersten Ebene nicht eingerückt, während direkte Aufgaben und Meilensteine des Projekts eine Stufe tiefer standen – Projekt und Teilprojekt ließen sich so nur schwer unterscheiden, und in der Übersicht mehrerer Projekte verschwamm die Grenze zwischen ihnen. buildRowModel() reiht sie jetzt mit Tiefe 1 ein; alle darunterliegenden Ebenen rücken entsprechend eine Stufe nach
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturewwr3SUZOKRGnM7uwZfWOLH4FzXNvzjqMOcqVDH3mlte3y8BbmOzAeE5tvPajrvld
xLoe/OaOJ6tLpE8e/7MRaKiB10CuP0sFMA0j5nzOLw7sDPpTpqAV7xVRBbtZhio6
ZWQ0HjSdRU2JGnlP+MFPi7b/c+fSYVz/M9/q/b79Wyf1x2cDRhUJymN8tmFeD8f0
VVL0kXoMiX8oIJVHj0FrMdEWj+nz8pMPLF17bXluzu5REBHi4YLAnjWdO1YYkTOL
QNxQ/b3lAKZG4jvHXTADGlwbGh3iMjidMSjhpnKt2GsDylHxGozgBFb+rCDiSr9Y
PTM6dC/Nmyy4O3/p8Sbs/ZBpqb8wgO51k7Qd6ltXLU+n7eiSoHQyXL+uQihGA2dg
tBbHeKhcPPR5gz2/cSSqkeEyVkesmg015dLkbdStJoq5zR6s7fGYB3ZoUjqoqR2O
5cVF8Jf4k/swhXM7fMQ2zjoZj8yKePguCx7CqppbUFvr5DXltbHh0KDWVVrSgfXM
/zjL9lqQQXrrgTT16oXJfmq1dajsd1e6qwP/OqRY304W9HceyRCh/jQu0oUoQcSz
+fKenL7yWw3IB2UCGHdiXqzGqyGcGdVL0eVPvZhkzpqoDrr82mjPkXshmM/oF//U
942f6SK2u3YG+bKx/n1Up3ZX90WhZxNsZdW+eDqCbmY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.2
Release Details
UpdatedSept. 17, 2026, 1:01 p.m.
Changelog

Changed

  • PHPUnit-Stubs auf Nextcloud 31 angehoben (Issue #219): nextcloud/ocp steht in composer.json/composer.lock jetzt auf dev-stable31 statt dev-stable28 und damit auf der kleinsten unterstützten Nextcloud-Version; psr/log zieht dabei von 1.1 auf 3.0 nach. OCP-Klassen ab NC 29 sind in Unit-Tests damit verfügbar, die Suite lief ohne Anpassungen an Mocks durch. Nachgeholt ist der Unit-Test für das Mapping ParameterOutOfRangeException → 400 invalid_<parameter> in ExceptionMiddlewareTest (bisher nur per E2E abgedeckt). Entfallen ist die Laufzeitprüfung im Notifier, die nur wegen der alten Stubs nötig war: Er wirft jetzt direkt OCP\Notification\UnknownNotificationException (seit NC 30). Das Release-Artefakt enthält die Stubs nicht
  • Wartbarkeit im Frontend (Issue #226): Der tasks-Watcher in GanttChart.vue ruft refreshOverlays() nicht mehr zusätzlich auf – renderGantt() plant das selbst ein, Workload-Diagramm, Baselines, Klammern, Fortschritt und Meilensteine wurden bei jeder Datenänderung doppelt gezeichnet. Das Dashboard-Widget beendet sein Aktualisierungsintervall beim Unmount. Das Entpacken von API-Antworten (data.ocs?.data ?? data, 61 Stellen) übernimmt der Helfer unwrap() in src/api.js. AdminHolidays.vue arbeitet über die Stores für Einstellungen, Kalender und Feiertage (holidays.js hat dafür load() und remove() bekommen), PersonalReminders.vue über den Settings-Store (neu: remindersMuted, setRemindersMuted(); die persönliche Einstellungsseite nutzt dafür jetzt Pinia), ShareDialog.vue über api() aus src/api.js statt einer eigenen Kopie. Der Projekt-Store spiegelt die Lösch-Kaskade des Backends: Beim Löschen eines Teilprojekts verschwinden auch Unter-Teilprojekte, deren Aufgaben und die Meilensteine, beim Löschen eines Projekts dessen Teilprojekte, Aufgaben und Meilensteine, und beim Löschen von Aufgaben die Abhängigkeiten anderer Aufgaben darauf. Kleinkram: keine Überschattung der Übersetzungsfunktion t mehr, ungenutzte catch-Variable entfernt. Der E2E-Test aus #223 wartet vor dem Ziehen auf eine stabile Balkenposition, weil die nachladende Tabelle das Diagramm sonst unter dem Mauszeiger verschob
  • Wartbarkeit im Backend (Issue #226): TaskService::create() und update() laufen in einer Transaktion (TTransactional::atomic()): Speichern, Ressourcen-Zuordnungen, Abhängigkeiten, Constraints, Propagation und Change-Events gelingen gemeinsam oder gar nicht, Benachrichtigungen gehen erst nach dem Commit hinaus. Die drei identischen Parser in TaskController sind zu parseJsonArray() zusammengelegt. Die seit dem 3-Ebenen-Modell ungenutzte Tabelle nc_gantt_sp_deps entfernt Migration Version1038Date20260917000000, Mapper und Entity sind gelöscht. Milestone hat nur noch einen Docblock (die @method-Annotationen des ersten waren für IDE und statische Analyse unsichtbar), Holiday die fehlenden Annotationen und keinen toten DateTime-Zweig mehr. WorkCalendarController und WorkScheduleController verzichten beim Ändern und Löschen auf die Eigentümerprüfung, die für die ohnehin nur Nextcloud-Admins zugänglichen Endpunkte immer zutraf. ExceptionMiddleware unterscheidet reguläre Controller: Eine ApiException wird dort zu einer JSONResponse mit Status und Meldung statt zu einer OCSException ohne OCS-Kontext, andere Exceptions behandelt Nextcloud selbst. POST /admin/holidays lehnt ein Datum außerhalb von Y-m-d bzw. einen nicht existierenden Kalendertag mit 400 invalid_date ab und einen unbekannten Kalender mit 400 invalid_calendar_id
  • Statische Analyse in der CI (Issue #226): PHPStan Level 5 prüft lib/ gegen die OCP-Stubs (phpstan.neon, composer phpstan) und läuft in jedem PHP-Job der CI nach dem Coding-Standard. Die Klassen der dav-App und von Sabre in DavTaskCalendarBackend sind ausgenommen, weil es dafür keine Stubs gibt. Behoben sind dabei die übrigen Befunde: HolidayMapper deklariert seinen Entity-Typ, ApiException ihre möglichen Status, ChangeEventService speichert Aufgaben und Teilprojekte typgerecht, Rohdaten des Clients sind nicht mehr als bereits geprüfte Strukturen annotiert, TaskCalendarSyncService erkennt neue Zuordnungszeilen ohne Vergleich einer nie leeren ID, und die Zyklusprüfung der Teilprojekt-Hierarchie greift nicht mehr auf ein fehlendes Array-Element zu (PHP-Warnung bei Altdaten mit verwaistem parent_id). Das Release-Archiv enthält phpstan.neon nicht

Fixed

  • Abhängigkeitsmodus „Pflicht“ griff beim Ziehen und in der Tabelle nicht (Issue #223): Der Server erzwingt die Abhängigkeiten der geänderten Aufgabe nur, wenn der Client dep_mode: 'strict' mitsendet. Das taten nur der Detail-Dialog und die Spalte „Vorgänger“, nicht aber Drag&Drop im Chart und die Start-/Endzellen der Tabelle – ausgerechnet die häufigsten Wege, einen Termin zu ändern. Eine Aufgabe ließ sich so vor das Ende ihres Vorgängers schieben und wurde dort gespeichert. store.updateTask() ergänzt den eingestellten Modus jetzt selbst, sobald start_date oder end_date im Payload stehen; ein explizit übergebener dep_mode hat Vorrang. Das gilt damit auch für „Simulation übernehmen“
  • Fehlgeschlagenes Speichern nach Drag&Drop blieb ohne Rückmeldung (Issue #223): Lehnte der Server eine Verschiebung ab (fehlende Rechte, ungültiges Datum, Netzwerkfehler), blieb der Balken an der neuen Position stehen, obwohl der alte Termin galt, und der Fehler landete nur in der Browser-Konsole. GanttChart.vue zeigt jetzt „Speichern fehlgeschlagen“ und setzt den Chart auf den Stand des Stores zurück, der Balken springt zurück – auch für Meilensteine und im Simulationsmodus. Neue Unit-Tests src/stores/projects.test.js, neuer E2E-Test tests/e2e/specs/dependency-mode.spec.js
  • Löschen hinterließ verwaiste Datensätze (Issue #224): Die Lösch-Kaskade einer Aufgabe war beim Löschen von Aufgaben, Teilprojekten und Projekten jeweils von Hand geschrieben und dabei auseinandergelaufen. Alle drei Wege nutzen jetzt den neuen TaskDeletionService (Abhängigkeiten in beide Richtungen, Meilensteine an der Aufgabe, Ressourcen-Zuordnungen). Dadurch räumen Teilprojekt- und Projektlöschung auch die Ressourcen-Zuordnungen ab, die bisher stehen blieben. Die Teilprojektlöschung entfernt zusätzlich die Meilensteine direkt an den gelöschten Teilprojekten (auf das Projekt eingeschränkt, vgl. Issue #179), die Projektlöschung Baselines samt Einträgen. Das Löschen einer Ressource entfernt ihre Zuordnungen und Kapazitätseinträge; war sie die primäre Ressource einer Aufgabe, rückt wie beim Speichern die nächste Zuordnung nach, sonst wird resource_id null. Den Aufwand (work) der Aufgaben lässt es bewusst unverändert: Die Arbeit fällt mit der Ressource nicht weg, sondern ist neu zu verteilen, und eine Neuberechnung senkte stillschweigend Plankosten und Fortschrittsgewichte. Aufgaben- und Ressourcenlöschung laufen jetzt wie die übrigen Löschwege in einer Transaktion. Bereits vorhandene Waisen werden nicht nachträglich bereinigt. Entfallen sind die dadurch ungenutzten TaskMapper::deleteByProject() und TaskSchedulingService::deleteDependencies()
  • Gelöschte Nextcloud-Konten wurden nicht aufgeräumt (Issue #225): Die App reagierte nicht auf das Löschen eines Kontos. Freigaben an das Konto blieben stehen, und ein später gleichnamig angelegtes Konto erbte darüber den Zugriff auf die Projekte – ebenso das Eigentum an den Projekten des gelöschten Kontos. Der neue UserDeletedListener (registriert für OCP\User\Events\UserDeletedEvent) delegiert an UserDeletionService, der in einer Transaktion die Freigaben an das Konto löscht, die Verknüpfung von Ressourcen mit dem Konto (uid, Issue #203) aufhebt und Projekte, Ressourcen, Ressourcengruppen, Kapazitäten, Wochenmodelle und Kalender des Kontos erhält, aber auf einen Platzhalter-Eigentümer #deleted-<zufällig> überträgt. # ist in Nextcloud-Benutzer-IDs nicht zulässig, der Platzhalter kann also nie einem Konto gehören; er ist je Löschung neu und für alle Tabellen derselben Löschung gleich, sodass Projekte und Ressourcen des Kontos zusammengehörig bleiben. Verwalten können diese Daten danach Nextcloud-Administratoren und Inhaber einer Admin-Freigabe. Historie (change_events.changed_by, baselines.created_by) bleibt unverändert; Erinnerungen und Tasks-Synchronisation gleichen sich wie bisher selbst ab. Neuer E2E-Test tests/e2e/specs/user-deletion.spec.js
  • Drag&Drop öffnete zusätzlich den Bearbeiten-Dialog: Nach dem Ziehen eines Balkens löst der Browser einen Klick auf den Balken aus, und GanttChart.vue öffnete daraufhin den Detail-Dialog – nach jeder Verschiebung musste man ihn wieder schließen. _onBarClick() ignoriert den Klick jetzt, wenn sich die Maus zwischen Drücken und Loslassen um mehr als 4 Pixel bewegt hat. Der E2E-Test zum Verschieben per Drag prüft, dass der Dialog geschlossen bleibt
  • Glossar beschrieb noch Abhängigkeiten zwischen Teilprojekten: Seit dem 3-Ebenen-Modell hängen Abhängigkeiten, kritischer Pfad und Puffer an Aufgaben, und der Abweichungsbericht rechnet über die Aufgaben der Baseline. Das Glossar sprach dagegen von Teilprojekten, die voneinander abhängen, eigene Termine und eigenen Aufwand haben. Korrigiert sind die Einträge Teilprojekt, Kritischer Pfad, Puffer, Abhängigkeit, Gesamte Terminabweichung und Ø Fortschritt (samt Formel) – auf Englisch und in allen Übersetzungen (de, es, fr, it, nl, pt_BR)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureKOPH7uJRRB6f+Iqi2oeNidpUdRwDrpvn6N99cSmWLf+W+QrWztXKyDbTLOq71mBy
cXEkYbN+4KnMt0Zie40fLjvXqI8sWgouPHQdobRBCvq60TUpHVT1umUSAWrP7KDJ
XMyCnbKt6jqpTmUusE7sMGkZwBB1SEdx5tAilm2gu67wzR5WQakpZXXsp3Au+GyM
sxPgqD5bgRMcKdRMF+VXIF5gZTsJrNgal9J3SZZ1m8KGMArFPm+dEJaxAF+z0NxX
NTNCKR/tF2y20xgc2Rt4KLNOg5TTrRLB931vwkoM0eLUjFavTXjFAMAoVndvX5yb
37sRCzJX2tzekoch1/e6BWVbo5PH5YJmP5at2lWmrhDcWHrzFxTQZ2ZiY6GYISAO
b/oKUbvmmTs5VFToXRWAQASOC9afDEbKdWes+zoglIP3euOwok+2VD4rXZtVUNlU
fZ/FwsIYt8OB9UF7pQWKQSma8OPsZNnsBaoJVbFinieAwgKs35XtYHeTUWfsXAsL
7GaRK2AJ4jkV4uu35/mHQOjm2DM0ZfkC4OpUI3qXhLM70YTn3BIC0Y3k1P8ZmG4J
RtMSXNmqjBDMO+A+qclcRULziVStXCSlkDVAIw3I37JdrGYlB+avZ9yTX3EsNR0C
gVjxSTao2fhfLKp6o1tQO0+ImF1XqRIxPE1vAWS0hOE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.0
Release Details
UpdatedSept. 16, 2026, 1:50 p.m.
Changelog

Added

  • Ressource optional mit einem Nextcloud-Benutzerkonto verknüpfen (Issue #203, Datenmodell + API): Ressourcen haben ein neues, optionales Feld uid (Migration Version1034Date20260915000000, Spalte nc_gantt_resources.uid, nullable und indiziert). POST /resources und PUT /resources/{id} nehmen es entgegen, GET /resources liefert es mit; uid: null bzw. "" hebt die Verknüpfung auf. Ein nicht existierendes Konto lehnt ResourceService über IUserManager::userExists() mit 400 user_not_found ab; je Eigentümer der Ressource darf ein Konto nur einer Ressource zugeordnet sein, sonst 409 uid_already_assigned (verhindert später doppelte Benachrichtigungen). Ressourcen ohne Konto — Maschinen, externe Dienstleister, Sammelrollen — bleiben uneingeschränkt nutzbar. Der JSON-Export enthält uid je Ressource; beim Import wird eine im Zielsystem unbekannte oder dort bereits vergebene uid verworfen, die Ressource ohne Verknüpfung angelegt und nach dem Import ein Hinweis mit den betroffenen Konten angezeigt, statt abzubrechen. Die Auswahl des Kontos in der Oberfläche siehe Issue #204
  • Nextcloud-Benutzer in der Ressourcenverwaltung auswählen (Issue #204): Der Ressourcen-Dialog hat ein optionales Feld „Nextcloud-Benutzer“ mit Typeahead-Suche über die vorhandene Benutzersuche (GET /users/search, keine Vollliste); geleert bleibt bzw. wird die Ressource ohne Konto. Die Auswahl übernimmt den Anzeigenamen als Vorschlag ins Namensfeld, solange dort noch nichts Eigenes steht — ein leeres Feld oder der unveränderte vorige Vorschlag wird ersetzt, ein selbst eingegebener Name bleibt. Lehnt der Server die Verknüpfung ab (400 user_not_found/invalid_uid, 409 uid_already_assigned), erscheint die Meldung direkt am Feld statt als allgemeiner Speicherfehler; beim Bearbeiten wird die uid nur bei einer Änderung mitgesendet, damit ein inzwischen gelöschtes Konto das Speichern anderer Felder nicht blockiert. Verknüpfte Ressourcen zeigen in der Ressourcenliste, im Kapazitätsraster und in der Ressourcen-Zuordnung des Aufgabendialogs Avatar und Anzeigenamen des Kontos (Ressourcenname, bei Abweichung ergänzt um den Anzeigenamen in Klammern); nicht verknüpfte bleiben unverändert. Dafür liefert die Ressourcen-API zusätzlich das berechnete Feld uid_display_name (bei gelöschtem Konto null, die Oberfläche zeigt dann die uid), und GET /users/search nimmt mit include_self=1 auch das eigene Konto auf, damit man sich selbst als Ressource verplanen kann — der Teilen-Dialog schließt es weiterhin aus. Das „+“-Menü im Kapazitätsraster schließt sich jetzt nach „Neue Ressourcengruppe“/„Neue Ressource“ (close-after-click); bisher blieb es über der Ansicht offen und entzog dem ersten Feld der Sidebar beim Schließen den Fokus
  • „Meine Aufgaben“: Filter, Endpunkt und Dashboard-Widget (Issue #205): Auf Basis der Verknüpfung aus #203 sieht man projektübergreifend, was einem selbst zugewiesen ist. Neuer Endpunkt GET /api/v1/my-tasks (optional include_done, limit 1–500) liefert die Aufgaben, deren primäre oder zusätzliche Ressource (Issue #103) mit dem eigenen Konto verknüpft ist — gleich, wem die Ressource gehört —, sortiert nach Ende, dann Start. Offen heißt Fortschritt < 100 % und Status nicht „erledigt“; overdue folgt der Kennzahl „kritisch“ des Projekt-Widgets. Die Verknüpfung allein gewährt keinen Zugriff: MyTasksService prüft je Projekt AccessService::canViewOrOwn(). has_linked_resource unterscheidet „keine Ressource verknüpft“ von „nichts zu tun“. Das neue Dashboard-Widget „Meine Gantt-Aufgaben“ (nc_gantt_my_tasks) ist ein API-Widget (IAPIWidgetV2, IButtonWidget, IIconWidget, IReloadableWidget), das Dashboard rendert es ohne eigenes Bundle: die nächsten fälligen und überfälligen Aufgaben mit Projekt und Fälligkeit, überfällige mit rotem Overlay-Symbol, jeder Eintrag ein Direktlink. Ohne verknüpfte Ressource zeigt es einen erklärenden Leerzustand mit Setup-Button zur Ressourcenverwaltung. Direktlinks ?project=<id>&task=<id> aktivieren das Projekt und öffnen den Aufgabendialog, ?view=resources öffnet die Ressourcenverwaltung; die Parameter verschwinden danach aus der URL. In der Filterleiste dimmt der neue Filter „Mir zugewiesen“ in Chart, Tabelle und Projektliste alles ohne verknüpfte Ressource; nicht aktivierte Projekte mit zugewiesenen Aufgaben erkennt die Projektliste über den Endpunkt. Ist keine Ressource mit dem Konto verknüpft, erklärt ein Hinweis im Filter das und führt zur Ressourcenverwaltung
  • Fälligkeits-Erinnerungen als Nextcloud-Benachrichtigung (Issue #206): Ein Hintergrund-Job (DueReminderJob, appinfo/info.xml) meldet einmal täglich jede offene Aufgabe und jeden Meilenstein, die heute fällig sind, in den nächsten Tagen fällig werden oder überfällig sind. Der Job läuft stündlich und meldet höchstens einmal je Tag, sobald die Serverzeit die eingestellte Stunde erreicht hat (reminder_hour, Standard 6 Uhr); die Vorlaufzeit steuert reminder_lead_days (Standard 1 Tag) — die Oberfläche zu beidem kommt mit Issue #207. Empfänger sind die über Issue #203 verknüpften Konten der zugeordneten Ressourcen (primäre wie zusätzliche, Issue #103); ist keine Ressource verknüpft, erhält der Projekteigentümer die Meldung, damit die Funktion auch ohne gepflegte Verknüpfungen nützt. Ein Meilenstein erbt die Empfänger seiner Aufgabe. Wie bei „Meine Aufgaben“ gewährt die Verknüpfung allein keinen Zugriff: wer das Projekt nicht sehen darf, wird nicht benachrichtigt. Der übersetzte Notifier verlinkt direkt auf das Element (?project=<id>&task=<id>, beim Meilenstein auf das Projekt) und kommt ohne Datenbankzugriff aus — alles Nötige steht in den Parametern der Benachrichtigung. Gegen Dubletten führt die App über die neue Tabelle nc_gantt_reminders (Migration Version1035Date20260916000000, eindeutig über Element und Empfänger) selbst Buch: je Element, Empfänger und Anlass höchstens eine offene Benachrichtigung. Jeder Lauf gleicht Soll und Ist ab — wechselt der Anlass („wird bald fällig“ → „überfällig“), ersetzt der Job die Benachrichtigung; ist das Element erledigt, verschoben oder gelöscht, zieht er sie zurück. Ohne aktivierte Notifications-App tut der Job nichts und protokolliert keine Fehler
  • Erinnerungen konfigurierbar: Vorlaufzeit, Umfang, Abschalten (Issue #207): Jedes Projekt legt selbst fest, ob es erinnert, wie weit im Voraus (0–14 Arbeitstage; ohne eigenen Wert gilt die Vorlaufzeit der Instanz), worüber (Meilensteine und Aufgaben oder nur Meilensteine) und ob auch an Überfälliges erinnert wird. Der Detaildialog des Projekts hat dafür den Abschnitt „Erinnerungen"; die Felder liegen in nc_gantt_projects (Migration Version1036Date20260916100000), PUT /projects/{id} nimmt reminders_enabled, reminder_lead_days, reminder_scope und reminder_overdue entgegen, GET /projects liefert sie mit, unzulässige Werte weist die API mit 400 invalid_reminder_lead_days bzw. invalid_reminder_scope ab. Die Vorlaufzeit zählt jetzt Arbeitstage statt Kalendertage — dieselben Wochenmodelle und Feiertage wie die Terminplanung —, sodass eine montags fällige Aufgabe bei einem Vorlauf von einem Tag am Freitag davor gemeldet wird und nicht am arbeitsfreien Sonntag. In den persönlichen Nextcloud-Einstellungen (Abschnitt „Gantt-Projekte") lässt sich die Erinnerung der App für das eigene Konto stummschalten (PUT /settings/reminder-mute); bereits zugestellte Benachrichtigungen zieht der nächste Lauf dann zurück. Die Vorbelegung aller neuen Felder entspricht dem Verhalten aus Issue #206

  • Ereignis-Benachrichtigungen: Zuweisung und Terminverschiebung (Issue #208): Zusätzlich zum Tageslauf aus Issue #206 meldet die App Änderungen in dem Moment, in dem sie passieren. Wer über eine Ressourcenzuordnung neu für eine Aufgabe zuständig wird, bekommt „Aufgabe … wurde dir zugewiesen"; ändert sich ein Start- oder Enddatum, geht „Der Termin der Aufgabe … hat sich verschoben" an die Zuständigen — auch dann, wenn die Verschiebung erst über die Propagationskette (TaskSchedulingService) oder über „Termine neu berechnen" entstanden ist. Verschiebt eine Aktion eine ganze Kette, erhält jede Person eine Meldung, die die angefasste Aufgabe benennt und die Zahl der weiteren für sie betroffenen Elemente nennt, statt einer Flut von Einzelmeldungen. Wer die Änderung auslöst, hört nichts davon; stummgeschaltete Konten und Projekte mit abgeschalteten Erinnerungen (Issue #207) bleiben still, und wer das Projekt nicht sehen darf, wird nicht benachrichtigt. Anders als die Fälligkeits-Erinnerungen fallen die Ereignis-Meldungen nicht auf den Projekteigentümer zurück: gemeldet wird nur, wer über eine verknüpfte Ressource (Issue #203, #103) tatsächlich zuständig ist. Die Meldungen tragen den eigenen Objekttyp task_event, damit der Tageslauf sie beim Aufräumen seiner eigenen Erinnerungen zum selben Element nicht mit zurückzieht; je Aufgabe und Empfänger bleibt höchstens eine offene Ereignis-Meldung stehen — die neue ersetzt die vorige. Die Empfängerermittlung samt Stummschaltung liegt dafür jetzt im gemeinsamen NotificationRecipientService, den sich DueReminderService und der neue EventNotificationService teilen. Die persönliche Einstellung heißt entsprechend „Benachrichtigungen" und schaltet beides stumm

Fixed

  • Parameter außerhalb des Wertebereichs ergaben 500 statt 400 (gefunden bei Issue #205): Nextclouds Dispatcher prüft Integer-Parameter mit @param int<min, max> — und limit ohne Angabe auf 1–500 — noch vor dem Controller. Die ParameterOutOfRangeException machte die ExceptionMiddleware bisher zu 500 internal_error samt Fehlerprotokoll; jetzt antwortet die API mit 400 invalid_<parameter>
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureHgTu9/xKCVwgEQ94i1KF1Wc1ucKK+FK0W47ldxlBDNCRKgbhuvGpV7QKdpXiYR5G
Y18Tdot2H1tVhxwYqNPF4/dRt2zuaXzudAIrMrZ70Rxhxo15HNIcFulblH97Td76
HpBO3VxAlBeB6bSQS+YnC3HRC0koDMVJu7VtabL0duzWYZap0539AhczP2rXiAnN
+PxyU8dubrSLOljWGiL6jExiK7Eg6L1luFujNp4MnI8/cBqlBobjRgGmXF5SVfF0
kIcBN8T3Dcpg1w8fuGe68YnaR0xv3ML63zn1nBFb+5zkANv8Yb08dMIQdaNmkCEq
SNSWazd81/HbHDyLEUTK1fpDol9Hn8fv1+4u3MuvCzOcVZjxrvDozAyLLGQX7Y/Y
bup5FbA/WUYXwW6H4XyltYjW60+ivbTsbKflYn6lrbdevFb+z7s0niDgRzSGXIc3
jFh+7NpYzrCVnawKHxJjnyQGrjUNuuozbeAgUh2MaU0D7hh2LuZlLJ7pf5hZTvO+
q1yqdLMiSnmHIFeBh35qH7wywsUmkQYViopegvbRt0kz+ksyHmaxgOE9TOp6JoIc
kivnQLil1Tl0J0gK8w3Nk8q2Hj0WRzVuMyqTqBvAeP54rHM+jOtw3GIlX6XxjBUE
knAkfESZIbDGMrBH4sIFWQP28xCViSGAmideS61nwAY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.2
Release Details
UpdatedSept. 13, 2026, 4:16 p.m.
Changelog

Security

  • Eine zyklische Teilprojekt-Hierarchie konnte den Server blockieren (Issue #199). SubProjectService::create()/update() prüften bei parent_id nur, ob das Ziel-Teilprojekt zum selben Projekt gehört, nicht aber, ob dadurch ein Zyklus entsteht (z. B. A → Parent B, B → Parent A). findDescendantIds() lief bei einem solchen Zyklus beim Löschen in eine Endlosschleife (CPU-Hang, unbegrenztes Speicherwachstum) — auslösbar für jede Person mit Bearbeitungsrecht am Projekt, nicht nur für Eigentümer oder Admins. Eine Zyklusprüfung analog zu TaskSchedulingService::assertValidDependencies() weist solche Änderungen jetzt ab (BadRequestException parent_cycle); zusätzlich bricht findDescendantIds() über ein visited-Set ab, falls Altdaten bereits einen Zyklus enthalten
  • Beim Anlegen einer Aufgabe ließen sich Abhängigkeiten zu Aufgaben fremder Projekte setzen (Issue #190). TaskService::create() reichte mitgesendete dependencies ungeprüft an TaskSchedulingService::addDependencies() weiter; eine predecessor_id aus einem nicht zugänglichen Projekt erzeugte so einen task_dependencies-Eintrag über die Projektgrenze hinweg. Der Create-Pfad ruft jetzt wie schon update() vorher assertValidDependencies() auf, analog zur IDOR-Prüfung in MilestoneService

Added

  • Projekte, Teilprojekte und Aufgaben einklappbar (Issue #197): Ein Dreieck vor jeder Zeile mit Unterobjekten (Projekt, Teilprojekt oder Aufgabe mit Meilenstein) blendet deren Nachfahren per Klick aus und wieder ein; ein Tooltip nennt den aktuellen Zustand. Weil Tabelle und Chart zeilengleich scrollen, wirkt das Einklappen auf beide Ansichten — buildRowModel() filtert die gemeinsame Zeilenliste, bevor buildFrappeRows()/buildTableRows() sie übernehmen. Vorgänger-Verweise auf ausgeblendete Zeilen werden aus den Chart-Balken entfernt, damit frappe-gantt beim Ziehen eines Balkens nicht auf einen fehlenden Balken zugreift. Der Zustand wird pro Nutzer über die bestehenden UI-Einstellungen gespeichert
  • Eigene Farbe für Teilprojekte und Aufgaben (Issue #196): Im Detaildialog von Teilprojekten und Aufgaben lässt sich jetzt — wie bisher nur beim Projekt — eine Farbe aus der Palette oder frei wählen. Ohne eigene Farbe („Geerbt") übernimmt das Objekt die Farbe des nächsten übergeordneten Teilprojekts bzw. des Projekts; Meilensteine erscheinen in der Farbe ihres übergeordneten Objekts. Balken, Summenklammern und Meilenstein-Rauten im Chart sowie der Farbpunkt vor jedem Eintrag der Tabellenansicht (bisher stets die Projektfarbe) verwenden diese effektive Farbe (src/gantt/colors.js); die dynamischen CSS-Regeln hängen dafür nicht mehr an der Projekt-id (gantt-color-<id>), sondern am Farbwert (gantt-fill-<hex>)
  • Die color-Spalte von Aufgaben und Teilprojekten gab es schon, sie war aber mit #0082c9 vorbelegt und wurde nie angezeigt. Die Migration Version1033Date20260913000000 setzt den Spalten-Default auf NULL (= erbt) und den Vorbelegungswert in den Bestandsdaten zurück. Objekte, die ihn trugen, sehen daher unverändert aus; Farben, die davon abweichen — etwa aus JSON-/CSV-Importen oder direkten API-Aufrufen, die bisher gespeichert, aber nie angezeigt wurden —, erscheinen nach dem Update erstmals im Chart. Die API liefert für Objekte ohne eigene Farbe jetzt color: null; PUT /tasks/{id} und PUT /subprojects/{id} akzeptieren color: null bzw. "" zum Zurücksetzen — bisher verwarf das array_filter im Controller einen solchen Wert

Changed

  • Dokumentation nach Sprache (DE/EN) und Zielgruppe organisiert (Issue #191): Entwickler-Doku liegt jetzt übersetzt unter docs/de/ und docs/en/ im Repository-Root (CONTRIBUTE.md, RELEASE.md, HOWTO_Release.md, sowie die vormals in src/detail/, tests/e2e/ und tests/fixtures/ liegenden READMEs als detail-module.md/e2e-tests.md/test-fixtures.md, mit kurzen Verweis-Stubs an den alten Pfaden), Nutzer-Doku unter nc_gantt/docs/de/ und nc_gantt/docs/en/ (FEATURES.md, HOWTO.md, INSTALL.md, LICENSE.md). Die Root-README.md ist die vormalige nc_gantt/README.md, ergänzt um ein Sprachen-/Themen-Inhaltsverzeichnis am Anfang; die alte Root-README (zehn Anwendungsfälle) ist darin aufgegangen. nc_gantt/CHANGELOG.md bleibt bewusst an seinem bisherigen Pfad und einsprachig, weil release.sh, scripts/validate-appinfo.js und tests/Unit/AppInfoXmlTest.php exakt diesen Pfad für die Versionsabschnitts-Prüfung voraussetzen und der historische Log als technisches Protokoll nicht rückwirkend übersetzt wurde; LICENSE.md führt den englischen AGPLv3-Text als maßgeblich, die deutsche Fassung ist als inoffizielle, nicht rechtsverbindliche Übersetzung gekennzeichnet. appinfo/info.xml verlinkt in allen sieben App-Store-Sprachen sowie in <documentation> (user/admin/developer) jetzt auf die neuen docs/<lang>/…-Pfade, mit Englisch als Fallback für Sprachen ohne eigene Doku (fr/es/it/nl/pt-br)
  • Deutsche Übersetzung der README (Issue #194): nc_gantt/docs/de/README.md übersetzt die Root-README.md vollständig (Anwendungsfälle, Funktionen, Installation, Architektur); beide Dateien verlinken oben in einer kleinen Sprachtabelle aufeinander
  • App-Store-Beschreibung ergänzt: Der Abschnitt „Gantt-Diagramm & Tabellenansicht“ in appinfo/info.xml nennt in allen sieben Sprachfassungen jetzt auch das Einklappen von Projekten, Teilprojekten und Aufgaben sowie die eigenen Farben für Teilprojekte und Aufgaben

Fixed

  • Ist-Kosten ließen sich im Aufgaben-Formular nicht speichern (Issue #198): Das Feld für actual_cost las das bei numberField() nicht vorhandene .value statt getValue(), sodass beim Speichern stets null statt des eingegebenen Werts an den Server ging
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureHezqeAXIywdFI8vq5gO4MON/W8MjpkNqrjO2PZgZFFAHkSovdh5IqDbaUoltzPgk
rPiZtmytaqM29hGS2Qqh+DyymRKK7hco5chJ7tcrNqZcDx3KC9UDH2u+GK8GzkS0
jMsvi603ajD3ApEAiZUH9f0eiCYVerg6+/vUh63LFYQuYb7zjyUrNzmNTRl55eeS
cSgDjEvM0kqbaD/FXtdWK0ZzCaOJcSpuCLorf5i7WJU2lQx0Wi0I6lFHYFBVzLBj
W+U8NMiFZE+Wy1YGGwz7QOY1o9fOmrxzwIchuLvLVPCT4j9Vk9YNByc2XREv++UJ
/l3q8EYLLASMKHkscessRS5izHRaTR3iwgHNrxvb8vaXjzu6+he45/LaeQC6pg1M
wsBgAdHTb7alK48/W4p8ex0W5x8l/XyGeBzcSXidxlFD4SKFE2LAGxRoDzIIPeOj
gC3MT8IvAuKU5/g2fruxOxa9Nv2HNh3V9zw4reGxf018TeKVESxE7oMYpn4aFwmK
aq8c+n7EVqGnY1imH+BwvL5EeKzjjBTKcb8Z8DHSxNx6fOG+8ExyJLL+/s9zUSXY
uB1EnmU6QTWH5Tb93gqG/JgVV/8MoERfXSNMi2VOAjsniaGHkagXKs830EwamrDs
ttDDE3UQ2EymEVIDa1v8WTKAxWtFC2sl4GXwuyOTUyA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.1
Release Details
UpdatedSept. 2, 2026, 6:15 p.m.
Changelog

Security

  • Ressourcen-, Gruppen- und Kapazitätsdaten fremder Projekte waren über geteilte Projekte einsehbar (Issue #183). ResourceMapper, ResourceGroupMapper und ResourceCapacityMapper::findVisibleToUser() gaben pauschal alle Datensätze eines Eigentümers frei, sobald irgendein Projekt dieses Eigentümers mit dem Nutzer geteilt war — unabhängig davon, ob die konkrete Ressource überhaupt in einem geteilten Projekt verwendet wird. Der neue zentrale ResourceVisibilityService prüft stattdessen, ob die Ressource tatsächlich in einer für den Nutzer sichtbaren Aufgabe verwendet wird, und wird von ResourceService, ResourceCapacityService, ResourceGroupService und TaskResourceAssignmentService genutzt. Damit lassen sich über eine geteilte Ressource weder die übrigen, nicht geteilten Ressourcen desselben Eigentümers einsehen noch eigenen Aufgaben zuordnen
  • GET /api/v1/resources/{id}/utilization gab Planungsdaten aus nicht zugänglichen Projekten preis (Issue #181). ResourceUtilizationService::findVisibleResource() prüfte nur, ob der Ressourcen-Eigentümer irgendein Projekt mit dem Nutzer geteilt hat; über die Auslastung waren dadurch Aufgaben- und Projekttitel samt Terminen aus fremden Projekten abgreifbar. Die Prüfung stellt jetzt projektbezogen fest, ob die Ressource in mindestens einem über AccessService::canViewOrOwn() sichtbaren Projekt verwendet wird; zusätzlich filtert collectPlannedHours() die Aufgaben auf genau diese Projekte, sodass auch bei erlaubtem Ressourcenzugriff nichts aus fremden Projekten durchsickert
  • Aufgaben und Teilprojekte ließen sich an fremde Projekte hängen (Issue #179). TaskService::create()/update() und SubProjectService::create()/update() prüften nur die Schreibberechtigung auf die eigene projectId, nicht aber, ob eine mitgesendete subproject_id bzw. parent_id überhaupt zu diesem Projekt gehört — mit Folgewirkung beim Löschen eines Teilprojekts (SubProjectService::deleteTasksForSubproject()) und im öffentlichen ICS-Feed (CalendarFeedService::buildIcs()), da beide über TaskMapper::findBySubProject() allein nach subproject_id filtern. Analog zum Schutz aus Issue #97 prüfen jetzt AccessService::assertTaskBelongsToProject() und assertSubprojectBelongsToProject() die Zugehörigkeit; deleteTasksForSubproject() und buildIcs() filtern die geladenen Aufgaben zusätzlich defensiv nach project_id, um vor dem Fix entstandene Altdaten abzudecken

Changed

  • Dokumentation aufgeräumt: Die Doku ist jetzt nach Zielgruppe getrennt — alles, was Nutzerinnen und Nutzer brauchen, liegt in nc_gantt/ und wird damit vom release.sh-Archiv mit ausgeliefert, das Repository-Root enthält nur noch Entwickler-Doku. Dafür sind FEATURES.md, INSTALL.md und die neue HOWTO.md nach nc_gantt/ gewandert; die Root-README.md beschreibt die zehn wichtigsten Anwendungsfälle des Werkzeugs, erklärt je Fall den gedachten Arbeitsweg und führt in einer Repository-Übersicht auf, welches Dokument für wen gedacht ist. Neu ist nc_gantt/HOWTO.md: eine Schritt-für-Schritt-Anleitung entlang eines durchgerechneten Beispielprojekts, die alle zehn Anwendungsfälle mit konkreten Feldwerten, Abhängigkeiten und Terminen durchspielt
  • Dabei korrigierte Verweise und Angaben: der Lizenz-Link in nc_gantt/README.md zeigte auf das nicht vorhandene LICENSES/AGPL-3.0-or-later.txt statt auf LICENSE.md; die Nextcloud-Anforderung stand mit „28–33" in README.md und „28–34" in FEATURES.md gegen die tatsächlichen 31–34 aus appinfo/info.xml; FEATURES.md nannte noch Version 1.5.0; INSTALL.md verwies auf ein dist/nc_gantt-<version>.zip im Repository, das es nicht gibt (dist/ ist nicht versioniert und release.sh baut .tar.gz), und in CONTRIBUTE.md trug ein Link auf RELEASE.md die Beschriftung release.sh. Alle relativen Links und Abschnitts-Anker der versionierten Markdown-Dateien lösen jetzt auf
  • Tests laufen künftig vor jedem Commit (Issue #185): setup-dev.sh installiert einen pre-commit-Hook, der Vitest und PHPUnit (über Docker) ausführt und den Commit bei Fehlern abbricht — genau die Stelle, an der die vier defekten Tests aus dem Ressourcen-Sichtbarkeits-Commit hängen geblieben wären. phpunit.xml schaltet zusätzlich failOnRisky und failOnWarning ein, damit ein Test ohne Assertion den Lauf rot färbt statt unauffällig grün zu bleiben, und CONTRIBUTE.md dokumentiert die Regel zu Mock-Defaults ohne with() in setUp()
  • Die Default-Stubs in setUp() von ResourceVisibilityServiceTest blockierten die spezifischeren Stubs der einzelnen Testmethoden — PHPUnit wertet für dieselbe Methode den zuerst registrierten Matcher aus, und der greift per any() bei jedem Aufruf. findByResourceId(), findByIds() und findByResourceIdExcluding() lieferten dadurch immer ein leeres Array, was vier Failures und zwei risky Tests verursachte. Die Defaults sind entbehrlich, weil PHPUnit-Mocks für den Rückgabetyp array ohnehin ein leeres Array liefern; der Produktivcode blieb unverändert
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerHm/UxN1WTCOMd9LklleINo7LKDOEAyn1zlsR5eTCaUmdGaN1m8Knqk/EKDYcj73
zWx7TXmT4cP91Ag7mCMwY/gRqJabF7TgeXiA7CeV7VE7/D/+apIatMWRT7zYCjkF
oxXKimNrtpDSKWNXKSQn6RfJaEHu5WnIoF4X9Nyx/wf1WoWoThyvWMSSW/3jdajV
WXCV4XQjzbTULw9T+Ini/e8vp6mXt5CrmCdyUDQQcj2t9PF9Hq3GvWT1VQlYA8Kf
IJuqnArSc4JdtJiJnozpOKSBkAbDPTrFytwUTVUO4XlrGIvyqHv14ZVmBM8UCMo6
yVNA9HEBW62/b8W+Z3BtCBTuBnkRk3Wka8qhMMbqOe8kl/1vQtTKQkaDdnXaShUs
18mfDHjLZhcIrJEwbtPvo9Xpf+IWEZeoiRzEqa30n70iZTkPEVv/GxNVmJ74XYH/
Xa/vMWI/sxtXKtKY60XBXXCEVMuyxR+vfIbJOpDCcFlKyWHiXPEdYh2Ac8Bs1hc7
bk+lolw8FEWISrFdUwYdOrfRr4CMW+h2CYzgZsDKKYrVn+hRUy5mVOL1QXZJbNJJ
wr9O/JrUbXAMGdhtLuRUwM4tsPgMF2+cTLMe+wuFzUjWBBNiqb8GKJP2K94hdurD
+VMd/p/TtU02M23ooysu2mft5RCmD0/QbP8yxEACbHg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.0
Release Details
UpdatedAug. 28, 2026, 2:34 p.m.
Changelog

Achtung: Diese Version setzt Nextcloud 31 oder neuer voraus. Die bisherige Untergrenze war Nextcloud 28. Mit dem Umstieg auf @nextcloud/vue 9 — unvermeidbar, weil Vue 2 seit dem 31.12.2023 End-of-Life ist und keine Sicherheitskorrekturen mehr bekommt — fällt die Unterstützung für Nextcloud 28, 29 und 30 weg. Installationen auf diesen Versionen bekommen das Update nicht angeboten und bleiben auf 1.5.4; sie werden von dort nicht weiter gepflegt. Wer die App weiter nutzen möchte, aktualisiert zuerst den Nextcloud-Server auf mindestens Version 31 (max-version bleibt unverändert 34).

Changed

  • Das Frontend läuft auf Vue 3.5 statt Vue 2.7 (Issue #150). Vue 2 ist seit dem 31.12.2023 End-of-Life und bekommt weder Sicherheits- noch Fehlerkorrekturen; die Nextcloud-Server-Generation, gegen die die App freigegeben ist, hat ihren Frontend-Stack längst auf Vue 3 umgestellt. Mit umgestellt sind @nextcloud/vue 8 → 9, @nextcloud/dialogs 6 → 7, Pinia 2 → 3 und @nextcloud/webpack-vue-config 5 → 7; die dreizehn Build-Pakete, die das Webpack-Preset ab Version 7 als peerDependencies erwartet (webpack, vue-loader, sass, babel-loader …), stehen jetzt explizit in den devDependencies. npm audit --omit=dev meldet damit 0 statt 14 Befunde — die vier an Vue 2 hängenden Advisories (vue, vue-template-compiler, vue-resize, vue2-datepicker) waren ohne diese Migration nicht auflösbar
  • appinfo/info.xml fordert jetzt Nextcloud 31 statt 28 (max-version bleibt 34). @nextcloud/vue 9 lässt die Unterstützung für Nextcloud vor 31 fallen; das ist die unvermeidbare Gegenleistung der Migration. Installationen auf Nextcloud 28–30 bleiben auf Version 1.5.4
  • Die drei Einstiegspunkte src/main.js, src/admin.js und src/dashboard.js erzeugen ihre Anwendung über createApp() statt new Vue(); Pinia wird als Plugin registriert (app.use(pinia)), das Weiterreichen der Store-Instanz an das Vanilla-Detailmodal per setActivePinia() bleibt unverändert. Die Vue.prototype-Zuweisungen im Dashboard-Einstiegspunkt sind entfallen — DashboardWidget.vue importiert t ohnehin selbst
  • Anpassungen in den 17 Komponenten an die API von @nextcloud/vue 9: Import-Pfade (@nextcloud/vue/components/NcButton statt @nextcloud/vue/dist/Components/NcButton.js), type="primary" → variant="primary" bei NcButton/NcActions, NcActionCheckbox und NcSelect auf modelValue (:checked/@update:checked bzw. :value/@input sind entfallen), :shown.sync → v-model:shown bei NcPopover. Dazu die Vue-3-Umstellungen im eigenen Code: beforeDestroy() → beforeUnmount() in GanttTable.vue, ResourceGrid.vue und GanttChart.vue (siehe Issue #149), this.$set()/this.$delete() durch direkte Zuweisung bzw. splice() ersetzt
  • css/main.css und der zugehörige Util::addStyle()-Aufruf im PageController sind entfallen. Die Datei enthielt einzig #content { margin-top: 0 !important } gegen eine Doppelmarge, die NcContent unter @nextcloud/vue 8 zusätzlich zum Nextcloud-Layout gesetzt hat. Unter Version 9 gibt es diese Marge nicht mehr, der Reset schob die App stattdessen um die Höhe der Kopfzeile nach oben — die Navigationsleiste lag dadurch unter dem Nextcloud-Header und war nicht mehr anklickbar. Der Mount-Punkt #nc-gantt-app bleibt als Wrapper im DOM stehen (Vue 3 rendert in das Element hinein, statt es wie Vue 2 zu ersetzen), nimmt aber nachgemessen exakt die Maße des Content-Bereichs an und braucht keine eigene Regel
  • ESLint nutzt das Preset @nextcloud/eslint-config/vue3. Es lässt vue/custom-event-name-casing auf dem Vue-3-Default (camelCase) laufen, während das Vue-2-Preset kebab-case erzwungen hat; da der Bestand durchgängig kebab-case verwendet — unter Vue 3 gleichermaßen gültig — setzt .eslintrc.js die Regel explizit auf kebab-case
  • Der <style>-Block in GanttChart.vue war mit 581 Zeilen ungescopt und wirkte damit auf die gesamte Nextcloud-Oberfläche statt nur auf die Komponente (Issue #151). Die 65 eigenen .nc-gantt-*/.nc-*-Regeln laufen jetzt über <style scoped>, mit :deep() dort, wo Overlays ihre Elemente per createElementNS() bzw. innerHTML außerhalb des Vue-Templates in .nc-gantt-container einhängen. Die Regeln, die auf frappe-gantt-eigene Klassennamen zugreifen (.gantt .bar & Co.), liegen gebündelt in src/vendor/frappe-gantt/theme.css direkt neben dem vendorten Quellcode der Bibliothek und werden von dort per index.js importiert; die beiden nicht unter .gantt verankerten Selektoren (.bar-wrapper.gantt-milestone .bar-label/.handle) sind dabei korrigiert. In App.vue laufen die drei Core-Überschreibungen (.app-navigation__content, #app-settings, .app-navigation .app-navigation-list) ebenfalls über :deep() in einem scoped Block. Abgesichert ist das durch vue/enforce-style-attribute aus eslint-plugin-vue, das ab sofort scoped für jeden <style>-Block in src/ erzwingt
  • TaskService bündelte auf 774 Zeilen drei Verantwortlichkeiten und brauchte dafür acht Konstruktor-Abhängigkeiten; der Schnitt trennt sie (Issue #153). TaskSchedulingService übernimmt Constraint-Propagation über EA/AA/EE/AE mit Lag, Kahn-Sortierung, Arbeitstagsrechnung, Zyklenprüfung und What-if-Simulation — CriticalPathService wird dort regulär injiziert, statt simulate() als Methodenparameter durchgereicht zu werden. TaskResourceAssignmentService übernimmt Auflösung und Persistierung der Ressourcenzuordnung inklusive MAX_TASK_RESOURCES und PT-Umrechnung. TaskService bleibt als CRUD-Fassade mit Berechtigungsprüfung, Validierung, Änderungsprotokoll und API-Aufbereitung: 311 statt 774 Zeilen, sieben statt acht Abhängigkeiten. Die Methodenrümpfe sind unverändert verschoben, es ändert sich keine Terminlogik. TaskServiceTest ist entlang derselben Grenzen aufgeteilt (TaskServiceTestCase als gemeinsame Basis, dazu TaskSchedulingServiceTest mit 39, TaskResourceAssignmentServiceTest mit 8 und TaskServiceTest mit 11 Fällen — exakt der bisherige Umfang), wobei die ausgelagerten Services weiterhin über ihren Aufrufer TaskService angetrieben werden, damit neben der Rechnung auch die Verdrahtung zwischen Fassade und Engine abgedeckt bleibt
  • Die 212 Zeilen CSS der Vanilla-Detaildialoge wurden zur Laufzeit als <style> in den <head> injiziert (src/detail/styles.js); sie liegen jetzt als src/detail/detail.css vor und werden von Webpack eingebunden (Issue #152). Der im Kopfkommentar genannte Grund — nicht von der Loader-Konfiguration abhängen zu wollen — trifft seit dem Vendoring nicht mehr zu, src/vendor/ importiert .css und .scss ebenfalls direkt. Dabei fiel auf, dass das Stylelint-Glob unquoted an die Shell ging und ohne globstar nur eine Verzeichnisebene traf: weder src/App.vue noch eine .css in src/vendor/ wurde je geprüft. Mit Quotes globbt Stylelint selbst; die dadurch aufgedeckten Findings in App.vue sind mitbehoben
  • Die PHP-Einrückung ist auf Tabs vereinheitlicht (Issue #170). nextcloud/coding-standard erwartet Tabs, die Codebasis nutzte durchgehend vier Leerzeichen — composer cs:check beanstandete dadurch 144 von 146 Dateien, der CI-Job wäre ab dem ersten Lauf dauerhaft rot und als Gate wertlos gewesen. Ursache der Drift war ein falscher Kommentar in .editorconfig, der dem Coding-Standard vier Leerzeichen unterstellte; der ist mitkorrigiert. Die Änderung ist rein mechanisch (php-cs-fixer fix): Einrückung, Klammern um einzeilige if-Rümpfe, sortierte use-Blöcke und einfache statt doppelte Anführungszeichen, wo keine Escape-Sequenz im Spiel ist. Der Commit steht in .git-blame-ignore-revs, damit git blame weiterhin die inhaltlich letzte Änderung einer Zeile zeigt

Added

  • appinfo/info.xml wird jetzt automatisch gegen das App-Store-Schema geprüft, statt den Fehler erst beim Upload zu erfahren: scripts/validate-appinfo.js (npm-Script validate:appinfo) validiert gegen die eingecheckte Schema-Kopie tests/fixtures/info.xsd — per xmllint, ersatzweise über lokales php oder das composer:2-Image — und prüft zusätzlich Semver-Format, Gleichstand mit package.json und den CHANGELOG-Abschnitt zur Version. release.sh ruft das als erstes Gate mit --online auf, lädt das Schema dabei frisch und meldet, wenn die Kopie veraltet ist
  • tests/Unit/AppInfoXmlTest.php führt dieselbe Schema-Validierung in der Testsuite aus (DOMDocument::schemaValidate()) und gleicht zusätzlich die Übersetzungen in l10n/ gegen die Store-Texte ab: gettext-Codes wie pt_BR werden als solche erkannt, und zu jeder übersetzten Sprache müssen <name>, <summary> und <description> in info.xml existieren
  • Die Stunden je Personentag sind konfigurierbar (Issue #169). Der Umrechnungsfaktor lag dreifach hartkodiert vor (TaskResourceAssignmentService, ResourceUtilizationService, ResourceGrid.vue) und wurde über gegenseitige Kommentarverweise von Hand konsistent gehalten — eine feste 8-Stunden-Annahme passt aber nicht zu jedem Arbeitszeitmodell. Der Wert liegt jetzt an genau einer Stelle: WorkTimeService liest ihn als App-Config-Wert hours_per_pt (Standard 8), validiert ihn gegen 1..24 und fällt bei unplausibel gespeicherten Werten auf den Standard zurück, damit die Auslastungsrechnung nicht durch 0 teilt; in ResourceUtilizationService entfällt damit auch die Konstante DEFAULT_CAPACITY_H, die denselben Vollzeit-Referenzwert doppelt hielt. Ins Frontend kommt der Faktor über den Initial State des PageController, sodass er schon beim ersten Rendern des Ressourcen-Rasters bereitsteht; GET/PUT /settings liefern und setzen ihn zusätzlich, wobei ein PUT ohne hours_per_pt den gespeicherten Wert unverändert lässt, damit ältere Clients ihn nicht auf den Standard zurücksetzen. Gepflegt wird er im Admin-Bereich neben Arbeitsbeginn und -ende
  • Die Vanilla-Detaildialoge sind für Screenreader und Tastatur verdrahtet (Issue #152): Die Dialoge verweisen über aria-labelledby auf ihre Überschrift, section() verknüpft Beschriftung und Bedienelement (bei Gruppen über role="group"), Knöpfe ohne Text bekommen einen Namen, Umschaltknöpfe melden ihren Zustand über aria-pressed, und die Datums-Plausibilitätsmeldung hängt per aria-describedby am Feld statt nur im title-Attribut des Icons zu stehen. Der Fokus beginnt beim Öffnen im Formular statt auf dem Schließen-Knopf und kehrt beim Schließen zum Auslöser zurück. Abgesichert durch payload.test.js (19 Fälle für Speicher- und Löschlogik) und accessibility.spec.js (6 E2E-Fälle) — für DOM-Code ist der E2E-Lauf die tragfähigere Absicherung als eine neue jsdom-Abhängigkeit. src/detail/README.md erklärt, warum das Modul außerhalb von Vue steht und welche Regeln daraus folgen; CONTRIBUTE.md verweist darauf
  • Die CI läuft. Der Workflow lag unter nc_gantt/.gitea/workflows/ci.yml und war eine reine GitHub-Actions-Datei; vier voneinander unabhängige Gründe verhinderten, dass davon jemals etwas ausgeführt wurde (Issue #170): Forgejo liest Workflows ausschließlich im Repo-Root, runs-on: ubuntu-latest passt auf kein Codeberg-Label (die gehosteten Runner heißen codeberg-tiny/-small/-medium), die Kurzform shivammathur/setup-php@v2 wird gegen Forgejos Action-Host aufgelöst, wo es die Action nicht gibt, und die Repo-Einheit „Actions" war deaktiviert. Die Datei liegt jetzt unter .forgejo/workflows/ci.yml, die App bleibt via defaults.run.working-directory erreichbar. Inhaltlich sind php-lint und php-tests zu einem Job zusammengefasst und laufen beide über die volle Matrix 8.1/8.2/8.3 (vorher lief PHPUnit nur auf 8.2, obwohl composer.json >=8.1 fordert), die Syntaxprüfung nutzt xargs statt find -exec (find liefert seinen eigenen Exitcode, nicht den von php -l — der Schritt konnte gar nicht fehlschlagen), und der JS-Job führt neu vitest mit 180 bislang nie automatisiert gelaufenen Tests aus, dazu validate:appinfo und l10n:check. paths-ignore hält die geteilten Codeberg-Runner von reinen Doku- und Screenshot-Änderungen frei

Fixed

  • Nach der Migration auf @nextcloud/dialogs 7 erschienen Statusmeldungen wie „Einstellungen gespeichert" als unformatierter Text oben links statt als Badge. Version 7 hat Toastify durch eigene Vue-Komponenten ersetzt, deren Styles nicht mehr im JS-Bundle stecken, sondern über den separaten Export @nextcloud/dialogs/style.css eingebunden werden müssen; ohne diesen Import lief der Toast-Container ungestylt im normalen Dokumentfluss mit. src/main.js importiert das Stylesheet jetzt. Die Toasts erscheinen dabei an der neuen Standardposition der Bibliothek unten links neben der Navigation statt wie früher oben zentriert — Version 7 bietet dafür keine Option mehr
  • testBuildIcsStripsCarriageReturnToPreventLineInjection prüfte das gesamte ICS-Dokument auf Freiheit von \r. RFC 5545 trennt Zeilen aber genau damit, und buildIcs() setzt sie korrekt als CRLF — der Test konnte nie grün sein und schlug auf main dauerhaft fehl. Der Schutz selbst greift; nur die Assertion war zu weit gefasst. Geprüft wird jetzt, was die Line-Injection tatsächlich ausschließt: kein einzelnes CR außerhalb der CRLF-Zeilentrenner, der eingeschleuste Titel erzeugt keine eigene ICS-Zeile, und er landet escaped im SUMMARY-Wert. Gegenprobe mit entfernter CR-Normalisierung in escapeText() schlägt weiterhin fehl, der Schutz bleibt also scharf getestet. Dass der Fehlschlag so lange unbemerkt blieb, lag daran, dass die Workflows nicht ausgeführt wurden (siehe Issue #170)
  • validate:appinfo brach im ersten CI-Lauf mit einer Meldung über einen nicht erreichbaren Docker-Socket ab (Issue #170). Die Validator-Kette in scripts/validate-appinfo.js probiert xmllint, dann php, dann docker und nimmt den erstbesten verfügbaren — „verfügbar" hieß dabei aber nur, dass das Binary existiert. Das Runner-Image bringt die docker-CLI mit, ohne dass ein Daemon erreichbar wäre, und hat weder xmllint noch php. isAvailable prüft jetzt, ob der Validator wirklich läuft (docker info statt --version), sodass die Kette korrekt durchfällt und bei gänzlich untauglicher Umgebung „Kein Schema-Validator gefunden" meldet statt eines Socket-Fehlers; zusätzlich installiert der JS-Job libxml2-utils, damit im CI immer xmllint gewinnt — der native XSD-Validator, ohne Daemon und ohne Netzwerk. Welcher Validator läuft, hing zuvor davon ab, was auf der Maschine zufällig installiert war
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegDK8JkRgxBUCFljhxHFzTzf37tjhwQ/k9E+DCDeVHxPECDOF0J3FbWmv2uqmm9R7
H8yyFfL/KFEDK3Jt24o2gujQZ9KlyBYJvrfwv//zvACtgGoErBUamME+FzFfK3x8
rRdXt1iXctmwutRvTNPj8uvfMJmo/vdbJRDGEihI/9wVdt4S+O3/OlpdjUg+QGFP
cMmlQwIAHAWrYSUllnN4GeryrFPGB7fLi/Nrhx0eAwQHUjIZHEb/s9ZxjmnYZd0c
8FJsgAUXfjowOIrDtWxr0P+HVj4c5qhGx4XEUFdfR0slriaL+zPbo2Li1wgsBA7r
+yoo8ocOJPaN3iCpRNL4z8q2sxEuWRt2Dx0I5ck6F7g+gpzDoTo5bFEvq4Eir0wN
lGHhyxcdEK653dTKOBYI07tCjJYU+9VxCbRzR7O+sVI412Tox5W5cYGl4WpN8aNH
00CX7PLaruaEjwuFdAvhY6SzsXA3RFmN3Dv9qFx3dl3HjoQ+OCqy89XzT3tbBN07
3aZO7OgyB26al2XdRFaaRQaVLqjJzUCz2B5nh+z9+L+QcBl3F9sE2sFZnkWwx3bp
MOdj2nCS11r3Z0utRI9uDEanys6HkqnpGed4WEGX9O+EfocqNg2b0wgyqjFNWbxI
n4uoeakhyWYOVHCUfzU8tn6kmU4b8n+GfpMjKPhBeRw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.4
Release Details
UpdatedAug. 27, 2026, 7:06 p.m.
Changelog

Fixed

  • appinfo/info.xml wurde vom Nextcloud App Store abgelehnt: der Sprachcode pt_BR in <name>, <summary> und <description> ist im Schema nicht enthalten — die Enumeration kennt nur BCP-47-Schreibweise in Kleinbuchstaben mit Bindestrich. Alle drei Vorkommen heißen jetzt pt-br
  • Die Kindelemente von <info> sind im Schema eine feste Sequenz; <settings> stand vor <dependencies> und wäre nach dem Sprachcode-Fix als nächster Validierungsfehler aufgeschlagen. Der Block steht jetzt an der vom Schema vorgesehenen Position hinter <dependencies>. xmllint --schema gegen https://apps.nextcloud.com/schema/apps/info.xsd läuft damit sauber durch
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureYbjiEkGYW1s8Xg9Jvrt4nNo/67zkrQDA16yfK2MzVa58R982qrLUupNbvUE/FscP
pFg9nlqxd81cpyH1ZhWOM7fMq98mPyjSUsJZ7xjYzNccF13xvDpBXFlfX4UyCknc
50Rmw9a8RxiCH0mEpH9zWjdanX194R8cUIMDY+BcymCgyBVRQub9yaL5iiHH3hNA
OOD+UOiSUNHns9R6sgZ+tF4OeoTufWR5//AfRmuL5qC5spyuP1iKr7FnzPar+/sS
pHKojfUyi0sAjlrThVZDo8fvVPqkkH4jgvVmNE41t1UOlVdajIRd2yhbHX0h0SSs
rmt4CQqNXAjLE/Wlq2lu9Viw4M2BhhPIfVb5YJFzaxjhB8RRy53rquXRL64f7vGa
agq0ri3eD+0ZWmMOlJmHaMv6AXYd0ZG8myb3PAyNC0NYXzrw2PAVL8jDfzmq2cNY
Q3rIh101vtGPytqanNS2VmtfZ8BX63rd4SLR4PJdRYrm3Wbqanx8WZa20eLgYF8S
M9xhxl5Jf8+TnYLwHi1sGm1ID2XJ4krfgOxiUlB8lGeSweUK1uueC0bmUDsrXyhJ
RMGST41TU1HqXx/3Eeh91JKq/Jbx15kPuxu7WNKyuSOj4vKbgFwBUSmHKDU6lvEZ
L/bnxZi965/4K3kvyJ8o2j5e4xUmJLeusi9Fg0yrRqE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.2
Release Details
UpdatedAug. 26, 2026, 5:18 p.m.
Changelog

Changed

  • Teilen-Dialog: das Eingabefeld für den einzuladenden Nutzer ist jetzt ein Suchfeld statt eines reinen Textfelds für die Benutzer-ID (Issue #144). Getippt wird nach Anzeigename, Benutzer-ID oder E-Mail-Adresse; ein neuer, admin-unabhängiger Endpunkt GET /api/v1/users/search (UserController::search(), IUserManager::search(), max. 20 Treffer, der eigene Account wird ausgeblendet) liefert dazu Anzeigename, Benutzer-ID und E-Mail als Vorschlagsliste. Gespeichert wird weiterhin die Benutzer-ID als principal_id – für Umgebungen mit LDAP/AD-Benutzerverwaltung war die zufällig generierte ID zuvor nicht ohne Umweg über die Benutzerverwaltung in Erfahrung zu bringen. Die Liste bereits geteilter Zugriffe zeigt ebenfalls den Anzeigenamen statt der rohen ID (ShareController::index() reichert die Antwort um display_name an, Fallback auf principal_id falls der Account inzwischen gelöscht wurde)
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureyLn/jlYbk3gIYfgIcjON0z467/xL9L6QNcmfgOa/4zhTJjLknXq7eu1UJddWWlLJ
NG2U9d/OEFO9wLHPYPGNiWgQMRIk+TGYlQVTXcZH3WpBxdJOm3XMnk5hq2kNB2FN
TJemqr/dxafGB2hTIgivPMXMnemdrtxWN1RAdzCxZIg9HwJWjG5JxdqHmURHwske
p/Ki/iZghKjrxbPIghbqtrvkCJRImbgVWY8AmUNUPLgotNIthiWFnkTDVhYWcreP
pWleWHxEn57UOtehff1aJa+hRI+8WVkx9iXArx0SThmOCYYgJ/HeG8dOOtmr9jSm
1nzETAL87YLxL/+qM6DyBIgNWhrq1oj9Q12bBm1+la477z6fpOgUlMjWkPEYRQi/
vhxirXwB4BZBYvaMEkNlN2U3+7BqgigGoaTcbN2rG/TP1JuOjdj9jEjMMRye0JbT
35XOoM/VPYGovrT9slHg8r3Wolyd8ioXVOu4IcMImvC5OrTzpKY7NLEc/S+cOKwz
ijOKcnoV0j++KxtQKKgEO96HJpEM0jteQVOGCoPbghjS3OFzkiB1bUVJAlu1jn54
38Q2zw8rZ0EMm1rEPmIrl72DIIXionbWKatREgy7QPwuL/WKZ2c+J1UIUTKck5eO
7M2VHh6P3FnIg/UsOCMu3U7ja0Y8vQ3YUo3tazLfob0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.1
Release Details
UpdatedAug. 25, 2026, 1:28 p.m.
Changelog

Added

  • Screenshot des gerenderten Gantt-Charts als PNG, SVG oder PDF (Issue #116): ein neuer „Screenshot"-Eintrag in der Chart-Toolbar öffnet ein Menü mit den drei Formaten, derselbe Export steht im Bearbeiten-Dialog des Projekts neben JSON und CSV bereit (dort mit vorgeschalteter Formatwahl statt Menü, openScreenshotFormatDialog()). Exportiert wird stets das gesamte Chart, nicht nur der sichtbare Scroll-Ausschnitt — frappe-gantt rendert die volle Breite und Höhe ohnehin als eine SVG. PNG und SVG klonen die aktuell gerenderte SVG und schreiben getComputedStyle() als Inline-Attribute (löst auch CSS-Variablen auf), sodass die Datei ohne das App-Stylesheet eigenständig darstellbar ist; sie folgen dem eingestellten UI-Theme, ein hinterlegtes Rechteck in --color-main-background deckt dabei auch die Fläche unterhalb der Aufgabenzeilen (Workload-Chart, Leerraum) ab, die sonst transparent bliebe. PNG rastert per Canvas mit doppelter Auflösung
  • Zeitraum-Auswahl für den PDF-/Druck-Export (Issue #116): vor dem Drucken fragt ein Dialog, welchen Zeitraum die PDF abdecken soll — gesamter Projektzeitraum (Vorbelegung), laufendes Kalenderjahr, „aktives Quartal" (Vormonat bis einschließlich übernächstem Monat) oder ein frei eingegebener Bereich. Der Vorschlag ist der tatsächlich von Balken belegte Zeitraum, nicht gantt_start/gantt_end — frappe-gantt legt darum großzügig Puffer, der andernfalls mitgedruckt würde. Umgesetzt wird der Zuschnitt als viewBox auf dem SVG-Klon; die dafür nötige Skala (Chart-Start, Pixel pro Millisekunde) schreibt GanttChart.vue nach jedem Render als data-Attribute an den Chart-Container, sodass auch Aufrufer ohne Zugriff auf die frappe-Instanz (Bearbeiten-Dialog) sie lesen können. Die Zeitraum-Logik steckt isoliert testbar in src/gantt/printRange.js (src/gantt/printRange.test.js)

Changed

  • Der PDF-Export druckt nicht mehr den Live-Container, sondern einen gestylten, selbst gerasterten Klon des Charts (Issue #116). Bisher wurde das Inline-SVG direkt gedruckt — wie fein die Browser-Engine das rastert, ist nicht steuerbar und fiel je nach Browser sichtbar grob aus. Jetzt wird der Klon mit 300 dpi bezogen auf die bedruckbare Fläche (A4 quer, 10 mm Rand → 277 × 190 mm) in ein Canvas gerastert und als Bild mit fest vorgegebener physischer Größe in den Druck gegeben; schlägt die Rasterung fehl, bleibt der SVG-Klon als Druckinhalt übrig und skaliert per CSS auf die Seitenbreite. Der Klon wird immer im hellen Theme berechnet (Papier ist weiß, ein dunkles UI-Theme ergäbe sonst dunkelgraue Flächen und helle Schrift) — dafür genügt ein ausgelagerter Container mit gesetztem data-theme-light, da Nextcloud die Stylesheets aller Theme-Varianten lädt und nur über dieses Attribut umschaltet. Das Ausblenden der übrigen Seite läuft über display: none statt visibility: hidden, sonst blieb die Fläche der App als Leerseiten erhalten; Nextclouds Hintergrundbild auf html/body wird für den Druck abgeschaltet. Aufgeräumt wird über afterprint mit Timeout-Fallback für Browser, die das Event nicht feuern
  • Der Toolbar-Eintrag heißt „Screenshot" (Icon MonitorScreenshot) statt „Export" (Icon ExportVariant) — „Export" ist im Projekt bereits für den Daten-Export nach JSON/CSV belegt, der Chart-Export ist demgegenüber eine reine Abbildung der aktuellen Darstellung
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturek6kpvF53IR8tnkOFXyoSPFPGlwVzO/RUTgI1p1ppRcFmoFPUyFeIokOqWA1OtD9q
wdo+qkQQPyJGph0Uwp+hwP9oGmeKWErnlv8HMdA5I5iaXIN7afGvdfxWfKgqWetw
tGmCF7l2IzMmT2Ol/3pKgCnFkXTDPCVeNINNbUdi7EjeRpbcbOGWt3+5UVwZgVTX
Z9/kEEPvjMV98s1zT5yBXcFvyqZA7bhhezmBkfzaLa0DAIczFJbhyFtR06WuS7bP
CWBCs/xvSRuCwo61oyXRAWLohz/2GsrMSa5jvPvnEG1XEHpGPVorCijAvsBm4sX5
yAZTrBm3zbNYObqVE2idxTH/eqP8xXtE38JFjpG5IahpeFNb9V2EbH1nrjfRKYcH
NVvXcXFREAmvorShKDE0ymFN+/BCjvU1vnJCnRD5RMsoSGUzXSVHPrF1emtusLHj
ltP22eYOCpraEpZheTkFftqvpbiQKVXsZ+8wY4dJnh5Ymi71loxZhggoH8s/pmp+
WbSx6MZwSPBqJ0HOJWckFiZXur2OQYOVLfXt8uPYNU/EZSoYDqm81GVMglQXxyM6
YDh0xXqwJ7Qft7k0O/3Fa5YLJGP4B4RvjU29N3/WDsfjuk7PeCSTPhRlTOfV/Fbn
jS83WVBavmyklhu0rNYuCYfdYvUxemIO3VuGmYzFXgI=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.0
Release Details
UpdatedAug. 24, 2026, 5:26 p.m.
Changelog

Added

  • Editierbare Vorgänger-Spalte in der Tabellenansicht, Teilschritt F von #117 (Issue #124): eine neue, über das Spaltenmenü einblendbare Spalte „Vorgänger“ zeigt die Abhängigkeiten einer Aufgabe in der aus MS Project bekannten Kurznotation (12EA+2d = Vorgänger Aufgabe 12, Ende-Anfang, zwei Arbeitstage Abstand; mehrere durch ; getrennt) und nimmt sie dort auch entgegen. Typ (EA/AA/EE/AE, englische Kürzel FS/SS/FF/SF werden beim Eingeben mit akzeptiert) und Abstand sind optional, 12 allein bedeutet also EA ohne Abstand. Geprüft wird beim Verlassen der Zelle: Syntax, Existenz der Aufgabe im selben Projekt, Selbstbezug, Dubletten und Kreisschluss über die gesamte Abhängigkeitskette – bei einem Fehler bleibt die Zelle im Bearbeitungsmodus und nennt den Grund im Klartext, die bisherigen Abhängigkeiten bleiben unangetastet. Gespeichert wird über dieselbe PUT /tasks/{id}-Route wie der Detail-Dialog (inkl. dep_mode), sodass der Server die Termine anwendet und durch die Nachfolgerkette propagiert; Balken, Pfeile und Typ-Labels im Chart zeichnen sich mit der Antwort neu. Bewusst ohne Undo-Toast, da ein Zurücknehmen nur der Termine (nicht der Abhängigkeit) beim nächsten Neuberechnen wieder überschrieben würde. Nur Aufgaben haben eine editierbare Zelle – Meilensteine hängen über task_id an genau einer Aufgabe, Projekte und Teilprojekte sind berechnete Strukturebenen. Notation und Prüfungen stecken isoliert testbar in src/gantt/dependencyNotation.js (src/gantt/dependencyNotation.test.js)
  • Zeitabstand (Lag/Lead) je Aufgaben-Abhängigkeit (Issue #124): neue Spalte lag_days in nc_gantt_task_deps (Version1032; der Name vermeidet das seit MySQL 8.0 reservierte Wort LAG und nennt die Einheit). Der Abstand zählt in Arbeitstagen über den Ressourcenkalender wie die übrige Terminberechnung, wirkt bei allen vier Abhängigkeitstypen auf deren Ankertag und darf negativ sein (Lead: der Nachfolger beginnt früher). Bestandsabhängigkeiten bekommen 0 und verhalten sich unverändert. TaskService::update() weist eine gesendete Vorgängerliste jetzt vollständig zurück (invalid_dependency, dependency_cycle), bevor die bisherigen Abhängigkeiten dafür gelöscht werden, statt Vorgänger aus fremden Projekten oder Zyklen zu speichern – letztere hätten die topologische Sortierung in recalculateProject() stillschweigend Aufgaben überspringen lassen

  • Konfigurierbare Spalten in der Tabellenansicht, Teilschritt E von #117 (Issue #122): ein Menü in der Tabellenkopfzeile blendet Spalten ein und aus, die Reihenfolge lässt sich per Drag & Drop der Kopfzellen ändern (barrierefreie Alternative: Strg+Pfeil links/rechts auf der fokussierten Kopfzelle), die Breiten per Ziehen am Spaltentrenner (Doppelklick setzt auf die Standardbreite zurück). Zusätzlich zu den bisherigen Spalten (Titel, Start, Ende, Fortschritt) stehen ID, Dauer (Kalendertage inkl. Start- und Endtag, bei Meilensteinen 0), Status (Issue #78, nur Aufgaben/Teilprojekte) und Ressource (Issue #103, zugeordnete Ressourcennamen einer Aufgabe) bereit – alle vier als reine Anzeigespalten, die das Inline-Editing und die Tastaturnavigation daher überspringen. Auswahl, Reihenfolge und Breiten werden pro Nutzer über den in Teilschritt B angelegten UI-Preferences-Endpunkt persistiert (neuer Key gantt_table_columns, kein Schema- oder Endpunktwechsel nötig); eine gespeicherte Konfiguration wird beim Laden validiert, sodass entfernte Spalten oder unplausible Breiten die Ansicht nicht beschädigen können. Die Tastaturnavigation folgt jetzt der sichtbaren Spaltenreihenfolge statt einer festen Liste. Reicht die Tabellenbreite für die gewählten Spalten nicht aus, scrollen Kopfzeile und Zeilen gemeinsam horizontal; die Obergrenze des Splitters steigt dafür von 560 auf 900 px. Die Spaltenlogik steckt isoliert testbar in src/gantt/tableColumns.js (src/gantt/tableColumns.test.js). Ein Sortieren der Zeilen nach Spaltenwert ist bewusst nicht enthalten – es kollidiert mit der Hierarchie Projekt → Teilprojekt → Aufgabe und der sort_order, die das Chart für dieselbe Zeilenreihenfolge braucht

  • Tastatur-Navigation in der Tabellenansicht, Teilschritt D von #117 (Issue #121): Tab/Shift+Tab springen zur nächsten bzw. vorigen editierbaren Zelle (überspringt Zeilen ohne Schreibrecht, springt am Zeilenende zur nächsten/vorigen Zeile), Enter speichert und springt in dieselbe Spalte der nächsten Zeile, Esc bricht ab und stellt den vorigen Wert wieder her. Pfeiltasten bewegen die Zellauswahl, wenn gerade keine Zelle im Editiermodus ist (innerhalb einer Zelle bleiben sie für Cursor-/Wertänderung nutzbar). Die reine Traversierungslogik steckt isoliert testbar in src/gantt/tableNav.js (src/gantt/tableNav.test.js). Die ausgewählte Zelle bekommt einen sichtbaren Fokusrahmen und wird per scrollIntoView() ins Sichtfeld gescrollt – über den bestehenden bidirektionalen Scroll-Sync (Issue #119) folgt das Gantt-Chart automatisch mit. Neue ARIA-Rollen (grid/row/columnheader/gridcell, aria-readonly je Zelle) machen die Tabelle für Screenreader als Grid erkennbar
  • Read-only Tabellenansicht links vom Zeitstrahl, Teilschritt B von #117 (Issue #119): neue Komponente GanttTable.vue zeigt dieselben Zeilen wie das Chart (Titel mit Einrückung nach depth, Startdatum, Enddatum, Fortschritt), gespeist aus buildRowModel()/buildTableRows() (siehe Teilschritt A, Issue #118) – Reihenfolge, Filter-Dimming, kritischer Pfad und Projektfarben bleiben so exakt konsistent zum Chart. Zeilenhöhen sind fest an frappe-gantt gekoppelt (40 px = bar_height 22 + padding 18, Kopfzeile 52 px = header_height). Ein Toolbar-Button blendet die Tabelle ein/aus, ein Splitter macht die Breite verstellbar (160–560 px), vertikales Scrollen ist bidirektional mit .nc-gantt-container synchronisiert. Sichtbarkeit und Breite werden pro Nutzer über einen neuen, generischen UI-Preferences-Endpunkt (GET/PUT /api/v1/ui-preferences, SettingsController über IConfig::get/setUserValue() als JSON-Blob) persistiert, damit Teilschritt E (Editieren) das Schema nur noch um weitere Keys erweitern muss, ohne Endpunkt oder Speicherformat anzupassen. Rein lesend – Editieren folgt in Teilschritt C
  • Projekt-Export/-Import (JSON, format_version: 2) kann jetzt optional die von den Aufgaben referenzierten Ressourcen inkl. Gruppe, Wochenmodell, Arbeitskalender+Feiertagen und Tageskapazitäten mitnehmen, statt wie bisher nur die rohe resource_id in eine Zieldatei zu schreiben, die dort ungeprüft übernommen wurde – auf einem System ohne identische Ressourcen-IDs ging die Zuordnung damit verloren oder landete schlimmstenfalls bei einer fremden Ressource (Issue #115). Vor dem JSON-Download entscheidet ein Options-Dialog, was mitexportiert wird (Ressourcen / Verfügbarkeiten / Kalender, gestaffelt); vor dem JSON-Import zeigt ein passender Dialog, wie viele Ressourcen die Datei enthält, und lässt auswählen, was übernommen wird sowie ob bei Namensgleichheit die vorhandene Ressource wiederverwendet (Default) oder immer neu angelegt wird. Referenzen werden ausschließlich über eine Mapping-Tabelle aufgelöst und nie roh durchgereicht; nicht auflösbare Referenzen werden verworfen (Regressionsschutz zu #112/#113). Neu angelegte Ressourcen/Gruppen/Kapazitäten gehören stets dem importierenden Nutzer. Wochenmodelle/Kalender/Feiertage sind weiterhin admin-only anlegbar – ohne Adminrechte werden nur namensgleiche vorhandene Einträge gemappt. CSV bleibt unverändert aufgabenzentriert mit Ressourcen nur als ID-Referenz; der volle Ressourcenumfang ist JSON-only. Alt-Exporte ohne resources-Schlüssel importieren unverändert wie bisher, ohne Options-Dialog
  • Inline-Editing der Basisfelder in der Tabellenansicht, Teilschritt C von #117 (Issue #120): die Zellen für Titel, Start, Ende und Fortschritt sind direkt editierbar und aktualisieren die Balken im Chart sofort. Gespeichert wird über die bestehenden Store-Actions (updateTask/updateSubproject/updateMilestone/updateProject) inkl. Terminpropagation und dem vorhandenen Undo-Toast für Aufgaben (analog Drag & Drop im Chart); ein lokaler pendingEdits-Overlay zeigt die Änderung optimistisch an und rollt sie bei einem fehlgeschlagenen Request mit Fehlermeldung zurück. Die Schreibrechte löst tableRows.js je Feld über store.canEditProject() auf (titleEditable/startEditable/endEditable/progressEditable) – Projekte und Teilprojekte bleiben bei Datum und Fortschritt read-only, da diese Werte aus den Aufgaben berechnet werden. Geprüft wird: Titel nicht leer, Enddatum nicht vor Startdatum (Meilensteine ausgenommen, deren Start- und Endspalte dasselbe milestone_date zeigen) und Fortschritt 0–100. Im Simulationsmodus rufen Datumsänderungen an Aufgaben simulateTaskChange() statt einer echten Speicherung auf
  • Ressourcen-Überbuchungswarnung (Issue #82): die Kapazitätsplanung war bisher reine Datenerfassung ohne Abgleich mit dem tatsächlich verplanten Aufwand. Der neue Endpunkt GET /api/v1/resources/{id}/utilization?from=&to=&group= (ResourceUtilizationService/ResourceUtilizationController) stellt den geplanten Aufwand aus den Aufgaben-Ressourcen-Zuordnungen – gleichmäßig auf die Arbeitstage der Aufgabe verteilt, über WorkCalendarService mit ressourcenspezifischen Arbeitstagen und Feiertagen – der erfassten Tageskapazität gegenüber. Mehrfach-Ressourcen-Zuordnungen (Issue #103) samt Alt-Aufgaben werden berücksichtigt, ohne gepflegte Tageskapazität gilt ein Fallback von 1 PT/Tag. Im Ressourcen-Raster färben sich die Zellen nach Auslastungsgrad (> 90 % gelb, > 100 % rot) und der Tooltip zeigt geplante Stunden gegen Kapazität samt Aufschlüsselung nach Projekt und Aufgabe; das Workload-Chart im Gantt markiert überbuchte Perioden jetzt rot. Übersetzungen für alle sechs Sprachen ergänzt
  • Nachgetragen – beide Funktionen wurden bereits mit 1.4.2 ausgeliefert, waren dort aber nicht im Changelog dokumentiert:
  • Einheitliches Statusmodell für Teilprojekte und Aufgaben (Issue #78): neues Feld status (not_started/in_progress/blocked/done) auf nc_gantt_subprojects und nc_gantt_tasks ergänzend zum numerischen Fortschritt, Bestandsdaten leitet Migration Version1030 daraus ab. Validierung per StatusValidator (Whitelist analog ColorValidator), Status-Dropdown in den Detailformularen, blockierte Balken im Gantt schraffiert und mit Warnsymbol, blocked_count je Projekt im Rollup sowie ein Badge in der Projektliste
  • Mehrere Ressourcen mit individuellem Aufwand je Aufgabe (Issue #103): bis zu vier Ressourcen je Aufgabe mit eigener Aufwandsschätzung in der Einheit der Ressource (neue Tabelle nc_gantt_task_resources, Migration Version1029, Backfill aus den vorhandenen resource_id/work-Werten). Das manuelle Work-Feld entfällt: TaskService::resolveResources() summiert die Einzel-Aufwände zur Gesamt-Work (1 PT = 8 h) und übernimmt die erste Zuordnung als primäre resource_id, die weiterhin für Arbeitskalender und CPM maßgeblich ist. Ressourcen-Filter sowie CSV-/JSON-Export und -Import berücksichtigen alle Zuordnungen statt nur der primären

Changed

  • buildFrappeRows() (src/gantt/frappeRows.js) mischte bisher Datenmodell und frappe-gantt-Formatierung: die Einrückungstiefe steckte als Leerzeichen im name, Zustände (Dimming, kritischer Pfad, Summenbalken) hingen in custom_class – eine geplante Tabellenansicht neben dem Gantt-Chart (Issue #117) hätte dieselbe Logik duplizieren müssen (Teilschritt A, Issue #118). Neue Funktion buildRowModel() (src/gantt/rowModel.js) liefert dieselbe Zeilenliste jetzt als neutrales Modell (id, type, entityId, depth, title, start, end, progress, parentId, editable, matched, critical), resolveMilestoneDate() wandert mit ins Modell. buildFrappeRows() ist nur noch ein dünner Adapter darüber, der name/custom_class/dependencies wie bisher erzeugt. Reines Refactoring ohne sichtbare Änderung, per Fixture-Vergleich gegen die alte Implementierung geprüft; neue Unit-Tests (src/gantt/rowModel.test.js, npm run test:unit, via neu eingeführtem vitest) decken Hierarchie, Einrückungstiefe, Meilenstein-Datumsauflösung und Filter-Dimming ab

Fixed

  • Abhängigkeitsfortschreibung bei Aufgaben lieferte für alle vier Abhängigkeitstypen (EA/AA/EE/AE) einen um einen Arbeitstag zu langen Nachfolger-Termin – bei EA zusätzlich mit falschem Anschlusstag, da der Nachfolger auf dem Endtag des Vorgängers statt am folgenden Arbeitstag begann und dadurch mit dem Vorgänger überlappte (Issue #114). Ursache war eine Off-by-one-Vermischung zwischen der inklusiven Arbeitstagszählung und der exklusiven Datumsfortschreibung in TaskService::applyConstraint()/computeMultiConstraint(). Betroffen sind sowohl das direkte Setzen einer Abhängigkeit als auch die Terminpropagation nach Drag & Drop und die What-if-Simulation. Verhaltensänderung: Bestehende Pläne mit EA-Abhängigkeiten verschieben sich beim nächsten Speichern/Neuberechnen um einen Arbeitstag nach vorne (auf den korrekten Anschlusstag); bei AA/EE/AE verkürzt sich der zuvor fälschlich verlängerte Nachfolger-Zeitraum um einen Arbeitstag, die ursprüngliche Dauer bleibt jetzt garantiert erhalten. Abhängige Meilensteine (Dauer 0) sind unverändert korrekt und von diesem Fix nicht betroffen

  • Security: jeder Nutzer mit Schreibrecht auf ein eigenes Projekt konnte jede in der Instanz existierende resource_id einer eigenen Aufgabe zuordnen – unabhängig vom Eigentümer und ohne jede Share-Beziehung (Cross-Tenant-IDOR, Issue #112). TaskService::resolveResources() lud die vom Client gesendeten IDs bisher nur über ResourceMapper::findByIds(), also als reine Existenzprüfung ohne Berechtigungscheck; da die IDs fortlaufende Integer sind, waren sie trivial zu erraten. Der Listen- wie der Legacy-Einzelfeld-Pfad in create()/update() filtern referenzierte IDs jetzt über ResourceMapper::findVisibleToUser(), nicht sichtbare IDs werden wie unbekannte verworfen (Liste) bzw. nicht übernommen (Einzelfeld, der bestehende Wert bleibt erhalten). Regressionstests decken beide Pfade für create() und update() ab

  • Feiertage ließen sich nicht in mehreren Regionskalendern führen: POST /admin/holidays lief in einen HTTP 500, sobald dasselbe Datum in einem zweiten Kalender angelegt wurde, weil der Unique-Index aus Version1006 allein auf holiday_date lag, obwohl Feiertage seit Version1013 über calendar_id kalenderbezogen sind. Version1031 verschiebt den Index auf (holiday_date, calendar_id); die Eindeutigkeit globaler Feiertage sichert weiterhin der Upsert in HolidayAdminController::create(). Betroffen war neben der Feiertagsverwaltung auch der Kalender-Import aus einem Projekt-Export, wo der Fehler still verschluckt wurde. Zweitens war GET /admin/holidays admin-only, während work-calendars und work-schedules lesend für alle offen sind – für normale Nutzer blieb der Store deshalb immer leer (keine Feiertage im Projekt-Export, keine Markierung arbeitsfreier Tage im Ressourcen-Raster); der Lese-Endpunkt zieht nach dem Muster der übrigen Stammdaten auf /api/v1/holidays um (NoAdminRequired), Anlegen und Löschen bleiben unter /api/v1/admin/holidays. Ergänzend werden Feiertage jetzt mit ihrem Kalender gelöscht (WorkCalendarController::destroy(), HolidayMapper::deleteByCalendar()) und Bestandswaisen bereits gelöschter Kalender in Version1031 aufgeräumt – über verweisende Ressourcen haben sie bis dahin weiter als arbeitsfreie Tage gewirkt
  • Die Installation der Tabelle nc_gantt_task_resources scheiterte auf Instanzen, deren Tabellenpräfix die von Nextcloud aus Oracle-Kompatibilität erzwungene 30-Zeichen-Grenze für Bezeichner reizt: der von Doctrine automatisch vergebene Primary-Key-Name (Tabellenname + _pkey) wird mit Präfix oc_ zu lang (Issue #132). Die Primärschlüssel-Definition bekommt – analog zu den bestehenden Migrationen für nc_gantt_resource_capacity und nc_gantt_resource_groups – einen expliziten kurzen Namen
  • Der absolut positionierte Toggle-Button der Nextcloud-App-Navigation ragte in den App-Content hinein und verdeckte dort den Titel des ersten Eintrags in der Projekt-Rollup-Leiste (Issue #134); der linke Innenabstand reserviert jetzt Platz für den Button
  • Ein leerer Balken in Höhe der Kopfzeile erschien oberhalb des App-Inhalts, weil der CSS-Reset für den Nextcloud-Content-Wrapper an main#content.app-nc_gantt gekoppelt war (Issue #126). Ändert sich das Markup zwischen Nextcloud-Versionen (Klasse oder Tag), griff der Reset nicht mehr und der von NcContent (#content-vue) selbst gesetzte margin-top: var(--header-height) addierte sich zum Rand des Cores. Der Reset hängt jetzt robust an #content mit !important, ohne Abhängigkeit vom app-spezifischen Klassennamen
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegO8Qmtdwg8LOBKr5G+mq4co374hsqqSN6oCIRV9atUx0KqogPxisJiIExkPoSIbu
e4EeIaUzg2alhZsKBLWOwR3Ab3fNm5vvd2DzJn6Hei7A70woz9m/LXkVlcx3vALW
bwolClUX1XCZOBVo/kSS5AOVwak3/huzT1iF9xH9BnpdzVuOjZ563LYwSayfb2Gq
SkWNfLZlKlDkTTF2PIszrNmaa89GmyqTbraxSP16/9nYdIj8ZU0zrhC3HtpihgAT
wya2lwnMf6I73HU0xxgT/+ITTmurx4eHwKWH80P4tjg/Y9mnHODIpAslbW3351mc
qdTHXIBW1MB1YGCQ+JPt11v1X2ccn29kEF9d1nkizKZ1+Du+LJQhPhwHiQxPWSwAUlTlQ4tj1lbe3+FJ1jsVTCqWbDUN02EoV7ZwtN15/HLJfM6w4drUi8ThJ1aLG3v6
cIwMJjYgWeepxR2gv4YgBf4u7txodm5bAWuzySk4rfAG1dBxmie/bfNnWmQ9LrYy
spwKc73gsPhg03ll+jp0b27fJfNF4IpOysz3i+r1ExVfeNIWWO91JPatO2pXZh4O
q0rczM8wYvctvcEkKQIL67HoIrKw4GVIXfqM9Oc8e/Ay7d/NPL+BjpCO6zvoFBY+
SRmjit15YmGW4EQwweQfKXuZ2YeySj+7OjhKHpKfvfg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.2
Release Details
UpdatedAug. 20, 2026, 4:41 a.m.
Changelog

Changed

  • release.sh fragt die Versionsnummer jetzt interaktiv ab, statt sie nur aus appinfo/info.xml zu lesen: zu Beginn werden die aktuelle Version und das letzte Release-Tag angezeigt und es wird gefragt, ob eine neue Nummer vergeben werden soll (Vorschlag: nächster Patch-Level). Die Eingabe wird auf das Format X.Y.Z, auf einen echten Versionssprung und auf ein noch nicht vergebenes Tag geprüft und dann nach appinfo/info.xml, package.json und package-lock.json geschrieben. Fehlt ein Changelog-Abschnitt zur neuen Version, warnt das Skript; anschließend bietet es an, den Bump als chore: Version X.Y.Z zu committen — nötig, weil der Tag-Name den Commit-Hash enthält. Für nicht-interaktive Läufe gibt es --version=X.Y.Z und --no-bump; ohne TTY wird nie gefragt, make release bleibt also unverändert nutzbar
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturepoVHZxteU3AR2cpRRV98jNfv4j7rWnLEd3kbpn5Dp4T1ubAI+0QlRf9jxi0BaYz1
+1dg+3Yt25uN6Slp1FHiw7kyqt5HJ60XgnOWo+Zt0YBHSnWqLDhF8zxPEnQCkF+v
nUvscpcoQY2bRiIBZhVWVgeAD592uvOA4gfRDrUyLVDKjOrzBu3ScbNvCqTqE9IC
vs8qZkRLIcaD6tKoSLT2pZqmwBoCdClvEw5D5v40WSvLq232BZnXpQ4ogG3QvJdL
mYO8HlPZKVOxANp0MGnWle4IFRMbMt22Ebpkjc18Q9Q4AIv/Sd3zTsc+uMONTM95
s0GXvLSm95o0UCFZ1CL7fCEq7NwlprjVrTeWGmlJNVjHJuIyRKHEz5DR12s0lAGm
/rKGWjoVP+Wvk3upRizR/5y2fpWoiwuDqCtz9gQN6+HHYLaqLvYqsAJ/SxmtLtP7
8B2rK61ClKowz4phbTZdrCaF8w3IWnFFBTFquIVFuN2jUoHvdCMtd0910sLAMY5M
Xs9nk0FVsP23zKXlQpOojq7ouCQagRhsnBJDLxWRR+NIq582f7SDgcrkBqxlGJX4
/Ql9V+2iFx61QANCTjdJIO3LbvbK60vGos+ency0GfvSDbPovDAVyJzt8ZUh5BNw
UXQ4XOHJZAagAr36rKttk1DQbZZ/2bUEq231c96m83g=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.1
Release Details
UpdatedAug. 17, 2026, 7:43 p.m.
Changelog

Fixed

  • Die Meilenstein-Raute hatte eine fest verdrahtete Farbe (#fbbf24) statt der Projektfarbe zu folgen, obwohl Balken für Aufgaben und Teilprojekte bereits korrekt eingefärbt wurden (Issue #102). Zusätzlich wurden die separaten – und wirkungslosen – Farbwähler bei Teilprojekt und Aufgabe entfernt, da deren Anzeige ohnehin immer der Projektfarbe folgt
  • „Meilenstein hinzufügen" im Drei-Punkte-Menü der Projektliste nutzte zwei aufeinanderfolgende window.prompt()-Dialoge (Name, dann Datum als Freitext) statt des regulären Detail-Dialogs (Issue #101). Der Klick emittiert jetzt add-milestone analog zu „Aufgabe hinzufügen" (Issue #91) und öffnet denselben Meilenstein-Dialog wie beim Bearbeiten, mit vorbelegtem Projekt
  • Im festen Datumsmodus des Aufgaben-Detaildialogs konnte ein Enddatum vor dem Startdatum eingegeben und gespeichert werden (Issue #100). Jetzt zieht ein über das Enddatum hinaus verschobenes Startdatum das Enddatum mit; ein zu frühes Enddatum erzeugt ein rotes Fehler-Icon am Feld, hält den Fokus dort und deaktiviert den Speichern-Button, bis ein gültiges Datum eingetragen ist (dateField() in fields.js mit neuer setError()-Methode)
  • Eine Aufgabe mit Abhängigkeit behielt trotzdem die manuell eingegebenen Start-/Enddatumsfelder, die beim Speichern durch die Abhängigkeitsberechnung überschrieben wurden – für Nutzer nicht nachvollziehbar (Issue #99). Der Aufgaben-Detaildialog hat jetzt denselben Fixed-date/Dependency-Umschalter wie Meilensteine (Issue #90): im Dependency-Modus ersetzt eine Dauer (Tage) die Datumsfelder, die Abhängigkeit bestimmt den Termin. Serverseitig wendet TaskService::create() neu hinzugefügte Abhängigkeiten sofort an (bisher nur update()), damit auch neu angelegte Aufgaben keinen die Abhängigkeit ignorierenden Platzhaltertermin behalten
  • Der Speichern-Button des Detail-Dialogs blieb nach dem Hinzufügen einer Abhängigkeit deaktiviert: die Gültigkeitsprüfung hing allein am input-Event, das bei einem <select> vor dem change-Event feuert – zu diesem Zeitpunkt war die Abhängigkeit noch nicht in der Liste. detailModal.js wertet jetzt zusätzlich change aus, der Abhängigkeits-Editor (fields.js) meldet jede Mutation (Hinzufügen, Entfernen, Typwechsel) und der Fixed-date/Dependency-Umschalter in taskForm.js den Moduswechsel über ein eigenes bubbelndes change-Event

Added

  • CONTRIBUTE.md und setup-dev.sh für neue Mitwirkende (Issue #98): Projekt-Leitlinien (FOSS, Datensparsamkeit, Security), VIBE Coding ausdrücklich erlaubt, sowie der komplette lokale Dev-Setup-Weg (Docker-only, Nextcloud + MariaDB unter localhost:8088, optionales E2E-Setup, Git-/Bot-Auth). setup-dev.sh automatisiert diese Schritte, konfigurierbar über --workdir/--no-e2e/--no-docker-install
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturetYcUvVtDEQrAakG32EQF1atSOmz91/8XudCUh60A9r/AxMv+AA5AX2rIvNYDkEn0
OuTmXA6Iz0DoVkdij11MPDhfLaUy0mXeLbwB5fj/xF/cFYcNeCFGKD3k2jyRwOTo
2n1sEp0r8sn0fitHzLfhcB/By0VJkZyhaO/NctP01XfcSQM7lGEOeY/uagtGaDTI
JnPLoQTHo0nUOv4y7jUMZuurEgBOc4yeUxTKv4A/m642hvNQIS0q6K9ON41JHKIO
UtI7DbDWSboVhct1Go3zoj5XcaAGG9ZiwSkJDt/BXT5tWH4SOnSn7mys0j1F6Y0g
vIJWjz6S2RlekwIrIxgyaeEVBOeQ4OM1ilJGWKXDyYJM/0IBxqnqRXDkAFlOopDo
xezlcFdWTkYL5OBKMFJhKuTy7dEQ9hxx5Nz4+vLbNV8gDJZBI+5yI5m8W+809WIH
iKUpIda1ShOblvYH6Ek5AzBdTmoFQ6T4f7iWT7j1i4uUP2DsgehjGVl8yDIT/wyx
nV9t7koXapu1WeXrunSa++mXsi3lceNpthdz5/cUEOe9yWvc1rrulRhiWzePga8H
zjVQYGAIPvwYkWa47U6Zh7HBsQaS+9yQ8NDnD49SS4jUWTGlwNqfM+q9fZ16/G9d
7l4chZUPPbBv+xWfdxmFw+MC5zdkAq27isHToTqnm+w=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.0
Release Details
UpdatedAug. 13, 2026, 6:41 a.m.
Changelog

Added

  • Meilensteine werden jetzt immer direkt unter ihrem übergeordneten Objekt angezeigt statt gesammelt am Projektende (Issue #90): Meilensteine lassen sich neu auch einer Aufgabe zuordnen (bisher nur Projekt/Teilprojekt, Spalte task_id war bereits vorhanden, es fehlte nur die UI), das Auswahlfeld im Detail-Dialog heißt entsprechend „Übergeordnetes Objekt" statt „Teilprojekt". Zusätzlich können Meilensteine ihr Datum jetzt statt eines fixen Werts aus einer Abhängigkeit zum übergeordneten Objekt beziehen (EA/AA/EE/AE, analog zu Aufgaben-Abhängigkeiten; neue Spalte dependency_type, Migration Version1028) – da ein Meilenstein keine Dauer hat, bestimmen EA/EE das Ende und AA/AE den Start des Zielobjekts. Die Berechnung übernimmt der neue MilestoneDateService (serverseitig live bei jedem Abruf sowie im ICS-Feed neu aufgelöst, nicht persistiert propagiert) sowie clientseitig in frappeRows.js für sofortige Reaktivität ohne Neuladen
  • Undo für Drag-&-Drop-Verschiebungen inkl. Propagationskette (Issue #85, Umsetzung der einstufigen Empfehlung: letzte eigene Operation, kein Redo, zunächst nur Drag & Drop): Migration Version1027Date20260810020000 ergänzt nc_gantt_change_events um op_id (UUID) – alle Change-Events einer zusammenhängenden Operation (Drag + gesamte Propagationskette B→C→D) erhalten dieselbe op_id, erzeugt in TaskService::update()/propagateDates() und über ChangeEventService::log() protokolliert (dieses Change-Tracking war bisher tote Scaffolding-Logik ohne Aufrufer). Neuer Endpoint POST /api/v1/changes/{opId}/revert (ChangeEventController::revert, ChangeEventService::revertOperation) wendet alle Change-Events der Operation invers an, in einer DB-Transaktion mit Rechteprüfung über AccessService::canEditOrOwn(). Konfliktschutz: Revert wird mit HTTP 409 abgelehnt, sobald der aktuelle Feldwert nicht mehr dem protokollierten new_value entspricht (jemand hat zwischenzeitlich erneut geändert); der Revert selbst erzeugt wiederum neue Change-Events, die Audit-Historie bleibt lückenlos. Frontend: nach jedem gespeicherten Drag zeigt GanttChart.vue einen Toast „N Termine verschoben — Rückgängig" (showUndo aus @nextcloud/dialogs, 10 s sichtbar) sowie ein globales Ctrl+Z für die letzte eigene Operation der Session (stores/projects.js: lastOperation, revertOperation(), undoLastOperation())
  • Kalender-Export als abonnierbarer ICS-Feed pro Projekt (Issue #83, Variante A aus dem Umsetzungsvorschlag): neue öffentliche Route GET /apps/nc_gantt/projects/{id}/calendar.ics?token=… (regulärer, unauthentifizierter CalendarFeedController, damit externe Clients wie Thunderbird, Nextcloud Kalender oder mobile Apps ohne Nextcloud-Login abonnieren können) liefert Meilensteine als ganztägige VEVENTs (◆-Präfix), den aus den Aufgaben abgeleiteten Projektzeitraum sowie optional Teilprojekt-Zeiträume (Schalter „Auch Teilprojekt-Zeiträume einschließen", standardmäßig aus, um den Feed bei großen Projekten nicht zu überladen). Zugriff erfolgt über einen pro Projekt generierten, 256-Bit-zufälligen Token in der URL (CalendarFeedService, neue Spalte calendar_token auf nc_gantt_projects, Migration Version1026), der über einen neuen „Kalender-Feed"-Bereich im Freigabe-Dialog angezeigt, kopiert und jederzeit widerrufen/neu generiert werden kann (CalendarTokenController, nur für Projekt-Admins/-Eigentümer). ICS-Generierung ohne neue Abhängigkeit, RFC-5545-konform (Escaping, Zeilenfaltung)
  • What-if-Simulationsmodus für Terminverschiebungen (Issue #84): neuer Endpunkt POST /api/v1/projects/{id}/simulate führt Terminpropagation und CPM rein im Speicher aus (kein DB-Schreibzugriff) – TaskService::simulate() nutzt die neu extrahierte, stateless propagateDatesCore()-Kernlogik, die auch der persistierende update()-Pfad verwendet, CriticalPathService::computeFromTasks() erlaubt CPM auf einem hypothetisch veränderten Task-Datensatz. Frontend: „Simulation"-Toggle in der Gantt-Toolbar mit Banner inkl. Projektende-Delta in Arbeitstagen, Geisterbalken für die Ist-Termine (analog Baseline-Overlay) sowie Verwerfen/Übernehmen. Drag & Drop ruft im Simulationsmodus simulate() statt einer echten Speicherung auf. Szenarien bleiben bewusst flüchtig (kein Speichern benannter Szenarien), „Übernehmen" wendet die Änderungen als einzelne Task-Updates an, Simulation läuft pro Projekt
  • Neuer Eintrag „Aufgabe hinzufügen" im Drei-Punkte-Menü der Projektliste (Issue #91): legt wie der „Neu"-Button eine Aufgabe an, belegt dabei aber das gewählte Projekt direkt vor, sodass der Projekt-Auswahlschritt in der Maske entfällt
  • Such-/Filterleiste für Projekte, Teilprojekte und Aufgaben (Issue #81, Stufe 1: rein clientseitig auf Basis der bereits geladenen Daten aktivierter Projekte): Freitextsuche im Titel, Risiko, „überfällig" (Ende < heute und Fortschritt < 100 %), „über Budget" (Ist-Kosten > geplantes Budget), Ressource und „ohne Deck-Verknüpfung". Neuer Pinia-Store stores/filters.js (Zustand pro Nutzer in localStorage persistiert) und reine Matching-Logik in gantt/filterMatch.js. Nicht passende Balken werden in GanttChart.vue gedimmt statt ausgeblendet (Abhängigkeitspfeile und Zeitkontext bleiben lesbar), Projekte/Teilprojekte gelten als Treffer, sobald ein Nachfahre passt. Dieselbe Filterleiste (components/FilterBar.vue) dimmt in ProjectList.vue nicht passende Projekte; für nicht aktivierte Projekte sind nur Titel-Treffer zuverlässig auswertbar, da deren Aufgaben/Teilprojekte nicht geladen sind

Changed

  • Freigabe für Nextcloud 34 (Issue #94): max-version in appinfo/info.xml von 33 auf 34 angehoben, unterstützter Bereich damit NC 28–34. Verifiziert gegen Nextcloud 34.0.2 mit PHP 8.5 und MariaDB 11.4 (148 Unit-Tests, 39 E2E-Tests)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerdYQI+Semnk0cWtKqUZinVDHk0KpxyqB5Ox4NBL9pyf5nrpK8xZpe9lBxc6zhDfW
8adLfVIwQtX+bI7S1O05xm0hwrUHQQ2ii6ADD8mlAxnZjdhKSCAhZ0NcaqT7Em0X
nD7yRP2B8zAAQMJNjzeBsLbzveNrXMoGzFrvm/jFxskUIUS01ykfWEg2C5+5/IAf
7jBAM8x2Qj92b8zyBFpLjfJQw9aTE/trtCsmZXQFmkJjGS0anrJc9dXKfqnHxknX
8jmLOv4Yq4Qtk3UF35HTiIGcA2W0UKfVyFb4XcrIMeSmLXXcagJWjeH5nB18UUNn
JyCgE/qY4v/SnrNQU92mIdMOQGuvYg7xbaz226lgNdwM2HuM9U8nc2juqJxAO+Wp
xs85yl82qIviZ5YY39HK371MZ/ufMsaVsVy3Nfn3PjqqkzGLmtAL6KJDcKHZWdDw
ugEPwO61DxnERZsNGeFpXfzCxjMpZc/qwUIfGw85idmU2vdedtrxHBkDGoqu1IP7
xZOO8uSYrzbXM6g1gs3WPBOY51CmZLs5pIQTN1tVl5ifGw4uOuzDBUCKiR5oA2MI
352HbA7wwWFgwoN9W5t4MRTowmaYpM8AB+nh9f1A5q9S8SelfMpXOqGew9VZb2jd
AuiJK/LEPjRgs5s3/FfC4Sb1MSsXDr1N1jziaF4MomM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.3.1
Release Details
UpdatedAug. 6, 2026, 12:57 p.m.
Changelog

Added

  • Regressionstests für die Rollentrennung Viewer/Editor/Admin: ForbiddenException-Tests für TaskService::create/update/delete/syncProgress und ProjectService::updateProject/deleteProject (inkl. „Editor darf nicht löschen") sowie erstmalige Testabdeckung für SubProjectService und MilestoneService

Fixed

  • CSV-/Formel-Injection im Aufgaben-Export (detail/io.js exportCsv): Task-Felder wie Titel oder Beschreibung, die mit =, +, - oder @ beginnen, wurden unverändert übernommen und von Excel/LibreOffice/Google Sheets als Formel ausgewertet. Die Absicherung aus ReportView.vue liegt jetzt in einer gemeinsamen Utility (src/utils/csv.js, escCsvCell) und wird an beiden Export-Stellen verwendet
  • Viewer sahen im Frontend dieselbe Oberfläche wie Editor/Admin (Add Task/Subproject/Milestone, Share, Delete Project, Drag/Resize im Gantt-Chart, volle Bearbeitungsformulare), obwohl das Backend Schreibversuche ohnehin korrekt ablehnte – kein Least-Privilege an der UI. ProjectService::getAccessibleProjects()/getProject()/createProject()/updateProject() liefern jetzt das effektive permission-Feld (AccessService::getEffectivePermission()) mit; ProjectList.vue, GanttChart.vue (inkl. Drag/Resize-Sperre je Projekt) und das Detail-Modal (detail/detailModal.js) blenden Schreib-Aktionen für Viewer aus bzw. schalten Formulare read-only
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureBUyDeI9y7iKD1wQJLHrW6IFq0zuXOJQ1Zv/x+0RryIIQ0fAGAUjwQyLDYLYpRzPg
6ITDPfY9bftVlwkB8w9LqZiFETkH8mgc6l5XB7F6NKzFrdaSnNw21CIVJjWyHpkR
mxDq+vQepZd+YtB9jMz39kbFk9BNHrpKIwIXXZVqEqj1U54NmT+5XnT892m9mY6o
RWQNf3Mn5WcH3vmoDi/UAOgDaO30GFCRJePryurVpK7stkub8K6fYsWIkS5NeJlP
YHdb4nBlkhUzQFjo0ZDSkCSiWjMdyCrtEanX6m6Ffhr+j0jpd+J9soMSjrzdzt4T
5mETo8xc61IQIVFWMD6fZ3/oCmbx2TFDZsJjRpFKxoPA08AkdC7xjxHoaKYZHvi4
+tzowxPzMfyjQ/hEj2dwq22doM2lXK1Y13VvV8kkaomrAQjB97gf+ODrYZNDgNvc
2ijU08B4cAB0HbEveDpDRjCJj8D5OwCMafRMa4Ul/nm0+RiBFOhIjDYdVAnCp7Ql
IEc4UQZM9WYLQlouwGPk83POpbqkbc8vKAucGBXvHAbxNpUSOGbssMYFGU/e9pOg
+oT2gcJrC3MYPGIWnS8iLn75PK1H063KFJv5yqC+h/rqwX/FxyTzKvofJi68GpLs
WtG3y67W8Ju4y1I9iQ9raJxxhFUJrStibGy9gc7L0DM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.3.0
Release Details
UpdatedJuly 9, 2026, 5:23 a.m.
Changelog

Fixed

  • Baseline-Toggle im Gantt-Toolbar zeigte nie Baseline-Balken und PUT baseline/active warf einen SQL-Fehler: baseline_id auf nc_gantt_projects wurde mit der 3-Ebenen-Migration versehentlich zusammen mit den tatsächlich berechneten Spalten entfernt (Migration Version1025 stellt die Spalte wieder her). BaselineService::create() aktiviert eine frisch eingefrorene Baseline jetzt automatisch fürs Projekt; renderBaselineBars() (GanttChart.vue) und baselineOverlay.js lesen Baseline-Items dafür über task_id statt des seit der Umstellung auf das 3-Ebenen-Modell nicht mehr existierenden subproject_id-Felds
  • OCS-API-Fehlerantworten (400/404/500 …) lieferten für alle OCSController-Endpunkte einen leeren text/html-Body statt des erwarteten JSON-Envelopes mit message; ExceptionMiddleware wirft jetzt passende OCP\AppFramework\OCS\OCSException-Subklassen statt einer rohen DataResponse, die Nextcloud-Core nur für 401/403 automatisch formatiert. Betroffene Controller (ProjectController, ReportController, ShareController, ChangeEventController, WorkScheduleController, WorkCalendarController, HolidayAdminController, SettingsController) wurden zusätzlich auf die bestehende ApiException-Hierarchie umgestellt

Removed

  • Nie ausgewertete Gruppen-Freigabe-Option entfernt: Share::TYPE_GROUP, principal_type-Parameter in ShareController sowie die DB-Spalte principal_type (Migration Version1024) wurden gestrichen; Freigaben sind jetzt ausschließlich benutzerbezogen

Changed

  • Dashboard-Summary (DashboardController::summary) lädt Baselines und Aufgaben-KPIs jetzt über gruppierte SQL-Aggregat-Queries statt pro Projekt einzeln (N+1-Fix, gilt auch für den Admin-Fall mit allen Instanz-Projekten)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureNJfbRJEXzKGipUjUW9tUKayHoY6ltSU0ZeCQXcDx6XVDY/naLK0dVY9ciofXU7oy
40sJIOsjxcUZbalDwqncPn0vjGoqmk3BFUT7KotEpLLf8/ZmIjdHEbQbJHzEpdQV
y5YedjqRLKB7/Ul35JWVFxY8eutCsYkonfof7v6AGMGB/RjQ7GDB6jWunEEwqUGV
3Y1isvdq5N3oo9NDY/QR0C2C7jiMnB9Kon0KzZ2yp0oF5euOpA/qrtmqxG3L9PoZ
DW1ppwAuARWtRDu4tcDAAQAkF1Mrg5YyYgRoKm7uVazlKEP7DxX56g/g4RS2otEE
rKsA/ER9yYiHnNyGcZuK9Jkfe2Ua6/R7gzJilRvHZ7CBPkhY+XNPcrjlsHbCJEJJ
cX/b0B56p7FyAFy6XjMY9PYFhxy/ubHE7Sk8ixX88uxgM4vf/sSFQaN84CcG/qqA
Nq/OEytBjZP82TsFFYBRcPKEPskF4ejjhNVC4bJpiuCIpo51nIxjeFGtDpeQiDG7
0OWc/GCvsz5W3eG7ShmQsRf0DoivL8tjgDUlMWO9boTpUy2Vnm1rDMKu6nFGKZ8o
TWTZR6u4zGZW1OBERdlU5NQmqxqXrsuGlT6r6DuQnWsWiscjXOyIFeHz+EOKhQOk
wUuN6xN8QES1CDFB27PYV0Sz2KhYhM78GKLCc9B10Ic=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.2.0
Release Details
UpdatedJuly 5, 2026, 9:21 a.m.
Changelog

Added

  • Löschen-Buttons im Detail-Modal für Teilprojekt, Aufgabe und Meilenstein
  • Deutsche App-Beschreibung in info.xml ergänzt

Fixed

  • Baseline-Datum und Kommentar im Report werden untereinander angezeigt
  • Dashboard-Widget zeigt Baseline-Datum und KPI-Werte korrekt an
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureXuYTZMxTlGNJVrGvU/GcIjSlBTYreXdvF5FilCmsi+l7MBvFlyeW1DsnxAsao3pu
HyhvaKXRnkraVQNT2ULgX4BmjUnhsELROrK03gtY88Odzqzh7RIXYkNkRcy92ara
56HIP8BBRD3m1c8pYPfpZfLAzkuurAzVqbJ6qbxw1AebdlPCFEq2g6C+IOcTon0T
q9ZD2I2XJmWd42EXXqva+NMrcx1jfjmHWVtl7xHKhwPndFCtHPMMAdoDS0Xpbh1V
3Ns4h/DphDpC8/ZYZ5sbpMCBekHw4VTZDGxNAuXJ5DMPhkqg2MxVplkS19PuBHLx
r74sgsz611Gjg55gjKOqCb5ZZEvqZTrwKTEYIbu8HicWtq/HxKRZS3FZN9MThTTX
Hesv65vsq9fH0UhEM6MVJjE5Phnw8Wb5ivtAiaUA+NzPrDfrHZAsSfR9NFL3rhFC
7wOmnXtzK40NjmqyMWJu/UP9UVbz3SeEiHiQO9GwBc2krzmXzcmLZFgEjty4CGoJ
SzXAA1QGpUGO4n0b3Y/5zSY4UxogGWybW+mOs27KDLytI915wNYGvBtcdAKCj6Ws
1uVA98npwMid/WSJJ04BXHX8LbowrDeTezSFJyGh+u/KnhlzkA7fqEuu3nQGT5rA
a9qeMmd5kmLBIVcG9RVVmw1kc0AK0qIqZDmlk9dWGJs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1
Release Details
UpdatedMay 27, 2026, 6:43 p.m.
Changelog

Added

  • Glossar-Button in der Navigation (über den Einstellungen) mit interaktivem Nachschlagewerk
  • Dreispaltige Glossar-Tabelle: Begriff/Abkürzung, Bedeutung/Englisch, Erklärung mit Berechnungsformeln
  • 30 Einträge in 6 Abschnitten: Allgemein, Terminplanung, Baseline & Berichtswesen, Earned Value Management, Ressourcen & Kapazität, Budget

Fixed

  • Fehlende deutsche Übersetzungen für englische UI-Strings ergänzt (Add dependency, Baseline, Dependencies, Remove, Work, No projects yet, Open Gantt Projects, u.a.)
  • Leere Übersetzung für Deck-Fortschritt-Hinweis befüllt
  • de.js vollständig mit de.json synchronisiert (war ~70 Einträge veraltet)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF1RHJx15J/N8tWNrDlAX1MFE/9tsct4PViEcBDeo8U/V2qJ/tezL7Xk0BVmYxjWw
pB+J685mcHjikh93/aZUqG8C6y0mR3TTJfaa7dLCIRMc1034x6Xj9Nd7Q9YVH4yi
RLTcRhrG2pN1Iqc23kwId995GJXHnCBEyuQuzo9m1VtA7GoFa8W2nqhXVT94khDS
8ZHQrixDfv63d4GfA5N2x4+phaiyObdskvCmVSuKrmouYvCGpPevtKG5YxzOrHid
aL48Kod0YgOCAD8nAqOhEmioP0m+wP2e/NC4Z5svSKT3vdlE67W1M68ipQQfnhPI
txui+2X8zFvoe/M5sE3DaXbLnOVNGQ7YRpyXFI5GsQmQ0cPSSO/94Mw15hLZfwMf
TDOrrvzf+Vu05sUTZ6CFh4FrSl4e5Q6Xjv6uH4BZtyOpPxl+ajgW4OkZvQDbt4KC
8aTZodMcDpXlRMDdBSxT1bT9tD05KUKmEZYtpxHiARrHDuQQCRSuxtjitQD9lbla
CMxKrBiO0YgdUb3FuczYkWEH+y8gW5egU6TDMnjAVbuUecGuGZN1Cc6tarcFWRu2
+V7Qn+aNoXEwP5e/0TM4Ig8ETAYseZRiTPHxeWiSg3RnU3HuT6kPVWw8LERACDT4
6F4WU9t2+4kESMLMjeV0/2UBQSykR/42Idsg0PtvpyQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1 (nightly)
Release Details
UpdatedMay 27, 2026, 5:24 p.m.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF4W7QJB8rVJ2xARdnhUbEouzRZMKcowz/ToDwr0oVFrVUbInFvHAMqXFTOVD1a5N
kDTb0pOhv+hCStTh70bN+aapjbdtp7SdQ3SbP5VoXBKLhUii41OqN/i4yM7itdSs
M8d5PVo9TJYVnfaij2xwWghMqZEyzxgOw4Q98dYIysf2BxsjjCtxGEMo/pISS90G
OE2RaVEFbwUcvu1XKszLP5X/k/onOchengURYVuzHP7VgVuomZpPSZ9VEIRKITyL
GpgSDrdqm5AXdQc7l/2OqprW6G+MxsuVzChbyPGslIYYXYsP9HLc/FfwVxaCJjh2
Nsiqfd+dMwnf7f9yrrBuZjbNwNa5x++IynrwaU01jVISkl9pgkbUYRaPSMTGFDAd
lsEEqlgs9KdzGoTFjDXNdTwDOsUMiu/ag8tPo7YxsxLFbVGa7laRAUhlGUT9QGPr
IOAAWvKqivsYnU5iwTCY/5HGza3SDfZc/e5upPfB+lTQTYijp1aAYRBAsoOLmuga
f0aP45pnfnYBJkT1kMsthjbGMdlL46wLiNHKRct9OlFwx74IRIyZdcs5Q3pC0aNN
DSLvsCSmY8/9PtTLzcOJKXTqqDVupCqq82bUlGRn7NR6xsinzv74MuOVnGY21tBm
iB5ceYpgiEukUz6YtS2quIIiMTnYmM+HBFnkXgBV0/Y=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32

Nextcloud 31

Gantt Projects 2.3.0
Release Details
UpdatedSept. 30, 2026, 11:45 a.m.
Changelog

Added

  • Aufgaben und Teilprojekte per Drag & Drop umsortieren (Issue #299): Vor jeder Aufgabe und jedem Teilprojekt steht in der Tabelle ein Ziehgriff (☰, ganz links, unabhängig von der Einrückung); eine blaue Linie zeigt die Einfügeposition. Verschoben wird nur innerhalb desselben Elternobjekts und unter Zeilen desselben Typs, sonst zeigt der Mauszeiger „nicht erlaubt“ und ein Ablegen bricht ab. Neuer Endpunkt PUT /api/v1/projects/{projectId}/row-order (kind, id, before_id) mit RowOrderService: vergibt die sort_order aller Geschwister fortlaufend neu (auch bei gleichen Werten), Schreibrecht nötig. Bewusst nicht Teil: Griff für Meilensteine (sie haben keine sort_order, stehen vor den Aufgaben ihres Elternobjekts) und Projekte, Umhängen in ein anderes Teilprojekt; der Zeiger ist der Standard-„nicht erlaubt“-Zeiger des Browsers. Doku (FEATURES.md, de/en) ergänzt. Neue PHPUnit-Tests (RowOrderServiceTest), Vitest-Tests (rowDrag.test.js) und der E2E-Test row-reorder.spec.js

Fixed

  • Klick auf das Deck-Abweichungssymbol öffnet den Dialog wieder beim Bereich „Abweichungen von Deck“ (jetzt im Reiter „Verbindungen“): focusDeckSection wurde im Dialog-Kontext nicht mehr weitergereicht. E2E-Tests für die Reiter angepasst
  • Tabelle und Gantt-Chart scrollen vertikal zuverlässig gemeinsam (Issue #292): Tabellenzeilen und Balken standen schon ganz oben um 9 px versetzt (Tabellenkopf 52 px, erste Chart-Zeile erst bei header_height + padding / 2 = 61 px), und weil nur das Chart den Ressourcen-Bereich enthält, konnte es weiter scrollen als die Tabelle – die Tabelle blieb am Ende hängen, das Chart zeigte einen anderen Ausschnitt oder sprang zurück. Der Tabellenkopf ist jetzt so hoch wie der Chart-Kopf bis zur ersten Zeile, ein Platzhalter unter den Tabellenzeilen gleicht Ressourcen-Bereich und Scrollleisten aus (per ResizeObserver laufend nachgeführt), sodass beide Seiten denselben maximalen scrollTop haben. Der Sync überträgt nur noch bei tatsächlicher Abweichung statt über ein synchron zurückgesetztes syncing-Flag, das asynchrone Scroll-Events nicht abfangen konnte. Geometrie in src/gantt/tableScrollSync.js (Vitest), neuer E2E-Test table-scroll-sync.spec.js

Added

  • Deck-Karten schon beim Anlegen einer Aufgabe verknüpfen oder neu anlegen (Issue #306): Der Deck-Bereich im Reiter „Verbindungen“ war bei neuen Aufgaben gesperrt („Speichere zuerst die Aufgabe“). Jetzt lassen sich Karten auswählen und „+“ vormerken; beim Speichern verknüpft bzw. legt die App sie an (deckCardsSection().commit()). Schlägt eine Deck-Aktion fehl, bleibt die Aufgabe gespeichert und ein Hinweis nennt die Zahl der Fehlschläge. Board und Kartenliste des Pickers folgen dem gewählten Projekt und übergeordneten Objekt (auch beim projektlosen Anlegen über „Aufgabe“). Der Termin-Abgleichsdialog entfällt dabei; Abweichungen zeigt danach der Bereich „Abweichungen von Deck“
  • Reiter „Anhänge“ in allen Detaildialogen (Projekt, Teilprojekt, Aufgabe, Meilenstein; vor „Verbindungen“) mit den Bereichen Dateien und Notizen: beliebig viele Dateien (Nextcloud-Dateiauswahldialog mit eigenen Ordnern) und Notizen (Auswahl aus der App Notes im Stil der Deck-Kartenauswahl) verknüpfen. Der Server teilt jede angehängte Datei automatisch mit allen Personen, die Zugriff auf das Projekt haben (Betrachter lesen, Bearbeiter/Admins dürfen ändern) und gleicht die Freigaben nach jeder Änderung an Anhängen, Projektfreigaben und Löschungen ab; entfernt werden nur die von der App angelegten Freigaben (nc_gantt_attach_shares). Die Freigabe gilt je Datei, nicht je Objekt: Sie bleibt bestehen, solange die Datei an mindestens einem Objekt der App hängt. Doku ergänzt in FEATURES.md (Abschnitt „Anhänge: Dateien und Notizen“, de/en), READMEs, HOWTO (Schritt 10) und Store-Beschreibung (info.xml). Neue Tabellen nc_gantt_attachments und nc_gantt_attach_shares (Migration Version1044), Endpunkte GET/POST /attachments, DELETE /attachments/{id}, AttachmentService. Die Dateiauswahl hängt sich in den modalen Detaildialog ein (sonst läge sie unsichtbar dahinter), und nachgeladene Bundle-Teile (Dateiauswahl, Datumsformate) nutzen den tatsächlichen Installationsort der App als Adresse (src/publicPath.js; die Vorgabe /apps/nc_gantt/js/ griff z. B. unter custom_apps nicht). Neue PHPUnit- (AttachmentServiceTest) und Vitest-Tests (attachments.test.js) sowie der E2E-Test attachments.spec.js
  • Aufgaben können von Meilensteinen abhängen (Issue #308): Vorgänger m-<id> neben t-<id> (keine Migration), in allen vier Typen mit positivem und negativem Abstand. Ein Meilenstein zählt als Punkt mit Start = Ende = seinem Datum; ein fester verschiebt sich nie und zieht seine abhängigen Aufgaben samt Nachfolgern mit, ein abgeleiteter folgt weiter seinem Ziel und gibt dessen Verschiebung an die Aufgaben dahinter weiter. TaskSchedulingService (Prüfung, Termine, Propagation, Neuberechnung) kennt Meilensteine als Knoten; die Zyklenprüfung berücksichtigt die Kanten Zielaufgaben → abgeleiteter Meilenstein und läuft auch beim Speichern eines Meilensteins (dependency_cycle); der Dialog sperrt solche Meilensteine als Vorgänger bzw. Ziele mit Hinweis. Im kritischen Pfad sind Meilensteine Knoten: ein fester ersetzt das Projektende als spätestes Ende für die Aufgaben, die über eine Abhängigkeit vor ihm liegen; ein negativer Puffer verhindert das Speichern mit dem Hinweis auf die betroffene Aufgabe (negative_float:<Titel>, bereits negativer Puffer wird nicht erneut gemeldet). Beim Löschen eines Meilensteins (auch über Aufgabe/Teilprojekt) entfallen die Abhängigkeiten auf ihn, ProjectDuplicationService und JSON-Import/-Export münzen m-<id> auf die neuen Meilensteine um. Oberfläche: Auswahl M<id> <Titel> im Abhängigkeits-Editor, Kurznotation M3EA-2d in der Vorgänger-Spalte, Pfeile von und zu Meilenstein-Balken, Nummerierung M<id> im Diagramm, Glossar und FEATURES.md (de/en) nachgezogen. Bekannte Vereinfachungen: die Pufferrechnung zählt den Meilenstein-Termin in Arbeitstagen des globalen Kalenders ab dem frühesten Aufgabenbeginn, und ein Meilenstein-Termin verschiebt keine Deck-Karten (Deck-Abgleich läuft nur bei Änderungen an Aufgaben). Neue PHPUnit-Tests (TaskSchedulingMilestoneTest, CriticalPathMilestoneTest, ergänzt: Meilenstein-, Duplizierungs-, Lösch- und Datums-Tests), Vitest-Tests und der E2E-Test milestone-dependency.spec.js
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureS4LN+K40y1DKrwQo02FOHkiugxuFllfcAsVp7IBoUthbKYnNdZtSmzBH+ZxY0V8C
U954MnYEijFZD14jMuW2wkvMomspSCRGfCfEHytRCLfNwmsK9AV5UfxutjQBe6Y/
ATjKYnhOEPrE2iXLOZ+P73VbC8GBPY6W21P1APsy0Af+XUoFycIdTnLJUU5QJgWY
+K/N7nUfXyqVA17kCO+/Zhw6w5wl5O7ZIMezWbNn3ApvOeIC8AE/CwJ/JccrQgE9
F4A1RTqFZcK0hCrluGelanyva6ygwAmYPoJtkR+CeiJr7vFdSSq6HJe0lG0y9SAm
RNFnDuEzt6uFqSl9EqK6ykVp7ns0k6npvld3gSqGN0Frq4dwGjxSjcRkpPGAYT7M
IH6YJDVlNbVdF/wRJtEmOvO1duI4uzBhjX3PIFX1LBoFR0p5OsKuMIAXGEupbBi4
BV9Zu5578h5VQzZt32gFnsQcGVsvAn2nayCCbcQx/WUHSW57NmBdktF4l+7tme/p
vL+hzncZ2sdDbhDrrDq2G167QwkAJgpGJFQR+34YpGrY3DDhj767+Q/bMHwm4zmk
VYm+g4MAf49P2CKyqwVB1E9SGe3MgTK2rDRnibhVIlgVEDvih1Uf/E5wigpBJerf
E1DvLXTLEtteko53AjIpT/GWpiiD9md4QpCUUdfGMnU=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.2.0
Release Details
UpdatedSept. 28, 2026, 7:36 a.m.
Changelog

Added

  • Implizite Kopfzahl je Aufgabe (Issue #252, Schritt C aus #249; R11): Der Aufgabendialog nennt unter jeder Zuordnung mit Aufwand im Klartext, wie viele Personen gleichzeitig nötig sind („Erfordert im Schnitt 2,0 × Java-Entwickler gleichzeitig“) — Aufwand ÷ Arbeitstage des Balkens ÷ Tageskapazität einer vollen Kraft, je Zuordnung, live bei Änderung von Aufwand, Start, Ende oder Dauer. Gezählt wird nach dem globalen Kalender (Mo–Fr abzüglich globaler Feiertage) für Rollen und Personen gleich; Personenkalender zählen nicht (Entscheidung in #252, daher entfällt das Akzeptanzkriterium „Teilzeitkalender“). Ab der Warnschwelle (globale App-Einstellung headcount_warn_threshold, Standard 3,0, gültig 0,5–100, im Admin-Bereich unter „Warnen ab“; GET/PUT /settings liefern und setzen headcount_threshold, ein PUT ohne den Wert lässt ihn unverändert, Initial State für das erste Rendern) erscheint dort zusätzlich die Konsequenz („mit weniger Personen dauert die Aufgabe entsprechend länger“), und die Tabelle zeigt vor dem Titel ein Personen-Symbol mit Tooltip — ein Hinweis, keine Blockade. Aufgaben ohne Aufwand oder ohne Arbeitstage und Zuordnungen in anderen Einheiten als PT/h zeigen nichts. Die Rechnung liegt als reine Funktion in src/utils/headcount.js, die Schwelle in WorkTimeService. Nicht Teil: Hinweis am Balken im Diagramm, Prüfung der tatsächlichen Verfügbarkeit (Schritt B, #251). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue Vitest-Tests (headcount.test.js, tableRows.test.js, settings.test.js) und PHPUnit-Tests (WorkTimeServiceTest)
  • Kapazitätsprüfung über Rollen hinweg: Engpass-Teilmenge statt Einzelrolle, Alarm über ein gleitendes 5-Tage-Fenster (Issue #251, Schritt B aus #249; R6, R10): Rollenkapazität ist nicht additiv — bei zwei Java-Entwicklern, von denen einer zusätzlich IT-Architekt ist, besteht „2× Java + 1× Architekt“ die Einzelprüfung (2 ≤ 2, 1 ≤ 1), ist aber nicht zu leisten. Neuer UI- und datenbankfreier Rechenkern: CapacityFlow löst je Fenster ein Flussproblem (Bedarfsknoten = Rollen und direkt zugewiesene Personen im selben Fluss, Kante wo die Person die Rolle ausfüllt, Personenkapazität zur Senke; Dinic in CapacityFlowGraph) und liest die Engpass-Teilmenge aus dem Min-Cut, zerlegt in unabhängige Engpässe samt den Personen mit Mehrfachrolle als Ursache; CapacityWindowCheck legt darüber das gleitende Fenster von 5 Arbeitstagen (fest, nicht konfigurierbar, Feiertage verkürzen es nicht): rot = irgendein Fenster fließt nicht (nicht durch Verschieben lösbar), gelb = ein einzelner Tag fließt nicht, aber alle Fenster um ihn (Tagesspitze, umsortierbar); Tage, an denen niemand des Knotens arbeitet, zählen nicht als Spitze (Teilzeit Mo–Do erzeugt keine Meldung); überlappende Verstöße mit gemeinsamen Rollen werden zu einem Zeitraum zusammengefasst. CapacityCheckService trägt Aufgaben der für den Betrachter sichtbaren Projekte (Lücke bewusst akzeptiert, wie in #181), Rollen und Personenkapazität zusammen — Bedarf auf dem globalen Kalender, Kapazität aus den Personenkalendern (ResourceUtilizationService::personCapacityFn() ist dafür öffentlich), Material ausgeschlossen, Rolle ohne Personen meldet rot — und liefert GET /api/v1/capacity-check (Rollen, direkte Personen, Zeitraum, Fehlbetrag in Stunden, Ursache, betroffene Aufgaben). Die Tabelle zeigt vor dem Titel eine kleine Person in Gelb oder Rot mit Tooltip („Java-Entwickler + IT-Architekt zusammen überbucht (KW 42). Fehlbetrag: 5 PT. Ursache: Mehrfachrollen von …“), keine Benachrichtigungen; der Store capacityCheck lädt bei Änderungen an Aufgaben, Rollen, Kapazität oder Feiertagen neu. Ressourcen-Raster und Workload-Chart färben nach demselben Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder über 90 % im Fenster, Tooltip mit Reserve), die Rollenzeile übernimmt das Urteil der Prüfung. Nicht Teil: Sequenzierungsvorschlag (#282), Kostenminimierung der Zuordnung (entfällt), Projektkalender (#284). Bekannte Grenzen (in FEATURES.md): nur sichtbare Projekte, Aufgaben kürzer als das Fenster werden verdünnt (fängt die implizite Kopfzahl aus #252 auf). Neue Texte in allen Übersetzungen; FEATURES.md/HOWTO.md (de/en) und die Integrationsmatrix sind nachgezogen, der Hinweis auf die naive Rechnung aus #250 ist entfernt. Neue PHPUnit-Tests (CapacityFlowTest, CapacityWindowCheckTest, CapacityCheckServiceTest; u. a. Prüffall, Kette über drei Rollen, Bedarf exakt auf Kapazität, Rolle ohne Personen, Laufzeit 30 Personen/10 Rollen/1 Jahr), Vitest-Tests (capacityCheck.test.js, workloadModel.test.js, tableRows.test.js) und der E2E-Test capacity-check.spec.js

  • Projekt duplizieren bzw. als Vorlage für ein neues Projekt verwenden (Issue #270): Die Aktion Duplizieren im Menü des Projekts in der Navigation (auch mit reinem Leserecht) und im Projektdialog legt ein neues Projekt der ausführenden Person an. Der Dialog fragt Titel („Kopie von …“), neuen Starttermin (Vorschlag: bisheriger Projektstart = frühester Aufgabenbeginn) und die Ressourcenzuordnungen (alle / nur Rollen / keine). Alle Termine — Aufgaben, Meilensteine, Planungszeiträume von Projekt und Teilprojekten — verschieben sich um die Differenz in Arbeitstagen des globalen Kalenders, ein Start auf einem freien Tag rückt auf den nächsten Arbeitstag, bei unverändertem Start bleiben die Termine unberührt. Übernommen werden Teilprojekte samt Hierarchie, Aufgaben, Abhängigkeiten mit Typ und Abstand, Meilensteine mit Zuordnung und Abhängigkeitstyp (abgeleitete folgen dem kopierten Termin), Zuordnungen samt Aufwand (nicht sichtbare Ressourcen entfallen, der Aufwand wird bei weggelassenen Zuordnungen neu gebildet), Farben, Beschreibungen und Projektregeln; zurückgesetzt werden Fortschritt (Modus „Deck“ wird „manuell“), Status, Ist-Kosten, Baselines, Änderungsprotokoll, Freigaben, ICS-Token, Deck-Board, Deck-Listen und Kartenverknüpfungen (Deck-Abgleich „Aus“). Neuer ProjectDuplicationService legt alles in einer Transaktion über die Mapper an (kein Änderungsprotokoll, keine Benachrichtigungen, kein Deck-Zugriff); GET /projects/{id}/duplicate liefert Start und Umfang für den Dialog, POST /projects/{id}/duplicate (title, start_date, resources) die Kopie (201). Nicht Teil: eigene Vorlagenverwaltung (Projekte als „Vorlage“ markieren, Galerie beim Anlegen). Neue Texte in allen Übersetzungen; FEATURES.md und HOWTO.md (de/en) sind nachgezogen. Neue PHPUnit-Tests (ProjectDuplicationServiceTest), Vitest-Tests (duplicate.test.js, ioDialogs.duplicate.test.js) und der E2E-Test project-duplicate.spec.js

Changed

  • Rollenzeile im Ressourcen-Raster ohne „naiv additiv“ (#251): GET /resources/{id}/utilization liefert für Rollen nur noch capacity_derived; capacity_naive und multi_role_member_ids entfallen, ebenso der Hinweis „zu optimistisch“ im Raster (ersetzt durch den Verweis auf die Prüfung über alle Rollen). Das Workload-Chart unter dem Diagramm färbt Perioden nicht mehr je Periode rot, sondern nach dem 5-Tage-Fenster (rot = Fenster überbucht, gelb = Tagesspitze oder knapp)

Fixed

  • Server-Zeitzone der Arbeitszeiten aus default_timezone statt immer UTC (Issue #294): Nextcloud setzt die PHP-Zeitzone in lib/base.php fest auf UTC, deshalb zeigte der Admin-Bereich unter „Arbeitszeiten“ immer „UTC (+00:00)“ und Arbeitsbeginn/-ende wurden als UTC ausgelegt — neue Termine im Detaildialog/Import und die Deck-Fälligkeiten lagen dadurch 1–2 Stunden daneben. Neuer ServerTimeZoneService liest default_timezone aus der config.php (fehlt der Wert oder ist er ungültig, bleibt es bei UTC); GET/PUT /settings liefern dessen Namen und aktuellen Versatz, der Deck-Terminabgleich legt work_start/work_end in dieser Zeitzone aus. Neue PHPUnit-Tests (ServerTimeZoneServiceTest, DeckDateSyncServiceTest)
  • Schrift im Balken passte nicht zur Balkenfarbe (Issue #287): .bar-label stand in theme.css fest auf Weiß, unabhängig von Balkenfarbe und Theme — auf hellen und mittleren Farben, darunter die Standardfarbe #0082c9 (Kontrast 4,2:1), war der Titel im Balken im hellen Theme schwer lesbar, während der Titel außerhalb des Balkens (.big) bereits die Textfarbe des Themes bekam. injectProjectColors() setzt jetzt je Farbklasse die Schriftfarbe, die nach WCAG den höheren Kontrast liefert (labelTextColor() in colors.js: Weiß oder #111111). Die Balkenfarbe ist in beiden Themes dieselbe, die Schrift deshalb ebenfalls. Bei Projekt- und Teilprojektzeilen (Summenbalken, nur eine auf 18 % abgeschwächte Klammerfläche darunter) war die vorgesehene Textfarbe des Themes wirkungslos: .gantt-summary-bar .bar-label und .gantt .bar-label hatten gleiche Spezifität und beide !important, die spätere gewann — der Titel blieb im hellen Theme weiß. Die Regel ist jetzt spezifischer (.gantt .bar-wrapper.gantt-summary-bar .bar-label); Meilenstein und Titel neben dem Balken (.big) behalten ihre Regeln. Neue Vitest-Tests in colors.test.js
  • JSON-Export ohne Datenbank-IDs der Abhängigkeiten (Issue #289): Je Abhängigkeit enthält der Export nur noch predecessor_id, dependency_type und lag_days; id und task_id (Datenbank-IDs des Quellsystems, im Zielsystem bedeutungslos) entfallen. Der Import bleibt kompatibel, ältere Exporte mit diesen Feldern werden weiter gelesen. Der E2E-Test zum Round-Trip aus #269 war seit seiner Einführung rot (erwartete exakt drei Felder je Abhängigkeit und übersah den Import-Optionsdialog) und ist korrigiert.

Security

  • Benutzersuche beachtet die Enumeration-Einstellungen von Nextcloud und leakt keine E-Mail-Adressen mehr (Issue #285): GET /users/search rief IUserManager::search() direkt auf und lieferte jedem angemeldeten Nutzer zu jedem Treffer UID, Anzeigenamen und E-Mail-Adresse — mit kurzen Suchbegriffen ließen sich so alle Konten der Instanz auslesen, auch wenn die Admins das Auflisten abgeschaltet oder auf Gruppen/Telefonbuch beschränkt hatten. Die Suche läuft jetzt über die Collaborators-API (ISearch::search() mit IShare::TYPE_USER, ohne Lookup-Server) wie der Teilen-Dialog im Kern und beachtet damit shareapi_allow_share_dialog_user_enumeration, shareapi_restrict_user_enumeration_to_group/_to_phone/_full_match* und shareapi_only_share_with_group_members; exakte Treffer stehen vorn. subname stammt ausschließlich aus deren Ergebnis, die App lädt keine E-Mail-Adresse mehr selbst nach. Das eigene Konto (nur mit include_self, #204) ergänzt der Controller selbst, wenn Benutzer-ID, Anzeigename oder E-Mail den Suchbegriff enthalten, weil die Collaborators-API es nie liefert. Neue PHPUnit-Tests in UserControllerTest (u. a. keine Fremdkonten bei abgeschalteter Enumeration)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureesXfyynRSXx6Sc+EbEb+n8jhv9KklmBcayRslKKo0dpavjZ1x7CE/oUKrOppOVm7
YsCst1sFoTz/sPZ4qveDz/erE0STxAi2UMyiHHUglJ3JmcCJKtF5byki45BAm02V
PV7dn+EYQP8bd4jWy96mQa66eGzuuAsgR/mA7S1UC9SvcKq6UF6A0LJpJqMyInaR
ycEg6H78WtrIOKAuB5WyaEo20IRsXIechdfyl39Xz1bl1IextE3DPey0OaV6CzfF
6YQeMho+ja2iqh6JKfAzq2zKgTDBLo/bcgk8Gy/b4djBX5KES2w96x535UG7a6JM
fYEg7QdRUqOGgXoCTRSwIAGtqX8bq9sc7058+5NqO9xYOiGDzfv6dJ94x6+KXpd4
eN1N1P0+MfL9TKP27NQLsy46vXPU36w4cSijoMmxyrYGUbVVudrb8XANUvv/jZc+
d/yNvPzhYRDHvoGJ3gn5exgVpQW2M7qQMOBVDYDkROo1UqKv1k3da9s14VMHtX6y
PyhIdtt6Qi7Lu0cMykFH0rYQYoe84Ot3Xb547ldyNCCDAeta4vTczDbA5dTrdstM
HCrbbSl4iF/Lu7zeVhlxnWnQyuC5HjUg0vVuikxkEyYScruNJw4prkXD04XHOMyG
1HOBUrG2tiFl60B8XGyibo5+fc1ioQFwe2iIjdLOW3M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.1.0
Release Details
UpdatedSept. 25, 2026, 6:24 a.m.
Changelog

Changed

  • Detaildialog in abgesetzte Bereiche gegliedert (Issue #263): Die Formulare für Projekt, Teilprojekt, Aufgabe und Meilenstein fassen ihre Felder jetzt in Bereiche mit Rahmen, farbig hinterlegtem Kopf, Symbol und Überschrift zusammen (z. B. Grundinformationen, Darstellung, Terminplanung, Budget und Kosten, Ressourcen, Risikostufe, Fortschritt und Status, Deck, Erinnerungen, Baselines, Export und Import). Die Bereiche heben sich vor einem dezent getönten Dialoghintergrund ab, der Dialogkopf zeigt ein Symbol der Objektart, Feldbeschriftungen stehen in normaler statt Versal-Schrift, und die Risikostufen tragen ihre Ampelfarbe auch ungewählt. Die Farbtöne sind aus den Nextcloud-Farbvariablen gemischt und funktionieren im hellen wie im dunklen Design. Die Trennlinien zwischen den einzelnen Feldern aus dem ersten Anlauf entfallen dafür. Symbole kommen aus @mdi/js, das jetzt direkte Abhängigkeit ist
  • Neue App-Store-Screenshots zur überarbeiteten Oberfläche: Die zehn verlinkten Bilder in appinfo/info.xml sind neu aufgenommen (Projektstruktur, Abhängigkeiten im gegliederten Detaildialog, Tabelle, kritischer Pfad, Ressourcen, Kalender und Feiertage, Abweichungsbericht, Kennzahlen/EVM, Simulation, Mehrprojektansicht mit Filter). Sie liegen jetzt unter screenshots/appstore/ statt screenshots/usecases/, damit der App Store nicht auf zwischengespeicherte alte Bilder unter den bisherigen URLs zurückgreift; die übrigen, veralteten Bilder aus screenshots/ sind entfernt. Das Showcase-Skript scrollt dafür in UC 2 den Abschnitt „Abhängigkeiten“ des Dialogs ins Bild und in UC 5 das Ressourcenraster ganz nach links
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureZQ2bK6Wi8ifFLdOZU9fKEitqGU2/V3SC8sQadWpViyGbPp9TFzXz/m+6VOgxRM0K
88XrfFLwkUN5CrQ8Mzj8rzUUKAp0ANWZVMC43yWzABpywSQ+FosMPTPoPyS7dymn
eKXUnbqfSE3yebLbOtpQaK9VIMVXfvIpMQuIaSnnSX/Mi3ofY4oN3eyBy9NT1uH+
icAEBU4EFZbByJe3tF+IdKP81OX0kFydqeuiSgWJph7yRxP+ucBioO+BsjGB1ZGh
2kiCYjlYoZPbgc4WSoWfe3WGY/cUiqr/6sPEkdWfYHrG6c/tUuNhM1E1pmBXlwIK
S3IzZVklRWFkFcTHdYLAg5QKWTN2+m0sCpPkMkZQn2NYXQNQ5oAPKNoDTMBmW+zG
B72pYfEAs5TQFGcn1M6k6e4IdVjnLtM6AdmyeMdu9y8hKUH8en1RT/MVbXZlFN7O
fZoXKZSwpdocdqMZOqaT3qoUTFUZbmOBzcQQ3xeYQ4H2UvIp4PlyPkXeiFEHKY93
hZWcmtTHthlbPGhtnAutXA7zCzruiXisJXCNaf7ZM+xSbtW3GeLqRMwkLImyxydK
1C+BekGHiJaWI32PyqZH1mwR+V1qgqLLcD3yb+gKOBOUSBW4pdMnNpmvnO7y8KBg
jw8MQf3e5b0JVVoksbULqjqvCOm2ug60NIZYCfp59BA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.1
Release Details
UpdatedSept. 24, 2026, 7:49 a.m.
Changelog

Changed

  • App-Store-Beschreibung und README neu gefasst: Zusammenfassung und Beschreibung (info.xml, Deutsch und Englisch) stellen jetzt das professionelle Projektmanagement mit der Deck-Integration als Alleinstellungsmerkmal in den Vordergrund, die Funktionsliste ist kompakter, und ein eigener Abschnitt erklärt die Datensouveränität (alle Daten bleiben auf der eigenen Instanz). Das englische README und docs/de/README.md übernehmen den Wortlaut. In den übrigen Sprachen entfällt an zwei Emojis der Variation Selector
  • Toolbar-Schalter einheitlich: Die segmentierten Schalter der Toolbars (Zeitskala und Darstellung im Diagramm, Zeitskala im Ressourcenraster) und der Ansichts-Umschalter der App teilen sich jetzt dieselbe Optik: eckige Segmente ohne eigenen Rand und ohne eigene Rundung, sodass Hover- und Aktiv-Flächen bis zur Kante reichen und kein Randstreifen mehr stehen bleibt; die Rundung kommt allein vom Container. Die gemeinsame Regel liegt in der neuen segmented-switch.css statt in mehreren nahezu identischen Blöcken

Fixed

  • Leerzustand im Diagramm nicht mittig: Der Leerzustand des Diagramms (GanttChart.vue) steht jetzt mittig, und sein Icon ist optisch zentriert (der viewBox war seitlich versetzt)
  • Stylelint-Fehler in ResourceGrid.vue: fehlende Leerzeile vor einer Regel (rule-empty-line-before), die den JS-Job der CI rot färbte
  • JSON-Import brach bei Abhängigkeiten ab, Meilensteine fehlten im Export (Issue #269): importJson() übergab predecessor_id-Werte unverändert an store.createTask() – die alten IDs des Quellprojekts bestanden den IDOR-Schutz aus #190 nicht (Import in ein anderes Projekt/eine andere Instanz brach mit invalid_dependency ab und hinterließ ein halb angelegtes Projekt) bzw. zeigten beim Import ins selbe Projekt auf die falschen (alten) Aufgaben. Aufgaben werden jetzt zunächst ohne Abhängigkeiten angelegt und diese erst danach mit auf die importierten Aufgaben umgemünzten predecessor_id-Werten nachgetragen – unabhängig davon, in welcher Reihenfolge Vorgänger und Nachfolger in der Datei stehen. exportJson() nimmt außerdem Meilensteine samt ihrer Zuordnung zu Teilprojekt/Aufgabe und ihres dependency_type mit; der Import schreibt diese Bezüge ebenfalls auf die neuen IDs um
  • Balken mit Abhängigkeiten ziehen schrieb doppelt nach Deck (Issue #267): frappe-gantt zieht beim Verschieben/Resizen eines Balkens dessen Abhängigkeits-Balken in der Vorschau um denselben Pixel-Versatz mit und feuerte on_date_change für jeden von ihnen einzeln – der Server berechnet die Kette beim Speichern des tatsächlich gezogenen Balkens aber bereits selbst (propagated). Der zweite, nur aus der Vorschau abgeleitete Save überschrieb die korrekt propagierten Termine mit dem naiven Pixel-Versatz und erzeugte bei aktiver Deck-Anbindung eine doppelte Rückfrage bzw. Erfolgsmeldung. GanttChart.vue verarbeitet on_date_change jetzt nur noch für den Balken, auf dem die Ziehbewegung tatsächlich begann
  • Picker für eine zweite Deck-Karte konnte dauerhaft deaktiviert bleiben (Issue #267): Der Deck-Bereich im Aufgabendialog rendert einmal sofort (Ladezustand) und ein zweites Mal, sobald die Verknüpfungen geladen sind – traf eine Auswahl im Picker genau in dieses Fenster, ersetzte der zweite Render Select und „Add“-Button durch neue, wieder deaktivierte Knoten und die Auswahl ging verloren. Der Bereich rendert jetzt erst, nachdem die Verknüpfungen geladen sind
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureblE0iU/iUXgQIlpljnjDbdufoMyB2l1S9iGos0PQ3DDhQU9wFB13iopDY4YfCjrW
Y2bVw//uxXAO1S4wzURGPTAEgXGI3NM4A2JaHsxMREiJoU6ANjCSOnt45f9yvgNC
se02XpRKzbTtIUXTIGsH2KUe3grZ7hHg95x4riQhhrLhsDESZMZANpO60O52/AMM
gb4yZiCTbUUr1IzM/IowbHWJcghd6RG8KG+EwONJpRqJVjZhcqhNgcM9k/nKyCxY
tw6o7MIlwQmOUzh6kZXxl8p4auRpiHcGrruiWzJwEb6WSmH/XFTdGwP/ZV8lNnoj
KrR2e+yaKiPMR7UQLrtE8yUe1LGmghJejF/cDO3uLEOGD87gZfHjqXAJOrHI+zyx
FcT65clu8MUG2Tk1PQammIkYty0sPaHKwB/woLOlEBWX8LqrAK9ylOD7NHTdArhJ
TibyfWC9UI1f/iyBYu0xCEq11T23DtrFcpun7ELMNUiQ2z260/PykzEK6WtEhRVd
hgLyzf3YoFSqRNOM5IfewWlGt9YdyM4J3bLIYP01GY4MFV90y9M7/RcaehOG3U/5
DFnQswUXRr9ivk4S7dFt7J0WqltzLDqbIwtXi5f6TEqahrYD1Y46W4JXArdhOK+P
FE4VR6KQrLzKQ9PQhTbrfqCLj0tewWnxc8g6OgIGGk4=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 2.0.0
Release Details
UpdatedSept. 23, 2026, 9 a.m.
Changelog

Major-Release mit zwei Schwerpunkten. Die Einzelheiten stehen in den Einträgen darunter.

  • Komplett überarbeitete Deck-Schnittstelle (Zielbild #234, Issues #209–#211, #235–#241, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit geworden. Pro Projekt gibt es eine Stufe für den Deck-Abgleich: Aus (Standard), Nur lesen, Schreiben mit Rückfrage oder Schreiben ohne Rückfrage. Eine Aufgabe kann mehrere Karten haben, und Karten lassen sich direkt aus der Aufgabe anlegen. Termine werden mit Konfliktregel nach Deck geschrieben. Fortschritt und Status kommen sofort aus Deck. Abweichungen zwischen Plan und Deck erscheinen im Überblick, lassen sich annehmen und filtern. Beim Löschen können verknüpfte Karten archiviert werden, doppelte Erinnerungen werden unterdrückt, und ein occ-Befehl gleicht einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich am Verhalten nichts; Bestandsprojekte starten höchstens auf Nur lesen.
  • Neuer Umgang mit Planungsdaten für Projekte und Teilprojekte (Issue #229): Bisher ergaben sich Start und Ende von Projekten und Teilprojekten ausschließlich aus ihren Aufgaben. Jetzt lässt sich zusätzlich ein eigener Planungszeitraum setzen, im Detaildialog oder direkt in der Tabelle. Gezeigt wird er statt des berechneten Zeitraums, auch bei noch leeren Teilprojekten. Laufen die Aufgaben über den Rahmen hinaus, markiert ihn ein rotes Ausrufezeichen. Ampel, kritischer Pfad, Filter und Rollups rechnen weiter mit dem tatsächlich verplanten Inhalt. Ohne gesetzten Planungszeitraum bleibt alles wie bisher.
  • Warum eine neue Hauptversion: Die API-Felder haben sich teilweise geändert. deck_card_id an Aufgaben ist nur noch ein berechnetes Lesefeld, Karten werden über /tasks/{id}/deck-links verknüpft (siehe „Changed“). Löschen über DELETE liefert jetzt einen Körper mit deck. Beim Update laufen die Migrationen Version1039–Version1043 automatisch; vorhandene Kartenverknüpfungen werden übernommen.

Added

  • Detail-Dialog per Klick aus der Tabelle öffnen (Issue #264): Bislang öffnete der Detail-Dialog nur über einen Klick auf den Balken im Gantt-Diagramm, was bei größeren Projekten erst Scrollen zum richtigen Monat verlangte. Die Titel-Spalte der Tabelle hat jetzt einen geteilten Klickbereich: Bei Aufgaben und Meilensteinen öffnet ein Klick auf den Platz vor dem Bezeichnungs-Rahmen (Zusammenklapp-Pfeil, Farbpunkt, Abweichungssymbol/-zähler) den Detail-Dialog, während der Rahmen selbst wie bisher die Inline-Bearbeitung der Bezeichnung öffnet. Bei Projekten und Teilprojekten öffnet der gesamte bisherige Rahmen den Detail-Dialog; die Inline-Bearbeitung der Bezeichnung entfällt dort, da beide Objekttypen ohnehin nur das Titelfeld inline editierbar hatten. Der Rahmen ist jetzt als sichtbarer Rand um die Bezeichnung dargestellt (GanttTable.vue)
  • Deck-Anbindung abgeschlossen (Stufen 1–3 aus #234, Issues #209–#211, #235–#240, #244): Aus der reinen Verknüpfung mit Fortschrittsübernahme ist eine vollständige, optionale Zusammenarbeit mit Deck geworden. Je Projekt lässt sich eine von vier Stufen wählen — Aus, Nur lesen (Fortschritt und Status kommen sofort aus Deck), Schreiben mit Rückfrage und Schreiben ohne Rückfrage (beide erlauben zusätzlich, Karten aus einer Aufgabe anzulegen und ihre Termine sowie einmalig die Zuständigen nachzuführen). Wo Deck und Plan auseinanderlaufen — Karte gelöscht/archiviert, Termin oder Zuständigkeit von Hand geändert, Karte überfällig, ziehende Person ohne die geforderte Rolle —, zeigt die App das an, statt es aufzulösen; angenommen wird je Aufgabe. Doppelte Erinnerungen zwischen Gantt und Deck werden unterdrückt, ein occ-Befehl gleicht bei Bedarf alles einmal vollständig ab. Ohne Deck oder ohne Opt-in ändert sich nichts am bisherigen Verhalten. Die folgenden Einträge beschreiben die einzelnen Schritte im Detail; Endstand siehe FEATURES.md Kapitel 16 und die Integrationsmatrix, HOWTO.md und README („Optionale Abhängigkeit zu Deck“)
  • Deck-Fundament: Adapter, Verknüpfungstabelle (1:n) und Opt-in-Stufen je Projekt (Issue #209, Stufe 1, Schritte 1–2 aus #234): Die Migration Version1039 legt nc_gantt_deck_links an (eine Zeile je Karte mit Aufgabe, Board, zuletzt gesehenem Kartenstand, zuletzt geschriebenen Terminen, angenommenem Stand je Abweichungsart und Zeitpunkt des Abgleichs; eindeutiger Index auf der Karten-ID) und übernimmt vorhandene deck_card_id-Werte. Die Spalte deck_card_id bleibt lesbar, ist veraltet und wird bis zum Rückbau mit #237 aus der Tabelle befüllt. Das Löschen einer Aufgabe entfernt ihre Verknüpfungen, die Karte bleibt. Neue API: GET/POST /tasks/{id}/deck-links, DELETE /tasks/{id}/deck-links/{cardId}, GET /deck-links/occupied?project_id=|board_id= (belegte Karten → Aufgabe)
  • DeckGateway als einzige Andockstelle zu Deck (Deck-Services erst im Aufruf aus dem Container, isAvailable(), supportsStartDate(), readCard(), permissions(), Ergebnisobjekt DeckResult statt Exception, nur mit angemeldetem Benutzer, Schreibmethoden nur in den beiden Schreibstufen — ihre Umsetzung folgt mit #210, #211, #237, #238)
  • Opt-in-Stufe „Deck-Abgleich“ je Projekt mit vier Stufen (Aus — Standard, Nur lesen, Schreiben mit Rückfrage, Schreiben ohne Rückfrage); serverseitig beantwortet DeckSyncService getrennt „darf lesen“, „darf schreiben“, „muss fragen“. Projektdialog mit Vorschlag „Nur lesen“ beim Verknüpfen eines Boards, Hinweis beim Umschalten auf eine Schreibstufe und ausgegraut mit sichtbarem Hinweistext ohne Deck. Bestandsprojekte mit Board und Aufgabe im Fortschrittsmodus „aus Deck“ starten auf Nur lesen, alle übrigen auf Aus; nie auf einer Schreibstufe. Der clientseitige Abgleich aus #221 prüft die Stufe
  • Abweichungsdienst (DeckDeviationService, DeckDeviationProvider) mit einheitlichem „Annehmen“ (GET /tasks/{id}/deck-deviations, POST /tasks/{id}/deck-deviations/accept) und Bereich „Abweichungen zu Deck“ im Aufgabendialog; die Abweichungsarten liefern die Folgetickets
  • Vertragstest gegen Deck (make test-deck-contract, tests/deck-contract/, CI-Job Deck-Vertragstest) und README-Abschnitt „Optionale Abhängigkeit zu Deck“; FEATURES.md (de/en) um Stufen und mehrere Karten ergänzt
  • Deck im E2E-Setup optional: setup-dev.sh --no-deck, tests/e2e/helpers/deck.js (Tests ohne Deck überspringen sich selbst), tests/e2e/specs/deck-foundation.spec.js
  • Deck-Rückmeldung per Event-Listener: Fortschritt und Status sofort übernehmen (Issue #235, Stufe 1, Schritt 3 aus #234): DeckCardListener ist für CardCreatedEvent, CardUpdatedEvent und CardDeletedEvent per Klassenname registriert und wertet nur getWebhookSerializable()['card'] aus; er wirft nie. DeckCardSyncService aktualisiert ab der Stufe Nur lesen den zuletzt gesehenen Kartenstand an der Verknüpfung (erledigt, archiviert, gelöscht, Fälligkeit, Start, Zuweisungen, Checkliste) und berechnet für Aufgaben im Modus „aus Deck“ Fortschritt (Mittelwert je Karte: erledigt = 100 %, sonst Checklistenanteil; archiviert zählt als erledigt, gelöscht zählt nicht mit; ohne mitzählende Karte bleibt alles stehen) und Status (done / in_progress / not_started, blocked bleibt). Die Formel DeckProgress in PHP spiegelt cardProgress(); beide prüfen tests/fixtures/deck-progress-cases.json. Neue Abweichungsarten „Karte archiviert“ / „Karte gelöscht“ über den Abweichungsdienst; Übernahmen stehen mit Operations-ID deck:… im Änderungsprotokoll. Der clientseitige Abgleich beim Öffnen bleibt als Fallback und setzt den Status nach denselben Regeln (PUT /tasks/{id}/progress/sync akzeptiert done). Der Aufgabendialog weist im Modus „aus Deck“ neben dem Statusfeld darauf hin, dass der Status Deck folgt. Vertragstest, README-Abschnitt und FEATURES.md (de/en) sind nachgezogen
  • occ nc_gantt:deck:sync --user=<uid> [--project=<id>] [--dry-run]: einmaliger Vollabgleich aus Deck (Issue #236, Stufe 1, Schritt 4 aus #234): Der Befehl setzt den Kontext des Kontos per setVolatileActiveUser, liest jede verknüpfte Karte, die das Konto in Deck sehen darf, und übernimmt sie über dieselbe Logik wie der Listener aus #235 (DeckCardSyncService::applyStates(), gemeinsamer Weg statt zweiter Implementierung). Nur Projekte ab Nur lesen; nach Deck wird nie geschrieben. Ausgabe je Projekt: geprüfte und geänderte Aufgaben, übersprungene Karten (keine Berechtigung / nicht gefunden); Rückgabewert ≠ 0 nur bei Fehlern, auch wenn Deck fehlt. --dry-run speichert nichts. Registriert in appinfo/info.xml (<commands>); FEATURES.md (de/en) und README ergänzt
  • Mehrere Deck-Karten je Aufgabe: Picker, „+“ zum Anlegen und Rolle der Deck-Listen je Projekt (Issue #237, Stufe 2, Schritt 5 aus #234; UC1 „Übergabe“, O1, O2, O8, O14): Der Aufgabendialog hat den Bereich „Deck-Karten“ (für bestehende Aufgaben) mit der Liste der verknüpften Karten (Titel, Liste, erledigt, „In Deck öffnen“, „Lösen“ — das Lösen ändert die Karte nie), dem Picker (Karten an einer anderen Aufgabe sind gesperrt und nennen sie) und „+“, das über den Server und DeckGateway::createCard() eine Karte mit Titel und Beschreibung der Aufgabe samt Rücklink „Geplant in Gantt-Projekte: <URL>“ (Deeplink ?project=&task=), Fälligkeit = Ende, ab Deck 1.18 Start = Start anlegt; „+“ gibt es erst ab „Schreiben mit Rückfrage“ und mit Bearbeitungsrecht am Board (sonst gesperrt mit Hinweis auf Grund und Stufe) und braucht keine zweite Rückfrage. DeckGateway::setDates() ist umgesetzt (Fälligkeit, ab Deck 1.18 Start; Ergebnisobjekt statt Exception; nur in einer Schreibstufe; prüft nach dem Schreiben, ob Deck die Termine übernommen hat) — #210 baut darauf auf. Beim Verknüpfen einer Karte mit abweichendem Datum fragt der Dialog sofort: Plan in Deck durchsetzen (nur in einer Schreibstufe, selbst die Bestätigung), Plan anpassen oder Abweichung stehen lassen; eine datumslose Karte bekommt die Planlage ohne Rückfrage, aber nur ab einer Schreibstufe. Der Stand wird als „zuletzt gesehen“ bzw. „zuletzt von Gantt geschrieben“ an der Verknüpfung gespeichert. Projekteinstellung „Rolle der Deck-Listen“ (deck_list_role: subproject — Standard, storage, none) mit „?“-Hilfe; das Listenfeld am Teilprojekt entfällt bei none, gespeicherte Listen-IDs bleiben; ein Wechsel ändert keine Verknüpfung. Ohne Liste am Teilprojekt nutzt „+“ die Liste des nächsten übergeordneten Teilprojekts, sonst die erste Liste des Boards. Neue API: POST /tasks/{id}/deck-cards, POST /tasks/{id}/deck-links mit optionalem date_action. Der Fortschritt „aus Deck“ im Dialog bildet bei mehreren Karten den Mittelwert. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kapitel 16 und Integrationsmatrix), HOWTO.md und README (Deck-Vertrag) sind nachgezogen; Vertragstest um CardService::create()/update(), StackMapper::findAll() und den Startparameter ergänzt

  • Termine der Aufgabe in die verknüpften Deck-Karten schreiben, mit Konfliktregel und gebündelter Rückfrage (Issue #210, Stufe 2, Schritt 6 aus #234; UC3 „Umplanung“, O4, O8, O9, O11, O12): Ab einer Schreibstufe folgen alle verknüpften Karten den Terminen der Aufgabe — Fälligkeit = Ende zum Arbeitstagsende (Einstellung „Arbeitszeit“), ab Deck 1.18 zusätzlich Start — bei jedem Weg der Terminänderung (Dialog, Tabelle, Ziehen im Diagramm, Propagation über Abhängigkeiten inklusive Karten fremder Aufgaben, „Termine neu berechnen“, Undo). Der neue DeckDateSyncService schreibt synchron im Request nach dem Speichern über DeckGateway::setDates(), fehlertolerant (Deck-Fehler brechen das Speichern nie ab); das Frontend zeigt danach „3 Deck-Karten aktualisiert“, aufklappbar mit Liste, auch fehlgeschlagene Karten. Konfliktregel: Geschrieben wird nur, wenn die Karte noch den zuletzt von Gantt geschriebenen Wert trägt (ohne geschriebenen Wert: wenn sie kein Datum trägt); sonst bleibt sie unangetastet und der Abweichungsdienst zeigt „Fälligkeit/Start in Deck abweichend“ mit Deck- und Plan-Datum. Aktionen im Aufgabendialog: Plan in Deck durchsetzen (POST /tasks/{id}/deck-deviations/enforce, ohne Rückfrage), Plan anpassen (Deck-Datum wird Ende der Aufgabe, gespeichert mit dem Dialog) und Annehmen. Rückfrage in „Schreiben mit Rückfrage“: POST /projects/{id}/deck-preview rechnet die Propagation ohne Speichern; ein Dialog listet einmal gebündelt alle betroffenen Karten mit altem und neuem Wert, Karten fremder Aufgaben hervorgehoben, „Abbrechen“ verwirft die gesamte Änderung, kein „Nicht mehr fragen“; ohne Bestätigung (deck_confirmed) wird nicht geschrieben. Bei „Schreiben ohne Rückfrage“ entfällt der Dialog, bei „Aus“ und „Nur lesen“ wird nichts geschrieben. Rechte: Ohne Bearbeitungsrecht am Board wird die Karte als „Deck-Abgleich ausstehend (keine Berechtigung)“ markiert und beim nächsten Speichern durch eine Person mit Rechten mitgeschrieben (Migration Version1041, Spalte sync_pending); eine in Deck gelöschte oder nicht auffindbare Karte bleibt verknüpft und erscheint als Abweichung „Karte gelöscht“. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kapitel 16) und HOWTO.md sind nachgezogen, der Hinweis „Terminverschiebungen im Gantt ändern derzeit keine Deck-Karten“ ist ersetzt. Neue PHPUnit-Tests (DeckDateSyncServiceTest, TaskDeckSyncTest, DeckDateDeviationProviderTest, DeckDeviationActionServiceTest), Vitest-Tests (deckWrite.test.js, deckWriteDialogs.test.js, deckDeviations.test.js, projects.test.js) und der E2E-Test deck-date-sync.spec.js

  • Verknüpfte Deck-Karten beim Löschen auf Rückfrage archivieren (Issue #238, Stufe 2, Schritt 7 aus #234; O10, O11, O12): Der Löschdialog einer Aufgabe mit Karten fragt „Verknüpfte Deck-Karte(n) ebenfalls archivieren?“ mit der Kartenzahl, Standard „Nein“; Teilprojekt und Projekt stellen eine Sammelrückfrage mit der Gesamtzahl. Gefragt wird nur in den beiden Schreibstufen und nur für Karten an Boards mit Bearbeitungsrecht (DeckGateway::permissions()); für die übrigen sagt der Dialog „N Karten bleiben unverändert“, der Löschdialog ist die Bestätigung (keine zweite Rückfrage). DeckGateway::archive() ist umgesetzt (CardService::archive(), Erfolg erst nach erneutem Lesen); DeckDeletionService plant vor dem Löschen (die Kaskade entfernt die Verknüpfungen) und archiviert synchron danach, fehlertolerant: scheitert Deck, ist die Aufgabe trotzdem gelöscht und die Meldung nennt die nicht archivierten Karten, sonst die archivierten mit Link. Neu: GET /tasks|subprojects|projects/{id}/deck-delete-preview; DELETE nimmt archive_deck_cards an und liefert jetzt deck (archived, failed) statt eines leeren Körpers. Verknüpfung lösen ändert die Karte nie; ein Undo für das Löschen gibt es nicht. PHPUnit mit gemocktem Gateway, Vitest, E2E deck-archive-on-delete.spec.js; Vertragstest um CardService::archive(id) ergänzt; FEATURES.md (de/en) und README ergänzt
  • Rollen als Ressourcentyp: Aufgaben fordern Rollen, Personen tragen die Kapazität (Issue #250, Schritt A aus #249; R2–R5): nc_gantt_resources.type (person — Standard für alle bestehenden Zeilen —, role, material) und die n:m-Tabelle nc_gantt_resource_role (Person ↔ Rolle, eindeutige Kombination; Migration Version1042, dazu nc_gantt_projects.roles_only_assignment). Eine Rolle ist Zuweisungsziel ohne Konto, Arbeitszeitmodell, Kalender und eigene Kapazität (Kapazitätswerte an Rollen werden nicht angelegt); TaskResource und ResourceGroup bleiben unverändert, Rollen sind eine zur Gruppe orthogonale Achse. Rollen werden in der Ressourcenverwaltung über „Neue Rolle“ angelegt, umbenannt und gelöscht und getrennt in der Navigation geführt; die Zuordnung Person ↔ Rolle ist von beiden Seiten pflegbar (role_ids an der Person, person_ids an der Rolle, ohne Admin-Rechte). Löschen einer Rolle mit Aufgaben fragt nach und nennt sie (GET /resources/{id}/usage), Löschen einer Person entfernt ihre Zuordnungen, die Rollen bleiben. Im Aufgabendialog lassen sich Rollen und Personen gemischt mit Aufwand zuordnen, die Auswahl kennzeichnet „(Rolle)“/„(Person)“ (ohne Rollen unverändert); keine Migration bestehender Zuordnungen. Projekteinstellung „Nur Rollenzuweisung zulassen“ (Standard aus) verhindert nur die Neuzuweisung von Personen. ResourceUtilizationService leitet die Kapazität einer Rolle je Tag aus der Summe ihrer Personen ab (Urlaub/Teilzeit senkt alle ihre Rollen) — bewusst naiv additiv, das Ressourcen-Raster weist auf die Mehrfachzählung hin (capacity_naive); Personen- und Rollenzuordnungen laufen durch dieselbe Auswertung, das Workload-Chart summiert Rollen aus ihren Personen. Sichtbarkeit und Rechte folgen den bestehenden Ressourcenregeln. Nicht Teil: Konflikterkennung über Rollen (Schritt B), Kopfzahl (Schritt C), Rollen-Level, Historisierung, Deck-Schnittstelle. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 11 und Integrationsmatrix) und HOWTO.md sind nachgezogen. Neue PHPUnit-Tests (ResourceServiceTest, ResourceUtilizationServiceTest, TaskResourceAssignmentServiceTest, ResourceRolesMigrationTest), Vitest-Tests (resourceType.test.js, resourceDelete.test.js, resources.test.js) und der E2E-Test resource-roles.spec.js
  • Zuständigkeit bei der Kartenanlage übernehmen, danach Abweichungen anzeigen (Issue #211, Stufe 3, Schritt 8 aus #234; O7): „+“ im Aufgabendialog weist der neuen Karte alle Nextcloud-Konten zu, die mit den Ressourcen der Aufgabe verknüpft sind (DeckGateway::assign() über AssignmentService::assignUser(), jedes Konto einzeln). Der Dialog unterscheidet beim Überspringen Person ohne Konto (Lücke: „2 Ressourcen ohne Nextcloud-Konto wurden nicht zugewiesen“), Rolle (#250; sachliche Auskunft, die Karte bleibt absichtlich frei zum Ziehen, nicht als „nicht zugewiesen“ gezählt) und Konto ohne Zugriff auf das Board (übersprungen und genannt; Gantt teilt das Board nicht). Scheitert die Zuweisung, bleibt die Karte angelegt und verknüpft. Danach gibt es keine laufende Synchronisation. Neue Abweichungsart assignees („Zuständige abweichend“, DeckAssigneeDeviationProvider über den Abweichungsdienst aus #209): Menge der zugewiesenen Benutzer der Karte gegen Menge der verknüpften Konten der Ressourcen, in beide Richtungen, mit Annehmen (Hinweis kehrt erst bei erneuter Änderung zurück); Gruppen/Teams und Rollen erzeugen keine Abweichung (Gruppen erscheinen als „auch zugewiesen: …“ an der Karte, bei gemischter Zuordnung zählt nur der Personenanteil). Der Kartenstand trennt jetzt assignees (Benutzer) und assigned_groups. Neue Antwortfelder assignment bei POST /tasks/{id}/deck-cards; Vertragstest und README um AssignmentService::assignUser() und BadRequestException ergänzt. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Integrationsmatrix und Kap. 16) und HOWTO.md nachgezogen. PHPUnit (DeckAssigneeDeviationProviderTest, DeckAssigneeResolverTest, DeckAssigneesTest, DeckLinkServiceTest, DeckGatewayTest), Vitest (deckAssignees.test.js, deckCards.test.js, deckDeviations.test.js) und E2E deck-assignment.spec.js
  • Abweichungen zwischen Plan und Deck im Überblick anzeigen, annehmen und filtern (Issue #239, Stufe 3, Schritt 9 aus #234; UC4 „Abweichungen erkennen“, Leitprinzip 8): Zwei neue Abweichungsarten über den gemeinsamen Abweichungsdienst aus #209 — overdue (DeckOverdueDeviationProvider: Fälligkeit vor heute, nicht erledigt, nicht archiviert/gelöscht, ausschließlich aus dem zuletzt gesehenen Kartenstand, kein Deck-Zugriff nötig) und role (DeckRoleDeviationProvider, setzt #250 voraus: eine Karte einer Aufgabe mit Rollenzuweisung ist einer Person zugewiesen, die keiner der geforderten Rollen zugeordnet ist — ermittelt über ResourceRoleMapper::findPersonIdsOfRoles(); ohne Rollenzuweisung an der Aufgabe greift die Art nicht, bei gemischter Zuordnung läuft sie unabhängig neben assignees). Neu DeckDeviationService::kindsByTask() fasst die offenen Arten je Aufgabe eines Projekts in einem Rutsch zusammen (ein Durchlauf über alle Verknüpfungen statt eine Anfrage je Aufgabe); TaskService hängt daraus das berechnete Lesefeld deck_deviation_kinds an jede Aufgabe aus GET /projects/{id}/tasks, RollupService liefert zusätzlich deck_deviation_count (Summe über das Projekt) im bestehenden Rollup-Endpunkt. Im Frontend: Warnsymbol an der Aufgabe in Tabelle (GanttTable.vue, echtes <button>) und Diagramm (gantt/overlays/deckDeviationOverlay.js, fokussierbares SVG mit eigenem Klick-/Tastatur-Handler statt des abgeschalteten Bibliotheks-Popups) mit Tooltip/aria-label über die betroffenen Arten (utils/deckDeviationKinds.js); ein Klick öffnet den Aufgabendialog gescrollt und fokussiert beim Bereich „Abweichungen zu Deck“ (detailModal.js, neue Option focusDeckSection, Ereigniskette open-deck-deviations/edit-task-deck). Zähler an Teilprojekt (rekursiv über verschachtelte Teilprojekte) und Projekt in der Tabelle sowie, über den Rollup, in der Projektliste und im Projekt-Rollup-Balken. Neuer Filter „Deck weicht ab“ (filters.js, filterMatch.js, FilterBar.vue) wirkt wie die übrigen Filter auf Tabelle und Diagramm. Für „Karte gelöscht/archiviert“ gibt es im Aufgabendialog zusätzlich die Aktion Verknüpfung lösen (DELETE /tasks/{id}/deck-links/{cardId}, ändert die Karte nie); nach Annehmen, Durchsetzen oder Verknüpfung lösen lädt der Dialog die Aufgabenliste des Projekts neu (onDeviationsChanged), damit Symbol und Zähler ohne Neuladen der Seite mitziehen. Wie immer gilt: Abweichungen nur ab der Projektstufe „Nur lesen“, Annehmen nur mit Bearbeitungsrecht. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, neuer Abschnitt in Kap. 16 und Filterliste in Kap. 5) und HOWTO.md (de/en) nachgezogen. PHPUnit (DeckOverdueDeviationProviderTest, DeckRoleDeviationProviderTest, DeckCardStateDeviationProviderTest — bislang fehlend zu #235 —, erweiterte DeckDeviationServiceTest/RollupServiceTest), Vitest (deckDeviationKinds.test.js, erweiterte filterMatch.test.js/tableRows.test.js/deckDeviations.test.js) und E2E deck-deviations-overview.spec.js
  • Doppelte Signale vermeiden: Gantt-Erinnerungen und Tasks-Einträge bei Aufgaben mit Deck-Karte unterdrücken (Issue #240, Stufe 3, Schritt 10 aus #234; O13): Neuer DeckSuppressionService beantwortet für Aufgaben mit mindestens einer verknüpften, nicht archivierten Karte (nach dem zuletzt gesehenen Kartenstand aus #235) in einem Projekt ab Stufe „Nur lesen“, ob NC-Gantt für diese Aufgabe still bleiben soll — DueReminderService, EventNotificationService::notifyAssigned() und TaskCalendarSyncService fragen dort statt die Regel je einmal selbst nachzubilden, statt selbst zu unterdrücken. Unterdrückt werden die Fälligkeits-Erinnerung der Aufgabe, die Zuweisungsmeldung und ihr Eintrag in der Tasks-App; nicht unterdrückt bleiben notifyRescheduled() (Deck kennt keine Kettenverschiebung) und Meilenstein-Erinnerungen (Meilensteine haben keine Karte). Persönliche Einstellung „Gantt-Benachrichtigungen auch für Aufgaben mit Deck-Karte erhalten“ im Abschnitt „Gantt-Projekte“ der Nextcloud-Benutzereinstellungen (PUT /api/v1/settings/reminder-deck-override), Standard aus — die Unterdrückung ist also aktiv; gilt gleichermaßen für Benachrichtigungen und Tasks-App-Einträge. Wird die Unterdrückung wirksam oder aufgehoben, räumt bzw. holt der jeweils nächste Lauf von DueReminderService/TaskCalendarSyncService die betroffenen Erinnerungen bzw. To-dos ab — über denselben Soll-/Ist-Abgleich, der schon das Stummschalten trägt, ohne eigenen Sonderfall. Neue Texte in l10n/de.json/de.js; FEATURES.md (de/en, Integrationsmatrix, Kap. 14, 18 und 20) nachgezogen. Neue PHPUnit-Tests (DeckSuppressionServiceTest, erweiterte DueReminderServiceTest/EventNotificationServiceTest/TaskCalendarSyncServiceTest) und Vitest (stores/settings.test.js)
  • Optionaler Planungszeitraum für Projekte und Teilprojekte (Issue #229): Projekte und Teilprojekte können neben dem aus den Aufgaben berechneten Zeitraum einen eigenen, bewusst gesetzten Rahmen haben (plan_start/plan_end, Migration Version1043; leer für alle Bestandsprojekte, die sich damit unverändert verhalten). Eingabe im Detaildialog (Abschnitt „Planungszeitraum“) und per Inline-Editing in den Spalten Start/Ende der Tabelle, die dafür erstmals auch bei Projekt- und Teilprojektzeilen editierbar sind. Start und Ende sind nur gemeinsam gesetzt: ein leeres Gegenfeld übernimmt den eingegebenen Wert, ein vorhandener wird nie überschrieben, das Leeren eines Felds entfernt den Zeitraum; Start ≤ Ende und Schreibrecht werden serverseitig geprüft (PlanningPeriod, Fehlercodes invalid_plan_date, plan_start_after_end). Ist ein Planungszeitraum gesetzt, zeigen Balken, Tabelle (Start/Ende/Dauer), PDF/Druck und ICS-Feed ihn anstelle des berechneten Zeitraums — auch ein Teilprojekt ohne Aufgaben wird so sichtbar; ein Elternobjekt ohne eigenen Planungszeitraum umfasst in der Darstellung die Zeiträume seiner Kind-Teilprojekte. Liegen die Aufgaben nicht vollständig im Rahmen oder ragt ein Kind-Teilprojekt mit Planungszeitraum heraus, markiert ein rotes Ausrufezeichen die Zeile in Tabelle und Diagramm (planWarningOverlay.js), der Tooltip nennt die Ursache; der Rahmen passt sich nie selbst an und verschiebt keine Aufgaben. Ampel, Rollups, kritischer Pfad, Meilensteine, Filter, Fortschritt und Budget rechnen weiter mit dem berechneten Zeitraum; Baselines und Deck bleiben unberührt. Änderungen stehen mit Operations-ID im Änderungsprotokoll und lassen sich per Toast rückgängig machen (revertOperation() kennt dafür jetzt auch Projekte und liefert entity_types); der JSON-Export/-Import führt den Planungszeitraum mit. Neue Texte in allen Übersetzungen; FEATURES.md (de/en, Kap. 1, 3, 4, 10, 14) und HOWTO.md (de/en) nachgezogen. PHPUnit (PlanningPeriodTest, erweiterte ProjectServiceTest/SubProjectServiceTest/ChangeEventServiceTest/CalendarFeedServiceTest), Vitest (planningPeriod.test.js, erweiterte rowModel.test.js/tableRows.test.js/io.test.js) und E2E planning-period.spec.js
  • Rich-Vorschau für Links auf Projekte und Aufgaben (Issue #241, Stufe 4, Schritt 12 aus #234; O14): Eigener GanttReferenceProvider (OCP\Collaboration\Reference, ohne jede Deck-Abhängigkeit) erkennt Links der Form ?project=<id> bzw. ?project=<id>&task=<id>, mit und ohne index.php, und löst nur auf, wenn die anfragende Person laut AccessService mindestens Leserecht auf das Projekt hat — sonst bleibt der Link ein nackter Link. Die Vorschau einer Aufgabe zeigt Titel, Projekt/Teilprojekt, Start–Ende, Fortschritt, Status und Zuständige, die eines Projekts Titel, Zeitraum, Rollup-Fortschritt und den nächsten Meilenstein; ein eigenes Vue-Widget (registerWidget aus @nextcloud/vue, geladen nur beim RenderReferenceEvent) rendert das im Nextcloud-Look mit Dark-Mode. Cache-Präfix je Projekt bzw. Aufgabe und Cache-Schlüssel je Benutzer (ReferenceCachePrefix); TaskService, ProjectService und MilestoneService verwerfen den betroffenen Cache über IReferenceManager::invalidateCache() bei jeder Änderung, damit die Vorschau nicht bis zu einer Stunde veraltet bleibt. FEATURES.md (de/en, neues Kapitel 23 und Integrationsmatrix) nachgezogen. Neue PHPUnit-Tests (GanttReferenceProviderTest, TaskServiceReferenceCacheTest, ProjectServiceReferenceCacheTest, MilestoneServiceReferenceCacheTest) und E2E-Test reference-preview.spec.js (prüft über den Core-Endpunkt references/resolve, denselben Weg, den Text, Notizen und Talk nutzen). Nicht Teil: ein Smart-Picker-Eintrag zum Einfügen einer Gantt-Aufgabe per Suche (geplanter Folgeschritt zusammen mit der Talk-Integration)

Changed

  • Aufgaben-API: deck_card_id ist nur noch ein berechnetes Lesefeld (Issue #237): Die Migration Version1040 entfernt die Spalte nc_gantt_tasks.deck_card_id (Werte, die noch nicht in nc_gantt_deck_links stehen, werden vorher übernommen) und ergänzt nc_gantt_projects.deck_list_role. Die API liefert deck_card_id weiter — als erste Verknüpfung der Aufgabe —, damit bestehende Nutzer nicht brechen; geschrieben wird nur noch über die Verknüpfungs-Endpunkte, ein mitgesendetes deck_card_id bei POST/PUT auf Aufgaben wird ignoriert. Der Aufgabendialog und alle Frontend-Pfade lesen die Verknüpfungen aus der Tabelle

Fixed

  • Rollenkapazität im Ressourcen-Raster stimmte nicht mit den Personenzeilen überein (Issue #260): Drei unabhängige Fehler wirkten zusammen. Ein Options-API-Watcher auf store.activeIds (Getter-Pfad, nicht deep) feuerte nie, weil toggle() das Array per push()/splice() mutierte statt neu zuzuweisen — eine nachträglich in der Seitenleiste aktivierte Ressource blieb bis zum Moduswechsel ohne Auslastungsfarbe, Tooltip oder abgeleiteten Rollenwert. ResourceUtilizationService und die Personenzeile der Tabelle (getVal()) lasen unterschiedliche Kapazitätsquellen: die Rolle über die 8h/Tag-Pauschale von WorkTimeService::getHoursPerPt() für jeden ungepflegten Tag, die Person über den Mittelwert der gepflegten Tage hochgerechnet auf die ganze Periode. Beide laufen jetzt über dieselbe Tagesfunktion (personCapacityFn() im Backend, resourceCapacity.js im Frontend) — die Pauschale greift nur noch, wenn für eine Person überhaupt keine Kapazität gepflegt ist, nicht mehr für einzelne Lücken innerhalb eines gepflegten Verlaufs, und ist dann auch in der Personenzeile sichtbar. Ein gepflegter Tageswert auf einem Feiertag zählte bisher voll statt 0h, sowohl in der Auslastungsberechnung als auch in resources.js::capacityOf() (Überbuchungsprüfung/Auslastungsgraph im Gantt-Diagramm) — beide prüfen jetzt Arbeitszeitmodell und Feiertagskalender der Ressource unabhängig vom gepflegten Wert. „Wert bis Monats-/Jahresende kopieren“ (doFill()) legt keine Kapazitätssätze mehr auf Feiertagen oder arbeitsfreien Tagen an. Eine Rollenspalte gilt im Tagesmodus als arbeitsfrei, wenn keine ihrer Personen an diesem Tag arbeitet. Neue PHPUnit-Tests (ResourceUtilizationServiceTest), Vitest (resourceCapacity.test.js, erweiterte resources.test.js) und E2E-Test (resource-roles.spec.js)
  • Deck-Vertragstest schlug falsch an (Issue #256): Für CardService::create()/update() prüfte er die Parameter positional, obwohl NC-Gantt sie über DeckGateway::invokeNamed() nach Namen aufruft. Ab Deck 1.18 steht startdate hinter dem von NC-Gantt nicht genutzten done, der CI-Job Deck-Vertragstest wurde dadurch rot, ohne dass etwas kaputt war. Die beiden Methoden stehen in contract.php jetzt unter named: geprüft wird, dass es die Parameternamen gibt und dass NC-Gantt jeden Pflichtparameter der Deck-Methode kennt — die Reihenfolge in Deck ist gleichgültig, neue Parameter dazwischen (zuletzt color) brechen den Vertrag nicht mehr. Positional aufgerufene Methoden bleiben unverändert
  • Startdatum einer Deck-Karte wurde nie gelesen (DeckGateway::cardState()): Deck-Entities stellen ihre Getter über __call bereit, method_exists() war daher immer falsch; jetzt is_callable()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturekJ6Xo6R3EOShOn1PWZI+CpEgV34eRXUZy4CNPkmb+e7uqUL4Tu/tWsqGpu15PCRr
8HR2enQnsTmTk2fkKQUKBqbcfvO3vfylEzNRxU7YuOtyGW6sFAdLSKK8MDnq9XxH
1A91Bmumu7dxP+nh3u5EyGswDoH84tXcxVH0BHEr22ny4fo1/irWKT5RDcumVhyQ
SZSqcwL1iEsYzmmFZJoyqQu7Cfy6IbZXko4OJJGYWLTBvu3CBfDpQFoCg0GaMevf
SccjJXjY6+00oW3OGGn1JH3vz1dMm7l2vD7QfT04AoeFNxJkVcwFo9mTapZijalO
3kgLc7GOLXG6I7hgmqyRBHuUfJAskET9WUl9Ya92UC2Fa8NGwiBFkkQnzy8BGzEp
lbsQ/cDrFw9/U8c1RIfs3T0uOsjtSDeKIAaKQz/SJYAAYdv3SJvqWZxoIt6p04Vk
4fBM02pxBrMH+rNR+X2frwCD3+q4zBtRSxfZNWzz0kHuo9vt714yVLh5dVynwPjP
+6/5b6eE9BsobfL6M6HyZdAzZYIBkPZbfm11TA6nDT5WwUD5r+KBUJDT2hTChAVS
n7pHjc3PQ6wWgVNjvJqmtV8mvH+qvWnLFGGFd5ttHMYjfB555hcStB2UF5cAzkji
2hhP2aeeujkdcPkZ4XETBfQpkbNHaIPUxiEgUHz2oEs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.5
Release Details
UpdatedSept. 21, 2026, 8:05 a.m.
Changelog

Changed

  • Freigabe für Nextcloud 35: max-version in appinfo/info.xml von 34 auf 35 angehoben, unterstützter Bereich damit NC 31–35; die Angabe in der App-Store-Beschreibung (alle sieben Sprachen), README.md, FEATURES.md und INSTALL.md ist nachgezogen. Verifiziert gegen Nextcloud 35.0.0 mit PHP 8.5 und MariaDB 11.4 (302 Unit-Tests im Frontend, 431 PHPUnit-Tests, 98 E2E-Tests) – am Code der App war dafür keine Anpassung nötig. Die lokale Dev-Umgebung (setup-dev.sh) setzt jetzt auf nextcloud:35-apache und installiert Deck mit, weil die Fortschritts- und Karten-Integration von nc_gantt darauf aufsetzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureOtLYluq0hTNN8+vQY8AK0ALLooVxvUUxymkOjs0nAtdES78R6Sii2b9n1POirXvO
EwnIrLzKWwiT05WSV2dsrWliGU+hBhXmvUO0RviY4jmBpu9+mexpLgoHP5dUwIna
OwAN1TgJg0TOod9/bLSQts6qhr8ONk/giqNpMjId1dvQX+hymeQXFchZHk8ohVx4
4d2BaKizT6HQweBNOxxR+FGd6juzsfw4sC9ujASI6IzxPKhH9piiDFniKaERJuGo
iUvDbljcRA4YVRgM003aD9ytwdzdJ4Z3+JfI4L+LBuwaUsXFyQsC3q8bbaZWSj+w
VZX536GBNNwb0RE/nKkgGQ8WLmwG+dosiL26+Wh+a6lX2EWLFZaw9L9iwGFYG92I
iZmFDOnIpqSicG8PmCTV9YSg5u90fxPg53CaVlv6/eCxqY2mUADyErpBSHSDc1tn
g/JM58znHYQF3PfmpFc3Vh901bBytBPTkUY3p0LF1JKoH7tnZ75ar4fTORoF8nEO
Hfr50lB+bWYhEdj1LCvD9yHsRfGcaj5BZ5m6NGvFzvAf/DStAgzyF2jyz/0+YvnM
8XF+LLYY9TktnVyxHNbwjaoE32sHc/le6ABbWSLdI/R/pYWul1EVPKagxYlAQIS1
0IEJbQarL1SKdaQrVfxtxafKvVVsPLcV6DgEBzeem14=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
Gantt Projects 1.7.4
Release Details
UpdatedSept. 21, 2026, 6:44 a.m.
Changelog

Fixed

  • Fortschritt „aus Deck“ war nicht mehr erreichbar (Issue #221): Seit der Umstellung der Detail-Dialoge auf <dialog> speicherte der Aufgabendialog fest progress_mode: 'manual' – eine Aufgabe, die per API auf deck stand, wurde beim Speichern still zurückgesetzt, und fetchDeckProgress(), syncTaskProgress() und PUT /tasks/{id}/progress/sync hatten keinen Aufrufer mehr. Der Modus ist wiederhergestellt, jetzt mit der verknüpften Karte als Grundlage statt des ganzen Boards: Unter „Fortschritt“ schaltet der Dialog zwischen Manuell und Aus Deck um (neues Feld src/detail/forms/taskProgress.js). Eine erledigte Karte zählt 100 %, sonst der Anteil der abgehakten Punkte ihrer Markdown-Checkliste (cardProgress() in src/utils/deckProgress.js); der Dialog zeigt die Grundlage an und lädt die Karte per „Abgleichen“ neu. Beim Aktivieren eines Projekts übernimmt die neue Store-Aktion syncDeckProgress() den Wert für alle Aufgaben im Modus Aus Deck über den vorhandenen Endpunkt – nur geänderte Werte und nur mit Bearbeitungsrecht, ohne das Laden des Diagramms aufzuhalten. Ist Deck nicht erreichbar oder die Karte verschwunden, bleiben Modus und letzter Wert erhalten. Backend und Datenmodell bleiben unverändert. FEATURES.md (Übersicht und Kapitel 16), HOWTO.md und die App-Store-Beschreibung beschreiben den Modus wieder; neue Texte in allen Übersetzungen. Neue Unit-Tests (deckProgress.test.js, taskProgress.test.js, projects.test.js, TaskServiceTest) und E2E-Test tests/e2e/specs/deck-progress.spec.js, der die Deck-API nachbildet
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturedkpQCpaBGHbxMN8d8IvmDWANDU90LVl0Tp5u2TiH7QHbEz7fovU1pmVNKkGAF6VB
eTFeVCkt+Nq8ZdpfnmfC3ZF4wEO/wxd/fPDjm2pZM6WXb03HGpA7/J49KTifC/mj
KY68+niFgI7cjdxaIkI3KZPsWSnhsIZnTQA+HVaQxNRSIuXMV21NYkVQNZaCwZ1y
DmqesNT1/A9457BNtdk8tYpTF1IF9K98fgIB68MmJ3guBEF1jdh1eHqsp9Zhluxv
MgZNf5wNcBTdLT+9z98jxDoSZGx9+7U57ACA54Kd08f3TZLey+nBSIjjSusgyFYl
WDyYjimUfFMlNNktHMkinpvw0qTlu62cIG4yqO1Kd0mzJ4+ueUrwBFuvRIMscVjy
pXuaC2Try+5ZBw8pUl6XDbhPslzvfKI1uO8nUachDNWJR1ULS6HdX8PsWRTA3PFq
NdETu9H9tcLeheaEiGqNQbPEy/2A8TEWqsMcdzl3iX/W3tQJfj1O+KVMOPKC+Kek
rrZiNfEd4KtVgVH0YLU5xb3XyXDFG0q2Q1b3gJBsoKcn2Wr0D7ijK0FLb3U97RDj
tBlHHoy7gVE1JnXvO/Z8oau5KvytSVffwIbdxKAG6oW5Pi92+HTN4J0icMz3+NIr
LHSTqB4XsIdKGjWdKxMj7KOo2FqWwZR0rq0QuFWxWSY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.3
Release Details
UpdatedSept. 18, 2026, 8:33 p.m.
Changelog

Changed

  • Angleichung an die Nextcloud-Entwicklerstandards (Issue #227): App-Werte (Arbeitszeit, Stunden je PT, Erinnerungen) werden über die typisierten Getter und Setter von IAppConfig statt über das seit NC 29 veraltete IConfig::getAppValue()/setAppValue() gelesen und geschrieben; vorhandene Werte werden beim nächsten Speichern typisiert, Benutzerwerte bleiben bei IConfig (IUserConfig gibt es erst ab NC 32). Die Controller bekommen die Benutzer-ID wie im Developer Manual vorgesehen als ?string $userId per Dependency Injection, statt sie in jeder Methode aus der Session zu lesen. npm run lint läuft ohne Warnungen: require-jsdoc gilt nur noch für exportierte Funktionen, die leeren JSDoc-Hüllen an internen Funktionen sind entfernt, exportierte Funktionen vollständig dokumentiert. <style>-Blöcke und detail.css sind wie der übrige Code mit Tabs eingerückt (reine Whitespace-Änderung, .editorconfig hält das für CSS fest)

Fixed

  • Feste Signalfarben ignorierten Dark Mode und Kontrast-Themes (Issue #227): Chart-Overlays, Popups, Tabelle, Dialoge, Rollup, Report und Dashboard-Widget nutzten feste Rot-, Gelb- und Grüntöne. Sie kommen jetzt aus dem Nextcloud-Theming: Text über --color-error-text/-warning-text/-success-text, Flächen und Linien über --color-element-error usw. (ab NC 32) mit Rückfall auf --color-error für NC 31, Tönungen per color-mix()
  • Teilprojekte standen auf einer Ebene mit ihrem Projekt (Issue #230): In Tabelle und Chart-Beschriftung waren Teilprojekte der obersten Ebene nicht eingerückt, während direkte Aufgaben und Meilensteine des Projekts eine Stufe tiefer standen – Projekt und Teilprojekt ließen sich so nur schwer unterscheiden, und in der Übersicht mehrerer Projekte verschwamm die Grenze zwischen ihnen. buildRowModel() reiht sie jetzt mit Tiefe 1 ein; alle darunterliegenden Ebenen rücken entsprechend eine Stufe nach
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturewwr3SUZOKRGnM7uwZfWOLH4FzXNvzjqMOcqVDH3mlte3y8BbmOzAeE5tvPajrvld
xLoe/OaOJ6tLpE8e/7MRaKiB10CuP0sFMA0j5nzOLw7sDPpTpqAV7xVRBbtZhio6
ZWQ0HjSdRU2JGnlP+MFPi7b/c+fSYVz/M9/q/b79Wyf1x2cDRhUJymN8tmFeD8f0
VVL0kXoMiX8oIJVHj0FrMdEWj+nz8pMPLF17bXluzu5REBHi4YLAnjWdO1YYkTOL
QNxQ/b3lAKZG4jvHXTADGlwbGh3iMjidMSjhpnKt2GsDylHxGozgBFb+rCDiSr9Y
PTM6dC/Nmyy4O3/p8Sbs/ZBpqb8wgO51k7Qd6ltXLU+n7eiSoHQyXL+uQihGA2dg
tBbHeKhcPPR5gz2/cSSqkeEyVkesmg015dLkbdStJoq5zR6s7fGYB3ZoUjqoqR2O
5cVF8Jf4k/swhXM7fMQ2zjoZj8yKePguCx7CqppbUFvr5DXltbHh0KDWVVrSgfXM
/zjL9lqQQXrrgTT16oXJfmq1dajsd1e6qwP/OqRY304W9HceyRCh/jQu0oUoQcSz
+fKenL7yWw3IB2UCGHdiXqzGqyGcGdVL0eVPvZhkzpqoDrr82mjPkXshmM/oF//U
942f6SK2u3YG+bKx/n1Up3ZX90WhZxNsZdW+eDqCbmY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.2
Release Details
UpdatedSept. 17, 2026, 1:01 p.m.
Changelog

Changed

  • PHPUnit-Stubs auf Nextcloud 31 angehoben (Issue #219): nextcloud/ocp steht in composer.json/composer.lock jetzt auf dev-stable31 statt dev-stable28 und damit auf der kleinsten unterstützten Nextcloud-Version; psr/log zieht dabei von 1.1 auf 3.0 nach. OCP-Klassen ab NC 29 sind in Unit-Tests damit verfügbar, die Suite lief ohne Anpassungen an Mocks durch. Nachgeholt ist der Unit-Test für das Mapping ParameterOutOfRangeException → 400 invalid_<parameter> in ExceptionMiddlewareTest (bisher nur per E2E abgedeckt). Entfallen ist die Laufzeitprüfung im Notifier, die nur wegen der alten Stubs nötig war: Er wirft jetzt direkt OCP\Notification\UnknownNotificationException (seit NC 30). Das Release-Artefakt enthält die Stubs nicht
  • Wartbarkeit im Frontend (Issue #226): Der tasks-Watcher in GanttChart.vue ruft refreshOverlays() nicht mehr zusätzlich auf – renderGantt() plant das selbst ein, Workload-Diagramm, Baselines, Klammern, Fortschritt und Meilensteine wurden bei jeder Datenänderung doppelt gezeichnet. Das Dashboard-Widget beendet sein Aktualisierungsintervall beim Unmount. Das Entpacken von API-Antworten (data.ocs?.data ?? data, 61 Stellen) übernimmt der Helfer unwrap() in src/api.js. AdminHolidays.vue arbeitet über die Stores für Einstellungen, Kalender und Feiertage (holidays.js hat dafür load() und remove() bekommen), PersonalReminders.vue über den Settings-Store (neu: remindersMuted, setRemindersMuted(); die persönliche Einstellungsseite nutzt dafür jetzt Pinia), ShareDialog.vue über api() aus src/api.js statt einer eigenen Kopie. Der Projekt-Store spiegelt die Lösch-Kaskade des Backends: Beim Löschen eines Teilprojekts verschwinden auch Unter-Teilprojekte, deren Aufgaben und die Meilensteine, beim Löschen eines Projekts dessen Teilprojekte, Aufgaben und Meilensteine, und beim Löschen von Aufgaben die Abhängigkeiten anderer Aufgaben darauf. Kleinkram: keine Überschattung der Übersetzungsfunktion t mehr, ungenutzte catch-Variable entfernt. Der E2E-Test aus #223 wartet vor dem Ziehen auf eine stabile Balkenposition, weil die nachladende Tabelle das Diagramm sonst unter dem Mauszeiger verschob
  • Wartbarkeit im Backend (Issue #226): TaskService::create() und update() laufen in einer Transaktion (TTransactional::atomic()): Speichern, Ressourcen-Zuordnungen, Abhängigkeiten, Constraints, Propagation und Change-Events gelingen gemeinsam oder gar nicht, Benachrichtigungen gehen erst nach dem Commit hinaus. Die drei identischen Parser in TaskController sind zu parseJsonArray() zusammengelegt. Die seit dem 3-Ebenen-Modell ungenutzte Tabelle nc_gantt_sp_deps entfernt Migration Version1038Date20260917000000, Mapper und Entity sind gelöscht. Milestone hat nur noch einen Docblock (die @method-Annotationen des ersten waren für IDE und statische Analyse unsichtbar), Holiday die fehlenden Annotationen und keinen toten DateTime-Zweig mehr. WorkCalendarController und WorkScheduleController verzichten beim Ändern und Löschen auf die Eigentümerprüfung, die für die ohnehin nur Nextcloud-Admins zugänglichen Endpunkte immer zutraf. ExceptionMiddleware unterscheidet reguläre Controller: Eine ApiException wird dort zu einer JSONResponse mit Status und Meldung statt zu einer OCSException ohne OCS-Kontext, andere Exceptions behandelt Nextcloud selbst. POST /admin/holidays lehnt ein Datum außerhalb von Y-m-d bzw. einen nicht existierenden Kalendertag mit 400 invalid_date ab und einen unbekannten Kalender mit 400 invalid_calendar_id
  • Statische Analyse in der CI (Issue #226): PHPStan Level 5 prüft lib/ gegen die OCP-Stubs (phpstan.neon, composer phpstan) und läuft in jedem PHP-Job der CI nach dem Coding-Standard. Die Klassen der dav-App und von Sabre in DavTaskCalendarBackend sind ausgenommen, weil es dafür keine Stubs gibt. Behoben sind dabei die übrigen Befunde: HolidayMapper deklariert seinen Entity-Typ, ApiException ihre möglichen Status, ChangeEventService speichert Aufgaben und Teilprojekte typgerecht, Rohdaten des Clients sind nicht mehr als bereits geprüfte Strukturen annotiert, TaskCalendarSyncService erkennt neue Zuordnungszeilen ohne Vergleich einer nie leeren ID, und die Zyklusprüfung der Teilprojekt-Hierarchie greift nicht mehr auf ein fehlendes Array-Element zu (PHP-Warnung bei Altdaten mit verwaistem parent_id). Das Release-Archiv enthält phpstan.neon nicht

Fixed

  • Abhängigkeitsmodus „Pflicht“ griff beim Ziehen und in der Tabelle nicht (Issue #223): Der Server erzwingt die Abhängigkeiten der geänderten Aufgabe nur, wenn der Client dep_mode: 'strict' mitsendet. Das taten nur der Detail-Dialog und die Spalte „Vorgänger“, nicht aber Drag&Drop im Chart und die Start-/Endzellen der Tabelle – ausgerechnet die häufigsten Wege, einen Termin zu ändern. Eine Aufgabe ließ sich so vor das Ende ihres Vorgängers schieben und wurde dort gespeichert. store.updateTask() ergänzt den eingestellten Modus jetzt selbst, sobald start_date oder end_date im Payload stehen; ein explizit übergebener dep_mode hat Vorrang. Das gilt damit auch für „Simulation übernehmen“
  • Fehlgeschlagenes Speichern nach Drag&Drop blieb ohne Rückmeldung (Issue #223): Lehnte der Server eine Verschiebung ab (fehlende Rechte, ungültiges Datum, Netzwerkfehler), blieb der Balken an der neuen Position stehen, obwohl der alte Termin galt, und der Fehler landete nur in der Browser-Konsole. GanttChart.vue zeigt jetzt „Speichern fehlgeschlagen“ und setzt den Chart auf den Stand des Stores zurück, der Balken springt zurück – auch für Meilensteine und im Simulationsmodus. Neue Unit-Tests src/stores/projects.test.js, neuer E2E-Test tests/e2e/specs/dependency-mode.spec.js
  • Löschen hinterließ verwaiste Datensätze (Issue #224): Die Lösch-Kaskade einer Aufgabe war beim Löschen von Aufgaben, Teilprojekten und Projekten jeweils von Hand geschrieben und dabei auseinandergelaufen. Alle drei Wege nutzen jetzt den neuen TaskDeletionService (Abhängigkeiten in beide Richtungen, Meilensteine an der Aufgabe, Ressourcen-Zuordnungen). Dadurch räumen Teilprojekt- und Projektlöschung auch die Ressourcen-Zuordnungen ab, die bisher stehen blieben. Die Teilprojektlöschung entfernt zusätzlich die Meilensteine direkt an den gelöschten Teilprojekten (auf das Projekt eingeschränkt, vgl. Issue #179), die Projektlöschung Baselines samt Einträgen. Das Löschen einer Ressource entfernt ihre Zuordnungen und Kapazitätseinträge; war sie die primäre Ressource einer Aufgabe, rückt wie beim Speichern die nächste Zuordnung nach, sonst wird resource_id null. Den Aufwand (work) der Aufgaben lässt es bewusst unverändert: Die Arbeit fällt mit der Ressource nicht weg, sondern ist neu zu verteilen, und eine Neuberechnung senkte stillschweigend Plankosten und Fortschrittsgewichte. Aufgaben- und Ressourcenlöschung laufen jetzt wie die übrigen Löschwege in einer Transaktion. Bereits vorhandene Waisen werden nicht nachträglich bereinigt. Entfallen sind die dadurch ungenutzten TaskMapper::deleteByProject() und TaskSchedulingService::deleteDependencies()
  • Gelöschte Nextcloud-Konten wurden nicht aufgeräumt (Issue #225): Die App reagierte nicht auf das Löschen eines Kontos. Freigaben an das Konto blieben stehen, und ein später gleichnamig angelegtes Konto erbte darüber den Zugriff auf die Projekte – ebenso das Eigentum an den Projekten des gelöschten Kontos. Der neue UserDeletedListener (registriert für OCP\User\Events\UserDeletedEvent) delegiert an UserDeletionService, der in einer Transaktion die Freigaben an das Konto löscht, die Verknüpfung von Ressourcen mit dem Konto (uid, Issue #203) aufhebt und Projekte, Ressourcen, Ressourcengruppen, Kapazitäten, Wochenmodelle und Kalender des Kontos erhält, aber auf einen Platzhalter-Eigentümer #deleted-<zufällig> überträgt. # ist in Nextcloud-Benutzer-IDs nicht zulässig, der Platzhalter kann also nie einem Konto gehören; er ist je Löschung neu und für alle Tabellen derselben Löschung gleich, sodass Projekte und Ressourcen des Kontos zusammengehörig bleiben. Verwalten können diese Daten danach Nextcloud-Administratoren und Inhaber einer Admin-Freigabe. Historie (change_events.changed_by, baselines.created_by) bleibt unverändert; Erinnerungen und Tasks-Synchronisation gleichen sich wie bisher selbst ab. Neuer E2E-Test tests/e2e/specs/user-deletion.spec.js
  • Drag&Drop öffnete zusätzlich den Bearbeiten-Dialog: Nach dem Ziehen eines Balkens löst der Browser einen Klick auf den Balken aus, und GanttChart.vue öffnete daraufhin den Detail-Dialog – nach jeder Verschiebung musste man ihn wieder schließen. _onBarClick() ignoriert den Klick jetzt, wenn sich die Maus zwischen Drücken und Loslassen um mehr als 4 Pixel bewegt hat. Der E2E-Test zum Verschieben per Drag prüft, dass der Dialog geschlossen bleibt
  • Glossar beschrieb noch Abhängigkeiten zwischen Teilprojekten: Seit dem 3-Ebenen-Modell hängen Abhängigkeiten, kritischer Pfad und Puffer an Aufgaben, und der Abweichungsbericht rechnet über die Aufgaben der Baseline. Das Glossar sprach dagegen von Teilprojekten, die voneinander abhängen, eigene Termine und eigenen Aufwand haben. Korrigiert sind die Einträge Teilprojekt, Kritischer Pfad, Puffer, Abhängigkeit, Gesamte Terminabweichung und Ø Fortschritt (samt Formel) – auf Englisch und in allen Übersetzungen (de, es, fr, it, nl, pt_BR)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureKOPH7uJRRB6f+Iqi2oeNidpUdRwDrpvn6N99cSmWLf+W+QrWztXKyDbTLOq71mBy
cXEkYbN+4KnMt0Zie40fLjvXqI8sWgouPHQdobRBCvq60TUpHVT1umUSAWrP7KDJ
XMyCnbKt6jqpTmUusE7sMGkZwBB1SEdx5tAilm2gu67wzR5WQakpZXXsp3Au+GyM
sxPgqD5bgRMcKdRMF+VXIF5gZTsJrNgal9J3SZZ1m8KGMArFPm+dEJaxAF+z0NxX
NTNCKR/tF2y20xgc2Rt4KLNOg5TTrRLB931vwkoM0eLUjFavTXjFAMAoVndvX5yb
37sRCzJX2tzekoch1/e6BWVbo5PH5YJmP5at2lWmrhDcWHrzFxTQZ2ZiY6GYISAO
b/oKUbvmmTs5VFToXRWAQASOC9afDEbKdWes+zoglIP3euOwok+2VD4rXZtVUNlU
fZ/FwsIYt8OB9UF7pQWKQSma8OPsZNnsBaoJVbFinieAwgKs35XtYHeTUWfsXAsL
7GaRK2AJ4jkV4uu35/mHQOjm2DM0ZfkC4OpUI3qXhLM70YTn3BIC0Y3k1P8ZmG4J
RtMSXNmqjBDMO+A+qclcRULziVStXCSlkDVAIw3I37JdrGYlB+avZ9yTX3EsNR0C
gVjxSTao2fhfLKp6o1tQO0+ImF1XqRIxPE1vAWS0hOE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.7.0
Release Details
UpdatedSept. 16, 2026, 1:50 p.m.
Changelog

Added

  • Ressource optional mit einem Nextcloud-Benutzerkonto verknüpfen (Issue #203, Datenmodell + API): Ressourcen haben ein neues, optionales Feld uid (Migration Version1034Date20260915000000, Spalte nc_gantt_resources.uid, nullable und indiziert). POST /resources und PUT /resources/{id} nehmen es entgegen, GET /resources liefert es mit; uid: null bzw. "" hebt die Verknüpfung auf. Ein nicht existierendes Konto lehnt ResourceService über IUserManager::userExists() mit 400 user_not_found ab; je Eigentümer der Ressource darf ein Konto nur einer Ressource zugeordnet sein, sonst 409 uid_already_assigned (verhindert später doppelte Benachrichtigungen). Ressourcen ohne Konto — Maschinen, externe Dienstleister, Sammelrollen — bleiben uneingeschränkt nutzbar. Der JSON-Export enthält uid je Ressource; beim Import wird eine im Zielsystem unbekannte oder dort bereits vergebene uid verworfen, die Ressource ohne Verknüpfung angelegt und nach dem Import ein Hinweis mit den betroffenen Konten angezeigt, statt abzubrechen. Die Auswahl des Kontos in der Oberfläche siehe Issue #204
  • Nextcloud-Benutzer in der Ressourcenverwaltung auswählen (Issue #204): Der Ressourcen-Dialog hat ein optionales Feld „Nextcloud-Benutzer“ mit Typeahead-Suche über die vorhandene Benutzersuche (GET /users/search, keine Vollliste); geleert bleibt bzw. wird die Ressource ohne Konto. Die Auswahl übernimmt den Anzeigenamen als Vorschlag ins Namensfeld, solange dort noch nichts Eigenes steht — ein leeres Feld oder der unveränderte vorige Vorschlag wird ersetzt, ein selbst eingegebener Name bleibt. Lehnt der Server die Verknüpfung ab (400 user_not_found/invalid_uid, 409 uid_already_assigned), erscheint die Meldung direkt am Feld statt als allgemeiner Speicherfehler; beim Bearbeiten wird die uid nur bei einer Änderung mitgesendet, damit ein inzwischen gelöschtes Konto das Speichern anderer Felder nicht blockiert. Verknüpfte Ressourcen zeigen in der Ressourcenliste, im Kapazitätsraster und in der Ressourcen-Zuordnung des Aufgabendialogs Avatar und Anzeigenamen des Kontos (Ressourcenname, bei Abweichung ergänzt um den Anzeigenamen in Klammern); nicht verknüpfte bleiben unverändert. Dafür liefert die Ressourcen-API zusätzlich das berechnete Feld uid_display_name (bei gelöschtem Konto null, die Oberfläche zeigt dann die uid), und GET /users/search nimmt mit include_self=1 auch das eigene Konto auf, damit man sich selbst als Ressource verplanen kann — der Teilen-Dialog schließt es weiterhin aus. Das „+“-Menü im Kapazitätsraster schließt sich jetzt nach „Neue Ressourcengruppe“/„Neue Ressource“ (close-after-click); bisher blieb es über der Ansicht offen und entzog dem ersten Feld der Sidebar beim Schließen den Fokus
  • „Meine Aufgaben“: Filter, Endpunkt und Dashboard-Widget (Issue #205): Auf Basis der Verknüpfung aus #203 sieht man projektübergreifend, was einem selbst zugewiesen ist. Neuer Endpunkt GET /api/v1/my-tasks (optional include_done, limit 1–500) liefert die Aufgaben, deren primäre oder zusätzliche Ressource (Issue #103) mit dem eigenen Konto verknüpft ist — gleich, wem die Ressource gehört —, sortiert nach Ende, dann Start. Offen heißt Fortschritt < 100 % und Status nicht „erledigt“; overdue folgt der Kennzahl „kritisch“ des Projekt-Widgets. Die Verknüpfung allein gewährt keinen Zugriff: MyTasksService prüft je Projekt AccessService::canViewOrOwn(). has_linked_resource unterscheidet „keine Ressource verknüpft“ von „nichts zu tun“. Das neue Dashboard-Widget „Meine Gantt-Aufgaben“ (nc_gantt_my_tasks) ist ein API-Widget (IAPIWidgetV2, IButtonWidget, IIconWidget, IReloadableWidget), das Dashboard rendert es ohne eigenes Bundle: die nächsten fälligen und überfälligen Aufgaben mit Projekt und Fälligkeit, überfällige mit rotem Overlay-Symbol, jeder Eintrag ein Direktlink. Ohne verknüpfte Ressource zeigt es einen erklärenden Leerzustand mit Setup-Button zur Ressourcenverwaltung. Direktlinks ?project=<id>&task=<id> aktivieren das Projekt und öffnen den Aufgabendialog, ?view=resources öffnet die Ressourcenverwaltung; die Parameter verschwinden danach aus der URL. In der Filterleiste dimmt der neue Filter „Mir zugewiesen“ in Chart, Tabelle und Projektliste alles ohne verknüpfte Ressource; nicht aktivierte Projekte mit zugewiesenen Aufgaben erkennt die Projektliste über den Endpunkt. Ist keine Ressource mit dem Konto verknüpft, erklärt ein Hinweis im Filter das und führt zur Ressourcenverwaltung
  • Fälligkeits-Erinnerungen als Nextcloud-Benachrichtigung (Issue #206): Ein Hintergrund-Job (DueReminderJob, appinfo/info.xml) meldet einmal täglich jede offene Aufgabe und jeden Meilenstein, die heute fällig sind, in den nächsten Tagen fällig werden oder überfällig sind. Der Job läuft stündlich und meldet höchstens einmal je Tag, sobald die Serverzeit die eingestellte Stunde erreicht hat (reminder_hour, Standard 6 Uhr); die Vorlaufzeit steuert reminder_lead_days (Standard 1 Tag) — die Oberfläche zu beidem kommt mit Issue #207. Empfänger sind die über Issue #203 verknüpften Konten der zugeordneten Ressourcen (primäre wie zusätzliche, Issue #103); ist keine Ressource verknüpft, erhält der Projekteigentümer die Meldung, damit die Funktion auch ohne gepflegte Verknüpfungen nützt. Ein Meilenstein erbt die Empfänger seiner Aufgabe. Wie bei „Meine Aufgaben“ gewährt die Verknüpfung allein keinen Zugriff: wer das Projekt nicht sehen darf, wird nicht benachrichtigt. Der übersetzte Notifier verlinkt direkt auf das Element (?project=<id>&task=<id>, beim Meilenstein auf das Projekt) und kommt ohne Datenbankzugriff aus — alles Nötige steht in den Parametern der Benachrichtigung. Gegen Dubletten führt die App über die neue Tabelle nc_gantt_reminders (Migration Version1035Date20260916000000, eindeutig über Element und Empfänger) selbst Buch: je Element, Empfänger und Anlass höchstens eine offene Benachrichtigung. Jeder Lauf gleicht Soll und Ist ab — wechselt der Anlass („wird bald fällig“ → „überfällig“), ersetzt der Job die Benachrichtigung; ist das Element erledigt, verschoben oder gelöscht, zieht er sie zurück. Ohne aktivierte Notifications-App tut der Job nichts und protokolliert keine Fehler
  • Erinnerungen konfigurierbar: Vorlaufzeit, Umfang, Abschalten (Issue #207): Jedes Projekt legt selbst fest, ob es erinnert, wie weit im Voraus (0–14 Arbeitstage; ohne eigenen Wert gilt die Vorlaufzeit der Instanz), worüber (Meilensteine und Aufgaben oder nur Meilensteine) und ob auch an Überfälliges erinnert wird. Der Detaildialog des Projekts hat dafür den Abschnitt „Erinnerungen"; die Felder liegen in nc_gantt_projects (Migration Version1036Date20260916100000), PUT /projects/{id} nimmt reminders_enabled, reminder_lead_days, reminder_scope und reminder_overdue entgegen, GET /projects liefert sie mit, unzulässige Werte weist die API mit 400 invalid_reminder_lead_days bzw. invalid_reminder_scope ab. Die Vorlaufzeit zählt jetzt Arbeitstage statt Kalendertage — dieselben Wochenmodelle und Feiertage wie die Terminplanung —, sodass eine montags fällige Aufgabe bei einem Vorlauf von einem Tag am Freitag davor gemeldet wird und nicht am arbeitsfreien Sonntag. In den persönlichen Nextcloud-Einstellungen (Abschnitt „Gantt-Projekte") lässt sich die Erinnerung der App für das eigene Konto stummschalten (PUT /settings/reminder-mute); bereits zugestellte Benachrichtigungen zieht der nächste Lauf dann zurück. Die Vorbelegung aller neuen Felder entspricht dem Verhalten aus Issue #206

  • Ereignis-Benachrichtigungen: Zuweisung und Terminverschiebung (Issue #208): Zusätzlich zum Tageslauf aus Issue #206 meldet die App Änderungen in dem Moment, in dem sie passieren. Wer über eine Ressourcenzuordnung neu für eine Aufgabe zuständig wird, bekommt „Aufgabe … wurde dir zugewiesen"; ändert sich ein Start- oder Enddatum, geht „Der Termin der Aufgabe … hat sich verschoben" an die Zuständigen — auch dann, wenn die Verschiebung erst über die Propagationskette (TaskSchedulingService) oder über „Termine neu berechnen" entstanden ist. Verschiebt eine Aktion eine ganze Kette, erhält jede Person eine Meldung, die die angefasste Aufgabe benennt und die Zahl der weiteren für sie betroffenen Elemente nennt, statt einer Flut von Einzelmeldungen. Wer die Änderung auslöst, hört nichts davon; stummgeschaltete Konten und Projekte mit abgeschalteten Erinnerungen (Issue #207) bleiben still, und wer das Projekt nicht sehen darf, wird nicht benachrichtigt. Anders als die Fälligkeits-Erinnerungen fallen die Ereignis-Meldungen nicht auf den Projekteigentümer zurück: gemeldet wird nur, wer über eine verknüpfte Ressource (Issue #203, #103) tatsächlich zuständig ist. Die Meldungen tragen den eigenen Objekttyp task_event, damit der Tageslauf sie beim Aufräumen seiner eigenen Erinnerungen zum selben Element nicht mit zurückzieht; je Aufgabe und Empfänger bleibt höchstens eine offene Ereignis-Meldung stehen — die neue ersetzt die vorige. Die Empfängerermittlung samt Stummschaltung liegt dafür jetzt im gemeinsamen NotificationRecipientService, den sich DueReminderService und der neue EventNotificationService teilen. Die persönliche Einstellung heißt entsprechend „Benachrichtigungen" und schaltet beides stumm

Fixed

  • Parameter außerhalb des Wertebereichs ergaben 500 statt 400 (gefunden bei Issue #205): Nextclouds Dispatcher prüft Integer-Parameter mit @param int<min, max> — und limit ohne Angabe auf 1–500 — noch vor dem Controller. Die ParameterOutOfRangeException machte die ExceptionMiddleware bisher zu 500 internal_error samt Fehlerprotokoll; jetzt antwortet die API mit 400 invalid_<parameter>
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureHgTu9/xKCVwgEQ94i1KF1Wc1ucKK+FK0W47ldxlBDNCRKgbhuvGpV7QKdpXiYR5G
Y18Tdot2H1tVhxwYqNPF4/dRt2zuaXzudAIrMrZ70Rxhxo15HNIcFulblH97Td76
HpBO3VxAlBeB6bSQS+YnC3HRC0koDMVJu7VtabL0duzWYZap0539AhczP2rXiAnN
+PxyU8dubrSLOljWGiL6jExiK7Eg6L1luFujNp4MnI8/cBqlBobjRgGmXF5SVfF0
kIcBN8T3Dcpg1w8fuGe68YnaR0xv3ML63zn1nBFb+5zkANv8Yb08dMIQdaNmkCEq
SNSWazd81/HbHDyLEUTK1fpDol9Hn8fv1+4u3MuvCzOcVZjxrvDozAyLLGQX7Y/Y
bup5FbA/WUYXwW6H4XyltYjW60+ivbTsbKflYn6lrbdevFb+z7s0niDgRzSGXIc3
jFh+7NpYzrCVnawKHxJjnyQGrjUNuuozbeAgUh2MaU0D7hh2LuZlLJ7pf5hZTvO+
q1yqdLMiSnmHIFeBh35qH7wywsUmkQYViopegvbRt0kz+ksyHmaxgOE9TOp6JoIc
kivnQLil1Tl0J0gK8w3Nk8q2Hj0WRzVuMyqTqBvAeP54rHM+jOtw3GIlX6XxjBUE
knAkfESZIbDGMrBH4sIFWQP28xCViSGAmideS61nwAY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.2
Release Details
UpdatedSept. 13, 2026, 4:16 p.m.
Changelog

Security

  • Eine zyklische Teilprojekt-Hierarchie konnte den Server blockieren (Issue #199). SubProjectService::create()/update() prüften bei parent_id nur, ob das Ziel-Teilprojekt zum selben Projekt gehört, nicht aber, ob dadurch ein Zyklus entsteht (z. B. A → Parent B, B → Parent A). findDescendantIds() lief bei einem solchen Zyklus beim Löschen in eine Endlosschleife (CPU-Hang, unbegrenztes Speicherwachstum) — auslösbar für jede Person mit Bearbeitungsrecht am Projekt, nicht nur für Eigentümer oder Admins. Eine Zyklusprüfung analog zu TaskSchedulingService::assertValidDependencies() weist solche Änderungen jetzt ab (BadRequestException parent_cycle); zusätzlich bricht findDescendantIds() über ein visited-Set ab, falls Altdaten bereits einen Zyklus enthalten
  • Beim Anlegen einer Aufgabe ließen sich Abhängigkeiten zu Aufgaben fremder Projekte setzen (Issue #190). TaskService::create() reichte mitgesendete dependencies ungeprüft an TaskSchedulingService::addDependencies() weiter; eine predecessor_id aus einem nicht zugänglichen Projekt erzeugte so einen task_dependencies-Eintrag über die Projektgrenze hinweg. Der Create-Pfad ruft jetzt wie schon update() vorher assertValidDependencies() auf, analog zur IDOR-Prüfung in MilestoneService

Added

  • Projekte, Teilprojekte und Aufgaben einklappbar (Issue #197): Ein Dreieck vor jeder Zeile mit Unterobjekten (Projekt, Teilprojekt oder Aufgabe mit Meilenstein) blendet deren Nachfahren per Klick aus und wieder ein; ein Tooltip nennt den aktuellen Zustand. Weil Tabelle und Chart zeilengleich scrollen, wirkt das Einklappen auf beide Ansichten — buildRowModel() filtert die gemeinsame Zeilenliste, bevor buildFrappeRows()/buildTableRows() sie übernehmen. Vorgänger-Verweise auf ausgeblendete Zeilen werden aus den Chart-Balken entfernt, damit frappe-gantt beim Ziehen eines Balkens nicht auf einen fehlenden Balken zugreift. Der Zustand wird pro Nutzer über die bestehenden UI-Einstellungen gespeichert
  • Eigene Farbe für Teilprojekte und Aufgaben (Issue #196): Im Detaildialog von Teilprojekten und Aufgaben lässt sich jetzt — wie bisher nur beim Projekt — eine Farbe aus der Palette oder frei wählen. Ohne eigene Farbe („Geerbt") übernimmt das Objekt die Farbe des nächsten übergeordneten Teilprojekts bzw. des Projekts; Meilensteine erscheinen in der Farbe ihres übergeordneten Objekts. Balken, Summenklammern und Meilenstein-Rauten im Chart sowie der Farbpunkt vor jedem Eintrag der Tabellenansicht (bisher stets die Projektfarbe) verwenden diese effektive Farbe (src/gantt/colors.js); die dynamischen CSS-Regeln hängen dafür nicht mehr an der Projekt-id (gantt-color-<id>), sondern am Farbwert (gantt-fill-<hex>)
  • Die color-Spalte von Aufgaben und Teilprojekten gab es schon, sie war aber mit #0082c9 vorbelegt und wurde nie angezeigt. Die Migration Version1033Date20260913000000 setzt den Spalten-Default auf NULL (= erbt) und den Vorbelegungswert in den Bestandsdaten zurück. Objekte, die ihn trugen, sehen daher unverändert aus; Farben, die davon abweichen — etwa aus JSON-/CSV-Importen oder direkten API-Aufrufen, die bisher gespeichert, aber nie angezeigt wurden —, erscheinen nach dem Update erstmals im Chart. Die API liefert für Objekte ohne eigene Farbe jetzt color: null; PUT /tasks/{id} und PUT /subprojects/{id} akzeptieren color: null bzw. "" zum Zurücksetzen — bisher verwarf das array_filter im Controller einen solchen Wert

Changed

  • Dokumentation nach Sprache (DE/EN) und Zielgruppe organisiert (Issue #191): Entwickler-Doku liegt jetzt übersetzt unter docs/de/ und docs/en/ im Repository-Root (CONTRIBUTE.md, RELEASE.md, HOWTO_Release.md, sowie die vormals in src/detail/, tests/e2e/ und tests/fixtures/ liegenden READMEs als detail-module.md/e2e-tests.md/test-fixtures.md, mit kurzen Verweis-Stubs an den alten Pfaden), Nutzer-Doku unter nc_gantt/docs/de/ und nc_gantt/docs/en/ (FEATURES.md, HOWTO.md, INSTALL.md, LICENSE.md). Die Root-README.md ist die vormalige nc_gantt/README.md, ergänzt um ein Sprachen-/Themen-Inhaltsverzeichnis am Anfang; die alte Root-README (zehn Anwendungsfälle) ist darin aufgegangen. nc_gantt/CHANGELOG.md bleibt bewusst an seinem bisherigen Pfad und einsprachig, weil release.sh, scripts/validate-appinfo.js und tests/Unit/AppInfoXmlTest.php exakt diesen Pfad für die Versionsabschnitts-Prüfung voraussetzen und der historische Log als technisches Protokoll nicht rückwirkend übersetzt wurde; LICENSE.md führt den englischen AGPLv3-Text als maßgeblich, die deutsche Fassung ist als inoffizielle, nicht rechtsverbindliche Übersetzung gekennzeichnet. appinfo/info.xml verlinkt in allen sieben App-Store-Sprachen sowie in <documentation> (user/admin/developer) jetzt auf die neuen docs/<lang>/…-Pfade, mit Englisch als Fallback für Sprachen ohne eigene Doku (fr/es/it/nl/pt-br)
  • Deutsche Übersetzung der README (Issue #194): nc_gantt/docs/de/README.md übersetzt die Root-README.md vollständig (Anwendungsfälle, Funktionen, Installation, Architektur); beide Dateien verlinken oben in einer kleinen Sprachtabelle aufeinander
  • App-Store-Beschreibung ergänzt: Der Abschnitt „Gantt-Diagramm & Tabellenansicht“ in appinfo/info.xml nennt in allen sieben Sprachfassungen jetzt auch das Einklappen von Projekten, Teilprojekten und Aufgaben sowie die eigenen Farben für Teilprojekte und Aufgaben

Fixed

  • Ist-Kosten ließen sich im Aufgaben-Formular nicht speichern (Issue #198): Das Feld für actual_cost las das bei numberField() nicht vorhandene .value statt getValue(), sodass beim Speichern stets null statt des eingegebenen Werts an den Server ging
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureHezqeAXIywdFI8vq5gO4MON/W8MjpkNqrjO2PZgZFFAHkSovdh5IqDbaUoltzPgk
rPiZtmytaqM29hGS2Qqh+DyymRKK7hco5chJ7tcrNqZcDx3KC9UDH2u+GK8GzkS0
jMsvi603ajD3ApEAiZUH9f0eiCYVerg6+/vUh63LFYQuYb7zjyUrNzmNTRl55eeS
cSgDjEvM0kqbaD/FXtdWK0ZzCaOJcSpuCLorf5i7WJU2lQx0Wi0I6lFHYFBVzLBj
W+U8NMiFZE+Wy1YGGwz7QOY1o9fOmrxzwIchuLvLVPCT4j9Vk9YNByc2XREv++UJ
/l3q8EYLLASMKHkscessRS5izHRaTR3iwgHNrxvb8vaXjzu6+he45/LaeQC6pg1M
wsBgAdHTb7alK48/W4p8ex0W5x8l/XyGeBzcSXidxlFD4SKFE2LAGxRoDzIIPeOj
gC3MT8IvAuKU5/g2fruxOxa9Nv2HNh3V9zw4reGxf018TeKVESxE7oMYpn4aFwmK
aq8c+n7EVqGnY1imH+BwvL5EeKzjjBTKcb8Z8DHSxNx6fOG+8ExyJLL+/s9zUSXY
uB1EnmU6QTWH5Tb93gqG/JgVV/8MoERfXSNMi2VOAjsniaGHkagXKs830EwamrDs
ttDDE3UQ2EymEVIDa1v8WTKAxWtFC2sl4GXwuyOTUyA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.1
Release Details
UpdatedSept. 2, 2026, 6:15 p.m.
Changelog

Security

  • Ressourcen-, Gruppen- und Kapazitätsdaten fremder Projekte waren über geteilte Projekte einsehbar (Issue #183). ResourceMapper, ResourceGroupMapper und ResourceCapacityMapper::findVisibleToUser() gaben pauschal alle Datensätze eines Eigentümers frei, sobald irgendein Projekt dieses Eigentümers mit dem Nutzer geteilt war — unabhängig davon, ob die konkrete Ressource überhaupt in einem geteilten Projekt verwendet wird. Der neue zentrale ResourceVisibilityService prüft stattdessen, ob die Ressource tatsächlich in einer für den Nutzer sichtbaren Aufgabe verwendet wird, und wird von ResourceService, ResourceCapacityService, ResourceGroupService und TaskResourceAssignmentService genutzt. Damit lassen sich über eine geteilte Ressource weder die übrigen, nicht geteilten Ressourcen desselben Eigentümers einsehen noch eigenen Aufgaben zuordnen
  • GET /api/v1/resources/{id}/utilization gab Planungsdaten aus nicht zugänglichen Projekten preis (Issue #181). ResourceUtilizationService::findVisibleResource() prüfte nur, ob der Ressourcen-Eigentümer irgendein Projekt mit dem Nutzer geteilt hat; über die Auslastung waren dadurch Aufgaben- und Projekttitel samt Terminen aus fremden Projekten abgreifbar. Die Prüfung stellt jetzt projektbezogen fest, ob die Ressource in mindestens einem über AccessService::canViewOrOwn() sichtbaren Projekt verwendet wird; zusätzlich filtert collectPlannedHours() die Aufgaben auf genau diese Projekte, sodass auch bei erlaubtem Ressourcenzugriff nichts aus fremden Projekten durchsickert
  • Aufgaben und Teilprojekte ließen sich an fremde Projekte hängen (Issue #179). TaskService::create()/update() und SubProjectService::create()/update() prüften nur die Schreibberechtigung auf die eigene projectId, nicht aber, ob eine mitgesendete subproject_id bzw. parent_id überhaupt zu diesem Projekt gehört — mit Folgewirkung beim Löschen eines Teilprojekts (SubProjectService::deleteTasksForSubproject()) und im öffentlichen ICS-Feed (CalendarFeedService::buildIcs()), da beide über TaskMapper::findBySubProject() allein nach subproject_id filtern. Analog zum Schutz aus Issue #97 prüfen jetzt AccessService::assertTaskBelongsToProject() und assertSubprojectBelongsToProject() die Zugehörigkeit; deleteTasksForSubproject() und buildIcs() filtern die geladenen Aufgaben zusätzlich defensiv nach project_id, um vor dem Fix entstandene Altdaten abzudecken

Changed

  • Dokumentation aufgeräumt: Die Doku ist jetzt nach Zielgruppe getrennt — alles, was Nutzerinnen und Nutzer brauchen, liegt in nc_gantt/ und wird damit vom release.sh-Archiv mit ausgeliefert, das Repository-Root enthält nur noch Entwickler-Doku. Dafür sind FEATURES.md, INSTALL.md und die neue HOWTO.md nach nc_gantt/ gewandert; die Root-README.md beschreibt die zehn wichtigsten Anwendungsfälle des Werkzeugs, erklärt je Fall den gedachten Arbeitsweg und führt in einer Repository-Übersicht auf, welches Dokument für wen gedacht ist. Neu ist nc_gantt/HOWTO.md: eine Schritt-für-Schritt-Anleitung entlang eines durchgerechneten Beispielprojekts, die alle zehn Anwendungsfälle mit konkreten Feldwerten, Abhängigkeiten und Terminen durchspielt
  • Dabei korrigierte Verweise und Angaben: der Lizenz-Link in nc_gantt/README.md zeigte auf das nicht vorhandene LICENSES/AGPL-3.0-or-later.txt statt auf LICENSE.md; die Nextcloud-Anforderung stand mit „28–33" in README.md und „28–34" in FEATURES.md gegen die tatsächlichen 31–34 aus appinfo/info.xml; FEATURES.md nannte noch Version 1.5.0; INSTALL.md verwies auf ein dist/nc_gantt-<version>.zip im Repository, das es nicht gibt (dist/ ist nicht versioniert und release.sh baut .tar.gz), und in CONTRIBUTE.md trug ein Link auf RELEASE.md die Beschriftung release.sh. Alle relativen Links und Abschnitts-Anker der versionierten Markdown-Dateien lösen jetzt auf
  • Tests laufen künftig vor jedem Commit (Issue #185): setup-dev.sh installiert einen pre-commit-Hook, der Vitest und PHPUnit (über Docker) ausführt und den Commit bei Fehlern abbricht — genau die Stelle, an der die vier defekten Tests aus dem Ressourcen-Sichtbarkeits-Commit hängen geblieben wären. phpunit.xml schaltet zusätzlich failOnRisky und failOnWarning ein, damit ein Test ohne Assertion den Lauf rot färbt statt unauffällig grün zu bleiben, und CONTRIBUTE.md dokumentiert die Regel zu Mock-Defaults ohne with() in setUp()
  • Die Default-Stubs in setUp() von ResourceVisibilityServiceTest blockierten die spezifischeren Stubs der einzelnen Testmethoden — PHPUnit wertet für dieselbe Methode den zuerst registrierten Matcher aus, und der greift per any() bei jedem Aufruf. findByResourceId(), findByIds() und findByResourceIdExcluding() lieferten dadurch immer ein leeres Array, was vier Failures und zwei risky Tests verursachte. Die Defaults sind entbehrlich, weil PHPUnit-Mocks für den Rückgabetyp array ohnehin ein leeres Array liefern; der Produktivcode blieb unverändert
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerHm/UxN1WTCOMd9LklleINo7LKDOEAyn1zlsR5eTCaUmdGaN1m8Knqk/EKDYcj73
zWx7TXmT4cP91Ag7mCMwY/gRqJabF7TgeXiA7CeV7VE7/D/+apIatMWRT7zYCjkF
oxXKimNrtpDSKWNXKSQn6RfJaEHu5WnIoF4X9Nyx/wf1WoWoThyvWMSSW/3jdajV
WXCV4XQjzbTULw9T+Ini/e8vp6mXt5CrmCdyUDQQcj2t9PF9Hq3GvWT1VQlYA8Kf
IJuqnArSc4JdtJiJnozpOKSBkAbDPTrFytwUTVUO4XlrGIvyqHv14ZVmBM8UCMo6
yVNA9HEBW62/b8W+Z3BtCBTuBnkRk3Wka8qhMMbqOe8kl/1vQtTKQkaDdnXaShUs
18mfDHjLZhcIrJEwbtPvo9Xpf+IWEZeoiRzEqa30n70iZTkPEVv/GxNVmJ74XYH/
Xa/vMWI/sxtXKtKY60XBXXCEVMuyxR+vfIbJOpDCcFlKyWHiXPEdYh2Ac8Bs1hc7
bk+lolw8FEWISrFdUwYdOrfRr4CMW+h2CYzgZsDKKYrVn+hRUy5mVOL1QXZJbNJJ
wr9O/JrUbXAMGdhtLuRUwM4tsPgMF2+cTLMe+wuFzUjWBBNiqb8GKJP2K94hdurD
+VMd/p/TtU02M23ooysu2mft5RCmD0/QbP8yxEACbHg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.6.0
Release Details
UpdatedAug. 28, 2026, 2:34 p.m.
Changelog

Achtung: Diese Version setzt Nextcloud 31 oder neuer voraus. Die bisherige Untergrenze war Nextcloud 28. Mit dem Umstieg auf @nextcloud/vue 9 — unvermeidbar, weil Vue 2 seit dem 31.12.2023 End-of-Life ist und keine Sicherheitskorrekturen mehr bekommt — fällt die Unterstützung für Nextcloud 28, 29 und 30 weg. Installationen auf diesen Versionen bekommen das Update nicht angeboten und bleiben auf 1.5.4; sie werden von dort nicht weiter gepflegt. Wer die App weiter nutzen möchte, aktualisiert zuerst den Nextcloud-Server auf mindestens Version 31 (max-version bleibt unverändert 34).

Changed

  • Das Frontend läuft auf Vue 3.5 statt Vue 2.7 (Issue #150). Vue 2 ist seit dem 31.12.2023 End-of-Life und bekommt weder Sicherheits- noch Fehlerkorrekturen; die Nextcloud-Server-Generation, gegen die die App freigegeben ist, hat ihren Frontend-Stack längst auf Vue 3 umgestellt. Mit umgestellt sind @nextcloud/vue 8 → 9, @nextcloud/dialogs 6 → 7, Pinia 2 → 3 und @nextcloud/webpack-vue-config 5 → 7; die dreizehn Build-Pakete, die das Webpack-Preset ab Version 7 als peerDependencies erwartet (webpack, vue-loader, sass, babel-loader …), stehen jetzt explizit in den devDependencies. npm audit --omit=dev meldet damit 0 statt 14 Befunde — die vier an Vue 2 hängenden Advisories (vue, vue-template-compiler, vue-resize, vue2-datepicker) waren ohne diese Migration nicht auflösbar
  • appinfo/info.xml fordert jetzt Nextcloud 31 statt 28 (max-version bleibt 34). @nextcloud/vue 9 lässt die Unterstützung für Nextcloud vor 31 fallen; das ist die unvermeidbare Gegenleistung der Migration. Installationen auf Nextcloud 28–30 bleiben auf Version 1.5.4
  • Die drei Einstiegspunkte src/main.js, src/admin.js und src/dashboard.js erzeugen ihre Anwendung über createApp() statt new Vue(); Pinia wird als Plugin registriert (app.use(pinia)), das Weiterreichen der Store-Instanz an das Vanilla-Detailmodal per setActivePinia() bleibt unverändert. Die Vue.prototype-Zuweisungen im Dashboard-Einstiegspunkt sind entfallen — DashboardWidget.vue importiert t ohnehin selbst
  • Anpassungen in den 17 Komponenten an die API von @nextcloud/vue 9: Import-Pfade (@nextcloud/vue/components/NcButton statt @nextcloud/vue/dist/Components/NcButton.js), type="primary" → variant="primary" bei NcButton/NcActions, NcActionCheckbox und NcSelect auf modelValue (:checked/@update:checked bzw. :value/@input sind entfallen), :shown.sync → v-model:shown bei NcPopover. Dazu die Vue-3-Umstellungen im eigenen Code: beforeDestroy() → beforeUnmount() in GanttTable.vue, ResourceGrid.vue und GanttChart.vue (siehe Issue #149), this.$set()/this.$delete() durch direkte Zuweisung bzw. splice() ersetzt
  • css/main.css und der zugehörige Util::addStyle()-Aufruf im PageController sind entfallen. Die Datei enthielt einzig #content { margin-top: 0 !important } gegen eine Doppelmarge, die NcContent unter @nextcloud/vue 8 zusätzlich zum Nextcloud-Layout gesetzt hat. Unter Version 9 gibt es diese Marge nicht mehr, der Reset schob die App stattdessen um die Höhe der Kopfzeile nach oben — die Navigationsleiste lag dadurch unter dem Nextcloud-Header und war nicht mehr anklickbar. Der Mount-Punkt #nc-gantt-app bleibt als Wrapper im DOM stehen (Vue 3 rendert in das Element hinein, statt es wie Vue 2 zu ersetzen), nimmt aber nachgemessen exakt die Maße des Content-Bereichs an und braucht keine eigene Regel
  • ESLint nutzt das Preset @nextcloud/eslint-config/vue3. Es lässt vue/custom-event-name-casing auf dem Vue-3-Default (camelCase) laufen, während das Vue-2-Preset kebab-case erzwungen hat; da der Bestand durchgängig kebab-case verwendet — unter Vue 3 gleichermaßen gültig — setzt .eslintrc.js die Regel explizit auf kebab-case
  • Der <style>-Block in GanttChart.vue war mit 581 Zeilen ungescopt und wirkte damit auf die gesamte Nextcloud-Oberfläche statt nur auf die Komponente (Issue #151). Die 65 eigenen .nc-gantt-*/.nc-*-Regeln laufen jetzt über <style scoped>, mit :deep() dort, wo Overlays ihre Elemente per createElementNS() bzw. innerHTML außerhalb des Vue-Templates in .nc-gantt-container einhängen. Die Regeln, die auf frappe-gantt-eigene Klassennamen zugreifen (.gantt .bar & Co.), liegen gebündelt in src/vendor/frappe-gantt/theme.css direkt neben dem vendorten Quellcode der Bibliothek und werden von dort per index.js importiert; die beiden nicht unter .gantt verankerten Selektoren (.bar-wrapper.gantt-milestone .bar-label/.handle) sind dabei korrigiert. In App.vue laufen die drei Core-Überschreibungen (.app-navigation__content, #app-settings, .app-navigation .app-navigation-list) ebenfalls über :deep() in einem scoped Block. Abgesichert ist das durch vue/enforce-style-attribute aus eslint-plugin-vue, das ab sofort scoped für jeden <style>-Block in src/ erzwingt
  • TaskService bündelte auf 774 Zeilen drei Verantwortlichkeiten und brauchte dafür acht Konstruktor-Abhängigkeiten; der Schnitt trennt sie (Issue #153). TaskSchedulingService übernimmt Constraint-Propagation über EA/AA/EE/AE mit Lag, Kahn-Sortierung, Arbeitstagsrechnung, Zyklenprüfung und What-if-Simulation — CriticalPathService wird dort regulär injiziert, statt simulate() als Methodenparameter durchgereicht zu werden. TaskResourceAssignmentService übernimmt Auflösung und Persistierung der Ressourcenzuordnung inklusive MAX_TASK_RESOURCES und PT-Umrechnung. TaskService bleibt als CRUD-Fassade mit Berechtigungsprüfung, Validierung, Änderungsprotokoll und API-Aufbereitung: 311 statt 774 Zeilen, sieben statt acht Abhängigkeiten. Die Methodenrümpfe sind unverändert verschoben, es ändert sich keine Terminlogik. TaskServiceTest ist entlang derselben Grenzen aufgeteilt (TaskServiceTestCase als gemeinsame Basis, dazu TaskSchedulingServiceTest mit 39, TaskResourceAssignmentServiceTest mit 8 und TaskServiceTest mit 11 Fällen — exakt der bisherige Umfang), wobei die ausgelagerten Services weiterhin über ihren Aufrufer TaskService angetrieben werden, damit neben der Rechnung auch die Verdrahtung zwischen Fassade und Engine abgedeckt bleibt
  • Die 212 Zeilen CSS der Vanilla-Detaildialoge wurden zur Laufzeit als <style> in den <head> injiziert (src/detail/styles.js); sie liegen jetzt als src/detail/detail.css vor und werden von Webpack eingebunden (Issue #152). Der im Kopfkommentar genannte Grund — nicht von der Loader-Konfiguration abhängen zu wollen — trifft seit dem Vendoring nicht mehr zu, src/vendor/ importiert .css und .scss ebenfalls direkt. Dabei fiel auf, dass das Stylelint-Glob unquoted an die Shell ging und ohne globstar nur eine Verzeichnisebene traf: weder src/App.vue noch eine .css in src/vendor/ wurde je geprüft. Mit Quotes globbt Stylelint selbst; die dadurch aufgedeckten Findings in App.vue sind mitbehoben
  • Die PHP-Einrückung ist auf Tabs vereinheitlicht (Issue #170). nextcloud/coding-standard erwartet Tabs, die Codebasis nutzte durchgehend vier Leerzeichen — composer cs:check beanstandete dadurch 144 von 146 Dateien, der CI-Job wäre ab dem ersten Lauf dauerhaft rot und als Gate wertlos gewesen. Ursache der Drift war ein falscher Kommentar in .editorconfig, der dem Coding-Standard vier Leerzeichen unterstellte; der ist mitkorrigiert. Die Änderung ist rein mechanisch (php-cs-fixer fix): Einrückung, Klammern um einzeilige if-Rümpfe, sortierte use-Blöcke und einfache statt doppelte Anführungszeichen, wo keine Escape-Sequenz im Spiel ist. Der Commit steht in .git-blame-ignore-revs, damit git blame weiterhin die inhaltlich letzte Änderung einer Zeile zeigt

Added

  • appinfo/info.xml wird jetzt automatisch gegen das App-Store-Schema geprüft, statt den Fehler erst beim Upload zu erfahren: scripts/validate-appinfo.js (npm-Script validate:appinfo) validiert gegen die eingecheckte Schema-Kopie tests/fixtures/info.xsd — per xmllint, ersatzweise über lokales php oder das composer:2-Image — und prüft zusätzlich Semver-Format, Gleichstand mit package.json und den CHANGELOG-Abschnitt zur Version. release.sh ruft das als erstes Gate mit --online auf, lädt das Schema dabei frisch und meldet, wenn die Kopie veraltet ist
  • tests/Unit/AppInfoXmlTest.php führt dieselbe Schema-Validierung in der Testsuite aus (DOMDocument::schemaValidate()) und gleicht zusätzlich die Übersetzungen in l10n/ gegen die Store-Texte ab: gettext-Codes wie pt_BR werden als solche erkannt, und zu jeder übersetzten Sprache müssen <name>, <summary> und <description> in info.xml existieren
  • Die Stunden je Personentag sind konfigurierbar (Issue #169). Der Umrechnungsfaktor lag dreifach hartkodiert vor (TaskResourceAssignmentService, ResourceUtilizationService, ResourceGrid.vue) und wurde über gegenseitige Kommentarverweise von Hand konsistent gehalten — eine feste 8-Stunden-Annahme passt aber nicht zu jedem Arbeitszeitmodell. Der Wert liegt jetzt an genau einer Stelle: WorkTimeService liest ihn als App-Config-Wert hours_per_pt (Standard 8), validiert ihn gegen 1..24 und fällt bei unplausibel gespeicherten Werten auf den Standard zurück, damit die Auslastungsrechnung nicht durch 0 teilt; in ResourceUtilizationService entfällt damit auch die Konstante DEFAULT_CAPACITY_H, die denselben Vollzeit-Referenzwert doppelt hielt. Ins Frontend kommt der Faktor über den Initial State des PageController, sodass er schon beim ersten Rendern des Ressourcen-Rasters bereitsteht; GET/PUT /settings liefern und setzen ihn zusätzlich, wobei ein PUT ohne hours_per_pt den gespeicherten Wert unverändert lässt, damit ältere Clients ihn nicht auf den Standard zurücksetzen. Gepflegt wird er im Admin-Bereich neben Arbeitsbeginn und -ende
  • Die Vanilla-Detaildialoge sind für Screenreader und Tastatur verdrahtet (Issue #152): Die Dialoge verweisen über aria-labelledby auf ihre Überschrift, section() verknüpft Beschriftung und Bedienelement (bei Gruppen über role="group"), Knöpfe ohne Text bekommen einen Namen, Umschaltknöpfe melden ihren Zustand über aria-pressed, und die Datums-Plausibilitätsmeldung hängt per aria-describedby am Feld statt nur im title-Attribut des Icons zu stehen. Der Fokus beginnt beim Öffnen im Formular statt auf dem Schließen-Knopf und kehrt beim Schließen zum Auslöser zurück. Abgesichert durch payload.test.js (19 Fälle für Speicher- und Löschlogik) und accessibility.spec.js (6 E2E-Fälle) — für DOM-Code ist der E2E-Lauf die tragfähigere Absicherung als eine neue jsdom-Abhängigkeit. src/detail/README.md erklärt, warum das Modul außerhalb von Vue steht und welche Regeln daraus folgen; CONTRIBUTE.md verweist darauf
  • Die CI läuft. Der Workflow lag unter nc_gantt/.gitea/workflows/ci.yml und war eine reine GitHub-Actions-Datei; vier voneinander unabhängige Gründe verhinderten, dass davon jemals etwas ausgeführt wurde (Issue #170): Forgejo liest Workflows ausschließlich im Repo-Root, runs-on: ubuntu-latest passt auf kein Codeberg-Label (die gehosteten Runner heißen codeberg-tiny/-small/-medium), die Kurzform shivammathur/setup-php@v2 wird gegen Forgejos Action-Host aufgelöst, wo es die Action nicht gibt, und die Repo-Einheit „Actions" war deaktiviert. Die Datei liegt jetzt unter .forgejo/workflows/ci.yml, die App bleibt via defaults.run.working-directory erreichbar. Inhaltlich sind php-lint und php-tests zu einem Job zusammengefasst und laufen beide über die volle Matrix 8.1/8.2/8.3 (vorher lief PHPUnit nur auf 8.2, obwohl composer.json >=8.1 fordert), die Syntaxprüfung nutzt xargs statt find -exec (find liefert seinen eigenen Exitcode, nicht den von php -l — der Schritt konnte gar nicht fehlschlagen), und der JS-Job führt neu vitest mit 180 bislang nie automatisiert gelaufenen Tests aus, dazu validate:appinfo und l10n:check. paths-ignore hält die geteilten Codeberg-Runner von reinen Doku- und Screenshot-Änderungen frei

Fixed

  • Nach der Migration auf @nextcloud/dialogs 7 erschienen Statusmeldungen wie „Einstellungen gespeichert" als unformatierter Text oben links statt als Badge. Version 7 hat Toastify durch eigene Vue-Komponenten ersetzt, deren Styles nicht mehr im JS-Bundle stecken, sondern über den separaten Export @nextcloud/dialogs/style.css eingebunden werden müssen; ohne diesen Import lief der Toast-Container ungestylt im normalen Dokumentfluss mit. src/main.js importiert das Stylesheet jetzt. Die Toasts erscheinen dabei an der neuen Standardposition der Bibliothek unten links neben der Navigation statt wie früher oben zentriert — Version 7 bietet dafür keine Option mehr
  • testBuildIcsStripsCarriageReturnToPreventLineInjection prüfte das gesamte ICS-Dokument auf Freiheit von \r. RFC 5545 trennt Zeilen aber genau damit, und buildIcs() setzt sie korrekt als CRLF — der Test konnte nie grün sein und schlug auf main dauerhaft fehl. Der Schutz selbst greift; nur die Assertion war zu weit gefasst. Geprüft wird jetzt, was die Line-Injection tatsächlich ausschließt: kein einzelnes CR außerhalb der CRLF-Zeilentrenner, der eingeschleuste Titel erzeugt keine eigene ICS-Zeile, und er landet escaped im SUMMARY-Wert. Gegenprobe mit entfernter CR-Normalisierung in escapeText() schlägt weiterhin fehl, der Schutz bleibt also scharf getestet. Dass der Fehlschlag so lange unbemerkt blieb, lag daran, dass die Workflows nicht ausgeführt wurden (siehe Issue #170)
  • validate:appinfo brach im ersten CI-Lauf mit einer Meldung über einen nicht erreichbaren Docker-Socket ab (Issue #170). Die Validator-Kette in scripts/validate-appinfo.js probiert xmllint, dann php, dann docker und nimmt den erstbesten verfügbaren — „verfügbar" hieß dabei aber nur, dass das Binary existiert. Das Runner-Image bringt die docker-CLI mit, ohne dass ein Daemon erreichbar wäre, und hat weder xmllint noch php. isAvailable prüft jetzt, ob der Validator wirklich läuft (docker info statt --version), sodass die Kette korrekt durchfällt und bei gänzlich untauglicher Umgebung „Kein Schema-Validator gefunden" meldet statt eines Socket-Fehlers; zusätzlich installiert der JS-Job libxml2-utils, damit im CI immer xmllint gewinnt — der native XSD-Validator, ohne Daemon und ohne Netzwerk. Welcher Validator läuft, hing zuvor davon ab, was auf der Maschine zufällig installiert war
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegDK8JkRgxBUCFljhxHFzTzf37tjhwQ/k9E+DCDeVHxPECDOF0J3FbWmv2uqmm9R7
H8yyFfL/KFEDK3Jt24o2gujQZ9KlyBYJvrfwv//zvACtgGoErBUamME+FzFfK3x8
rRdXt1iXctmwutRvTNPj8uvfMJmo/vdbJRDGEihI/9wVdt4S+O3/OlpdjUg+QGFP
cMmlQwIAHAWrYSUllnN4GeryrFPGB7fLi/Nrhx0eAwQHUjIZHEb/s9ZxjmnYZd0c
8FJsgAUXfjowOIrDtWxr0P+HVj4c5qhGx4XEUFdfR0slriaL+zPbo2Li1wgsBA7r
+yoo8ocOJPaN3iCpRNL4z8q2sxEuWRt2Dx0I5ck6F7g+gpzDoTo5bFEvq4Eir0wN
lGHhyxcdEK653dTKOBYI07tCjJYU+9VxCbRzR7O+sVI412Tox5W5cYGl4WpN8aNH
00CX7PLaruaEjwuFdAvhY6SzsXA3RFmN3Dv9qFx3dl3HjoQ+OCqy89XzT3tbBN07
3aZO7OgyB26al2XdRFaaRQaVLqjJzUCz2B5nh+z9+L+QcBl3F9sE2sFZnkWwx3bp
MOdj2nCS11r3Z0utRI9uDEanys6HkqnpGed4WEGX9O+EfocqNg2b0wgyqjFNWbxI
n4uoeakhyWYOVHCUfzU8tn6kmU4b8n+GfpMjKPhBeRw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.4
Release Details
UpdatedAug. 27, 2026, 7:06 p.m.
Changelog

Fixed

  • appinfo/info.xml wurde vom Nextcloud App Store abgelehnt: der Sprachcode pt_BR in <name>, <summary> und <description> ist im Schema nicht enthalten — die Enumeration kennt nur BCP-47-Schreibweise in Kleinbuchstaben mit Bindestrich. Alle drei Vorkommen heißen jetzt pt-br
  • Die Kindelemente von <info> sind im Schema eine feste Sequenz; <settings> stand vor <dependencies> und wäre nach dem Sprachcode-Fix als nächster Validierungsfehler aufgeschlagen. Der Block steht jetzt an der vom Schema vorgesehenen Position hinter <dependencies>. xmllint --schema gegen https://apps.nextcloud.com/schema/apps/info.xsd läuft damit sauber durch
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureYbjiEkGYW1s8Xg9Jvrt4nNo/67zkrQDA16yfK2MzVa58R982qrLUupNbvUE/FscP
pFg9nlqxd81cpyH1ZhWOM7fMq98mPyjSUsJZ7xjYzNccF13xvDpBXFlfX4UyCknc
50Rmw9a8RxiCH0mEpH9zWjdanX194R8cUIMDY+BcymCgyBVRQub9yaL5iiHH3hNA
OOD+UOiSUNHns9R6sgZ+tF4OeoTufWR5//AfRmuL5qC5spyuP1iKr7FnzPar+/sS
pHKojfUyi0sAjlrThVZDo8fvVPqkkH4jgvVmNE41t1UOlVdajIRd2yhbHX0h0SSs
rmt4CQqNXAjLE/Wlq2lu9Viw4M2BhhPIfVb5YJFzaxjhB8RRy53rquXRL64f7vGa
agq0ri3eD+0ZWmMOlJmHaMv6AXYd0ZG8myb3PAyNC0NYXzrw2PAVL8jDfzmq2cNY
Q3rIh101vtGPytqanNS2VmtfZ8BX63rd4SLR4PJdRYrm3Wbqanx8WZa20eLgYF8S
M9xhxl5Jf8+TnYLwHi1sGm1ID2XJ4krfgOxiUlB8lGeSweUK1uueC0bmUDsrXyhJ
RMGST41TU1HqXx/3Eeh91JKq/Jbx15kPuxu7WNKyuSOj4vKbgFwBUSmHKDU6lvEZ
L/bnxZi965/4K3kvyJ8o2j5e4xUmJLeusi9Fg0yrRqE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.2
Release Details
UpdatedAug. 26, 2026, 5:18 p.m.
Changelog

Changed

  • Teilen-Dialog: das Eingabefeld für den einzuladenden Nutzer ist jetzt ein Suchfeld statt eines reinen Textfelds für die Benutzer-ID (Issue #144). Getippt wird nach Anzeigename, Benutzer-ID oder E-Mail-Adresse; ein neuer, admin-unabhängiger Endpunkt GET /api/v1/users/search (UserController::search(), IUserManager::search(), max. 20 Treffer, der eigene Account wird ausgeblendet) liefert dazu Anzeigename, Benutzer-ID und E-Mail als Vorschlagsliste. Gespeichert wird weiterhin die Benutzer-ID als principal_id – für Umgebungen mit LDAP/AD-Benutzerverwaltung war die zufällig generierte ID zuvor nicht ohne Umweg über die Benutzerverwaltung in Erfahrung zu bringen. Die Liste bereits geteilter Zugriffe zeigt ebenfalls den Anzeigenamen statt der rohen ID (ShareController::index() reichert die Antwort um display_name an, Fallback auf principal_id falls der Account inzwischen gelöscht wurde)
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureyLn/jlYbk3gIYfgIcjON0z467/xL9L6QNcmfgOa/4zhTJjLknXq7eu1UJddWWlLJ
NG2U9d/OEFO9wLHPYPGNiWgQMRIk+TGYlQVTXcZH3WpBxdJOm3XMnk5hq2kNB2FN
TJemqr/dxafGB2hTIgivPMXMnemdrtxWN1RAdzCxZIg9HwJWjG5JxdqHmURHwske
p/Ki/iZghKjrxbPIghbqtrvkCJRImbgVWY8AmUNUPLgotNIthiWFnkTDVhYWcreP
pWleWHxEn57UOtehff1aJa+hRI+8WVkx9iXArx0SThmOCYYgJ/HeG8dOOtmr9jSm
1nzETAL87YLxL/+qM6DyBIgNWhrq1oj9Q12bBm1+la477z6fpOgUlMjWkPEYRQi/
vhxirXwB4BZBYvaMEkNlN2U3+7BqgigGoaTcbN2rG/TP1JuOjdj9jEjMMRye0JbT
35XOoM/VPYGovrT9slHg8r3Wolyd8ioXVOu4IcMImvC5OrTzpKY7NLEc/S+cOKwz
ijOKcnoV0j++KxtQKKgEO96HJpEM0jteQVOGCoPbghjS3OFzkiB1bUVJAlu1jn54
38Q2zw8rZ0EMm1rEPmIrl72DIIXionbWKatREgy7QPwuL/WKZ2c+J1UIUTKck5eO
7M2VHh6P3FnIg/UsOCMu3U7ja0Y8vQ3YUo3tazLfob0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.1
Release Details
UpdatedAug. 25, 2026, 1:28 p.m.
Changelog

Added

  • Screenshot des gerenderten Gantt-Charts als PNG, SVG oder PDF (Issue #116): ein neuer „Screenshot"-Eintrag in der Chart-Toolbar öffnet ein Menü mit den drei Formaten, derselbe Export steht im Bearbeiten-Dialog des Projekts neben JSON und CSV bereit (dort mit vorgeschalteter Formatwahl statt Menü, openScreenshotFormatDialog()). Exportiert wird stets das gesamte Chart, nicht nur der sichtbare Scroll-Ausschnitt — frappe-gantt rendert die volle Breite und Höhe ohnehin als eine SVG. PNG und SVG klonen die aktuell gerenderte SVG und schreiben getComputedStyle() als Inline-Attribute (löst auch CSS-Variablen auf), sodass die Datei ohne das App-Stylesheet eigenständig darstellbar ist; sie folgen dem eingestellten UI-Theme, ein hinterlegtes Rechteck in --color-main-background deckt dabei auch die Fläche unterhalb der Aufgabenzeilen (Workload-Chart, Leerraum) ab, die sonst transparent bliebe. PNG rastert per Canvas mit doppelter Auflösung
  • Zeitraum-Auswahl für den PDF-/Druck-Export (Issue #116): vor dem Drucken fragt ein Dialog, welchen Zeitraum die PDF abdecken soll — gesamter Projektzeitraum (Vorbelegung), laufendes Kalenderjahr, „aktives Quartal" (Vormonat bis einschließlich übernächstem Monat) oder ein frei eingegebener Bereich. Der Vorschlag ist der tatsächlich von Balken belegte Zeitraum, nicht gantt_start/gantt_end — frappe-gantt legt darum großzügig Puffer, der andernfalls mitgedruckt würde. Umgesetzt wird der Zuschnitt als viewBox auf dem SVG-Klon; die dafür nötige Skala (Chart-Start, Pixel pro Millisekunde) schreibt GanttChart.vue nach jedem Render als data-Attribute an den Chart-Container, sodass auch Aufrufer ohne Zugriff auf die frappe-Instanz (Bearbeiten-Dialog) sie lesen können. Die Zeitraum-Logik steckt isoliert testbar in src/gantt/printRange.js (src/gantt/printRange.test.js)

Changed

  • Der PDF-Export druckt nicht mehr den Live-Container, sondern einen gestylten, selbst gerasterten Klon des Charts (Issue #116). Bisher wurde das Inline-SVG direkt gedruckt — wie fein die Browser-Engine das rastert, ist nicht steuerbar und fiel je nach Browser sichtbar grob aus. Jetzt wird der Klon mit 300 dpi bezogen auf die bedruckbare Fläche (A4 quer, 10 mm Rand → 277 × 190 mm) in ein Canvas gerastert und als Bild mit fest vorgegebener physischer Größe in den Druck gegeben; schlägt die Rasterung fehl, bleibt der SVG-Klon als Druckinhalt übrig und skaliert per CSS auf die Seitenbreite. Der Klon wird immer im hellen Theme berechnet (Papier ist weiß, ein dunkles UI-Theme ergäbe sonst dunkelgraue Flächen und helle Schrift) — dafür genügt ein ausgelagerter Container mit gesetztem data-theme-light, da Nextcloud die Stylesheets aller Theme-Varianten lädt und nur über dieses Attribut umschaltet. Das Ausblenden der übrigen Seite läuft über display: none statt visibility: hidden, sonst blieb die Fläche der App als Leerseiten erhalten; Nextclouds Hintergrundbild auf html/body wird für den Druck abgeschaltet. Aufgeräumt wird über afterprint mit Timeout-Fallback für Browser, die das Event nicht feuern
  • Der Toolbar-Eintrag heißt „Screenshot" (Icon MonitorScreenshot) statt „Export" (Icon ExportVariant) — „Export" ist im Projekt bereits für den Daten-Export nach JSON/CSV belegt, der Chart-Export ist demgegenüber eine reine Abbildung der aktuellen Darstellung
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturek6kpvF53IR8tnkOFXyoSPFPGlwVzO/RUTgI1p1ppRcFmoFPUyFeIokOqWA1OtD9q
wdo+qkQQPyJGph0Uwp+hwP9oGmeKWErnlv8HMdA5I5iaXIN7afGvdfxWfKgqWetw
tGmCF7l2IzMmT2Ol/3pKgCnFkXTDPCVeNINNbUdi7EjeRpbcbOGWt3+5UVwZgVTX
Z9/kEEPvjMV98s1zT5yBXcFvyqZA7bhhezmBkfzaLa0DAIczFJbhyFtR06WuS7bP
CWBCs/xvSRuCwo61oyXRAWLohz/2GsrMSa5jvPvnEG1XEHpGPVorCijAvsBm4sX5
yAZTrBm3zbNYObqVE2idxTH/eqP8xXtE38JFjpG5IahpeFNb9V2EbH1nrjfRKYcH
NVvXcXFREAmvorShKDE0ymFN+/BCjvU1vnJCnRD5RMsoSGUzXSVHPrF1emtusLHj
ltP22eYOCpraEpZheTkFftqvpbiQKVXsZ+8wY4dJnh5Ymi71loxZhggoH8s/pmp+
WbSx6MZwSPBqJ0HOJWckFiZXur2OQYOVLfXt8uPYNU/EZSoYDqm81GVMglQXxyM6
YDh0xXqwJ7Qft7k0O/3Fa5YLJGP4B4RvjU29N3/WDsfjuk7PeCSTPhRlTOfV/Fbn
jS83WVBavmyklhu0rNYuCYfdYvUxemIO3VuGmYzFXgI=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.0
Release Details
UpdatedAug. 24, 2026, 5:26 p.m.
Changelog

Added

  • Editierbare Vorgänger-Spalte in der Tabellenansicht, Teilschritt F von #117 (Issue #124): eine neue, über das Spaltenmenü einblendbare Spalte „Vorgänger“ zeigt die Abhängigkeiten einer Aufgabe in der aus MS Project bekannten Kurznotation (12EA+2d = Vorgänger Aufgabe 12, Ende-Anfang, zwei Arbeitstage Abstand; mehrere durch ; getrennt) und nimmt sie dort auch entgegen. Typ (EA/AA/EE/AE, englische Kürzel FS/SS/FF/SF werden beim Eingeben mit akzeptiert) und Abstand sind optional, 12 allein bedeutet also EA ohne Abstand. Geprüft wird beim Verlassen der Zelle: Syntax, Existenz der Aufgabe im selben Projekt, Selbstbezug, Dubletten und Kreisschluss über die gesamte Abhängigkeitskette – bei einem Fehler bleibt die Zelle im Bearbeitungsmodus und nennt den Grund im Klartext, die bisherigen Abhängigkeiten bleiben unangetastet. Gespeichert wird über dieselbe PUT /tasks/{id}-Route wie der Detail-Dialog (inkl. dep_mode), sodass der Server die Termine anwendet und durch die Nachfolgerkette propagiert; Balken, Pfeile und Typ-Labels im Chart zeichnen sich mit der Antwort neu. Bewusst ohne Undo-Toast, da ein Zurücknehmen nur der Termine (nicht der Abhängigkeit) beim nächsten Neuberechnen wieder überschrieben würde. Nur Aufgaben haben eine editierbare Zelle – Meilensteine hängen über task_id an genau einer Aufgabe, Projekte und Teilprojekte sind berechnete Strukturebenen. Notation und Prüfungen stecken isoliert testbar in src/gantt/dependencyNotation.js (src/gantt/dependencyNotation.test.js)
  • Zeitabstand (Lag/Lead) je Aufgaben-Abhängigkeit (Issue #124): neue Spalte lag_days in nc_gantt_task_deps (Version1032; der Name vermeidet das seit MySQL 8.0 reservierte Wort LAG und nennt die Einheit). Der Abstand zählt in Arbeitstagen über den Ressourcenkalender wie die übrige Terminberechnung, wirkt bei allen vier Abhängigkeitstypen auf deren Ankertag und darf negativ sein (Lead: der Nachfolger beginnt früher). Bestandsabhängigkeiten bekommen 0 und verhalten sich unverändert. TaskService::update() weist eine gesendete Vorgängerliste jetzt vollständig zurück (invalid_dependency, dependency_cycle), bevor die bisherigen Abhängigkeiten dafür gelöscht werden, statt Vorgänger aus fremden Projekten oder Zyklen zu speichern – letztere hätten die topologische Sortierung in recalculateProject() stillschweigend Aufgaben überspringen lassen

  • Konfigurierbare Spalten in der Tabellenansicht, Teilschritt E von #117 (Issue #122): ein Menü in der Tabellenkopfzeile blendet Spalten ein und aus, die Reihenfolge lässt sich per Drag & Drop der Kopfzellen ändern (barrierefreie Alternative: Strg+Pfeil links/rechts auf der fokussierten Kopfzelle), die Breiten per Ziehen am Spaltentrenner (Doppelklick setzt auf die Standardbreite zurück). Zusätzlich zu den bisherigen Spalten (Titel, Start, Ende, Fortschritt) stehen ID, Dauer (Kalendertage inkl. Start- und Endtag, bei Meilensteinen 0), Status (Issue #78, nur Aufgaben/Teilprojekte) und Ressource (Issue #103, zugeordnete Ressourcennamen einer Aufgabe) bereit – alle vier als reine Anzeigespalten, die das Inline-Editing und die Tastaturnavigation daher überspringen. Auswahl, Reihenfolge und Breiten werden pro Nutzer über den in Teilschritt B angelegten UI-Preferences-Endpunkt persistiert (neuer Key gantt_table_columns, kein Schema- oder Endpunktwechsel nötig); eine gespeicherte Konfiguration wird beim Laden validiert, sodass entfernte Spalten oder unplausible Breiten die Ansicht nicht beschädigen können. Die Tastaturnavigation folgt jetzt der sichtbaren Spaltenreihenfolge statt einer festen Liste. Reicht die Tabellenbreite für die gewählten Spalten nicht aus, scrollen Kopfzeile und Zeilen gemeinsam horizontal; die Obergrenze des Splitters steigt dafür von 560 auf 900 px. Die Spaltenlogik steckt isoliert testbar in src/gantt/tableColumns.js (src/gantt/tableColumns.test.js). Ein Sortieren der Zeilen nach Spaltenwert ist bewusst nicht enthalten – es kollidiert mit der Hierarchie Projekt → Teilprojekt → Aufgabe und der sort_order, die das Chart für dieselbe Zeilenreihenfolge braucht

  • Tastatur-Navigation in der Tabellenansicht, Teilschritt D von #117 (Issue #121): Tab/Shift+Tab springen zur nächsten bzw. vorigen editierbaren Zelle (überspringt Zeilen ohne Schreibrecht, springt am Zeilenende zur nächsten/vorigen Zeile), Enter speichert und springt in dieselbe Spalte der nächsten Zeile, Esc bricht ab und stellt den vorigen Wert wieder her. Pfeiltasten bewegen die Zellauswahl, wenn gerade keine Zelle im Editiermodus ist (innerhalb einer Zelle bleiben sie für Cursor-/Wertänderung nutzbar). Die reine Traversierungslogik steckt isoliert testbar in src/gantt/tableNav.js (src/gantt/tableNav.test.js). Die ausgewählte Zelle bekommt einen sichtbaren Fokusrahmen und wird per scrollIntoView() ins Sichtfeld gescrollt – über den bestehenden bidirektionalen Scroll-Sync (Issue #119) folgt das Gantt-Chart automatisch mit. Neue ARIA-Rollen (grid/row/columnheader/gridcell, aria-readonly je Zelle) machen die Tabelle für Screenreader als Grid erkennbar
  • Read-only Tabellenansicht links vom Zeitstrahl, Teilschritt B von #117 (Issue #119): neue Komponente GanttTable.vue zeigt dieselben Zeilen wie das Chart (Titel mit Einrückung nach depth, Startdatum, Enddatum, Fortschritt), gespeist aus buildRowModel()/buildTableRows() (siehe Teilschritt A, Issue #118) – Reihenfolge, Filter-Dimming, kritischer Pfad und Projektfarben bleiben so exakt konsistent zum Chart. Zeilenhöhen sind fest an frappe-gantt gekoppelt (40 px = bar_height 22 + padding 18, Kopfzeile 52 px = header_height). Ein Toolbar-Button blendet die Tabelle ein/aus, ein Splitter macht die Breite verstellbar (160–560 px), vertikales Scrollen ist bidirektional mit .nc-gantt-container synchronisiert. Sichtbarkeit und Breite werden pro Nutzer über einen neuen, generischen UI-Preferences-Endpunkt (GET/PUT /api/v1/ui-preferences, SettingsController über IConfig::get/setUserValue() als JSON-Blob) persistiert, damit Teilschritt E (Editieren) das Schema nur noch um weitere Keys erweitern muss, ohne Endpunkt oder Speicherformat anzupassen. Rein lesend – Editieren folgt in Teilschritt C
  • Projekt-Export/-Import (JSON, format_version: 2) kann jetzt optional die von den Aufgaben referenzierten Ressourcen inkl. Gruppe, Wochenmodell, Arbeitskalender+Feiertagen und Tageskapazitäten mitnehmen, statt wie bisher nur die rohe resource_id in eine Zieldatei zu schreiben, die dort ungeprüft übernommen wurde – auf einem System ohne identische Ressourcen-IDs ging die Zuordnung damit verloren oder landete schlimmstenfalls bei einer fremden Ressource (Issue #115). Vor dem JSON-Download entscheidet ein Options-Dialog, was mitexportiert wird (Ressourcen / Verfügbarkeiten / Kalender, gestaffelt); vor dem JSON-Import zeigt ein passender Dialog, wie viele Ressourcen die Datei enthält, und lässt auswählen, was übernommen wird sowie ob bei Namensgleichheit die vorhandene Ressource wiederverwendet (Default) oder immer neu angelegt wird. Referenzen werden ausschließlich über eine Mapping-Tabelle aufgelöst und nie roh durchgereicht; nicht auflösbare Referenzen werden verworfen (Regressionsschutz zu #112/#113). Neu angelegte Ressourcen/Gruppen/Kapazitäten gehören stets dem importierenden Nutzer. Wochenmodelle/Kalender/Feiertage sind weiterhin admin-only anlegbar – ohne Adminrechte werden nur namensgleiche vorhandene Einträge gemappt. CSV bleibt unverändert aufgabenzentriert mit Ressourcen nur als ID-Referenz; der volle Ressourcenumfang ist JSON-only. Alt-Exporte ohne resources-Schlüssel importieren unverändert wie bisher, ohne Options-Dialog
  • Inline-Editing der Basisfelder in der Tabellenansicht, Teilschritt C von #117 (Issue #120): die Zellen für Titel, Start, Ende und Fortschritt sind direkt editierbar und aktualisieren die Balken im Chart sofort. Gespeichert wird über die bestehenden Store-Actions (updateTask/updateSubproject/updateMilestone/updateProject) inkl. Terminpropagation und dem vorhandenen Undo-Toast für Aufgaben (analog Drag & Drop im Chart); ein lokaler pendingEdits-Overlay zeigt die Änderung optimistisch an und rollt sie bei einem fehlgeschlagenen Request mit Fehlermeldung zurück. Die Schreibrechte löst tableRows.js je Feld über store.canEditProject() auf (titleEditable/startEditable/endEditable/progressEditable) – Projekte und Teilprojekte bleiben bei Datum und Fortschritt read-only, da diese Werte aus den Aufgaben berechnet werden. Geprüft wird: Titel nicht leer, Enddatum nicht vor Startdatum (Meilensteine ausgenommen, deren Start- und Endspalte dasselbe milestone_date zeigen) und Fortschritt 0–100. Im Simulationsmodus rufen Datumsänderungen an Aufgaben simulateTaskChange() statt einer echten Speicherung auf
  • Ressourcen-Überbuchungswarnung (Issue #82): die Kapazitätsplanung war bisher reine Datenerfassung ohne Abgleich mit dem tatsächlich verplanten Aufwand. Der neue Endpunkt GET /api/v1/resources/{id}/utilization?from=&to=&group= (ResourceUtilizationService/ResourceUtilizationController) stellt den geplanten Aufwand aus den Aufgaben-Ressourcen-Zuordnungen – gleichmäßig auf die Arbeitstage der Aufgabe verteilt, über WorkCalendarService mit ressourcenspezifischen Arbeitstagen und Feiertagen – der erfassten Tageskapazität gegenüber. Mehrfach-Ressourcen-Zuordnungen (Issue #103) samt Alt-Aufgaben werden berücksichtigt, ohne gepflegte Tageskapazität gilt ein Fallback von 1 PT/Tag. Im Ressourcen-Raster färben sich die Zellen nach Auslastungsgrad (> 90 % gelb, > 100 % rot) und der Tooltip zeigt geplante Stunden gegen Kapazität samt Aufschlüsselung nach Projekt und Aufgabe; das Workload-Chart im Gantt markiert überbuchte Perioden jetzt rot. Übersetzungen für alle sechs Sprachen ergänzt
  • Nachgetragen – beide Funktionen wurden bereits mit 1.4.2 ausgeliefert, waren dort aber nicht im Changelog dokumentiert:
  • Einheitliches Statusmodell für Teilprojekte und Aufgaben (Issue #78): neues Feld status (not_started/in_progress/blocked/done) auf nc_gantt_subprojects und nc_gantt_tasks ergänzend zum numerischen Fortschritt, Bestandsdaten leitet Migration Version1030 daraus ab. Validierung per StatusValidator (Whitelist analog ColorValidator), Status-Dropdown in den Detailformularen, blockierte Balken im Gantt schraffiert und mit Warnsymbol, blocked_count je Projekt im Rollup sowie ein Badge in der Projektliste
  • Mehrere Ressourcen mit individuellem Aufwand je Aufgabe (Issue #103): bis zu vier Ressourcen je Aufgabe mit eigener Aufwandsschätzung in der Einheit der Ressource (neue Tabelle nc_gantt_task_resources, Migration Version1029, Backfill aus den vorhandenen resource_id/work-Werten). Das manuelle Work-Feld entfällt: TaskService::resolveResources() summiert die Einzel-Aufwände zur Gesamt-Work (1 PT = 8 h) und übernimmt die erste Zuordnung als primäre resource_id, die weiterhin für Arbeitskalender und CPM maßgeblich ist. Ressourcen-Filter sowie CSV-/JSON-Export und -Import berücksichtigen alle Zuordnungen statt nur der primären

Changed

  • buildFrappeRows() (src/gantt/frappeRows.js) mischte bisher Datenmodell und frappe-gantt-Formatierung: die Einrückungstiefe steckte als Leerzeichen im name, Zustände (Dimming, kritischer Pfad, Summenbalken) hingen in custom_class – eine geplante Tabellenansicht neben dem Gantt-Chart (Issue #117) hätte dieselbe Logik duplizieren müssen (Teilschritt A, Issue #118). Neue Funktion buildRowModel() (src/gantt/rowModel.js) liefert dieselbe Zeilenliste jetzt als neutrales Modell (id, type, entityId, depth, title, start, end, progress, parentId, editable, matched, critical), resolveMilestoneDate() wandert mit ins Modell. buildFrappeRows() ist nur noch ein dünner Adapter darüber, der name/custom_class/dependencies wie bisher erzeugt. Reines Refactoring ohne sichtbare Änderung, per Fixture-Vergleich gegen die alte Implementierung geprüft; neue Unit-Tests (src/gantt/rowModel.test.js, npm run test:unit, via neu eingeführtem vitest) decken Hierarchie, Einrückungstiefe, Meilenstein-Datumsauflösung und Filter-Dimming ab

Fixed

  • Abhängigkeitsfortschreibung bei Aufgaben lieferte für alle vier Abhängigkeitstypen (EA/AA/EE/AE) einen um einen Arbeitstag zu langen Nachfolger-Termin – bei EA zusätzlich mit falschem Anschlusstag, da der Nachfolger auf dem Endtag des Vorgängers statt am folgenden Arbeitstag begann und dadurch mit dem Vorgänger überlappte (Issue #114). Ursache war eine Off-by-one-Vermischung zwischen der inklusiven Arbeitstagszählung und der exklusiven Datumsfortschreibung in TaskService::applyConstraint()/computeMultiConstraint(). Betroffen sind sowohl das direkte Setzen einer Abhängigkeit als auch die Terminpropagation nach Drag & Drop und die What-if-Simulation. Verhaltensänderung: Bestehende Pläne mit EA-Abhängigkeiten verschieben sich beim nächsten Speichern/Neuberechnen um einen Arbeitstag nach vorne (auf den korrekten Anschlusstag); bei AA/EE/AE verkürzt sich der zuvor fälschlich verlängerte Nachfolger-Zeitraum um einen Arbeitstag, die ursprüngliche Dauer bleibt jetzt garantiert erhalten. Abhängige Meilensteine (Dauer 0) sind unverändert korrekt und von diesem Fix nicht betroffen

  • Security: jeder Nutzer mit Schreibrecht auf ein eigenes Projekt konnte jede in der Instanz existierende resource_id einer eigenen Aufgabe zuordnen – unabhängig vom Eigentümer und ohne jede Share-Beziehung (Cross-Tenant-IDOR, Issue #112). TaskService::resolveResources() lud die vom Client gesendeten IDs bisher nur über ResourceMapper::findByIds(), also als reine Existenzprüfung ohne Berechtigungscheck; da die IDs fortlaufende Integer sind, waren sie trivial zu erraten. Der Listen- wie der Legacy-Einzelfeld-Pfad in create()/update() filtern referenzierte IDs jetzt über ResourceMapper::findVisibleToUser(), nicht sichtbare IDs werden wie unbekannte verworfen (Liste) bzw. nicht übernommen (Einzelfeld, der bestehende Wert bleibt erhalten). Regressionstests decken beide Pfade für create() und update() ab

  • Feiertage ließen sich nicht in mehreren Regionskalendern führen: POST /admin/holidays lief in einen HTTP 500, sobald dasselbe Datum in einem zweiten Kalender angelegt wurde, weil der Unique-Index aus Version1006 allein auf holiday_date lag, obwohl Feiertage seit Version1013 über calendar_id kalenderbezogen sind. Version1031 verschiebt den Index auf (holiday_date, calendar_id); die Eindeutigkeit globaler Feiertage sichert weiterhin der Upsert in HolidayAdminController::create(). Betroffen war neben der Feiertagsverwaltung auch der Kalender-Import aus einem Projekt-Export, wo der Fehler still verschluckt wurde. Zweitens war GET /admin/holidays admin-only, während work-calendars und work-schedules lesend für alle offen sind – für normale Nutzer blieb der Store deshalb immer leer (keine Feiertage im Projekt-Export, keine Markierung arbeitsfreier Tage im Ressourcen-Raster); der Lese-Endpunkt zieht nach dem Muster der übrigen Stammdaten auf /api/v1/holidays um (NoAdminRequired), Anlegen und Löschen bleiben unter /api/v1/admin/holidays. Ergänzend werden Feiertage jetzt mit ihrem Kalender gelöscht (WorkCalendarController::destroy(), HolidayMapper::deleteByCalendar()) und Bestandswaisen bereits gelöschter Kalender in Version1031 aufgeräumt – über verweisende Ressourcen haben sie bis dahin weiter als arbeitsfreie Tage gewirkt
  • Die Installation der Tabelle nc_gantt_task_resources scheiterte auf Instanzen, deren Tabellenpräfix die von Nextcloud aus Oracle-Kompatibilität erzwungene 30-Zeichen-Grenze für Bezeichner reizt: der von Doctrine automatisch vergebene Primary-Key-Name (Tabellenname + _pkey) wird mit Präfix oc_ zu lang (Issue #132). Die Primärschlüssel-Definition bekommt – analog zu den bestehenden Migrationen für nc_gantt_resource_capacity und nc_gantt_resource_groups – einen expliziten kurzen Namen
  • Der absolut positionierte Toggle-Button der Nextcloud-App-Navigation ragte in den App-Content hinein und verdeckte dort den Titel des ersten Eintrags in der Projekt-Rollup-Leiste (Issue #134); der linke Innenabstand reserviert jetzt Platz für den Button
  • Ein leerer Balken in Höhe der Kopfzeile erschien oberhalb des App-Inhalts, weil der CSS-Reset für den Nextcloud-Content-Wrapper an main#content.app-nc_gantt gekoppelt war (Issue #126). Ändert sich das Markup zwischen Nextcloud-Versionen (Klasse oder Tag), griff der Reset nicht mehr und der von NcContent (#content-vue) selbst gesetzte margin-top: var(--header-height) addierte sich zum Rand des Cores. Der Reset hängt jetzt robust an #content mit !important, ohne Abhängigkeit vom app-spezifischen Klassennamen
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegO8Qmtdwg8LOBKr5G+mq4co374hsqqSN6oCIRV9atUx0KqogPxisJiIExkPoSIbu
e4EeIaUzg2alhZsKBLWOwR3Ab3fNm5vvd2DzJn6Hei7A70woz9m/LXkVlcx3vALW
bwolClUX1XCZOBVo/kSS5AOVwak3/huzT1iF9xH9BnpdzVuOjZ563LYwSayfb2Gq
SkWNfLZlKlDkTTF2PIszrNmaa89GmyqTbraxSP16/9nYdIj8ZU0zrhC3HtpihgAT
wya2lwnMf6I73HU0xxgT/+ITTmurx4eHwKWH80P4tjg/Y9mnHODIpAslbW3351mc
qdTHXIBW1MB1YGCQ+JPt11v1X2ccn29kEF9d1nkizKZ1+Du+LJQhPhwHiQxPWSwAUlTlQ4tj1lbe3+FJ1jsVTCqWbDUN02EoV7ZwtN15/HLJfM6w4drUi8ThJ1aLG3v6
cIwMJjYgWeepxR2gv4YgBf4u7txodm5bAWuzySk4rfAG1dBxmie/bfNnWmQ9LrYy
spwKc73gsPhg03ll+jp0b27fJfNF4IpOysz3i+r1ExVfeNIWWO91JPatO2pXZh4O
q0rczM8wYvctvcEkKQIL67HoIrKw4GVIXfqM9Oc8e/Ay7d/NPL+BjpCO6zvoFBY+
SRmjit15YmGW4EQwweQfKXuZ2YeySj+7OjhKHpKfvfg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.2
Release Details
UpdatedAug. 20, 2026, 4:41 a.m.
Changelog

Changed

  • release.sh fragt die Versionsnummer jetzt interaktiv ab, statt sie nur aus appinfo/info.xml zu lesen: zu Beginn werden die aktuelle Version und das letzte Release-Tag angezeigt und es wird gefragt, ob eine neue Nummer vergeben werden soll (Vorschlag: nächster Patch-Level). Die Eingabe wird auf das Format X.Y.Z, auf einen echten Versionssprung und auf ein noch nicht vergebenes Tag geprüft und dann nach appinfo/info.xml, package.json und package-lock.json geschrieben. Fehlt ein Changelog-Abschnitt zur neuen Version, warnt das Skript; anschließend bietet es an, den Bump als chore: Version X.Y.Z zu committen — nötig, weil der Tag-Name den Commit-Hash enthält. Für nicht-interaktive Läufe gibt es --version=X.Y.Z und --no-bump; ohne TTY wird nie gefragt, make release bleibt also unverändert nutzbar
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturepoVHZxteU3AR2cpRRV98jNfv4j7rWnLEd3kbpn5Dp4T1ubAI+0QlRf9jxi0BaYz1
+1dg+3Yt25uN6Slp1FHiw7kyqt5HJ60XgnOWo+Zt0YBHSnWqLDhF8zxPEnQCkF+v
nUvscpcoQY2bRiIBZhVWVgeAD592uvOA4gfRDrUyLVDKjOrzBu3ScbNvCqTqE9IC
vs8qZkRLIcaD6tKoSLT2pZqmwBoCdClvEw5D5v40WSvLq232BZnXpQ4ogG3QvJdL
mYO8HlPZKVOxANp0MGnWle4IFRMbMt22Ebpkjc18Q9Q4AIv/Sd3zTsc+uMONTM95
s0GXvLSm95o0UCFZ1CL7fCEq7NwlprjVrTeWGmlJNVjHJuIyRKHEz5DR12s0lAGm
/rKGWjoVP+Wvk3upRizR/5y2fpWoiwuDqCtz9gQN6+HHYLaqLvYqsAJ/SxmtLtP7
8B2rK61ClKowz4phbTZdrCaF8w3IWnFFBTFquIVFuN2jUoHvdCMtd0910sLAMY5M
Xs9nk0FVsP23zKXlQpOojq7ouCQagRhsnBJDLxWRR+NIq582f7SDgcrkBqxlGJX4
/Ql9V+2iFx61QANCTjdJIO3LbvbK60vGos+ency0GfvSDbPovDAVyJzt8ZUh5BNw
UXQ4XOHJZAagAr36rKttk1DQbZZ/2bUEq231c96m83g=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.1
Release Details
UpdatedAug. 17, 2026, 7:43 p.m.
Changelog

Fixed

  • Die Meilenstein-Raute hatte eine fest verdrahtete Farbe (#fbbf24) statt der Projektfarbe zu folgen, obwohl Balken für Aufgaben und Teilprojekte bereits korrekt eingefärbt wurden (Issue #102). Zusätzlich wurden die separaten – und wirkungslosen – Farbwähler bei Teilprojekt und Aufgabe entfernt, da deren Anzeige ohnehin immer der Projektfarbe folgt
  • „Meilenstein hinzufügen" im Drei-Punkte-Menü der Projektliste nutzte zwei aufeinanderfolgende window.prompt()-Dialoge (Name, dann Datum als Freitext) statt des regulären Detail-Dialogs (Issue #101). Der Klick emittiert jetzt add-milestone analog zu „Aufgabe hinzufügen" (Issue #91) und öffnet denselben Meilenstein-Dialog wie beim Bearbeiten, mit vorbelegtem Projekt
  • Im festen Datumsmodus des Aufgaben-Detaildialogs konnte ein Enddatum vor dem Startdatum eingegeben und gespeichert werden (Issue #100). Jetzt zieht ein über das Enddatum hinaus verschobenes Startdatum das Enddatum mit; ein zu frühes Enddatum erzeugt ein rotes Fehler-Icon am Feld, hält den Fokus dort und deaktiviert den Speichern-Button, bis ein gültiges Datum eingetragen ist (dateField() in fields.js mit neuer setError()-Methode)
  • Eine Aufgabe mit Abhängigkeit behielt trotzdem die manuell eingegebenen Start-/Enddatumsfelder, die beim Speichern durch die Abhängigkeitsberechnung überschrieben wurden – für Nutzer nicht nachvollziehbar (Issue #99). Der Aufgaben-Detaildialog hat jetzt denselben Fixed-date/Dependency-Umschalter wie Meilensteine (Issue #90): im Dependency-Modus ersetzt eine Dauer (Tage) die Datumsfelder, die Abhängigkeit bestimmt den Termin. Serverseitig wendet TaskService::create() neu hinzugefügte Abhängigkeiten sofort an (bisher nur update()), damit auch neu angelegte Aufgaben keinen die Abhängigkeit ignorierenden Platzhaltertermin behalten
  • Der Speichern-Button des Detail-Dialogs blieb nach dem Hinzufügen einer Abhängigkeit deaktiviert: die Gültigkeitsprüfung hing allein am input-Event, das bei einem <select> vor dem change-Event feuert – zu diesem Zeitpunkt war die Abhängigkeit noch nicht in der Liste. detailModal.js wertet jetzt zusätzlich change aus, der Abhängigkeits-Editor (fields.js) meldet jede Mutation (Hinzufügen, Entfernen, Typwechsel) und der Fixed-date/Dependency-Umschalter in taskForm.js den Moduswechsel über ein eigenes bubbelndes change-Event

Added

  • CONTRIBUTE.md und setup-dev.sh für neue Mitwirkende (Issue #98): Projekt-Leitlinien (FOSS, Datensparsamkeit, Security), VIBE Coding ausdrücklich erlaubt, sowie der komplette lokale Dev-Setup-Weg (Docker-only, Nextcloud + MariaDB unter localhost:8088, optionales E2E-Setup, Git-/Bot-Auth). setup-dev.sh automatisiert diese Schritte, konfigurierbar über --workdir/--no-e2e/--no-docker-install
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturetYcUvVtDEQrAakG32EQF1atSOmz91/8XudCUh60A9r/AxMv+AA5AX2rIvNYDkEn0
OuTmXA6Iz0DoVkdij11MPDhfLaUy0mXeLbwB5fj/xF/cFYcNeCFGKD3k2jyRwOTo
2n1sEp0r8sn0fitHzLfhcB/By0VJkZyhaO/NctP01XfcSQM7lGEOeY/uagtGaDTI
JnPLoQTHo0nUOv4y7jUMZuurEgBOc4yeUxTKv4A/m642hvNQIS0q6K9ON41JHKIO
UtI7DbDWSboVhct1Go3zoj5XcaAGG9ZiwSkJDt/BXT5tWH4SOnSn7mys0j1F6Y0g
vIJWjz6S2RlekwIrIxgyaeEVBOeQ4OM1ilJGWKXDyYJM/0IBxqnqRXDkAFlOopDo
xezlcFdWTkYL5OBKMFJhKuTy7dEQ9hxx5Nz4+vLbNV8gDJZBI+5yI5m8W+809WIH
iKUpIda1ShOblvYH6Ek5AzBdTmoFQ6T4f7iWT7j1i4uUP2DsgehjGVl8yDIT/wyx
nV9t7koXapu1WeXrunSa++mXsi3lceNpthdz5/cUEOe9yWvc1rrulRhiWzePga8H
zjVQYGAIPvwYkWa47U6Zh7HBsQaS+9yQ8NDnD49SS4jUWTGlwNqfM+q9fZ16/G9d
7l4chZUPPbBv+xWfdxmFw+MC5zdkAq27isHToTqnm+w=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.0
Release Details
UpdatedAug. 13, 2026, 6:41 a.m.
Changelog

Added

  • Meilensteine werden jetzt immer direkt unter ihrem übergeordneten Objekt angezeigt statt gesammelt am Projektende (Issue #90): Meilensteine lassen sich neu auch einer Aufgabe zuordnen (bisher nur Projekt/Teilprojekt, Spalte task_id war bereits vorhanden, es fehlte nur die UI), das Auswahlfeld im Detail-Dialog heißt entsprechend „Übergeordnetes Objekt" statt „Teilprojekt". Zusätzlich können Meilensteine ihr Datum jetzt statt eines fixen Werts aus einer Abhängigkeit zum übergeordneten Objekt beziehen (EA/AA/EE/AE, analog zu Aufgaben-Abhängigkeiten; neue Spalte dependency_type, Migration Version1028) – da ein Meilenstein keine Dauer hat, bestimmen EA/EE das Ende und AA/AE den Start des Zielobjekts. Die Berechnung übernimmt der neue MilestoneDateService (serverseitig live bei jedem Abruf sowie im ICS-Feed neu aufgelöst, nicht persistiert propagiert) sowie clientseitig in frappeRows.js für sofortige Reaktivität ohne Neuladen
  • Undo für Drag-&-Drop-Verschiebungen inkl. Propagationskette (Issue #85, Umsetzung der einstufigen Empfehlung: letzte eigene Operation, kein Redo, zunächst nur Drag & Drop): Migration Version1027Date20260810020000 ergänzt nc_gantt_change_events um op_id (UUID) – alle Change-Events einer zusammenhängenden Operation (Drag + gesamte Propagationskette B→C→D) erhalten dieselbe op_id, erzeugt in TaskService::update()/propagateDates() und über ChangeEventService::log() protokolliert (dieses Change-Tracking war bisher tote Scaffolding-Logik ohne Aufrufer). Neuer Endpoint POST /api/v1/changes/{opId}/revert (ChangeEventController::revert, ChangeEventService::revertOperation) wendet alle Change-Events der Operation invers an, in einer DB-Transaktion mit Rechteprüfung über AccessService::canEditOrOwn(). Konfliktschutz: Revert wird mit HTTP 409 abgelehnt, sobald der aktuelle Feldwert nicht mehr dem protokollierten new_value entspricht (jemand hat zwischenzeitlich erneut geändert); der Revert selbst erzeugt wiederum neue Change-Events, die Audit-Historie bleibt lückenlos. Frontend: nach jedem gespeicherten Drag zeigt GanttChart.vue einen Toast „N Termine verschoben — Rückgängig" (showUndo aus @nextcloud/dialogs, 10 s sichtbar) sowie ein globales Ctrl+Z für die letzte eigene Operation der Session (stores/projects.js: lastOperation, revertOperation(), undoLastOperation())
  • Kalender-Export als abonnierbarer ICS-Feed pro Projekt (Issue #83, Variante A aus dem Umsetzungsvorschlag): neue öffentliche Route GET /apps/nc_gantt/projects/{id}/calendar.ics?token=… (regulärer, unauthentifizierter CalendarFeedController, damit externe Clients wie Thunderbird, Nextcloud Kalender oder mobile Apps ohne Nextcloud-Login abonnieren können) liefert Meilensteine als ganztägige VEVENTs (◆-Präfix), den aus den Aufgaben abgeleiteten Projektzeitraum sowie optional Teilprojekt-Zeiträume (Schalter „Auch Teilprojekt-Zeiträume einschließen", standardmäßig aus, um den Feed bei großen Projekten nicht zu überladen). Zugriff erfolgt über einen pro Projekt generierten, 256-Bit-zufälligen Token in der URL (CalendarFeedService, neue Spalte calendar_token auf nc_gantt_projects, Migration Version1026), der über einen neuen „Kalender-Feed"-Bereich im Freigabe-Dialog angezeigt, kopiert und jederzeit widerrufen/neu generiert werden kann (CalendarTokenController, nur für Projekt-Admins/-Eigentümer). ICS-Generierung ohne neue Abhängigkeit, RFC-5545-konform (Escaping, Zeilenfaltung)
  • What-if-Simulationsmodus für Terminverschiebungen (Issue #84): neuer Endpunkt POST /api/v1/projects/{id}/simulate führt Terminpropagation und CPM rein im Speicher aus (kein DB-Schreibzugriff) – TaskService::simulate() nutzt die neu extrahierte, stateless propagateDatesCore()-Kernlogik, die auch der persistierende update()-Pfad verwendet, CriticalPathService::computeFromTasks() erlaubt CPM auf einem hypothetisch veränderten Task-Datensatz. Frontend: „Simulation"-Toggle in der Gantt-Toolbar mit Banner inkl. Projektende-Delta in Arbeitstagen, Geisterbalken für die Ist-Termine (analog Baseline-Overlay) sowie Verwerfen/Übernehmen. Drag & Drop ruft im Simulationsmodus simulate() statt einer echten Speicherung auf. Szenarien bleiben bewusst flüchtig (kein Speichern benannter Szenarien), „Übernehmen" wendet die Änderungen als einzelne Task-Updates an, Simulation läuft pro Projekt
  • Neuer Eintrag „Aufgabe hinzufügen" im Drei-Punkte-Menü der Projektliste (Issue #91): legt wie der „Neu"-Button eine Aufgabe an, belegt dabei aber das gewählte Projekt direkt vor, sodass der Projekt-Auswahlschritt in der Maske entfällt
  • Such-/Filterleiste für Projekte, Teilprojekte und Aufgaben (Issue #81, Stufe 1: rein clientseitig auf Basis der bereits geladenen Daten aktivierter Projekte): Freitextsuche im Titel, Risiko, „überfällig" (Ende < heute und Fortschritt < 100 %), „über Budget" (Ist-Kosten > geplantes Budget), Ressource und „ohne Deck-Verknüpfung". Neuer Pinia-Store stores/filters.js (Zustand pro Nutzer in localStorage persistiert) und reine Matching-Logik in gantt/filterMatch.js. Nicht passende Balken werden in GanttChart.vue gedimmt statt ausgeblendet (Abhängigkeitspfeile und Zeitkontext bleiben lesbar), Projekte/Teilprojekte gelten als Treffer, sobald ein Nachfahre passt. Dieselbe Filterleiste (components/FilterBar.vue) dimmt in ProjectList.vue nicht passende Projekte; für nicht aktivierte Projekte sind nur Titel-Treffer zuverlässig auswertbar, da deren Aufgaben/Teilprojekte nicht geladen sind

Changed

  • Freigabe für Nextcloud 34 (Issue #94): max-version in appinfo/info.xml von 33 auf 34 angehoben, unterstützter Bereich damit NC 28–34. Verifiziert gegen Nextcloud 34.0.2 mit PHP 8.5 und MariaDB 11.4 (148 Unit-Tests, 39 E2E-Tests)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerdYQI+Semnk0cWtKqUZinVDHk0KpxyqB5Ox4NBL9pyf5nrpK8xZpe9lBxc6zhDfW
8adLfVIwQtX+bI7S1O05xm0hwrUHQQ2ii6ADD8mlAxnZjdhKSCAhZ0NcaqT7Em0X
nD7yRP2B8zAAQMJNjzeBsLbzveNrXMoGzFrvm/jFxskUIUS01ykfWEg2C5+5/IAf
7jBAM8x2Qj92b8zyBFpLjfJQw9aTE/trtCsmZXQFmkJjGS0anrJc9dXKfqnHxknX
8jmLOv4Yq4Qtk3UF35HTiIGcA2W0UKfVyFb4XcrIMeSmLXXcagJWjeH5nB18UUNn
JyCgE/qY4v/SnrNQU92mIdMOQGuvYg7xbaz226lgNdwM2HuM9U8nc2juqJxAO+Wp
xs85yl82qIviZ5YY39HK371MZ/ufMsaVsVy3Nfn3PjqqkzGLmtAL6KJDcKHZWdDw
ugEPwO61DxnERZsNGeFpXfzCxjMpZc/qwUIfGw85idmU2vdedtrxHBkDGoqu1IP7
xZOO8uSYrzbXM6g1gs3WPBOY51CmZLs5pIQTN1tVl5ifGw4uOuzDBUCKiR5oA2MI
352HbA7wwWFgwoN9W5t4MRTowmaYpM8AB+nh9f1A5q9S8SelfMpXOqGew9VZb2jd
AuiJK/LEPjRgs5s3/FfC4Sb1MSsXDr1N1jziaF4MomM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.3.1
Release Details
UpdatedAug. 6, 2026, 12:57 p.m.
Changelog

Added

  • Regressionstests für die Rollentrennung Viewer/Editor/Admin: ForbiddenException-Tests für TaskService::create/update/delete/syncProgress und ProjectService::updateProject/deleteProject (inkl. „Editor darf nicht löschen") sowie erstmalige Testabdeckung für SubProjectService und MilestoneService

Fixed

  • CSV-/Formel-Injection im Aufgaben-Export (detail/io.js exportCsv): Task-Felder wie Titel oder Beschreibung, die mit =, +, - oder @ beginnen, wurden unverändert übernommen und von Excel/LibreOffice/Google Sheets als Formel ausgewertet. Die Absicherung aus ReportView.vue liegt jetzt in einer gemeinsamen Utility (src/utils/csv.js, escCsvCell) und wird an beiden Export-Stellen verwendet
  • Viewer sahen im Frontend dieselbe Oberfläche wie Editor/Admin (Add Task/Subproject/Milestone, Share, Delete Project, Drag/Resize im Gantt-Chart, volle Bearbeitungsformulare), obwohl das Backend Schreibversuche ohnehin korrekt ablehnte – kein Least-Privilege an der UI. ProjectService::getAccessibleProjects()/getProject()/createProject()/updateProject() liefern jetzt das effektive permission-Feld (AccessService::getEffectivePermission()) mit; ProjectList.vue, GanttChart.vue (inkl. Drag/Resize-Sperre je Projekt) und das Detail-Modal (detail/detailModal.js) blenden Schreib-Aktionen für Viewer aus bzw. schalten Formulare read-only
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureBUyDeI9y7iKD1wQJLHrW6IFq0zuXOJQ1Zv/x+0RryIIQ0fAGAUjwQyLDYLYpRzPg
6ITDPfY9bftVlwkB8w9LqZiFETkH8mgc6l5XB7F6NKzFrdaSnNw21CIVJjWyHpkR
mxDq+vQepZd+YtB9jMz39kbFk9BNHrpKIwIXXZVqEqj1U54NmT+5XnT892m9mY6o
RWQNf3Mn5WcH3vmoDi/UAOgDaO30GFCRJePryurVpK7stkub8K6fYsWIkS5NeJlP
YHdb4nBlkhUzQFjo0ZDSkCSiWjMdyCrtEanX6m6Ffhr+j0jpd+J9soMSjrzdzt4T
5mETo8xc61IQIVFWMD6fZ3/oCmbx2TFDZsJjRpFKxoPA08AkdC7xjxHoaKYZHvi4
+tzowxPzMfyjQ/hEj2dwq22doM2lXK1Y13VvV8kkaomrAQjB97gf+ODrYZNDgNvc
2ijU08B4cAB0HbEveDpDRjCJj8D5OwCMafRMa4Ul/nm0+RiBFOhIjDYdVAnCp7Ql
IEc4UQZM9WYLQlouwGPk83POpbqkbc8vKAucGBXvHAbxNpUSOGbssMYFGU/e9pOg
+oT2gcJrC3MYPGIWnS8iLn75PK1H063KFJv5yqC+h/rqwX/FxyTzKvofJi68GpLs
WtG3y67W8Ju4y1I9iQ9raJxxhFUJrStibGy9gc7L0DM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.3.0
Release Details
UpdatedJuly 9, 2026, 5:23 a.m.
Changelog

Fixed

  • Baseline-Toggle im Gantt-Toolbar zeigte nie Baseline-Balken und PUT baseline/active warf einen SQL-Fehler: baseline_id auf nc_gantt_projects wurde mit der 3-Ebenen-Migration versehentlich zusammen mit den tatsächlich berechneten Spalten entfernt (Migration Version1025 stellt die Spalte wieder her). BaselineService::create() aktiviert eine frisch eingefrorene Baseline jetzt automatisch fürs Projekt; renderBaselineBars() (GanttChart.vue) und baselineOverlay.js lesen Baseline-Items dafür über task_id statt des seit der Umstellung auf das 3-Ebenen-Modell nicht mehr existierenden subproject_id-Felds
  • OCS-API-Fehlerantworten (400/404/500 …) lieferten für alle OCSController-Endpunkte einen leeren text/html-Body statt des erwarteten JSON-Envelopes mit message; ExceptionMiddleware wirft jetzt passende OCP\AppFramework\OCS\OCSException-Subklassen statt einer rohen DataResponse, die Nextcloud-Core nur für 401/403 automatisch formatiert. Betroffene Controller (ProjectController, ReportController, ShareController, ChangeEventController, WorkScheduleController, WorkCalendarController, HolidayAdminController, SettingsController) wurden zusätzlich auf die bestehende ApiException-Hierarchie umgestellt

Removed

  • Nie ausgewertete Gruppen-Freigabe-Option entfernt: Share::TYPE_GROUP, principal_type-Parameter in ShareController sowie die DB-Spalte principal_type (Migration Version1024) wurden gestrichen; Freigaben sind jetzt ausschließlich benutzerbezogen

Changed

  • Dashboard-Summary (DashboardController::summary) lädt Baselines und Aufgaben-KPIs jetzt über gruppierte SQL-Aggregat-Queries statt pro Projekt einzeln (N+1-Fix, gilt auch für den Admin-Fall mit allen Instanz-Projekten)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureNJfbRJEXzKGipUjUW9tUKayHoY6ltSU0ZeCQXcDx6XVDY/naLK0dVY9ciofXU7oy
40sJIOsjxcUZbalDwqncPn0vjGoqmk3BFUT7KotEpLLf8/ZmIjdHEbQbJHzEpdQV
y5YedjqRLKB7/Ul35JWVFxY8eutCsYkonfof7v6AGMGB/RjQ7GDB6jWunEEwqUGV
3Y1isvdq5N3oo9NDY/QR0C2C7jiMnB9Kon0KzZ2yp0oF5euOpA/qrtmqxG3L9PoZ
DW1ppwAuARWtRDu4tcDAAQAkF1Mrg5YyYgRoKm7uVazlKEP7DxX56g/g4RS2otEE
rKsA/ER9yYiHnNyGcZuK9Jkfe2Ua6/R7gzJilRvHZ7CBPkhY+XNPcrjlsHbCJEJJ
cX/b0B56p7FyAFy6XjMY9PYFhxy/ubHE7Sk8ixX88uxgM4vf/sSFQaN84CcG/qqA
Nq/OEytBjZP82TsFFYBRcPKEPskF4ejjhNVC4bJpiuCIpo51nIxjeFGtDpeQiDG7
0OWc/GCvsz5W3eG7ShmQsRf0DoivL8tjgDUlMWO9boTpUy2Vnm1rDMKu6nFGKZ8o
TWTZR6u4zGZW1OBERdlU5NQmqxqXrsuGlT6r6DuQnWsWiscjXOyIFeHz+EOKhQOk
wUuN6xN8QES1CDFB27PYV0Sz2KhYhM78GKLCc9B10Ic=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.2.0
Release Details
UpdatedJuly 5, 2026, 9:21 a.m.
Changelog

Added

  • Löschen-Buttons im Detail-Modal für Teilprojekt, Aufgabe und Meilenstein
  • Deutsche App-Beschreibung in info.xml ergänzt

Fixed

  • Baseline-Datum und Kommentar im Report werden untereinander angezeigt
  • Dashboard-Widget zeigt Baseline-Datum und KPI-Werte korrekt an
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureXuYTZMxTlGNJVrGvU/GcIjSlBTYreXdvF5FilCmsi+l7MBvFlyeW1DsnxAsao3pu
HyhvaKXRnkraVQNT2ULgX4BmjUnhsELROrK03gtY88Odzqzh7RIXYkNkRcy92ara
56HIP8BBRD3m1c8pYPfpZfLAzkuurAzVqbJ6qbxw1AebdlPCFEq2g6C+IOcTon0T
q9ZD2I2XJmWd42EXXqva+NMrcx1jfjmHWVtl7xHKhwPndFCtHPMMAdoDS0Xpbh1V
3Ns4h/DphDpC8/ZYZ5sbpMCBekHw4VTZDGxNAuXJ5DMPhkqg2MxVplkS19PuBHLx
r74sgsz611Gjg55gjKOqCb5ZZEvqZTrwKTEYIbu8HicWtq/HxKRZS3FZN9MThTTX
Hesv65vsq9fH0UhEM6MVJjE5Phnw8Wb5ivtAiaUA+NzPrDfrHZAsSfR9NFL3rhFC
7wOmnXtzK40NjmqyMWJu/UP9UVbz3SeEiHiQO9GwBc2krzmXzcmLZFgEjty4CGoJ
SzXAA1QGpUGO4n0b3Y/5zSY4UxogGWybW+mOs27KDLytI915wNYGvBtcdAKCj6Ws
1uVA98npwMid/WSJJ04BXHX8LbowrDeTezSFJyGh+u/KnhlzkA7fqEuu3nQGT5rA
a9qeMmd5kmLBIVcG9RVVmw1kc0AK0qIqZDmlk9dWGJs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1
Release Details
UpdatedMay 27, 2026, 6:43 p.m.
Changelog

Added

  • Glossar-Button in der Navigation (über den Einstellungen) mit interaktivem Nachschlagewerk
  • Dreispaltige Glossar-Tabelle: Begriff/Abkürzung, Bedeutung/Englisch, Erklärung mit Berechnungsformeln
  • 30 Einträge in 6 Abschnitten: Allgemein, Terminplanung, Baseline & Berichtswesen, Earned Value Management, Ressourcen & Kapazität, Budget

Fixed

  • Fehlende deutsche Übersetzungen für englische UI-Strings ergänzt (Add dependency, Baseline, Dependencies, Remove, Work, No projects yet, Open Gantt Projects, u.a.)
  • Leere Übersetzung für Deck-Fortschritt-Hinweis befüllt
  • de.js vollständig mit de.json synchronisiert (war ~70 Einträge veraltet)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF1RHJx15J/N8tWNrDlAX1MFE/9tsct4PViEcBDeo8U/V2qJ/tezL7Xk0BVmYxjWw
pB+J685mcHjikh93/aZUqG8C6y0mR3TTJfaa7dLCIRMc1034x6Xj9Nd7Q9YVH4yi
RLTcRhrG2pN1Iqc23kwId995GJXHnCBEyuQuzo9m1VtA7GoFa8W2nqhXVT94khDS
8ZHQrixDfv63d4GfA5N2x4+phaiyObdskvCmVSuKrmouYvCGpPevtKG5YxzOrHid
aL48Kod0YgOCAD8nAqOhEmioP0m+wP2e/NC4Z5svSKT3vdlE67W1M68ipQQfnhPI
txui+2X8zFvoe/M5sE3DaXbLnOVNGQ7YRpyXFI5GsQmQ0cPSSO/94Mw15hLZfwMf
TDOrrvzf+Vu05sUTZ6CFh4FrSl4e5Q6Xjv6uH4BZtyOpPxl+ajgW4OkZvQDbt4KC
8aTZodMcDpXlRMDdBSxT1bT9tD05KUKmEZYtpxHiARrHDuQQCRSuxtjitQD9lbla
CMxKrBiO0YgdUb3FuczYkWEH+y8gW5egU6TDMnjAVbuUecGuGZN1Cc6tarcFWRu2
+V7Qn+aNoXEwP5e/0TM4Ig8ETAYseZRiTPHxeWiSg3RnU3HuT6kPVWw8LERACDT4
6F4WU9t2+4kESMLMjeV0/2UBQSykR/42Idsg0PtvpyQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1 (nightly)
Release Details
UpdatedMay 27, 2026, 5:24 p.m.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF4W7QJB8rVJ2xARdnhUbEouzRZMKcowz/ToDwr0oVFrVUbInFvHAMqXFTOVD1a5N
kDTb0pOhv+hCStTh70bN+aapjbdtp7SdQ3SbP5VoXBKLhUii41OqN/i4yM7itdSs
M8d5PVo9TJYVnfaij2xwWghMqZEyzxgOw4Q98dYIysf2BxsjjCtxGEMo/pISS90G
OE2RaVEFbwUcvu1XKszLP5X/k/onOchengURYVuzHP7VgVuomZpPSZ9VEIRKITyL
GpgSDrdqm5AXdQc7l/2OqprW6G+MxsuVzChbyPGslIYYXYsP9HLc/FfwVxaCJjh2
Nsiqfd+dMwnf7f9yrrBuZjbNwNa5x++IynrwaU01jVISkl9pgkbUYRaPSMTGFDAd
lsEEqlgs9KdzGoTFjDXNdTwDOsUMiu/ag8tPo7YxsxLFbVGa7laRAUhlGUT9QGPr
IOAAWvKqivsYnU5iwTCY/5HGza3SDfZc/e5upPfB+lTQTYijp1aAYRBAsoOLmuga
f0aP45pnfnYBJkT1kMsthjbGMdlL46wLiNHKRct9OlFwx74IRIyZdcs5Q3pC0aNN
DSLvsCSmY8/9PtTLzcOJKXTqqDVupCqq82bUlGRn7NR6xsinzv74MuOVnGY21tBm
iB5ceYpgiEukUz6YtS2quIIiMTnYmM+HBFnkXgBV0/Y=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32

Nextcloud 30

Gantt Projects 1.5.4
Release Details
UpdatedAug. 27, 2026, 7:06 p.m.
Changelog

Fixed

  • appinfo/info.xml wurde vom Nextcloud App Store abgelehnt: der Sprachcode pt_BR in <name>, <summary> und <description> ist im Schema nicht enthalten — die Enumeration kennt nur BCP-47-Schreibweise in Kleinbuchstaben mit Bindestrich. Alle drei Vorkommen heißen jetzt pt-br
  • Die Kindelemente von <info> sind im Schema eine feste Sequenz; <settings> stand vor <dependencies> und wäre nach dem Sprachcode-Fix als nächster Validierungsfehler aufgeschlagen. Der Block steht jetzt an der vom Schema vorgesehenen Position hinter <dependencies>. xmllint --schema gegen https://apps.nextcloud.com/schema/apps/info.xsd läuft damit sauber durch
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureYbjiEkGYW1s8Xg9Jvrt4nNo/67zkrQDA16yfK2MzVa58R982qrLUupNbvUE/FscP
pFg9nlqxd81cpyH1ZhWOM7fMq98mPyjSUsJZ7xjYzNccF13xvDpBXFlfX4UyCknc
50Rmw9a8RxiCH0mEpH9zWjdanX194R8cUIMDY+BcymCgyBVRQub9yaL5iiHH3hNA
OOD+UOiSUNHns9R6sgZ+tF4OeoTufWR5//AfRmuL5qC5spyuP1iKr7FnzPar+/sS
pHKojfUyi0sAjlrThVZDo8fvVPqkkH4jgvVmNE41t1UOlVdajIRd2yhbHX0h0SSs
rmt4CQqNXAjLE/Wlq2lu9Viw4M2BhhPIfVb5YJFzaxjhB8RRy53rquXRL64f7vGa
agq0ri3eD+0ZWmMOlJmHaMv6AXYd0ZG8myb3PAyNC0NYXzrw2PAVL8jDfzmq2cNY
Q3rIh101vtGPytqanNS2VmtfZ8BX63rd4SLR4PJdRYrm3Wbqanx8WZa20eLgYF8S
M9xhxl5Jf8+TnYLwHi1sGm1ID2XJ4krfgOxiUlB8lGeSweUK1uueC0bmUDsrXyhJ
RMGST41TU1HqXx/3Eeh91JKq/Jbx15kPuxu7WNKyuSOj4vKbgFwBUSmHKDU6lvEZ
L/bnxZi965/4K3kvyJ8o2j5e4xUmJLeusi9Fg0yrRqE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.2
Release Details
UpdatedAug. 26, 2026, 5:18 p.m.
Changelog

Changed

  • Teilen-Dialog: das Eingabefeld für den einzuladenden Nutzer ist jetzt ein Suchfeld statt eines reinen Textfelds für die Benutzer-ID (Issue #144). Getippt wird nach Anzeigename, Benutzer-ID oder E-Mail-Adresse; ein neuer, admin-unabhängiger Endpunkt GET /api/v1/users/search (UserController::search(), IUserManager::search(), max. 20 Treffer, der eigene Account wird ausgeblendet) liefert dazu Anzeigename, Benutzer-ID und E-Mail als Vorschlagsliste. Gespeichert wird weiterhin die Benutzer-ID als principal_id – für Umgebungen mit LDAP/AD-Benutzerverwaltung war die zufällig generierte ID zuvor nicht ohne Umweg über die Benutzerverwaltung in Erfahrung zu bringen. Die Liste bereits geteilter Zugriffe zeigt ebenfalls den Anzeigenamen statt der rohen ID (ShareController::index() reichert die Antwort um display_name an, Fallback auf principal_id falls der Account inzwischen gelöscht wurde)
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureyLn/jlYbk3gIYfgIcjON0z467/xL9L6QNcmfgOa/4zhTJjLknXq7eu1UJddWWlLJ
NG2U9d/OEFO9wLHPYPGNiWgQMRIk+TGYlQVTXcZH3WpBxdJOm3XMnk5hq2kNB2FN
TJemqr/dxafGB2hTIgivPMXMnemdrtxWN1RAdzCxZIg9HwJWjG5JxdqHmURHwske
p/Ki/iZghKjrxbPIghbqtrvkCJRImbgVWY8AmUNUPLgotNIthiWFnkTDVhYWcreP
pWleWHxEn57UOtehff1aJa+hRI+8WVkx9iXArx0SThmOCYYgJ/HeG8dOOtmr9jSm
1nzETAL87YLxL/+qM6DyBIgNWhrq1oj9Q12bBm1+la477z6fpOgUlMjWkPEYRQi/
vhxirXwB4BZBYvaMEkNlN2U3+7BqgigGoaTcbN2rG/TP1JuOjdj9jEjMMRye0JbT
35XOoM/VPYGovrT9slHg8r3Wolyd8ioXVOu4IcMImvC5OrTzpKY7NLEc/S+cOKwz
ijOKcnoV0j++KxtQKKgEO96HJpEM0jteQVOGCoPbghjS3OFzkiB1bUVJAlu1jn54
38Q2zw8rZ0EMm1rEPmIrl72DIIXionbWKatREgy7QPwuL/WKZ2c+J1UIUTKck5eO
7M2VHh6P3FnIg/UsOCMu3U7ja0Y8vQ3YUo3tazLfob0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.1
Release Details
UpdatedAug. 25, 2026, 1:28 p.m.
Changelog

Added

  • Screenshot des gerenderten Gantt-Charts als PNG, SVG oder PDF (Issue #116): ein neuer „Screenshot"-Eintrag in der Chart-Toolbar öffnet ein Menü mit den drei Formaten, derselbe Export steht im Bearbeiten-Dialog des Projekts neben JSON und CSV bereit (dort mit vorgeschalteter Formatwahl statt Menü, openScreenshotFormatDialog()). Exportiert wird stets das gesamte Chart, nicht nur der sichtbare Scroll-Ausschnitt — frappe-gantt rendert die volle Breite und Höhe ohnehin als eine SVG. PNG und SVG klonen die aktuell gerenderte SVG und schreiben getComputedStyle() als Inline-Attribute (löst auch CSS-Variablen auf), sodass die Datei ohne das App-Stylesheet eigenständig darstellbar ist; sie folgen dem eingestellten UI-Theme, ein hinterlegtes Rechteck in --color-main-background deckt dabei auch die Fläche unterhalb der Aufgabenzeilen (Workload-Chart, Leerraum) ab, die sonst transparent bliebe. PNG rastert per Canvas mit doppelter Auflösung
  • Zeitraum-Auswahl für den PDF-/Druck-Export (Issue #116): vor dem Drucken fragt ein Dialog, welchen Zeitraum die PDF abdecken soll — gesamter Projektzeitraum (Vorbelegung), laufendes Kalenderjahr, „aktives Quartal" (Vormonat bis einschließlich übernächstem Monat) oder ein frei eingegebener Bereich. Der Vorschlag ist der tatsächlich von Balken belegte Zeitraum, nicht gantt_start/gantt_end — frappe-gantt legt darum großzügig Puffer, der andernfalls mitgedruckt würde. Umgesetzt wird der Zuschnitt als viewBox auf dem SVG-Klon; die dafür nötige Skala (Chart-Start, Pixel pro Millisekunde) schreibt GanttChart.vue nach jedem Render als data-Attribute an den Chart-Container, sodass auch Aufrufer ohne Zugriff auf die frappe-Instanz (Bearbeiten-Dialog) sie lesen können. Die Zeitraum-Logik steckt isoliert testbar in src/gantt/printRange.js (src/gantt/printRange.test.js)

Changed

  • Der PDF-Export druckt nicht mehr den Live-Container, sondern einen gestylten, selbst gerasterten Klon des Charts (Issue #116). Bisher wurde das Inline-SVG direkt gedruckt — wie fein die Browser-Engine das rastert, ist nicht steuerbar und fiel je nach Browser sichtbar grob aus. Jetzt wird der Klon mit 300 dpi bezogen auf die bedruckbare Fläche (A4 quer, 10 mm Rand → 277 × 190 mm) in ein Canvas gerastert und als Bild mit fest vorgegebener physischer Größe in den Druck gegeben; schlägt die Rasterung fehl, bleibt der SVG-Klon als Druckinhalt übrig und skaliert per CSS auf die Seitenbreite. Der Klon wird immer im hellen Theme berechnet (Papier ist weiß, ein dunkles UI-Theme ergäbe sonst dunkelgraue Flächen und helle Schrift) — dafür genügt ein ausgelagerter Container mit gesetztem data-theme-light, da Nextcloud die Stylesheets aller Theme-Varianten lädt und nur über dieses Attribut umschaltet. Das Ausblenden der übrigen Seite läuft über display: none statt visibility: hidden, sonst blieb die Fläche der App als Leerseiten erhalten; Nextclouds Hintergrundbild auf html/body wird für den Druck abgeschaltet. Aufgeräumt wird über afterprint mit Timeout-Fallback für Browser, die das Event nicht feuern
  • Der Toolbar-Eintrag heißt „Screenshot" (Icon MonitorScreenshot) statt „Export" (Icon ExportVariant) — „Export" ist im Projekt bereits für den Daten-Export nach JSON/CSV belegt, der Chart-Export ist demgegenüber eine reine Abbildung der aktuellen Darstellung
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturek6kpvF53IR8tnkOFXyoSPFPGlwVzO/RUTgI1p1ppRcFmoFPUyFeIokOqWA1OtD9q
wdo+qkQQPyJGph0Uwp+hwP9oGmeKWErnlv8HMdA5I5iaXIN7afGvdfxWfKgqWetw
tGmCF7l2IzMmT2Ol/3pKgCnFkXTDPCVeNINNbUdi7EjeRpbcbOGWt3+5UVwZgVTX
Z9/kEEPvjMV98s1zT5yBXcFvyqZA7bhhezmBkfzaLa0DAIczFJbhyFtR06WuS7bP
CWBCs/xvSRuCwo61oyXRAWLohz/2GsrMSa5jvPvnEG1XEHpGPVorCijAvsBm4sX5
yAZTrBm3zbNYObqVE2idxTH/eqP8xXtE38JFjpG5IahpeFNb9V2EbH1nrjfRKYcH
NVvXcXFREAmvorShKDE0ymFN+/BCjvU1vnJCnRD5RMsoSGUzXSVHPrF1emtusLHj
ltP22eYOCpraEpZheTkFftqvpbiQKVXsZ+8wY4dJnh5Ymi71loxZhggoH8s/pmp+
WbSx6MZwSPBqJ0HOJWckFiZXur2OQYOVLfXt8uPYNU/EZSoYDqm81GVMglQXxyM6
YDh0xXqwJ7Qft7k0O/3Fa5YLJGP4B4RvjU29N3/WDsfjuk7PeCSTPhRlTOfV/Fbn
jS83WVBavmyklhu0rNYuCYfdYvUxemIO3VuGmYzFXgI=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.0
Release Details
UpdatedAug. 24, 2026, 5:26 p.m.
Changelog

Added

  • Editierbare Vorgänger-Spalte in der Tabellenansicht, Teilschritt F von #117 (Issue #124): eine neue, über das Spaltenmenü einblendbare Spalte „Vorgänger“ zeigt die Abhängigkeiten einer Aufgabe in der aus MS Project bekannten Kurznotation (12EA+2d = Vorgänger Aufgabe 12, Ende-Anfang, zwei Arbeitstage Abstand; mehrere durch ; getrennt) und nimmt sie dort auch entgegen. Typ (EA/AA/EE/AE, englische Kürzel FS/SS/FF/SF werden beim Eingeben mit akzeptiert) und Abstand sind optional, 12 allein bedeutet also EA ohne Abstand. Geprüft wird beim Verlassen der Zelle: Syntax, Existenz der Aufgabe im selben Projekt, Selbstbezug, Dubletten und Kreisschluss über die gesamte Abhängigkeitskette – bei einem Fehler bleibt die Zelle im Bearbeitungsmodus und nennt den Grund im Klartext, die bisherigen Abhängigkeiten bleiben unangetastet. Gespeichert wird über dieselbe PUT /tasks/{id}-Route wie der Detail-Dialog (inkl. dep_mode), sodass der Server die Termine anwendet und durch die Nachfolgerkette propagiert; Balken, Pfeile und Typ-Labels im Chart zeichnen sich mit der Antwort neu. Bewusst ohne Undo-Toast, da ein Zurücknehmen nur der Termine (nicht der Abhängigkeit) beim nächsten Neuberechnen wieder überschrieben würde. Nur Aufgaben haben eine editierbare Zelle – Meilensteine hängen über task_id an genau einer Aufgabe, Projekte und Teilprojekte sind berechnete Strukturebenen. Notation und Prüfungen stecken isoliert testbar in src/gantt/dependencyNotation.js (src/gantt/dependencyNotation.test.js)
  • Zeitabstand (Lag/Lead) je Aufgaben-Abhängigkeit (Issue #124): neue Spalte lag_days in nc_gantt_task_deps (Version1032; der Name vermeidet das seit MySQL 8.0 reservierte Wort LAG und nennt die Einheit). Der Abstand zählt in Arbeitstagen über den Ressourcenkalender wie die übrige Terminberechnung, wirkt bei allen vier Abhängigkeitstypen auf deren Ankertag und darf negativ sein (Lead: der Nachfolger beginnt früher). Bestandsabhängigkeiten bekommen 0 und verhalten sich unverändert. TaskService::update() weist eine gesendete Vorgängerliste jetzt vollständig zurück (invalid_dependency, dependency_cycle), bevor die bisherigen Abhängigkeiten dafür gelöscht werden, statt Vorgänger aus fremden Projekten oder Zyklen zu speichern – letztere hätten die topologische Sortierung in recalculateProject() stillschweigend Aufgaben überspringen lassen

  • Konfigurierbare Spalten in der Tabellenansicht, Teilschritt E von #117 (Issue #122): ein Menü in der Tabellenkopfzeile blendet Spalten ein und aus, die Reihenfolge lässt sich per Drag & Drop der Kopfzellen ändern (barrierefreie Alternative: Strg+Pfeil links/rechts auf der fokussierten Kopfzelle), die Breiten per Ziehen am Spaltentrenner (Doppelklick setzt auf die Standardbreite zurück). Zusätzlich zu den bisherigen Spalten (Titel, Start, Ende, Fortschritt) stehen ID, Dauer (Kalendertage inkl. Start- und Endtag, bei Meilensteinen 0), Status (Issue #78, nur Aufgaben/Teilprojekte) und Ressource (Issue #103, zugeordnete Ressourcennamen einer Aufgabe) bereit – alle vier als reine Anzeigespalten, die das Inline-Editing und die Tastaturnavigation daher überspringen. Auswahl, Reihenfolge und Breiten werden pro Nutzer über den in Teilschritt B angelegten UI-Preferences-Endpunkt persistiert (neuer Key gantt_table_columns, kein Schema- oder Endpunktwechsel nötig); eine gespeicherte Konfiguration wird beim Laden validiert, sodass entfernte Spalten oder unplausible Breiten die Ansicht nicht beschädigen können. Die Tastaturnavigation folgt jetzt der sichtbaren Spaltenreihenfolge statt einer festen Liste. Reicht die Tabellenbreite für die gewählten Spalten nicht aus, scrollen Kopfzeile und Zeilen gemeinsam horizontal; die Obergrenze des Splitters steigt dafür von 560 auf 900 px. Die Spaltenlogik steckt isoliert testbar in src/gantt/tableColumns.js (src/gantt/tableColumns.test.js). Ein Sortieren der Zeilen nach Spaltenwert ist bewusst nicht enthalten – es kollidiert mit der Hierarchie Projekt → Teilprojekt → Aufgabe und der sort_order, die das Chart für dieselbe Zeilenreihenfolge braucht

  • Tastatur-Navigation in der Tabellenansicht, Teilschritt D von #117 (Issue #121): Tab/Shift+Tab springen zur nächsten bzw. vorigen editierbaren Zelle (überspringt Zeilen ohne Schreibrecht, springt am Zeilenende zur nächsten/vorigen Zeile), Enter speichert und springt in dieselbe Spalte der nächsten Zeile, Esc bricht ab und stellt den vorigen Wert wieder her. Pfeiltasten bewegen die Zellauswahl, wenn gerade keine Zelle im Editiermodus ist (innerhalb einer Zelle bleiben sie für Cursor-/Wertänderung nutzbar). Die reine Traversierungslogik steckt isoliert testbar in src/gantt/tableNav.js (src/gantt/tableNav.test.js). Die ausgewählte Zelle bekommt einen sichtbaren Fokusrahmen und wird per scrollIntoView() ins Sichtfeld gescrollt – über den bestehenden bidirektionalen Scroll-Sync (Issue #119) folgt das Gantt-Chart automatisch mit. Neue ARIA-Rollen (grid/row/columnheader/gridcell, aria-readonly je Zelle) machen die Tabelle für Screenreader als Grid erkennbar
  • Read-only Tabellenansicht links vom Zeitstrahl, Teilschritt B von #117 (Issue #119): neue Komponente GanttTable.vue zeigt dieselben Zeilen wie das Chart (Titel mit Einrückung nach depth, Startdatum, Enddatum, Fortschritt), gespeist aus buildRowModel()/buildTableRows() (siehe Teilschritt A, Issue #118) – Reihenfolge, Filter-Dimming, kritischer Pfad und Projektfarben bleiben so exakt konsistent zum Chart. Zeilenhöhen sind fest an frappe-gantt gekoppelt (40 px = bar_height 22 + padding 18, Kopfzeile 52 px = header_height). Ein Toolbar-Button blendet die Tabelle ein/aus, ein Splitter macht die Breite verstellbar (160–560 px), vertikales Scrollen ist bidirektional mit .nc-gantt-container synchronisiert. Sichtbarkeit und Breite werden pro Nutzer über einen neuen, generischen UI-Preferences-Endpunkt (GET/PUT /api/v1/ui-preferences, SettingsController über IConfig::get/setUserValue() als JSON-Blob) persistiert, damit Teilschritt E (Editieren) das Schema nur noch um weitere Keys erweitern muss, ohne Endpunkt oder Speicherformat anzupassen. Rein lesend – Editieren folgt in Teilschritt C
  • Projekt-Export/-Import (JSON, format_version: 2) kann jetzt optional die von den Aufgaben referenzierten Ressourcen inkl. Gruppe, Wochenmodell, Arbeitskalender+Feiertagen und Tageskapazitäten mitnehmen, statt wie bisher nur die rohe resource_id in eine Zieldatei zu schreiben, die dort ungeprüft übernommen wurde – auf einem System ohne identische Ressourcen-IDs ging die Zuordnung damit verloren oder landete schlimmstenfalls bei einer fremden Ressource (Issue #115). Vor dem JSON-Download entscheidet ein Options-Dialog, was mitexportiert wird (Ressourcen / Verfügbarkeiten / Kalender, gestaffelt); vor dem JSON-Import zeigt ein passender Dialog, wie viele Ressourcen die Datei enthält, und lässt auswählen, was übernommen wird sowie ob bei Namensgleichheit die vorhandene Ressource wiederverwendet (Default) oder immer neu angelegt wird. Referenzen werden ausschließlich über eine Mapping-Tabelle aufgelöst und nie roh durchgereicht; nicht auflösbare Referenzen werden verworfen (Regressionsschutz zu #112/#113). Neu angelegte Ressourcen/Gruppen/Kapazitäten gehören stets dem importierenden Nutzer. Wochenmodelle/Kalender/Feiertage sind weiterhin admin-only anlegbar – ohne Adminrechte werden nur namensgleiche vorhandene Einträge gemappt. CSV bleibt unverändert aufgabenzentriert mit Ressourcen nur als ID-Referenz; der volle Ressourcenumfang ist JSON-only. Alt-Exporte ohne resources-Schlüssel importieren unverändert wie bisher, ohne Options-Dialog
  • Inline-Editing der Basisfelder in der Tabellenansicht, Teilschritt C von #117 (Issue #120): die Zellen für Titel, Start, Ende und Fortschritt sind direkt editierbar und aktualisieren die Balken im Chart sofort. Gespeichert wird über die bestehenden Store-Actions (updateTask/updateSubproject/updateMilestone/updateProject) inkl. Terminpropagation und dem vorhandenen Undo-Toast für Aufgaben (analog Drag & Drop im Chart); ein lokaler pendingEdits-Overlay zeigt die Änderung optimistisch an und rollt sie bei einem fehlgeschlagenen Request mit Fehlermeldung zurück. Die Schreibrechte löst tableRows.js je Feld über store.canEditProject() auf (titleEditable/startEditable/endEditable/progressEditable) – Projekte und Teilprojekte bleiben bei Datum und Fortschritt read-only, da diese Werte aus den Aufgaben berechnet werden. Geprüft wird: Titel nicht leer, Enddatum nicht vor Startdatum (Meilensteine ausgenommen, deren Start- und Endspalte dasselbe milestone_date zeigen) und Fortschritt 0–100. Im Simulationsmodus rufen Datumsänderungen an Aufgaben simulateTaskChange() statt einer echten Speicherung auf
  • Ressourcen-Überbuchungswarnung (Issue #82): die Kapazitätsplanung war bisher reine Datenerfassung ohne Abgleich mit dem tatsächlich verplanten Aufwand. Der neue Endpunkt GET /api/v1/resources/{id}/utilization?from=&to=&group= (ResourceUtilizationService/ResourceUtilizationController) stellt den geplanten Aufwand aus den Aufgaben-Ressourcen-Zuordnungen – gleichmäßig auf die Arbeitstage der Aufgabe verteilt, über WorkCalendarService mit ressourcenspezifischen Arbeitstagen und Feiertagen – der erfassten Tageskapazität gegenüber. Mehrfach-Ressourcen-Zuordnungen (Issue #103) samt Alt-Aufgaben werden berücksichtigt, ohne gepflegte Tageskapazität gilt ein Fallback von 1 PT/Tag. Im Ressourcen-Raster färben sich die Zellen nach Auslastungsgrad (> 90 % gelb, > 100 % rot) und der Tooltip zeigt geplante Stunden gegen Kapazität samt Aufschlüsselung nach Projekt und Aufgabe; das Workload-Chart im Gantt markiert überbuchte Perioden jetzt rot. Übersetzungen für alle sechs Sprachen ergänzt
  • Nachgetragen – beide Funktionen wurden bereits mit 1.4.2 ausgeliefert, waren dort aber nicht im Changelog dokumentiert:
  • Einheitliches Statusmodell für Teilprojekte und Aufgaben (Issue #78): neues Feld status (not_started/in_progress/blocked/done) auf nc_gantt_subprojects und nc_gantt_tasks ergänzend zum numerischen Fortschritt, Bestandsdaten leitet Migration Version1030 daraus ab. Validierung per StatusValidator (Whitelist analog ColorValidator), Status-Dropdown in den Detailformularen, blockierte Balken im Gantt schraffiert und mit Warnsymbol, blocked_count je Projekt im Rollup sowie ein Badge in der Projektliste
  • Mehrere Ressourcen mit individuellem Aufwand je Aufgabe (Issue #103): bis zu vier Ressourcen je Aufgabe mit eigener Aufwandsschätzung in der Einheit der Ressource (neue Tabelle nc_gantt_task_resources, Migration Version1029, Backfill aus den vorhandenen resource_id/work-Werten). Das manuelle Work-Feld entfällt: TaskService::resolveResources() summiert die Einzel-Aufwände zur Gesamt-Work (1 PT = 8 h) und übernimmt die erste Zuordnung als primäre resource_id, die weiterhin für Arbeitskalender und CPM maßgeblich ist. Ressourcen-Filter sowie CSV-/JSON-Export und -Import berücksichtigen alle Zuordnungen statt nur der primären

Changed

  • buildFrappeRows() (src/gantt/frappeRows.js) mischte bisher Datenmodell und frappe-gantt-Formatierung: die Einrückungstiefe steckte als Leerzeichen im name, Zustände (Dimming, kritischer Pfad, Summenbalken) hingen in custom_class – eine geplante Tabellenansicht neben dem Gantt-Chart (Issue #117) hätte dieselbe Logik duplizieren müssen (Teilschritt A, Issue #118). Neue Funktion buildRowModel() (src/gantt/rowModel.js) liefert dieselbe Zeilenliste jetzt als neutrales Modell (id, type, entityId, depth, title, start, end, progress, parentId, editable, matched, critical), resolveMilestoneDate() wandert mit ins Modell. buildFrappeRows() ist nur noch ein dünner Adapter darüber, der name/custom_class/dependencies wie bisher erzeugt. Reines Refactoring ohne sichtbare Änderung, per Fixture-Vergleich gegen die alte Implementierung geprüft; neue Unit-Tests (src/gantt/rowModel.test.js, npm run test:unit, via neu eingeführtem vitest) decken Hierarchie, Einrückungstiefe, Meilenstein-Datumsauflösung und Filter-Dimming ab

Fixed

  • Abhängigkeitsfortschreibung bei Aufgaben lieferte für alle vier Abhängigkeitstypen (EA/AA/EE/AE) einen um einen Arbeitstag zu langen Nachfolger-Termin – bei EA zusätzlich mit falschem Anschlusstag, da der Nachfolger auf dem Endtag des Vorgängers statt am folgenden Arbeitstag begann und dadurch mit dem Vorgänger überlappte (Issue #114). Ursache war eine Off-by-one-Vermischung zwischen der inklusiven Arbeitstagszählung und der exklusiven Datumsfortschreibung in TaskService::applyConstraint()/computeMultiConstraint(). Betroffen sind sowohl das direkte Setzen einer Abhängigkeit als auch die Terminpropagation nach Drag & Drop und die What-if-Simulation. Verhaltensänderung: Bestehende Pläne mit EA-Abhängigkeiten verschieben sich beim nächsten Speichern/Neuberechnen um einen Arbeitstag nach vorne (auf den korrekten Anschlusstag); bei AA/EE/AE verkürzt sich der zuvor fälschlich verlängerte Nachfolger-Zeitraum um einen Arbeitstag, die ursprüngliche Dauer bleibt jetzt garantiert erhalten. Abhängige Meilensteine (Dauer 0) sind unverändert korrekt und von diesem Fix nicht betroffen

  • Security: jeder Nutzer mit Schreibrecht auf ein eigenes Projekt konnte jede in der Instanz existierende resource_id einer eigenen Aufgabe zuordnen – unabhängig vom Eigentümer und ohne jede Share-Beziehung (Cross-Tenant-IDOR, Issue #112). TaskService::resolveResources() lud die vom Client gesendeten IDs bisher nur über ResourceMapper::findByIds(), also als reine Existenzprüfung ohne Berechtigungscheck; da die IDs fortlaufende Integer sind, waren sie trivial zu erraten. Der Listen- wie der Legacy-Einzelfeld-Pfad in create()/update() filtern referenzierte IDs jetzt über ResourceMapper::findVisibleToUser(), nicht sichtbare IDs werden wie unbekannte verworfen (Liste) bzw. nicht übernommen (Einzelfeld, der bestehende Wert bleibt erhalten). Regressionstests decken beide Pfade für create() und update() ab

  • Feiertage ließen sich nicht in mehreren Regionskalendern führen: POST /admin/holidays lief in einen HTTP 500, sobald dasselbe Datum in einem zweiten Kalender angelegt wurde, weil der Unique-Index aus Version1006 allein auf holiday_date lag, obwohl Feiertage seit Version1013 über calendar_id kalenderbezogen sind. Version1031 verschiebt den Index auf (holiday_date, calendar_id); die Eindeutigkeit globaler Feiertage sichert weiterhin der Upsert in HolidayAdminController::create(). Betroffen war neben der Feiertagsverwaltung auch der Kalender-Import aus einem Projekt-Export, wo der Fehler still verschluckt wurde. Zweitens war GET /admin/holidays admin-only, während work-calendars und work-schedules lesend für alle offen sind – für normale Nutzer blieb der Store deshalb immer leer (keine Feiertage im Projekt-Export, keine Markierung arbeitsfreier Tage im Ressourcen-Raster); der Lese-Endpunkt zieht nach dem Muster der übrigen Stammdaten auf /api/v1/holidays um (NoAdminRequired), Anlegen und Löschen bleiben unter /api/v1/admin/holidays. Ergänzend werden Feiertage jetzt mit ihrem Kalender gelöscht (WorkCalendarController::destroy(), HolidayMapper::deleteByCalendar()) und Bestandswaisen bereits gelöschter Kalender in Version1031 aufgeräumt – über verweisende Ressourcen haben sie bis dahin weiter als arbeitsfreie Tage gewirkt
  • Die Installation der Tabelle nc_gantt_task_resources scheiterte auf Instanzen, deren Tabellenpräfix die von Nextcloud aus Oracle-Kompatibilität erzwungene 30-Zeichen-Grenze für Bezeichner reizt: der von Doctrine automatisch vergebene Primary-Key-Name (Tabellenname + _pkey) wird mit Präfix oc_ zu lang (Issue #132). Die Primärschlüssel-Definition bekommt – analog zu den bestehenden Migrationen für nc_gantt_resource_capacity und nc_gantt_resource_groups – einen expliziten kurzen Namen
  • Der absolut positionierte Toggle-Button der Nextcloud-App-Navigation ragte in den App-Content hinein und verdeckte dort den Titel des ersten Eintrags in der Projekt-Rollup-Leiste (Issue #134); der linke Innenabstand reserviert jetzt Platz für den Button
  • Ein leerer Balken in Höhe der Kopfzeile erschien oberhalb des App-Inhalts, weil der CSS-Reset für den Nextcloud-Content-Wrapper an main#content.app-nc_gantt gekoppelt war (Issue #126). Ändert sich das Markup zwischen Nextcloud-Versionen (Klasse oder Tag), griff der Reset nicht mehr und der von NcContent (#content-vue) selbst gesetzte margin-top: var(--header-height) addierte sich zum Rand des Cores. Der Reset hängt jetzt robust an #content mit !important, ohne Abhängigkeit vom app-spezifischen Klassennamen
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegO8Qmtdwg8LOBKr5G+mq4co374hsqqSN6oCIRV9atUx0KqogPxisJiIExkPoSIbu
e4EeIaUzg2alhZsKBLWOwR3Ab3fNm5vvd2DzJn6Hei7A70woz9m/LXkVlcx3vALW
bwolClUX1XCZOBVo/kSS5AOVwak3/huzT1iF9xH9BnpdzVuOjZ563LYwSayfb2Gq
SkWNfLZlKlDkTTF2PIszrNmaa89GmyqTbraxSP16/9nYdIj8ZU0zrhC3HtpihgAT
wya2lwnMf6I73HU0xxgT/+ITTmurx4eHwKWH80P4tjg/Y9mnHODIpAslbW3351mc
qdTHXIBW1MB1YGCQ+JPt11v1X2ccn29kEF9d1nkizKZ1+Du+LJQhPhwHiQxPWSwAUlTlQ4tj1lbe3+FJ1jsVTCqWbDUN02EoV7ZwtN15/HLJfM6w4drUi8ThJ1aLG3v6
cIwMJjYgWeepxR2gv4YgBf4u7txodm5bAWuzySk4rfAG1dBxmie/bfNnWmQ9LrYy
spwKc73gsPhg03ll+jp0b27fJfNF4IpOysz3i+r1ExVfeNIWWO91JPatO2pXZh4O
q0rczM8wYvctvcEkKQIL67HoIrKw4GVIXfqM9Oc8e/Ay7d/NPL+BjpCO6zvoFBY+
SRmjit15YmGW4EQwweQfKXuZ2YeySj+7OjhKHpKfvfg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.2
Release Details
UpdatedAug. 20, 2026, 4:41 a.m.
Changelog

Changed

  • release.sh fragt die Versionsnummer jetzt interaktiv ab, statt sie nur aus appinfo/info.xml zu lesen: zu Beginn werden die aktuelle Version und das letzte Release-Tag angezeigt und es wird gefragt, ob eine neue Nummer vergeben werden soll (Vorschlag: nächster Patch-Level). Die Eingabe wird auf das Format X.Y.Z, auf einen echten Versionssprung und auf ein noch nicht vergebenes Tag geprüft und dann nach appinfo/info.xml, package.json und package-lock.json geschrieben. Fehlt ein Changelog-Abschnitt zur neuen Version, warnt das Skript; anschließend bietet es an, den Bump als chore: Version X.Y.Z zu committen — nötig, weil der Tag-Name den Commit-Hash enthält. Für nicht-interaktive Läufe gibt es --version=X.Y.Z und --no-bump; ohne TTY wird nie gefragt, make release bleibt also unverändert nutzbar
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturepoVHZxteU3AR2cpRRV98jNfv4j7rWnLEd3kbpn5Dp4T1ubAI+0QlRf9jxi0BaYz1
+1dg+3Yt25uN6Slp1FHiw7kyqt5HJ60XgnOWo+Zt0YBHSnWqLDhF8zxPEnQCkF+v
nUvscpcoQY2bRiIBZhVWVgeAD592uvOA4gfRDrUyLVDKjOrzBu3ScbNvCqTqE9IC
vs8qZkRLIcaD6tKoSLT2pZqmwBoCdClvEw5D5v40WSvLq232BZnXpQ4ogG3QvJdL
mYO8HlPZKVOxANp0MGnWle4IFRMbMt22Ebpkjc18Q9Q4AIv/Sd3zTsc+uMONTM95
s0GXvLSm95o0UCFZ1CL7fCEq7NwlprjVrTeWGmlJNVjHJuIyRKHEz5DR12s0lAGm
/rKGWjoVP+Wvk3upRizR/5y2fpWoiwuDqCtz9gQN6+HHYLaqLvYqsAJ/SxmtLtP7
8B2rK61ClKowz4phbTZdrCaF8w3IWnFFBTFquIVFuN2jUoHvdCMtd0910sLAMY5M
Xs9nk0FVsP23zKXlQpOojq7ouCQagRhsnBJDLxWRR+NIq582f7SDgcrkBqxlGJX4
/Ql9V+2iFx61QANCTjdJIO3LbvbK60vGos+ency0GfvSDbPovDAVyJzt8ZUh5BNw
UXQ4XOHJZAagAr36rKttk1DQbZZ/2bUEq231c96m83g=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.1
Release Details
UpdatedAug. 17, 2026, 7:43 p.m.
Changelog

Fixed

  • Die Meilenstein-Raute hatte eine fest verdrahtete Farbe (#fbbf24) statt der Projektfarbe zu folgen, obwohl Balken für Aufgaben und Teilprojekte bereits korrekt eingefärbt wurden (Issue #102). Zusätzlich wurden die separaten – und wirkungslosen – Farbwähler bei Teilprojekt und Aufgabe entfernt, da deren Anzeige ohnehin immer der Projektfarbe folgt
  • „Meilenstein hinzufügen" im Drei-Punkte-Menü der Projektliste nutzte zwei aufeinanderfolgende window.prompt()-Dialoge (Name, dann Datum als Freitext) statt des regulären Detail-Dialogs (Issue #101). Der Klick emittiert jetzt add-milestone analog zu „Aufgabe hinzufügen" (Issue #91) und öffnet denselben Meilenstein-Dialog wie beim Bearbeiten, mit vorbelegtem Projekt
  • Im festen Datumsmodus des Aufgaben-Detaildialogs konnte ein Enddatum vor dem Startdatum eingegeben und gespeichert werden (Issue #100). Jetzt zieht ein über das Enddatum hinaus verschobenes Startdatum das Enddatum mit; ein zu frühes Enddatum erzeugt ein rotes Fehler-Icon am Feld, hält den Fokus dort und deaktiviert den Speichern-Button, bis ein gültiges Datum eingetragen ist (dateField() in fields.js mit neuer setError()-Methode)
  • Eine Aufgabe mit Abhängigkeit behielt trotzdem die manuell eingegebenen Start-/Enddatumsfelder, die beim Speichern durch die Abhängigkeitsberechnung überschrieben wurden – für Nutzer nicht nachvollziehbar (Issue #99). Der Aufgaben-Detaildialog hat jetzt denselben Fixed-date/Dependency-Umschalter wie Meilensteine (Issue #90): im Dependency-Modus ersetzt eine Dauer (Tage) die Datumsfelder, die Abhängigkeit bestimmt den Termin. Serverseitig wendet TaskService::create() neu hinzugefügte Abhängigkeiten sofort an (bisher nur update()), damit auch neu angelegte Aufgaben keinen die Abhängigkeit ignorierenden Platzhaltertermin behalten
  • Der Speichern-Button des Detail-Dialogs blieb nach dem Hinzufügen einer Abhängigkeit deaktiviert: die Gültigkeitsprüfung hing allein am input-Event, das bei einem <select> vor dem change-Event feuert – zu diesem Zeitpunkt war die Abhängigkeit noch nicht in der Liste. detailModal.js wertet jetzt zusätzlich change aus, der Abhängigkeits-Editor (fields.js) meldet jede Mutation (Hinzufügen, Entfernen, Typwechsel) und der Fixed-date/Dependency-Umschalter in taskForm.js den Moduswechsel über ein eigenes bubbelndes change-Event

Added

  • CONTRIBUTE.md und setup-dev.sh für neue Mitwirkende (Issue #98): Projekt-Leitlinien (FOSS, Datensparsamkeit, Security), VIBE Coding ausdrücklich erlaubt, sowie der komplette lokale Dev-Setup-Weg (Docker-only, Nextcloud + MariaDB unter localhost:8088, optionales E2E-Setup, Git-/Bot-Auth). setup-dev.sh automatisiert diese Schritte, konfigurierbar über --workdir/--no-e2e/--no-docker-install
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturetYcUvVtDEQrAakG32EQF1atSOmz91/8XudCUh60A9r/AxMv+AA5AX2rIvNYDkEn0
OuTmXA6Iz0DoVkdij11MPDhfLaUy0mXeLbwB5fj/xF/cFYcNeCFGKD3k2jyRwOTo
2n1sEp0r8sn0fitHzLfhcB/By0VJkZyhaO/NctP01XfcSQM7lGEOeY/uagtGaDTI
JnPLoQTHo0nUOv4y7jUMZuurEgBOc4yeUxTKv4A/m642hvNQIS0q6K9ON41JHKIO
UtI7DbDWSboVhct1Go3zoj5XcaAGG9ZiwSkJDt/BXT5tWH4SOnSn7mys0j1F6Y0g
vIJWjz6S2RlekwIrIxgyaeEVBOeQ4OM1ilJGWKXDyYJM/0IBxqnqRXDkAFlOopDo
xezlcFdWTkYL5OBKMFJhKuTy7dEQ9hxx5Nz4+vLbNV8gDJZBI+5yI5m8W+809WIH
iKUpIda1ShOblvYH6Ek5AzBdTmoFQ6T4f7iWT7j1i4uUP2DsgehjGVl8yDIT/wyx
nV9t7koXapu1WeXrunSa++mXsi3lceNpthdz5/cUEOe9yWvc1rrulRhiWzePga8H
zjVQYGAIPvwYkWa47U6Zh7HBsQaS+9yQ8NDnD49SS4jUWTGlwNqfM+q9fZ16/G9d
7l4chZUPPbBv+xWfdxmFw+MC5zdkAq27isHToTqnm+w=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.0
Release Details
UpdatedAug. 13, 2026, 6:41 a.m.
Changelog

Added

  • Meilensteine werden jetzt immer direkt unter ihrem übergeordneten Objekt angezeigt statt gesammelt am Projektende (Issue #90): Meilensteine lassen sich neu auch einer Aufgabe zuordnen (bisher nur Projekt/Teilprojekt, Spalte task_id war bereits vorhanden, es fehlte nur die UI), das Auswahlfeld im Detail-Dialog heißt entsprechend „Übergeordnetes Objekt" statt „Teilprojekt". Zusätzlich können Meilensteine ihr Datum jetzt statt eines fixen Werts aus einer Abhängigkeit zum übergeordneten Objekt beziehen (EA/AA/EE/AE, analog zu Aufgaben-Abhängigkeiten; neue Spalte dependency_type, Migration Version1028) – da ein Meilenstein keine Dauer hat, bestimmen EA/EE das Ende und AA/AE den Start des Zielobjekts. Die Berechnung übernimmt der neue MilestoneDateService (serverseitig live bei jedem Abruf sowie im ICS-Feed neu aufgelöst, nicht persistiert propagiert) sowie clientseitig in frappeRows.js für sofortige Reaktivität ohne Neuladen
  • Undo für Drag-&-Drop-Verschiebungen inkl. Propagationskette (Issue #85, Umsetzung der einstufigen Empfehlung: letzte eigene Operation, kein Redo, zunächst nur Drag & Drop): Migration Version1027Date20260810020000 ergänzt nc_gantt_change_events um op_id (UUID) – alle Change-Events einer zusammenhängenden Operation (Drag + gesamte Propagationskette B→C→D) erhalten dieselbe op_id, erzeugt in TaskService::update()/propagateDates() und über ChangeEventService::log() protokolliert (dieses Change-Tracking war bisher tote Scaffolding-Logik ohne Aufrufer). Neuer Endpoint POST /api/v1/changes/{opId}/revert (ChangeEventController::revert, ChangeEventService::revertOperation) wendet alle Change-Events der Operation invers an, in einer DB-Transaktion mit Rechteprüfung über AccessService::canEditOrOwn(). Konfliktschutz: Revert wird mit HTTP 409 abgelehnt, sobald der aktuelle Feldwert nicht mehr dem protokollierten new_value entspricht (jemand hat zwischenzeitlich erneut geändert); der Revert selbst erzeugt wiederum neue Change-Events, die Audit-Historie bleibt lückenlos. Frontend: nach jedem gespeicherten Drag zeigt GanttChart.vue einen Toast „N Termine verschoben — Rückgängig" (showUndo aus @nextcloud/dialogs, 10 s sichtbar) sowie ein globales Ctrl+Z für die letzte eigene Operation der Session (stores/projects.js: lastOperation, revertOperation(), undoLastOperation())
  • Kalender-Export als abonnierbarer ICS-Feed pro Projekt (Issue #83, Variante A aus dem Umsetzungsvorschlag): neue öffentliche Route GET /apps/nc_gantt/projects/{id}/calendar.ics?token=… (regulärer, unauthentifizierter CalendarFeedController, damit externe Clients wie Thunderbird, Nextcloud Kalender oder mobile Apps ohne Nextcloud-Login abonnieren können) liefert Meilensteine als ganztägige VEVENTs (◆-Präfix), den aus den Aufgaben abgeleiteten Projektzeitraum sowie optional Teilprojekt-Zeiträume (Schalter „Auch Teilprojekt-Zeiträume einschließen", standardmäßig aus, um den Feed bei großen Projekten nicht zu überladen). Zugriff erfolgt über einen pro Projekt generierten, 256-Bit-zufälligen Token in der URL (CalendarFeedService, neue Spalte calendar_token auf nc_gantt_projects, Migration Version1026), der über einen neuen „Kalender-Feed"-Bereich im Freigabe-Dialog angezeigt, kopiert und jederzeit widerrufen/neu generiert werden kann (CalendarTokenController, nur für Projekt-Admins/-Eigentümer). ICS-Generierung ohne neue Abhängigkeit, RFC-5545-konform (Escaping, Zeilenfaltung)
  • What-if-Simulationsmodus für Terminverschiebungen (Issue #84): neuer Endpunkt POST /api/v1/projects/{id}/simulate führt Terminpropagation und CPM rein im Speicher aus (kein DB-Schreibzugriff) – TaskService::simulate() nutzt die neu extrahierte, stateless propagateDatesCore()-Kernlogik, die auch der persistierende update()-Pfad verwendet, CriticalPathService::computeFromTasks() erlaubt CPM auf einem hypothetisch veränderten Task-Datensatz. Frontend: „Simulation"-Toggle in der Gantt-Toolbar mit Banner inkl. Projektende-Delta in Arbeitstagen, Geisterbalken für die Ist-Termine (analog Baseline-Overlay) sowie Verwerfen/Übernehmen. Drag & Drop ruft im Simulationsmodus simulate() statt einer echten Speicherung auf. Szenarien bleiben bewusst flüchtig (kein Speichern benannter Szenarien), „Übernehmen" wendet die Änderungen als einzelne Task-Updates an, Simulation läuft pro Projekt
  • Neuer Eintrag „Aufgabe hinzufügen" im Drei-Punkte-Menü der Projektliste (Issue #91): legt wie der „Neu"-Button eine Aufgabe an, belegt dabei aber das gewählte Projekt direkt vor, sodass der Projekt-Auswahlschritt in der Maske entfällt
  • Such-/Filterleiste für Projekte, Teilprojekte und Aufgaben (Issue #81, Stufe 1: rein clientseitig auf Basis der bereits geladenen Daten aktivierter Projekte): Freitextsuche im Titel, Risiko, „überfällig" (Ende < heute und Fortschritt < 100 %), „über Budget" (Ist-Kosten > geplantes Budget), Ressource und „ohne Deck-Verknüpfung". Neuer Pinia-Store stores/filters.js (Zustand pro Nutzer in localStorage persistiert) und reine Matching-Logik in gantt/filterMatch.js. Nicht passende Balken werden in GanttChart.vue gedimmt statt ausgeblendet (Abhängigkeitspfeile und Zeitkontext bleiben lesbar), Projekte/Teilprojekte gelten als Treffer, sobald ein Nachfahre passt. Dieselbe Filterleiste (components/FilterBar.vue) dimmt in ProjectList.vue nicht passende Projekte; für nicht aktivierte Projekte sind nur Titel-Treffer zuverlässig auswertbar, da deren Aufgaben/Teilprojekte nicht geladen sind

Changed

  • Freigabe für Nextcloud 34 (Issue #94): max-version in appinfo/info.xml von 33 auf 34 angehoben, unterstützter Bereich damit NC 28–34. Verifiziert gegen Nextcloud 34.0.2 mit PHP 8.5 und MariaDB 11.4 (148 Unit-Tests, 39 E2E-Tests)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerdYQI+Semnk0cWtKqUZinVDHk0KpxyqB5Ox4NBL9pyf5nrpK8xZpe9lBxc6zhDfW
8adLfVIwQtX+bI7S1O05xm0hwrUHQQ2ii6ADD8mlAxnZjdhKSCAhZ0NcaqT7Em0X
nD7yRP2B8zAAQMJNjzeBsLbzveNrXMoGzFrvm/jFxskUIUS01ykfWEg2C5+5/IAf
7jBAM8x2Qj92b8zyBFpLjfJQw9aTE/trtCsmZXQFmkJjGS0anrJc9dXKfqnHxknX
8jmLOv4Yq4Qtk3UF35HTiIGcA2W0UKfVyFb4XcrIMeSmLXXcagJWjeH5nB18UUNn
JyCgE/qY4v/SnrNQU92mIdMOQGuvYg7xbaz226lgNdwM2HuM9U8nc2juqJxAO+Wp
xs85yl82qIviZ5YY39HK371MZ/ufMsaVsVy3Nfn3PjqqkzGLmtAL6KJDcKHZWdDw
ugEPwO61DxnERZsNGeFpXfzCxjMpZc/qwUIfGw85idmU2vdedtrxHBkDGoqu1IP7
xZOO8uSYrzbXM6g1gs3WPBOY51CmZLs5pIQTN1tVl5ifGw4uOuzDBUCKiR5oA2MI
352HbA7wwWFgwoN9W5t4MRTowmaYpM8AB+nh9f1A5q9S8SelfMpXOqGew9VZb2jd
AuiJK/LEPjRgs5s3/FfC4Sb1MSsXDr1N1jziaF4MomM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.3.1
Release Details
UpdatedAug. 6, 2026, 12:57 p.m.
Changelog

Added

  • Regressionstests für die Rollentrennung Viewer/Editor/Admin: ForbiddenException-Tests für TaskService::create/update/delete/syncProgress und ProjectService::updateProject/deleteProject (inkl. „Editor darf nicht löschen") sowie erstmalige Testabdeckung für SubProjectService und MilestoneService

Fixed

  • CSV-/Formel-Injection im Aufgaben-Export (detail/io.js exportCsv): Task-Felder wie Titel oder Beschreibung, die mit =, +, - oder @ beginnen, wurden unverändert übernommen und von Excel/LibreOffice/Google Sheets als Formel ausgewertet. Die Absicherung aus ReportView.vue liegt jetzt in einer gemeinsamen Utility (src/utils/csv.js, escCsvCell) und wird an beiden Export-Stellen verwendet
  • Viewer sahen im Frontend dieselbe Oberfläche wie Editor/Admin (Add Task/Subproject/Milestone, Share, Delete Project, Drag/Resize im Gantt-Chart, volle Bearbeitungsformulare), obwohl das Backend Schreibversuche ohnehin korrekt ablehnte – kein Least-Privilege an der UI. ProjectService::getAccessibleProjects()/getProject()/createProject()/updateProject() liefern jetzt das effektive permission-Feld (AccessService::getEffectivePermission()) mit; ProjectList.vue, GanttChart.vue (inkl. Drag/Resize-Sperre je Projekt) und das Detail-Modal (detail/detailModal.js) blenden Schreib-Aktionen für Viewer aus bzw. schalten Formulare read-only
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureBUyDeI9y7iKD1wQJLHrW6IFq0zuXOJQ1Zv/x+0RryIIQ0fAGAUjwQyLDYLYpRzPg
6ITDPfY9bftVlwkB8w9LqZiFETkH8mgc6l5XB7F6NKzFrdaSnNw21CIVJjWyHpkR
mxDq+vQepZd+YtB9jMz39kbFk9BNHrpKIwIXXZVqEqj1U54NmT+5XnT892m9mY6o
RWQNf3Mn5WcH3vmoDi/UAOgDaO30GFCRJePryurVpK7stkub8K6fYsWIkS5NeJlP
YHdb4nBlkhUzQFjo0ZDSkCSiWjMdyCrtEanX6m6Ffhr+j0jpd+J9soMSjrzdzt4T
5mETo8xc61IQIVFWMD6fZ3/oCmbx2TFDZsJjRpFKxoPA08AkdC7xjxHoaKYZHvi4
+tzowxPzMfyjQ/hEj2dwq22doM2lXK1Y13VvV8kkaomrAQjB97gf+ODrYZNDgNvc
2ijU08B4cAB0HbEveDpDRjCJj8D5OwCMafRMa4Ul/nm0+RiBFOhIjDYdVAnCp7Ql
IEc4UQZM9WYLQlouwGPk83POpbqkbc8vKAucGBXvHAbxNpUSOGbssMYFGU/e9pOg
+oT2gcJrC3MYPGIWnS8iLn75PK1H063KFJv5yqC+h/rqwX/FxyTzKvofJi68GpLs
WtG3y67W8Ju4y1I9iQ9raJxxhFUJrStibGy9gc7L0DM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.3.0
Release Details
UpdatedJuly 9, 2026, 5:23 a.m.
Changelog

Fixed

  • Baseline-Toggle im Gantt-Toolbar zeigte nie Baseline-Balken und PUT baseline/active warf einen SQL-Fehler: baseline_id auf nc_gantt_projects wurde mit der 3-Ebenen-Migration versehentlich zusammen mit den tatsächlich berechneten Spalten entfernt (Migration Version1025 stellt die Spalte wieder her). BaselineService::create() aktiviert eine frisch eingefrorene Baseline jetzt automatisch fürs Projekt; renderBaselineBars() (GanttChart.vue) und baselineOverlay.js lesen Baseline-Items dafür über task_id statt des seit der Umstellung auf das 3-Ebenen-Modell nicht mehr existierenden subproject_id-Felds
  • OCS-API-Fehlerantworten (400/404/500 …) lieferten für alle OCSController-Endpunkte einen leeren text/html-Body statt des erwarteten JSON-Envelopes mit message; ExceptionMiddleware wirft jetzt passende OCP\AppFramework\OCS\OCSException-Subklassen statt einer rohen DataResponse, die Nextcloud-Core nur für 401/403 automatisch formatiert. Betroffene Controller (ProjectController, ReportController, ShareController, ChangeEventController, WorkScheduleController, WorkCalendarController, HolidayAdminController, SettingsController) wurden zusätzlich auf die bestehende ApiException-Hierarchie umgestellt

Removed

  • Nie ausgewertete Gruppen-Freigabe-Option entfernt: Share::TYPE_GROUP, principal_type-Parameter in ShareController sowie die DB-Spalte principal_type (Migration Version1024) wurden gestrichen; Freigaben sind jetzt ausschließlich benutzerbezogen

Changed

  • Dashboard-Summary (DashboardController::summary) lädt Baselines und Aufgaben-KPIs jetzt über gruppierte SQL-Aggregat-Queries statt pro Projekt einzeln (N+1-Fix, gilt auch für den Admin-Fall mit allen Instanz-Projekten)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureNJfbRJEXzKGipUjUW9tUKayHoY6ltSU0ZeCQXcDx6XVDY/naLK0dVY9ciofXU7oy
40sJIOsjxcUZbalDwqncPn0vjGoqmk3BFUT7KotEpLLf8/ZmIjdHEbQbJHzEpdQV
y5YedjqRLKB7/Ul35JWVFxY8eutCsYkonfof7v6AGMGB/RjQ7GDB6jWunEEwqUGV
3Y1isvdq5N3oo9NDY/QR0C2C7jiMnB9Kon0KzZ2yp0oF5euOpA/qrtmqxG3L9PoZ
DW1ppwAuARWtRDu4tcDAAQAkF1Mrg5YyYgRoKm7uVazlKEP7DxX56g/g4RS2otEE
rKsA/ER9yYiHnNyGcZuK9Jkfe2Ua6/R7gzJilRvHZ7CBPkhY+XNPcrjlsHbCJEJJ
cX/b0B56p7FyAFy6XjMY9PYFhxy/ubHE7Sk8ixX88uxgM4vf/sSFQaN84CcG/qqA
Nq/OEytBjZP82TsFFYBRcPKEPskF4ejjhNVC4bJpiuCIpo51nIxjeFGtDpeQiDG7
0OWc/GCvsz5W3eG7ShmQsRf0DoivL8tjgDUlMWO9boTpUy2Vnm1rDMKu6nFGKZ8o
TWTZR6u4zGZW1OBERdlU5NQmqxqXrsuGlT6r6DuQnWsWiscjXOyIFeHz+EOKhQOk
wUuN6xN8QES1CDFB27PYV0Sz2KhYhM78GKLCc9B10Ic=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.2.0
Release Details
UpdatedJuly 5, 2026, 9:21 a.m.
Changelog

Added

  • Löschen-Buttons im Detail-Modal für Teilprojekt, Aufgabe und Meilenstein
  • Deutsche App-Beschreibung in info.xml ergänzt

Fixed

  • Baseline-Datum und Kommentar im Report werden untereinander angezeigt
  • Dashboard-Widget zeigt Baseline-Datum und KPI-Werte korrekt an
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureXuYTZMxTlGNJVrGvU/GcIjSlBTYreXdvF5FilCmsi+l7MBvFlyeW1DsnxAsao3pu
HyhvaKXRnkraVQNT2ULgX4BmjUnhsELROrK03gtY88Odzqzh7RIXYkNkRcy92ara
56HIP8BBRD3m1c8pYPfpZfLAzkuurAzVqbJ6qbxw1AebdlPCFEq2g6C+IOcTon0T
q9ZD2I2XJmWd42EXXqva+NMrcx1jfjmHWVtl7xHKhwPndFCtHPMMAdoDS0Xpbh1V
3Ns4h/DphDpC8/ZYZ5sbpMCBekHw4VTZDGxNAuXJ5DMPhkqg2MxVplkS19PuBHLx
r74sgsz611Gjg55gjKOqCb5ZZEvqZTrwKTEYIbu8HicWtq/HxKRZS3FZN9MThTTX
Hesv65vsq9fH0UhEM6MVJjE5Phnw8Wb5ivtAiaUA+NzPrDfrHZAsSfR9NFL3rhFC
7wOmnXtzK40NjmqyMWJu/UP9UVbz3SeEiHiQO9GwBc2krzmXzcmLZFgEjty4CGoJ
SzXAA1QGpUGO4n0b3Y/5zSY4UxogGWybW+mOs27KDLytI915wNYGvBtcdAKCj6Ws
1uVA98npwMid/WSJJ04BXHX8LbowrDeTezSFJyGh+u/KnhlzkA7fqEuu3nQGT5rA
a9qeMmd5kmLBIVcG9RVVmw1kc0AK0qIqZDmlk9dWGJs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1
Release Details
UpdatedMay 27, 2026, 6:43 p.m.
Changelog

Added

  • Glossar-Button in der Navigation (über den Einstellungen) mit interaktivem Nachschlagewerk
  • Dreispaltige Glossar-Tabelle: Begriff/Abkürzung, Bedeutung/Englisch, Erklärung mit Berechnungsformeln
  • 30 Einträge in 6 Abschnitten: Allgemein, Terminplanung, Baseline & Berichtswesen, Earned Value Management, Ressourcen & Kapazität, Budget

Fixed

  • Fehlende deutsche Übersetzungen für englische UI-Strings ergänzt (Add dependency, Baseline, Dependencies, Remove, Work, No projects yet, Open Gantt Projects, u.a.)
  • Leere Übersetzung für Deck-Fortschritt-Hinweis befüllt
  • de.js vollständig mit de.json synchronisiert (war ~70 Einträge veraltet)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF1RHJx15J/N8tWNrDlAX1MFE/9tsct4PViEcBDeo8U/V2qJ/tezL7Xk0BVmYxjWw
pB+J685mcHjikh93/aZUqG8C6y0mR3TTJfaa7dLCIRMc1034x6Xj9Nd7Q9YVH4yi
RLTcRhrG2pN1Iqc23kwId995GJXHnCBEyuQuzo9m1VtA7GoFa8W2nqhXVT94khDS
8ZHQrixDfv63d4GfA5N2x4+phaiyObdskvCmVSuKrmouYvCGpPevtKG5YxzOrHid
aL48Kod0YgOCAD8nAqOhEmioP0m+wP2e/NC4Z5svSKT3vdlE67W1M68ipQQfnhPI
txui+2X8zFvoe/M5sE3DaXbLnOVNGQ7YRpyXFI5GsQmQ0cPSSO/94Mw15hLZfwMf
TDOrrvzf+Vu05sUTZ6CFh4FrSl4e5Q6Xjv6uH4BZtyOpPxl+ajgW4OkZvQDbt4KC
8aTZodMcDpXlRMDdBSxT1bT9tD05KUKmEZYtpxHiARrHDuQQCRSuxtjitQD9lbla
CMxKrBiO0YgdUb3FuczYkWEH+y8gW5egU6TDMnjAVbuUecGuGZN1Cc6tarcFWRu2
+V7Qn+aNoXEwP5e/0TM4Ig8ETAYseZRiTPHxeWiSg3RnU3HuT6kPVWw8LERACDT4
6F4WU9t2+4kESMLMjeV0/2UBQSykR/42Idsg0PtvpyQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1 (nightly)
Release Details
UpdatedMay 27, 2026, 5:24 p.m.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF4W7QJB8rVJ2xARdnhUbEouzRZMKcowz/ToDwr0oVFrVUbInFvHAMqXFTOVD1a5N
kDTb0pOhv+hCStTh70bN+aapjbdtp7SdQ3SbP5VoXBKLhUii41OqN/i4yM7itdSs
M8d5PVo9TJYVnfaij2xwWghMqZEyzxgOw4Q98dYIysf2BxsjjCtxGEMo/pISS90G
OE2RaVEFbwUcvu1XKszLP5X/k/onOchengURYVuzHP7VgVuomZpPSZ9VEIRKITyL
GpgSDrdqm5AXdQc7l/2OqprW6G+MxsuVzChbyPGslIYYXYsP9HLc/FfwVxaCJjh2
Nsiqfd+dMwnf7f9yrrBuZjbNwNa5x++IynrwaU01jVISkl9pgkbUYRaPSMTGFDAd
lsEEqlgs9KdzGoTFjDXNdTwDOsUMiu/ag8tPo7YxsxLFbVGa7laRAUhlGUT9QGPr
IOAAWvKqivsYnU5iwTCY/5HGza3SDfZc/e5upPfB+lTQTYijp1aAYRBAsoOLmuga
f0aP45pnfnYBJkT1kMsthjbGMdlL46wLiNHKRct9OlFwx74IRIyZdcs5Q3pC0aNN
DSLvsCSmY8/9PtTLzcOJKXTqqDVupCqq82bUlGRn7NR6xsinzv74MuOVnGY21tBm
iB5ceYpgiEukUz6YtS2quIIiMTnYmM+HBFnkXgBV0/Y=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32

Nextcloud 29

Gantt Projects 1.5.4
Release Details
UpdatedAug. 27, 2026, 7:06 p.m.
Changelog

Fixed

  • appinfo/info.xml wurde vom Nextcloud App Store abgelehnt: der Sprachcode pt_BR in <name>, <summary> und <description> ist im Schema nicht enthalten — die Enumeration kennt nur BCP-47-Schreibweise in Kleinbuchstaben mit Bindestrich. Alle drei Vorkommen heißen jetzt pt-br
  • Die Kindelemente von <info> sind im Schema eine feste Sequenz; <settings> stand vor <dependencies> und wäre nach dem Sprachcode-Fix als nächster Validierungsfehler aufgeschlagen. Der Block steht jetzt an der vom Schema vorgesehenen Position hinter <dependencies>. xmllint --schema gegen https://apps.nextcloud.com/schema/apps/info.xsd läuft damit sauber durch
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureYbjiEkGYW1s8Xg9Jvrt4nNo/67zkrQDA16yfK2MzVa58R982qrLUupNbvUE/FscP
pFg9nlqxd81cpyH1ZhWOM7fMq98mPyjSUsJZ7xjYzNccF13xvDpBXFlfX4UyCknc
50Rmw9a8RxiCH0mEpH9zWjdanX194R8cUIMDY+BcymCgyBVRQub9yaL5iiHH3hNA
OOD+UOiSUNHns9R6sgZ+tF4OeoTufWR5//AfRmuL5qC5spyuP1iKr7FnzPar+/sS
pHKojfUyi0sAjlrThVZDo8fvVPqkkH4jgvVmNE41t1UOlVdajIRd2yhbHX0h0SSs
rmt4CQqNXAjLE/Wlq2lu9Viw4M2BhhPIfVb5YJFzaxjhB8RRy53rquXRL64f7vGa
agq0ri3eD+0ZWmMOlJmHaMv6AXYd0ZG8myb3PAyNC0NYXzrw2PAVL8jDfzmq2cNY
Q3rIh101vtGPytqanNS2VmtfZ8BX63rd4SLR4PJdRYrm3Wbqanx8WZa20eLgYF8S
M9xhxl5Jf8+TnYLwHi1sGm1ID2XJ4krfgOxiUlB8lGeSweUK1uueC0bmUDsrXyhJ
RMGST41TU1HqXx/3Eeh91JKq/Jbx15kPuxu7WNKyuSOj4vKbgFwBUSmHKDU6lvEZ
L/bnxZi965/4K3kvyJ8o2j5e4xUmJLeusi9Fg0yrRqE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.2
Release Details
UpdatedAug. 26, 2026, 5:18 p.m.
Changelog

Changed

  • Teilen-Dialog: das Eingabefeld für den einzuladenden Nutzer ist jetzt ein Suchfeld statt eines reinen Textfelds für die Benutzer-ID (Issue #144). Getippt wird nach Anzeigename, Benutzer-ID oder E-Mail-Adresse; ein neuer, admin-unabhängiger Endpunkt GET /api/v1/users/search (UserController::search(), IUserManager::search(), max. 20 Treffer, der eigene Account wird ausgeblendet) liefert dazu Anzeigename, Benutzer-ID und E-Mail als Vorschlagsliste. Gespeichert wird weiterhin die Benutzer-ID als principal_id – für Umgebungen mit LDAP/AD-Benutzerverwaltung war die zufällig generierte ID zuvor nicht ohne Umweg über die Benutzerverwaltung in Erfahrung zu bringen. Die Liste bereits geteilter Zugriffe zeigt ebenfalls den Anzeigenamen statt der rohen ID (ShareController::index() reichert die Antwort um display_name an, Fallback auf principal_id falls der Account inzwischen gelöscht wurde)
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureyLn/jlYbk3gIYfgIcjON0z467/xL9L6QNcmfgOa/4zhTJjLknXq7eu1UJddWWlLJ
NG2U9d/OEFO9wLHPYPGNiWgQMRIk+TGYlQVTXcZH3WpBxdJOm3XMnk5hq2kNB2FN
TJemqr/dxafGB2hTIgivPMXMnemdrtxWN1RAdzCxZIg9HwJWjG5JxdqHmURHwske
p/Ki/iZghKjrxbPIghbqtrvkCJRImbgVWY8AmUNUPLgotNIthiWFnkTDVhYWcreP
pWleWHxEn57UOtehff1aJa+hRI+8WVkx9iXArx0SThmOCYYgJ/HeG8dOOtmr9jSm
1nzETAL87YLxL/+qM6DyBIgNWhrq1oj9Q12bBm1+la477z6fpOgUlMjWkPEYRQi/
vhxirXwB4BZBYvaMEkNlN2U3+7BqgigGoaTcbN2rG/TP1JuOjdj9jEjMMRye0JbT
35XOoM/VPYGovrT9slHg8r3Wolyd8ioXVOu4IcMImvC5OrTzpKY7NLEc/S+cOKwz
ijOKcnoV0j++KxtQKKgEO96HJpEM0jteQVOGCoPbghjS3OFzkiB1bUVJAlu1jn54
38Q2zw8rZ0EMm1rEPmIrl72DIIXionbWKatREgy7QPwuL/WKZ2c+J1UIUTKck5eO
7M2VHh6P3FnIg/UsOCMu3U7ja0Y8vQ3YUo3tazLfob0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.1
Release Details
UpdatedAug. 25, 2026, 1:28 p.m.
Changelog

Added

  • Screenshot des gerenderten Gantt-Charts als PNG, SVG oder PDF (Issue #116): ein neuer „Screenshot"-Eintrag in der Chart-Toolbar öffnet ein Menü mit den drei Formaten, derselbe Export steht im Bearbeiten-Dialog des Projekts neben JSON und CSV bereit (dort mit vorgeschalteter Formatwahl statt Menü, openScreenshotFormatDialog()). Exportiert wird stets das gesamte Chart, nicht nur der sichtbare Scroll-Ausschnitt — frappe-gantt rendert die volle Breite und Höhe ohnehin als eine SVG. PNG und SVG klonen die aktuell gerenderte SVG und schreiben getComputedStyle() als Inline-Attribute (löst auch CSS-Variablen auf), sodass die Datei ohne das App-Stylesheet eigenständig darstellbar ist; sie folgen dem eingestellten UI-Theme, ein hinterlegtes Rechteck in --color-main-background deckt dabei auch die Fläche unterhalb der Aufgabenzeilen (Workload-Chart, Leerraum) ab, die sonst transparent bliebe. PNG rastert per Canvas mit doppelter Auflösung
  • Zeitraum-Auswahl für den PDF-/Druck-Export (Issue #116): vor dem Drucken fragt ein Dialog, welchen Zeitraum die PDF abdecken soll — gesamter Projektzeitraum (Vorbelegung), laufendes Kalenderjahr, „aktives Quartal" (Vormonat bis einschließlich übernächstem Monat) oder ein frei eingegebener Bereich. Der Vorschlag ist der tatsächlich von Balken belegte Zeitraum, nicht gantt_start/gantt_end — frappe-gantt legt darum großzügig Puffer, der andernfalls mitgedruckt würde. Umgesetzt wird der Zuschnitt als viewBox auf dem SVG-Klon; die dafür nötige Skala (Chart-Start, Pixel pro Millisekunde) schreibt GanttChart.vue nach jedem Render als data-Attribute an den Chart-Container, sodass auch Aufrufer ohne Zugriff auf die frappe-Instanz (Bearbeiten-Dialog) sie lesen können. Die Zeitraum-Logik steckt isoliert testbar in src/gantt/printRange.js (src/gantt/printRange.test.js)

Changed

  • Der PDF-Export druckt nicht mehr den Live-Container, sondern einen gestylten, selbst gerasterten Klon des Charts (Issue #116). Bisher wurde das Inline-SVG direkt gedruckt — wie fein die Browser-Engine das rastert, ist nicht steuerbar und fiel je nach Browser sichtbar grob aus. Jetzt wird der Klon mit 300 dpi bezogen auf die bedruckbare Fläche (A4 quer, 10 mm Rand → 277 × 190 mm) in ein Canvas gerastert und als Bild mit fest vorgegebener physischer Größe in den Druck gegeben; schlägt die Rasterung fehl, bleibt der SVG-Klon als Druckinhalt übrig und skaliert per CSS auf die Seitenbreite. Der Klon wird immer im hellen Theme berechnet (Papier ist weiß, ein dunkles UI-Theme ergäbe sonst dunkelgraue Flächen und helle Schrift) — dafür genügt ein ausgelagerter Container mit gesetztem data-theme-light, da Nextcloud die Stylesheets aller Theme-Varianten lädt und nur über dieses Attribut umschaltet. Das Ausblenden der übrigen Seite läuft über display: none statt visibility: hidden, sonst blieb die Fläche der App als Leerseiten erhalten; Nextclouds Hintergrundbild auf html/body wird für den Druck abgeschaltet. Aufgeräumt wird über afterprint mit Timeout-Fallback für Browser, die das Event nicht feuern
  • Der Toolbar-Eintrag heißt „Screenshot" (Icon MonitorScreenshot) statt „Export" (Icon ExportVariant) — „Export" ist im Projekt bereits für den Daten-Export nach JSON/CSV belegt, der Chart-Export ist demgegenüber eine reine Abbildung der aktuellen Darstellung
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturek6kpvF53IR8tnkOFXyoSPFPGlwVzO/RUTgI1p1ppRcFmoFPUyFeIokOqWA1OtD9q
wdo+qkQQPyJGph0Uwp+hwP9oGmeKWErnlv8HMdA5I5iaXIN7afGvdfxWfKgqWetw
tGmCF7l2IzMmT2Ol/3pKgCnFkXTDPCVeNINNbUdi7EjeRpbcbOGWt3+5UVwZgVTX
Z9/kEEPvjMV98s1zT5yBXcFvyqZA7bhhezmBkfzaLa0DAIczFJbhyFtR06WuS7bP
CWBCs/xvSRuCwo61oyXRAWLohz/2GsrMSa5jvPvnEG1XEHpGPVorCijAvsBm4sX5
yAZTrBm3zbNYObqVE2idxTH/eqP8xXtE38JFjpG5IahpeFNb9V2EbH1nrjfRKYcH
NVvXcXFREAmvorShKDE0ymFN+/BCjvU1vnJCnRD5RMsoSGUzXSVHPrF1emtusLHj
ltP22eYOCpraEpZheTkFftqvpbiQKVXsZ+8wY4dJnh5Ymi71loxZhggoH8s/pmp+
WbSx6MZwSPBqJ0HOJWckFiZXur2OQYOVLfXt8uPYNU/EZSoYDqm81GVMglQXxyM6
YDh0xXqwJ7Qft7k0O/3Fa5YLJGP4B4RvjU29N3/WDsfjuk7PeCSTPhRlTOfV/Fbn
jS83WVBavmyklhu0rNYuCYfdYvUxemIO3VuGmYzFXgI=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.0
Release Details
UpdatedAug. 24, 2026, 5:26 p.m.
Changelog

Added

  • Editierbare Vorgänger-Spalte in der Tabellenansicht, Teilschritt F von #117 (Issue #124): eine neue, über das Spaltenmenü einblendbare Spalte „Vorgänger“ zeigt die Abhängigkeiten einer Aufgabe in der aus MS Project bekannten Kurznotation (12EA+2d = Vorgänger Aufgabe 12, Ende-Anfang, zwei Arbeitstage Abstand; mehrere durch ; getrennt) und nimmt sie dort auch entgegen. Typ (EA/AA/EE/AE, englische Kürzel FS/SS/FF/SF werden beim Eingeben mit akzeptiert) und Abstand sind optional, 12 allein bedeutet also EA ohne Abstand. Geprüft wird beim Verlassen der Zelle: Syntax, Existenz der Aufgabe im selben Projekt, Selbstbezug, Dubletten und Kreisschluss über die gesamte Abhängigkeitskette – bei einem Fehler bleibt die Zelle im Bearbeitungsmodus und nennt den Grund im Klartext, die bisherigen Abhängigkeiten bleiben unangetastet. Gespeichert wird über dieselbe PUT /tasks/{id}-Route wie der Detail-Dialog (inkl. dep_mode), sodass der Server die Termine anwendet und durch die Nachfolgerkette propagiert; Balken, Pfeile und Typ-Labels im Chart zeichnen sich mit der Antwort neu. Bewusst ohne Undo-Toast, da ein Zurücknehmen nur der Termine (nicht der Abhängigkeit) beim nächsten Neuberechnen wieder überschrieben würde. Nur Aufgaben haben eine editierbare Zelle – Meilensteine hängen über task_id an genau einer Aufgabe, Projekte und Teilprojekte sind berechnete Strukturebenen. Notation und Prüfungen stecken isoliert testbar in src/gantt/dependencyNotation.js (src/gantt/dependencyNotation.test.js)
  • Zeitabstand (Lag/Lead) je Aufgaben-Abhängigkeit (Issue #124): neue Spalte lag_days in nc_gantt_task_deps (Version1032; der Name vermeidet das seit MySQL 8.0 reservierte Wort LAG und nennt die Einheit). Der Abstand zählt in Arbeitstagen über den Ressourcenkalender wie die übrige Terminberechnung, wirkt bei allen vier Abhängigkeitstypen auf deren Ankertag und darf negativ sein (Lead: der Nachfolger beginnt früher). Bestandsabhängigkeiten bekommen 0 und verhalten sich unverändert. TaskService::update() weist eine gesendete Vorgängerliste jetzt vollständig zurück (invalid_dependency, dependency_cycle), bevor die bisherigen Abhängigkeiten dafür gelöscht werden, statt Vorgänger aus fremden Projekten oder Zyklen zu speichern – letztere hätten die topologische Sortierung in recalculateProject() stillschweigend Aufgaben überspringen lassen

  • Konfigurierbare Spalten in der Tabellenansicht, Teilschritt E von #117 (Issue #122): ein Menü in der Tabellenkopfzeile blendet Spalten ein und aus, die Reihenfolge lässt sich per Drag & Drop der Kopfzellen ändern (barrierefreie Alternative: Strg+Pfeil links/rechts auf der fokussierten Kopfzelle), die Breiten per Ziehen am Spaltentrenner (Doppelklick setzt auf die Standardbreite zurück). Zusätzlich zu den bisherigen Spalten (Titel, Start, Ende, Fortschritt) stehen ID, Dauer (Kalendertage inkl. Start- und Endtag, bei Meilensteinen 0), Status (Issue #78, nur Aufgaben/Teilprojekte) und Ressource (Issue #103, zugeordnete Ressourcennamen einer Aufgabe) bereit – alle vier als reine Anzeigespalten, die das Inline-Editing und die Tastaturnavigation daher überspringen. Auswahl, Reihenfolge und Breiten werden pro Nutzer über den in Teilschritt B angelegten UI-Preferences-Endpunkt persistiert (neuer Key gantt_table_columns, kein Schema- oder Endpunktwechsel nötig); eine gespeicherte Konfiguration wird beim Laden validiert, sodass entfernte Spalten oder unplausible Breiten die Ansicht nicht beschädigen können. Die Tastaturnavigation folgt jetzt der sichtbaren Spaltenreihenfolge statt einer festen Liste. Reicht die Tabellenbreite für die gewählten Spalten nicht aus, scrollen Kopfzeile und Zeilen gemeinsam horizontal; die Obergrenze des Splitters steigt dafür von 560 auf 900 px. Die Spaltenlogik steckt isoliert testbar in src/gantt/tableColumns.js (src/gantt/tableColumns.test.js). Ein Sortieren der Zeilen nach Spaltenwert ist bewusst nicht enthalten – es kollidiert mit der Hierarchie Projekt → Teilprojekt → Aufgabe und der sort_order, die das Chart für dieselbe Zeilenreihenfolge braucht

  • Tastatur-Navigation in der Tabellenansicht, Teilschritt D von #117 (Issue #121): Tab/Shift+Tab springen zur nächsten bzw. vorigen editierbaren Zelle (überspringt Zeilen ohne Schreibrecht, springt am Zeilenende zur nächsten/vorigen Zeile), Enter speichert und springt in dieselbe Spalte der nächsten Zeile, Esc bricht ab und stellt den vorigen Wert wieder her. Pfeiltasten bewegen die Zellauswahl, wenn gerade keine Zelle im Editiermodus ist (innerhalb einer Zelle bleiben sie für Cursor-/Wertänderung nutzbar). Die reine Traversierungslogik steckt isoliert testbar in src/gantt/tableNav.js (src/gantt/tableNav.test.js). Die ausgewählte Zelle bekommt einen sichtbaren Fokusrahmen und wird per scrollIntoView() ins Sichtfeld gescrollt – über den bestehenden bidirektionalen Scroll-Sync (Issue #119) folgt das Gantt-Chart automatisch mit. Neue ARIA-Rollen (grid/row/columnheader/gridcell, aria-readonly je Zelle) machen die Tabelle für Screenreader als Grid erkennbar
  • Read-only Tabellenansicht links vom Zeitstrahl, Teilschritt B von #117 (Issue #119): neue Komponente GanttTable.vue zeigt dieselben Zeilen wie das Chart (Titel mit Einrückung nach depth, Startdatum, Enddatum, Fortschritt), gespeist aus buildRowModel()/buildTableRows() (siehe Teilschritt A, Issue #118) – Reihenfolge, Filter-Dimming, kritischer Pfad und Projektfarben bleiben so exakt konsistent zum Chart. Zeilenhöhen sind fest an frappe-gantt gekoppelt (40 px = bar_height 22 + padding 18, Kopfzeile 52 px = header_height). Ein Toolbar-Button blendet die Tabelle ein/aus, ein Splitter macht die Breite verstellbar (160–560 px), vertikales Scrollen ist bidirektional mit .nc-gantt-container synchronisiert. Sichtbarkeit und Breite werden pro Nutzer über einen neuen, generischen UI-Preferences-Endpunkt (GET/PUT /api/v1/ui-preferences, SettingsController über IConfig::get/setUserValue() als JSON-Blob) persistiert, damit Teilschritt E (Editieren) das Schema nur noch um weitere Keys erweitern muss, ohne Endpunkt oder Speicherformat anzupassen. Rein lesend – Editieren folgt in Teilschritt C
  • Projekt-Export/-Import (JSON, format_version: 2) kann jetzt optional die von den Aufgaben referenzierten Ressourcen inkl. Gruppe, Wochenmodell, Arbeitskalender+Feiertagen und Tageskapazitäten mitnehmen, statt wie bisher nur die rohe resource_id in eine Zieldatei zu schreiben, die dort ungeprüft übernommen wurde – auf einem System ohne identische Ressourcen-IDs ging die Zuordnung damit verloren oder landete schlimmstenfalls bei einer fremden Ressource (Issue #115). Vor dem JSON-Download entscheidet ein Options-Dialog, was mitexportiert wird (Ressourcen / Verfügbarkeiten / Kalender, gestaffelt); vor dem JSON-Import zeigt ein passender Dialog, wie viele Ressourcen die Datei enthält, und lässt auswählen, was übernommen wird sowie ob bei Namensgleichheit die vorhandene Ressource wiederverwendet (Default) oder immer neu angelegt wird. Referenzen werden ausschließlich über eine Mapping-Tabelle aufgelöst und nie roh durchgereicht; nicht auflösbare Referenzen werden verworfen (Regressionsschutz zu #112/#113). Neu angelegte Ressourcen/Gruppen/Kapazitäten gehören stets dem importierenden Nutzer. Wochenmodelle/Kalender/Feiertage sind weiterhin admin-only anlegbar – ohne Adminrechte werden nur namensgleiche vorhandene Einträge gemappt. CSV bleibt unverändert aufgabenzentriert mit Ressourcen nur als ID-Referenz; der volle Ressourcenumfang ist JSON-only. Alt-Exporte ohne resources-Schlüssel importieren unverändert wie bisher, ohne Options-Dialog
  • Inline-Editing der Basisfelder in der Tabellenansicht, Teilschritt C von #117 (Issue #120): die Zellen für Titel, Start, Ende und Fortschritt sind direkt editierbar und aktualisieren die Balken im Chart sofort. Gespeichert wird über die bestehenden Store-Actions (updateTask/updateSubproject/updateMilestone/updateProject) inkl. Terminpropagation und dem vorhandenen Undo-Toast für Aufgaben (analog Drag & Drop im Chart); ein lokaler pendingEdits-Overlay zeigt die Änderung optimistisch an und rollt sie bei einem fehlgeschlagenen Request mit Fehlermeldung zurück. Die Schreibrechte löst tableRows.js je Feld über store.canEditProject() auf (titleEditable/startEditable/endEditable/progressEditable) – Projekte und Teilprojekte bleiben bei Datum und Fortschritt read-only, da diese Werte aus den Aufgaben berechnet werden. Geprüft wird: Titel nicht leer, Enddatum nicht vor Startdatum (Meilensteine ausgenommen, deren Start- und Endspalte dasselbe milestone_date zeigen) und Fortschritt 0–100. Im Simulationsmodus rufen Datumsänderungen an Aufgaben simulateTaskChange() statt einer echten Speicherung auf
  • Ressourcen-Überbuchungswarnung (Issue #82): die Kapazitätsplanung war bisher reine Datenerfassung ohne Abgleich mit dem tatsächlich verplanten Aufwand. Der neue Endpunkt GET /api/v1/resources/{id}/utilization?from=&to=&group= (ResourceUtilizationService/ResourceUtilizationController) stellt den geplanten Aufwand aus den Aufgaben-Ressourcen-Zuordnungen – gleichmäßig auf die Arbeitstage der Aufgabe verteilt, über WorkCalendarService mit ressourcenspezifischen Arbeitstagen und Feiertagen – der erfassten Tageskapazität gegenüber. Mehrfach-Ressourcen-Zuordnungen (Issue #103) samt Alt-Aufgaben werden berücksichtigt, ohne gepflegte Tageskapazität gilt ein Fallback von 1 PT/Tag. Im Ressourcen-Raster färben sich die Zellen nach Auslastungsgrad (> 90 % gelb, > 100 % rot) und der Tooltip zeigt geplante Stunden gegen Kapazität samt Aufschlüsselung nach Projekt und Aufgabe; das Workload-Chart im Gantt markiert überbuchte Perioden jetzt rot. Übersetzungen für alle sechs Sprachen ergänzt
  • Nachgetragen – beide Funktionen wurden bereits mit 1.4.2 ausgeliefert, waren dort aber nicht im Changelog dokumentiert:
  • Einheitliches Statusmodell für Teilprojekte und Aufgaben (Issue #78): neues Feld status (not_started/in_progress/blocked/done) auf nc_gantt_subprojects und nc_gantt_tasks ergänzend zum numerischen Fortschritt, Bestandsdaten leitet Migration Version1030 daraus ab. Validierung per StatusValidator (Whitelist analog ColorValidator), Status-Dropdown in den Detailformularen, blockierte Balken im Gantt schraffiert und mit Warnsymbol, blocked_count je Projekt im Rollup sowie ein Badge in der Projektliste
  • Mehrere Ressourcen mit individuellem Aufwand je Aufgabe (Issue #103): bis zu vier Ressourcen je Aufgabe mit eigener Aufwandsschätzung in der Einheit der Ressource (neue Tabelle nc_gantt_task_resources, Migration Version1029, Backfill aus den vorhandenen resource_id/work-Werten). Das manuelle Work-Feld entfällt: TaskService::resolveResources() summiert die Einzel-Aufwände zur Gesamt-Work (1 PT = 8 h) und übernimmt die erste Zuordnung als primäre resource_id, die weiterhin für Arbeitskalender und CPM maßgeblich ist. Ressourcen-Filter sowie CSV-/JSON-Export und -Import berücksichtigen alle Zuordnungen statt nur der primären

Changed

  • buildFrappeRows() (src/gantt/frappeRows.js) mischte bisher Datenmodell und frappe-gantt-Formatierung: die Einrückungstiefe steckte als Leerzeichen im name, Zustände (Dimming, kritischer Pfad, Summenbalken) hingen in custom_class – eine geplante Tabellenansicht neben dem Gantt-Chart (Issue #117) hätte dieselbe Logik duplizieren müssen (Teilschritt A, Issue #118). Neue Funktion buildRowModel() (src/gantt/rowModel.js) liefert dieselbe Zeilenliste jetzt als neutrales Modell (id, type, entityId, depth, title, start, end, progress, parentId, editable, matched, critical), resolveMilestoneDate() wandert mit ins Modell. buildFrappeRows() ist nur noch ein dünner Adapter darüber, der name/custom_class/dependencies wie bisher erzeugt. Reines Refactoring ohne sichtbare Änderung, per Fixture-Vergleich gegen die alte Implementierung geprüft; neue Unit-Tests (src/gantt/rowModel.test.js, npm run test:unit, via neu eingeführtem vitest) decken Hierarchie, Einrückungstiefe, Meilenstein-Datumsauflösung und Filter-Dimming ab

Fixed

  • Abhängigkeitsfortschreibung bei Aufgaben lieferte für alle vier Abhängigkeitstypen (EA/AA/EE/AE) einen um einen Arbeitstag zu langen Nachfolger-Termin – bei EA zusätzlich mit falschem Anschlusstag, da der Nachfolger auf dem Endtag des Vorgängers statt am folgenden Arbeitstag begann und dadurch mit dem Vorgänger überlappte (Issue #114). Ursache war eine Off-by-one-Vermischung zwischen der inklusiven Arbeitstagszählung und der exklusiven Datumsfortschreibung in TaskService::applyConstraint()/computeMultiConstraint(). Betroffen sind sowohl das direkte Setzen einer Abhängigkeit als auch die Terminpropagation nach Drag & Drop und die What-if-Simulation. Verhaltensänderung: Bestehende Pläne mit EA-Abhängigkeiten verschieben sich beim nächsten Speichern/Neuberechnen um einen Arbeitstag nach vorne (auf den korrekten Anschlusstag); bei AA/EE/AE verkürzt sich der zuvor fälschlich verlängerte Nachfolger-Zeitraum um einen Arbeitstag, die ursprüngliche Dauer bleibt jetzt garantiert erhalten. Abhängige Meilensteine (Dauer 0) sind unverändert korrekt und von diesem Fix nicht betroffen

  • Security: jeder Nutzer mit Schreibrecht auf ein eigenes Projekt konnte jede in der Instanz existierende resource_id einer eigenen Aufgabe zuordnen – unabhängig vom Eigentümer und ohne jede Share-Beziehung (Cross-Tenant-IDOR, Issue #112). TaskService::resolveResources() lud die vom Client gesendeten IDs bisher nur über ResourceMapper::findByIds(), also als reine Existenzprüfung ohne Berechtigungscheck; da die IDs fortlaufende Integer sind, waren sie trivial zu erraten. Der Listen- wie der Legacy-Einzelfeld-Pfad in create()/update() filtern referenzierte IDs jetzt über ResourceMapper::findVisibleToUser(), nicht sichtbare IDs werden wie unbekannte verworfen (Liste) bzw. nicht übernommen (Einzelfeld, der bestehende Wert bleibt erhalten). Regressionstests decken beide Pfade für create() und update() ab

  • Feiertage ließen sich nicht in mehreren Regionskalendern führen: POST /admin/holidays lief in einen HTTP 500, sobald dasselbe Datum in einem zweiten Kalender angelegt wurde, weil der Unique-Index aus Version1006 allein auf holiday_date lag, obwohl Feiertage seit Version1013 über calendar_id kalenderbezogen sind. Version1031 verschiebt den Index auf (holiday_date, calendar_id); die Eindeutigkeit globaler Feiertage sichert weiterhin der Upsert in HolidayAdminController::create(). Betroffen war neben der Feiertagsverwaltung auch der Kalender-Import aus einem Projekt-Export, wo der Fehler still verschluckt wurde. Zweitens war GET /admin/holidays admin-only, während work-calendars und work-schedules lesend für alle offen sind – für normale Nutzer blieb der Store deshalb immer leer (keine Feiertage im Projekt-Export, keine Markierung arbeitsfreier Tage im Ressourcen-Raster); der Lese-Endpunkt zieht nach dem Muster der übrigen Stammdaten auf /api/v1/holidays um (NoAdminRequired), Anlegen und Löschen bleiben unter /api/v1/admin/holidays. Ergänzend werden Feiertage jetzt mit ihrem Kalender gelöscht (WorkCalendarController::destroy(), HolidayMapper::deleteByCalendar()) und Bestandswaisen bereits gelöschter Kalender in Version1031 aufgeräumt – über verweisende Ressourcen haben sie bis dahin weiter als arbeitsfreie Tage gewirkt
  • Die Installation der Tabelle nc_gantt_task_resources scheiterte auf Instanzen, deren Tabellenpräfix die von Nextcloud aus Oracle-Kompatibilität erzwungene 30-Zeichen-Grenze für Bezeichner reizt: der von Doctrine automatisch vergebene Primary-Key-Name (Tabellenname + _pkey) wird mit Präfix oc_ zu lang (Issue #132). Die Primärschlüssel-Definition bekommt – analog zu den bestehenden Migrationen für nc_gantt_resource_capacity und nc_gantt_resource_groups – einen expliziten kurzen Namen
  • Der absolut positionierte Toggle-Button der Nextcloud-App-Navigation ragte in den App-Content hinein und verdeckte dort den Titel des ersten Eintrags in der Projekt-Rollup-Leiste (Issue #134); der linke Innenabstand reserviert jetzt Platz für den Button
  • Ein leerer Balken in Höhe der Kopfzeile erschien oberhalb des App-Inhalts, weil der CSS-Reset für den Nextcloud-Content-Wrapper an main#content.app-nc_gantt gekoppelt war (Issue #126). Ändert sich das Markup zwischen Nextcloud-Versionen (Klasse oder Tag), griff der Reset nicht mehr und der von NcContent (#content-vue) selbst gesetzte margin-top: var(--header-height) addierte sich zum Rand des Cores. Der Reset hängt jetzt robust an #content mit !important, ohne Abhängigkeit vom app-spezifischen Klassennamen
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegO8Qmtdwg8LOBKr5G+mq4co374hsqqSN6oCIRV9atUx0KqogPxisJiIExkPoSIbu
e4EeIaUzg2alhZsKBLWOwR3Ab3fNm5vvd2DzJn6Hei7A70woz9m/LXkVlcx3vALW
bwolClUX1XCZOBVo/kSS5AOVwak3/huzT1iF9xH9BnpdzVuOjZ563LYwSayfb2Gq
SkWNfLZlKlDkTTF2PIszrNmaa89GmyqTbraxSP16/9nYdIj8ZU0zrhC3HtpihgAT
wya2lwnMf6I73HU0xxgT/+ITTmurx4eHwKWH80P4tjg/Y9mnHODIpAslbW3351mc
qdTHXIBW1MB1YGCQ+JPt11v1X2ccn29kEF9d1nkizKZ1+Du+LJQhPhwHiQxPWSwAUlTlQ4tj1lbe3+FJ1jsVTCqWbDUN02EoV7ZwtN15/HLJfM6w4drUi8ThJ1aLG3v6
cIwMJjYgWeepxR2gv4YgBf4u7txodm5bAWuzySk4rfAG1dBxmie/bfNnWmQ9LrYy
spwKc73gsPhg03ll+jp0b27fJfNF4IpOysz3i+r1ExVfeNIWWO91JPatO2pXZh4O
q0rczM8wYvctvcEkKQIL67HoIrKw4GVIXfqM9Oc8e/Ay7d/NPL+BjpCO6zvoFBY+
SRmjit15YmGW4EQwweQfKXuZ2YeySj+7OjhKHpKfvfg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.2
Release Details
UpdatedAug. 20, 2026, 4:41 a.m.
Changelog

Changed

  • release.sh fragt die Versionsnummer jetzt interaktiv ab, statt sie nur aus appinfo/info.xml zu lesen: zu Beginn werden die aktuelle Version und das letzte Release-Tag angezeigt und es wird gefragt, ob eine neue Nummer vergeben werden soll (Vorschlag: nächster Patch-Level). Die Eingabe wird auf das Format X.Y.Z, auf einen echten Versionssprung und auf ein noch nicht vergebenes Tag geprüft und dann nach appinfo/info.xml, package.json und package-lock.json geschrieben. Fehlt ein Changelog-Abschnitt zur neuen Version, warnt das Skript; anschließend bietet es an, den Bump als chore: Version X.Y.Z zu committen — nötig, weil der Tag-Name den Commit-Hash enthält. Für nicht-interaktive Läufe gibt es --version=X.Y.Z und --no-bump; ohne TTY wird nie gefragt, make release bleibt also unverändert nutzbar
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturepoVHZxteU3AR2cpRRV98jNfv4j7rWnLEd3kbpn5Dp4T1ubAI+0QlRf9jxi0BaYz1
+1dg+3Yt25uN6Slp1FHiw7kyqt5HJ60XgnOWo+Zt0YBHSnWqLDhF8zxPEnQCkF+v
nUvscpcoQY2bRiIBZhVWVgeAD592uvOA4gfRDrUyLVDKjOrzBu3ScbNvCqTqE9IC
vs8qZkRLIcaD6tKoSLT2pZqmwBoCdClvEw5D5v40WSvLq232BZnXpQ4ogG3QvJdL
mYO8HlPZKVOxANp0MGnWle4IFRMbMt22Ebpkjc18Q9Q4AIv/Sd3zTsc+uMONTM95
s0GXvLSm95o0UCFZ1CL7fCEq7NwlprjVrTeWGmlJNVjHJuIyRKHEz5DR12s0lAGm
/rKGWjoVP+Wvk3upRizR/5y2fpWoiwuDqCtz9gQN6+HHYLaqLvYqsAJ/SxmtLtP7
8B2rK61ClKowz4phbTZdrCaF8w3IWnFFBTFquIVFuN2jUoHvdCMtd0910sLAMY5M
Xs9nk0FVsP23zKXlQpOojq7ouCQagRhsnBJDLxWRR+NIq582f7SDgcrkBqxlGJX4
/Ql9V+2iFx61QANCTjdJIO3LbvbK60vGos+ency0GfvSDbPovDAVyJzt8ZUh5BNw
UXQ4XOHJZAagAr36rKttk1DQbZZ/2bUEq231c96m83g=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.1
Release Details
UpdatedAug. 17, 2026, 7:43 p.m.
Changelog

Fixed

  • Die Meilenstein-Raute hatte eine fest verdrahtete Farbe (#fbbf24) statt der Projektfarbe zu folgen, obwohl Balken für Aufgaben und Teilprojekte bereits korrekt eingefärbt wurden (Issue #102). Zusätzlich wurden die separaten – und wirkungslosen – Farbwähler bei Teilprojekt und Aufgabe entfernt, da deren Anzeige ohnehin immer der Projektfarbe folgt
  • „Meilenstein hinzufügen" im Drei-Punkte-Menü der Projektliste nutzte zwei aufeinanderfolgende window.prompt()-Dialoge (Name, dann Datum als Freitext) statt des regulären Detail-Dialogs (Issue #101). Der Klick emittiert jetzt add-milestone analog zu „Aufgabe hinzufügen" (Issue #91) und öffnet denselben Meilenstein-Dialog wie beim Bearbeiten, mit vorbelegtem Projekt
  • Im festen Datumsmodus des Aufgaben-Detaildialogs konnte ein Enddatum vor dem Startdatum eingegeben und gespeichert werden (Issue #100). Jetzt zieht ein über das Enddatum hinaus verschobenes Startdatum das Enddatum mit; ein zu frühes Enddatum erzeugt ein rotes Fehler-Icon am Feld, hält den Fokus dort und deaktiviert den Speichern-Button, bis ein gültiges Datum eingetragen ist (dateField() in fields.js mit neuer setError()-Methode)
  • Eine Aufgabe mit Abhängigkeit behielt trotzdem die manuell eingegebenen Start-/Enddatumsfelder, die beim Speichern durch die Abhängigkeitsberechnung überschrieben wurden – für Nutzer nicht nachvollziehbar (Issue #99). Der Aufgaben-Detaildialog hat jetzt denselben Fixed-date/Dependency-Umschalter wie Meilensteine (Issue #90): im Dependency-Modus ersetzt eine Dauer (Tage) die Datumsfelder, die Abhängigkeit bestimmt den Termin. Serverseitig wendet TaskService::create() neu hinzugefügte Abhängigkeiten sofort an (bisher nur update()), damit auch neu angelegte Aufgaben keinen die Abhängigkeit ignorierenden Platzhaltertermin behalten
  • Der Speichern-Button des Detail-Dialogs blieb nach dem Hinzufügen einer Abhängigkeit deaktiviert: die Gültigkeitsprüfung hing allein am input-Event, das bei einem <select> vor dem change-Event feuert – zu diesem Zeitpunkt war die Abhängigkeit noch nicht in der Liste. detailModal.js wertet jetzt zusätzlich change aus, der Abhängigkeits-Editor (fields.js) meldet jede Mutation (Hinzufügen, Entfernen, Typwechsel) und der Fixed-date/Dependency-Umschalter in taskForm.js den Moduswechsel über ein eigenes bubbelndes change-Event

Added

  • CONTRIBUTE.md und setup-dev.sh für neue Mitwirkende (Issue #98): Projekt-Leitlinien (FOSS, Datensparsamkeit, Security), VIBE Coding ausdrücklich erlaubt, sowie der komplette lokale Dev-Setup-Weg (Docker-only, Nextcloud + MariaDB unter localhost:8088, optionales E2E-Setup, Git-/Bot-Auth). setup-dev.sh automatisiert diese Schritte, konfigurierbar über --workdir/--no-e2e/--no-docker-install
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturetYcUvVtDEQrAakG32EQF1atSOmz91/8XudCUh60A9r/AxMv+AA5AX2rIvNYDkEn0
OuTmXA6Iz0DoVkdij11MPDhfLaUy0mXeLbwB5fj/xF/cFYcNeCFGKD3k2jyRwOTo
2n1sEp0r8sn0fitHzLfhcB/By0VJkZyhaO/NctP01XfcSQM7lGEOeY/uagtGaDTI
JnPLoQTHo0nUOv4y7jUMZuurEgBOc4yeUxTKv4A/m642hvNQIS0q6K9ON41JHKIO
UtI7DbDWSboVhct1Go3zoj5XcaAGG9ZiwSkJDt/BXT5tWH4SOnSn7mys0j1F6Y0g
vIJWjz6S2RlekwIrIxgyaeEVBOeQ4OM1ilJGWKXDyYJM/0IBxqnqRXDkAFlOopDo
xezlcFdWTkYL5OBKMFJhKuTy7dEQ9hxx5Nz4+vLbNV8gDJZBI+5yI5m8W+809WIH
iKUpIda1ShOblvYH6Ek5AzBdTmoFQ6T4f7iWT7j1i4uUP2DsgehjGVl8yDIT/wyx
nV9t7koXapu1WeXrunSa++mXsi3lceNpthdz5/cUEOe9yWvc1rrulRhiWzePga8H
zjVQYGAIPvwYkWa47U6Zh7HBsQaS+9yQ8NDnD49SS4jUWTGlwNqfM+q9fZ16/G9d
7l4chZUPPbBv+xWfdxmFw+MC5zdkAq27isHToTqnm+w=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.0
Release Details
UpdatedAug. 13, 2026, 6:41 a.m.
Changelog

Added

  • Meilensteine werden jetzt immer direkt unter ihrem übergeordneten Objekt angezeigt statt gesammelt am Projektende (Issue #90): Meilensteine lassen sich neu auch einer Aufgabe zuordnen (bisher nur Projekt/Teilprojekt, Spalte task_id war bereits vorhanden, es fehlte nur die UI), das Auswahlfeld im Detail-Dialog heißt entsprechend „Übergeordnetes Objekt" statt „Teilprojekt". Zusätzlich können Meilensteine ihr Datum jetzt statt eines fixen Werts aus einer Abhängigkeit zum übergeordneten Objekt beziehen (EA/AA/EE/AE, analog zu Aufgaben-Abhängigkeiten; neue Spalte dependency_type, Migration Version1028) – da ein Meilenstein keine Dauer hat, bestimmen EA/EE das Ende und AA/AE den Start des Zielobjekts. Die Berechnung übernimmt der neue MilestoneDateService (serverseitig live bei jedem Abruf sowie im ICS-Feed neu aufgelöst, nicht persistiert propagiert) sowie clientseitig in frappeRows.js für sofortige Reaktivität ohne Neuladen
  • Undo für Drag-&-Drop-Verschiebungen inkl. Propagationskette (Issue #85, Umsetzung der einstufigen Empfehlung: letzte eigene Operation, kein Redo, zunächst nur Drag & Drop): Migration Version1027Date20260810020000 ergänzt nc_gantt_change_events um op_id (UUID) – alle Change-Events einer zusammenhängenden Operation (Drag + gesamte Propagationskette B→C→D) erhalten dieselbe op_id, erzeugt in TaskService::update()/propagateDates() und über ChangeEventService::log() protokolliert (dieses Change-Tracking war bisher tote Scaffolding-Logik ohne Aufrufer). Neuer Endpoint POST /api/v1/changes/{opId}/revert (ChangeEventController::revert, ChangeEventService::revertOperation) wendet alle Change-Events der Operation invers an, in einer DB-Transaktion mit Rechteprüfung über AccessService::canEditOrOwn(). Konfliktschutz: Revert wird mit HTTP 409 abgelehnt, sobald der aktuelle Feldwert nicht mehr dem protokollierten new_value entspricht (jemand hat zwischenzeitlich erneut geändert); der Revert selbst erzeugt wiederum neue Change-Events, die Audit-Historie bleibt lückenlos. Frontend: nach jedem gespeicherten Drag zeigt GanttChart.vue einen Toast „N Termine verschoben — Rückgängig" (showUndo aus @nextcloud/dialogs, 10 s sichtbar) sowie ein globales Ctrl+Z für die letzte eigene Operation der Session (stores/projects.js: lastOperation, revertOperation(), undoLastOperation())
  • Kalender-Export als abonnierbarer ICS-Feed pro Projekt (Issue #83, Variante A aus dem Umsetzungsvorschlag): neue öffentliche Route GET /apps/nc_gantt/projects/{id}/calendar.ics?token=… (regulärer, unauthentifizierter CalendarFeedController, damit externe Clients wie Thunderbird, Nextcloud Kalender oder mobile Apps ohne Nextcloud-Login abonnieren können) liefert Meilensteine als ganztägige VEVENTs (◆-Präfix), den aus den Aufgaben abgeleiteten Projektzeitraum sowie optional Teilprojekt-Zeiträume (Schalter „Auch Teilprojekt-Zeiträume einschließen", standardmäßig aus, um den Feed bei großen Projekten nicht zu überladen). Zugriff erfolgt über einen pro Projekt generierten, 256-Bit-zufälligen Token in der URL (CalendarFeedService, neue Spalte calendar_token auf nc_gantt_projects, Migration Version1026), der über einen neuen „Kalender-Feed"-Bereich im Freigabe-Dialog angezeigt, kopiert und jederzeit widerrufen/neu generiert werden kann (CalendarTokenController, nur für Projekt-Admins/-Eigentümer). ICS-Generierung ohne neue Abhängigkeit, RFC-5545-konform (Escaping, Zeilenfaltung)
  • What-if-Simulationsmodus für Terminverschiebungen (Issue #84): neuer Endpunkt POST /api/v1/projects/{id}/simulate führt Terminpropagation und CPM rein im Speicher aus (kein DB-Schreibzugriff) – TaskService::simulate() nutzt die neu extrahierte, stateless propagateDatesCore()-Kernlogik, die auch der persistierende update()-Pfad verwendet, CriticalPathService::computeFromTasks() erlaubt CPM auf einem hypothetisch veränderten Task-Datensatz. Frontend: „Simulation"-Toggle in der Gantt-Toolbar mit Banner inkl. Projektende-Delta in Arbeitstagen, Geisterbalken für die Ist-Termine (analog Baseline-Overlay) sowie Verwerfen/Übernehmen. Drag & Drop ruft im Simulationsmodus simulate() statt einer echten Speicherung auf. Szenarien bleiben bewusst flüchtig (kein Speichern benannter Szenarien), „Übernehmen" wendet die Änderungen als einzelne Task-Updates an, Simulation läuft pro Projekt
  • Neuer Eintrag „Aufgabe hinzufügen" im Drei-Punkte-Menü der Projektliste (Issue #91): legt wie der „Neu"-Button eine Aufgabe an, belegt dabei aber das gewählte Projekt direkt vor, sodass der Projekt-Auswahlschritt in der Maske entfällt
  • Such-/Filterleiste für Projekte, Teilprojekte und Aufgaben (Issue #81, Stufe 1: rein clientseitig auf Basis der bereits geladenen Daten aktivierter Projekte): Freitextsuche im Titel, Risiko, „überfällig" (Ende < heute und Fortschritt < 100 %), „über Budget" (Ist-Kosten > geplantes Budget), Ressource und „ohne Deck-Verknüpfung". Neuer Pinia-Store stores/filters.js (Zustand pro Nutzer in localStorage persistiert) und reine Matching-Logik in gantt/filterMatch.js. Nicht passende Balken werden in GanttChart.vue gedimmt statt ausgeblendet (Abhängigkeitspfeile und Zeitkontext bleiben lesbar), Projekte/Teilprojekte gelten als Treffer, sobald ein Nachfahre passt. Dieselbe Filterleiste (components/FilterBar.vue) dimmt in ProjectList.vue nicht passende Projekte; für nicht aktivierte Projekte sind nur Titel-Treffer zuverlässig auswertbar, da deren Aufgaben/Teilprojekte nicht geladen sind

Changed

  • Freigabe für Nextcloud 34 (Issue #94): max-version in appinfo/info.xml von 33 auf 34 angehoben, unterstützter Bereich damit NC 28–34. Verifiziert gegen Nextcloud 34.0.2 mit PHP 8.5 und MariaDB 11.4 (148 Unit-Tests, 39 E2E-Tests)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerdYQI+Semnk0cWtKqUZinVDHk0KpxyqB5Ox4NBL9pyf5nrpK8xZpe9lBxc6zhDfW
8adLfVIwQtX+bI7S1O05xm0hwrUHQQ2ii6ADD8mlAxnZjdhKSCAhZ0NcaqT7Em0X
nD7yRP2B8zAAQMJNjzeBsLbzveNrXMoGzFrvm/jFxskUIUS01ykfWEg2C5+5/IAf
7jBAM8x2Qj92b8zyBFpLjfJQw9aTE/trtCsmZXQFmkJjGS0anrJc9dXKfqnHxknX
8jmLOv4Yq4Qtk3UF35HTiIGcA2W0UKfVyFb4XcrIMeSmLXXcagJWjeH5nB18UUNn
JyCgE/qY4v/SnrNQU92mIdMOQGuvYg7xbaz226lgNdwM2HuM9U8nc2juqJxAO+Wp
xs85yl82qIviZ5YY39HK371MZ/ufMsaVsVy3Nfn3PjqqkzGLmtAL6KJDcKHZWdDw
ugEPwO61DxnERZsNGeFpXfzCxjMpZc/qwUIfGw85idmU2vdedtrxHBkDGoqu1IP7
xZOO8uSYrzbXM6g1gs3WPBOY51CmZLs5pIQTN1tVl5ifGw4uOuzDBUCKiR5oA2MI
352HbA7wwWFgwoN9W5t4MRTowmaYpM8AB+nh9f1A5q9S8SelfMpXOqGew9VZb2jd
AuiJK/LEPjRgs5s3/FfC4Sb1MSsXDr1N1jziaF4MomM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.3.1
Release Details
UpdatedAug. 6, 2026, 12:57 p.m.
Changelog

Added

  • Regressionstests für die Rollentrennung Viewer/Editor/Admin: ForbiddenException-Tests für TaskService::create/update/delete/syncProgress und ProjectService::updateProject/deleteProject (inkl. „Editor darf nicht löschen") sowie erstmalige Testabdeckung für SubProjectService und MilestoneService

Fixed

  • CSV-/Formel-Injection im Aufgaben-Export (detail/io.js exportCsv): Task-Felder wie Titel oder Beschreibung, die mit =, +, - oder @ beginnen, wurden unverändert übernommen und von Excel/LibreOffice/Google Sheets als Formel ausgewertet. Die Absicherung aus ReportView.vue liegt jetzt in einer gemeinsamen Utility (src/utils/csv.js, escCsvCell) und wird an beiden Export-Stellen verwendet
  • Viewer sahen im Frontend dieselbe Oberfläche wie Editor/Admin (Add Task/Subproject/Milestone, Share, Delete Project, Drag/Resize im Gantt-Chart, volle Bearbeitungsformulare), obwohl das Backend Schreibversuche ohnehin korrekt ablehnte – kein Least-Privilege an der UI. ProjectService::getAccessibleProjects()/getProject()/createProject()/updateProject() liefern jetzt das effektive permission-Feld (AccessService::getEffectivePermission()) mit; ProjectList.vue, GanttChart.vue (inkl. Drag/Resize-Sperre je Projekt) und das Detail-Modal (detail/detailModal.js) blenden Schreib-Aktionen für Viewer aus bzw. schalten Formulare read-only
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureBUyDeI9y7iKD1wQJLHrW6IFq0zuXOJQ1Zv/x+0RryIIQ0fAGAUjwQyLDYLYpRzPg
6ITDPfY9bftVlwkB8w9LqZiFETkH8mgc6l5XB7F6NKzFrdaSnNw21CIVJjWyHpkR
mxDq+vQepZd+YtB9jMz39kbFk9BNHrpKIwIXXZVqEqj1U54NmT+5XnT892m9mY6o
RWQNf3Mn5WcH3vmoDi/UAOgDaO30GFCRJePryurVpK7stkub8K6fYsWIkS5NeJlP
YHdb4nBlkhUzQFjo0ZDSkCSiWjMdyCrtEanX6m6Ffhr+j0jpd+J9soMSjrzdzt4T
5mETo8xc61IQIVFWMD6fZ3/oCmbx2TFDZsJjRpFKxoPA08AkdC7xjxHoaKYZHvi4
+tzowxPzMfyjQ/hEj2dwq22doM2lXK1Y13VvV8kkaomrAQjB97gf+ODrYZNDgNvc
2ijU08B4cAB0HbEveDpDRjCJj8D5OwCMafRMa4Ul/nm0+RiBFOhIjDYdVAnCp7Ql
IEc4UQZM9WYLQlouwGPk83POpbqkbc8vKAucGBXvHAbxNpUSOGbssMYFGU/e9pOg
+oT2gcJrC3MYPGIWnS8iLn75PK1H063KFJv5yqC+h/rqwX/FxyTzKvofJi68GpLs
WtG3y67W8Ju4y1I9iQ9raJxxhFUJrStibGy9gc7L0DM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.3.0
Release Details
UpdatedJuly 9, 2026, 5:23 a.m.
Changelog

Fixed

  • Baseline-Toggle im Gantt-Toolbar zeigte nie Baseline-Balken und PUT baseline/active warf einen SQL-Fehler: baseline_id auf nc_gantt_projects wurde mit der 3-Ebenen-Migration versehentlich zusammen mit den tatsächlich berechneten Spalten entfernt (Migration Version1025 stellt die Spalte wieder her). BaselineService::create() aktiviert eine frisch eingefrorene Baseline jetzt automatisch fürs Projekt; renderBaselineBars() (GanttChart.vue) und baselineOverlay.js lesen Baseline-Items dafür über task_id statt des seit der Umstellung auf das 3-Ebenen-Modell nicht mehr existierenden subproject_id-Felds
  • OCS-API-Fehlerantworten (400/404/500 …) lieferten für alle OCSController-Endpunkte einen leeren text/html-Body statt des erwarteten JSON-Envelopes mit message; ExceptionMiddleware wirft jetzt passende OCP\AppFramework\OCS\OCSException-Subklassen statt einer rohen DataResponse, die Nextcloud-Core nur für 401/403 automatisch formatiert. Betroffene Controller (ProjectController, ReportController, ShareController, ChangeEventController, WorkScheduleController, WorkCalendarController, HolidayAdminController, SettingsController) wurden zusätzlich auf die bestehende ApiException-Hierarchie umgestellt

Removed

  • Nie ausgewertete Gruppen-Freigabe-Option entfernt: Share::TYPE_GROUP, principal_type-Parameter in ShareController sowie die DB-Spalte principal_type (Migration Version1024) wurden gestrichen; Freigaben sind jetzt ausschließlich benutzerbezogen

Changed

  • Dashboard-Summary (DashboardController::summary) lädt Baselines und Aufgaben-KPIs jetzt über gruppierte SQL-Aggregat-Queries statt pro Projekt einzeln (N+1-Fix, gilt auch für den Admin-Fall mit allen Instanz-Projekten)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureNJfbRJEXzKGipUjUW9tUKayHoY6ltSU0ZeCQXcDx6XVDY/naLK0dVY9ciofXU7oy
40sJIOsjxcUZbalDwqncPn0vjGoqmk3BFUT7KotEpLLf8/ZmIjdHEbQbJHzEpdQV
y5YedjqRLKB7/Ul35JWVFxY8eutCsYkonfof7v6AGMGB/RjQ7GDB6jWunEEwqUGV
3Y1isvdq5N3oo9NDY/QR0C2C7jiMnB9Kon0KzZ2yp0oF5euOpA/qrtmqxG3L9PoZ
DW1ppwAuARWtRDu4tcDAAQAkF1Mrg5YyYgRoKm7uVazlKEP7DxX56g/g4RS2otEE
rKsA/ER9yYiHnNyGcZuK9Jkfe2Ua6/R7gzJilRvHZ7CBPkhY+XNPcrjlsHbCJEJJ
cX/b0B56p7FyAFy6XjMY9PYFhxy/ubHE7Sk8ixX88uxgM4vf/sSFQaN84CcG/qqA
Nq/OEytBjZP82TsFFYBRcPKEPskF4ejjhNVC4bJpiuCIpo51nIxjeFGtDpeQiDG7
0OWc/GCvsz5W3eG7ShmQsRf0DoivL8tjgDUlMWO9boTpUy2Vnm1rDMKu6nFGKZ8o
TWTZR6u4zGZW1OBERdlU5NQmqxqXrsuGlT6r6DuQnWsWiscjXOyIFeHz+EOKhQOk
wUuN6xN8QES1CDFB27PYV0Sz2KhYhM78GKLCc9B10Ic=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.2.0
Release Details
UpdatedJuly 5, 2026, 9:21 a.m.
Changelog

Added

  • Löschen-Buttons im Detail-Modal für Teilprojekt, Aufgabe und Meilenstein
  • Deutsche App-Beschreibung in info.xml ergänzt

Fixed

  • Baseline-Datum und Kommentar im Report werden untereinander angezeigt
  • Dashboard-Widget zeigt Baseline-Datum und KPI-Werte korrekt an
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureXuYTZMxTlGNJVrGvU/GcIjSlBTYreXdvF5FilCmsi+l7MBvFlyeW1DsnxAsao3pu
HyhvaKXRnkraVQNT2ULgX4BmjUnhsELROrK03gtY88Odzqzh7RIXYkNkRcy92ara
56HIP8BBRD3m1c8pYPfpZfLAzkuurAzVqbJ6qbxw1AebdlPCFEq2g6C+IOcTon0T
q9ZD2I2XJmWd42EXXqva+NMrcx1jfjmHWVtl7xHKhwPndFCtHPMMAdoDS0Xpbh1V
3Ns4h/DphDpC8/ZYZ5sbpMCBekHw4VTZDGxNAuXJ5DMPhkqg2MxVplkS19PuBHLx
r74sgsz611Gjg55gjKOqCb5ZZEvqZTrwKTEYIbu8HicWtq/HxKRZS3FZN9MThTTX
Hesv65vsq9fH0UhEM6MVJjE5Phnw8Wb5ivtAiaUA+NzPrDfrHZAsSfR9NFL3rhFC
7wOmnXtzK40NjmqyMWJu/UP9UVbz3SeEiHiQO9GwBc2krzmXzcmLZFgEjty4CGoJ
SzXAA1QGpUGO4n0b3Y/5zSY4UxogGWybW+mOs27KDLytI915wNYGvBtcdAKCj6Ws
1uVA98npwMid/WSJJ04BXHX8LbowrDeTezSFJyGh+u/KnhlzkA7fqEuu3nQGT5rA
a9qeMmd5kmLBIVcG9RVVmw1kc0AK0qIqZDmlk9dWGJs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1
Release Details
UpdatedMay 27, 2026, 6:43 p.m.
Changelog

Added

  • Glossar-Button in der Navigation (über den Einstellungen) mit interaktivem Nachschlagewerk
  • Dreispaltige Glossar-Tabelle: Begriff/Abkürzung, Bedeutung/Englisch, Erklärung mit Berechnungsformeln
  • 30 Einträge in 6 Abschnitten: Allgemein, Terminplanung, Baseline & Berichtswesen, Earned Value Management, Ressourcen & Kapazität, Budget

Fixed

  • Fehlende deutsche Übersetzungen für englische UI-Strings ergänzt (Add dependency, Baseline, Dependencies, Remove, Work, No projects yet, Open Gantt Projects, u.a.)
  • Leere Übersetzung für Deck-Fortschritt-Hinweis befüllt
  • de.js vollständig mit de.json synchronisiert (war ~70 Einträge veraltet)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF1RHJx15J/N8tWNrDlAX1MFE/9tsct4PViEcBDeo8U/V2qJ/tezL7Xk0BVmYxjWw
pB+J685mcHjikh93/aZUqG8C6y0mR3TTJfaa7dLCIRMc1034x6Xj9Nd7Q9YVH4yi
RLTcRhrG2pN1Iqc23kwId995GJXHnCBEyuQuzo9m1VtA7GoFa8W2nqhXVT94khDS
8ZHQrixDfv63d4GfA5N2x4+phaiyObdskvCmVSuKrmouYvCGpPevtKG5YxzOrHid
aL48Kod0YgOCAD8nAqOhEmioP0m+wP2e/NC4Z5svSKT3vdlE67W1M68ipQQfnhPI
txui+2X8zFvoe/M5sE3DaXbLnOVNGQ7YRpyXFI5GsQmQ0cPSSO/94Mw15hLZfwMf
TDOrrvzf+Vu05sUTZ6CFh4FrSl4e5Q6Xjv6uH4BZtyOpPxl+ajgW4OkZvQDbt4KC
8aTZodMcDpXlRMDdBSxT1bT9tD05KUKmEZYtpxHiARrHDuQQCRSuxtjitQD9lbla
CMxKrBiO0YgdUb3FuczYkWEH+y8gW5egU6TDMnjAVbuUecGuGZN1Cc6tarcFWRu2
+V7Qn+aNoXEwP5e/0TM4Ig8ETAYseZRiTPHxeWiSg3RnU3HuT6kPVWw8LERACDT4
6F4WU9t2+4kESMLMjeV0/2UBQSykR/42Idsg0PtvpyQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1 (nightly)
Release Details
UpdatedMay 27, 2026, 5:24 p.m.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF4W7QJB8rVJ2xARdnhUbEouzRZMKcowz/ToDwr0oVFrVUbInFvHAMqXFTOVD1a5N
kDTb0pOhv+hCStTh70bN+aapjbdtp7SdQ3SbP5VoXBKLhUii41OqN/i4yM7itdSs
M8d5PVo9TJYVnfaij2xwWghMqZEyzxgOw4Q98dYIysf2BxsjjCtxGEMo/pISS90G
OE2RaVEFbwUcvu1XKszLP5X/k/onOchengURYVuzHP7VgVuomZpPSZ9VEIRKITyL
GpgSDrdqm5AXdQc7l/2OqprW6G+MxsuVzChbyPGslIYYXYsP9HLc/FfwVxaCJjh2
Nsiqfd+dMwnf7f9yrrBuZjbNwNa5x++IynrwaU01jVISkl9pgkbUYRaPSMTGFDAd
lsEEqlgs9KdzGoTFjDXNdTwDOsUMiu/ag8tPo7YxsxLFbVGa7laRAUhlGUT9QGPr
IOAAWvKqivsYnU5iwTCY/5HGza3SDfZc/e5upPfB+lTQTYijp1aAYRBAsoOLmuga
f0aP45pnfnYBJkT1kMsthjbGMdlL46wLiNHKRct9OlFwx74IRIyZdcs5Q3pC0aNN
DSLvsCSmY8/9PtTLzcOJKXTqqDVupCqq82bUlGRn7NR6xsinzv74MuOVnGY21tBm
iB5ceYpgiEukUz6YtS2quIIiMTnYmM+HBFnkXgBV0/Y=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32

Nextcloud 28

Gantt Projects 1.5.4
Release Details
UpdatedAug. 27, 2026, 7:06 p.m.
Changelog

Fixed

  • appinfo/info.xml wurde vom Nextcloud App Store abgelehnt: der Sprachcode pt_BR in <name>, <summary> und <description> ist im Schema nicht enthalten — die Enumeration kennt nur BCP-47-Schreibweise in Kleinbuchstaben mit Bindestrich. Alle drei Vorkommen heißen jetzt pt-br
  • Die Kindelemente von <info> sind im Schema eine feste Sequenz; <settings> stand vor <dependencies> und wäre nach dem Sprachcode-Fix als nächster Validierungsfehler aufgeschlagen. Der Block steht jetzt an der vom Schema vorgesehenen Position hinter <dependencies>. xmllint --schema gegen https://apps.nextcloud.com/schema/apps/info.xsd läuft damit sauber durch
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureYbjiEkGYW1s8Xg9Jvrt4nNo/67zkrQDA16yfK2MzVa58R982qrLUupNbvUE/FscP
pFg9nlqxd81cpyH1ZhWOM7fMq98mPyjSUsJZ7xjYzNccF13xvDpBXFlfX4UyCknc
50Rmw9a8RxiCH0mEpH9zWjdanX194R8cUIMDY+BcymCgyBVRQub9yaL5iiHH3hNA
OOD+UOiSUNHns9R6sgZ+tF4OeoTufWR5//AfRmuL5qC5spyuP1iKr7FnzPar+/sS
pHKojfUyi0sAjlrThVZDo8fvVPqkkH4jgvVmNE41t1UOlVdajIRd2yhbHX0h0SSs
rmt4CQqNXAjLE/Wlq2lu9Viw4M2BhhPIfVb5YJFzaxjhB8RRy53rquXRL64f7vGa
agq0ri3eD+0ZWmMOlJmHaMv6AXYd0ZG8myb3PAyNC0NYXzrw2PAVL8jDfzmq2cNY
Q3rIh101vtGPytqanNS2VmtfZ8BX63rd4SLR4PJdRYrm3Wbqanx8WZa20eLgYF8S
M9xhxl5Jf8+TnYLwHi1sGm1ID2XJ4krfgOxiUlB8lGeSweUK1uueC0bmUDsrXyhJ
RMGST41TU1HqXx/3Eeh91JKq/Jbx15kPuxu7WNKyuSOj4vKbgFwBUSmHKDU6lvEZ
L/bnxZi965/4K3kvyJ8o2j5e4xUmJLeusi9Fg0yrRqE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.2
Release Details
UpdatedAug. 26, 2026, 5:18 p.m.
Changelog

Changed

  • Teilen-Dialog: das Eingabefeld für den einzuladenden Nutzer ist jetzt ein Suchfeld statt eines reinen Textfelds für die Benutzer-ID (Issue #144). Getippt wird nach Anzeigename, Benutzer-ID oder E-Mail-Adresse; ein neuer, admin-unabhängiger Endpunkt GET /api/v1/users/search (UserController::search(), IUserManager::search(), max. 20 Treffer, der eigene Account wird ausgeblendet) liefert dazu Anzeigename, Benutzer-ID und E-Mail als Vorschlagsliste. Gespeichert wird weiterhin die Benutzer-ID als principal_id – für Umgebungen mit LDAP/AD-Benutzerverwaltung war die zufällig generierte ID zuvor nicht ohne Umweg über die Benutzerverwaltung in Erfahrung zu bringen. Die Liste bereits geteilter Zugriffe zeigt ebenfalls den Anzeigenamen statt der rohen ID (ShareController::index() reichert die Antwort um display_name an, Fallback auf principal_id falls der Account inzwischen gelöscht wurde)
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureyLn/jlYbk3gIYfgIcjON0z467/xL9L6QNcmfgOa/4zhTJjLknXq7eu1UJddWWlLJ
NG2U9d/OEFO9wLHPYPGNiWgQMRIk+TGYlQVTXcZH3WpBxdJOm3XMnk5hq2kNB2FN
TJemqr/dxafGB2hTIgivPMXMnemdrtxWN1RAdzCxZIg9HwJWjG5JxdqHmURHwske
p/Ki/iZghKjrxbPIghbqtrvkCJRImbgVWY8AmUNUPLgotNIthiWFnkTDVhYWcreP
pWleWHxEn57UOtehff1aJa+hRI+8WVkx9iXArx0SThmOCYYgJ/HeG8dOOtmr9jSm
1nzETAL87YLxL/+qM6DyBIgNWhrq1oj9Q12bBm1+la477z6fpOgUlMjWkPEYRQi/
vhxirXwB4BZBYvaMEkNlN2U3+7BqgigGoaTcbN2rG/TP1JuOjdj9jEjMMRye0JbT
35XOoM/VPYGovrT9slHg8r3Wolyd8ioXVOu4IcMImvC5OrTzpKY7NLEc/S+cOKwz
ijOKcnoV0j++KxtQKKgEO96HJpEM0jteQVOGCoPbghjS3OFzkiB1bUVJAlu1jn54
38Q2zw8rZ0EMm1rEPmIrl72DIIXionbWKatREgy7QPwuL/WKZ2c+J1UIUTKck5eO
7M2VHh6P3FnIg/UsOCMu3U7ja0Y8vQ3YUo3tazLfob0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.1
Release Details
UpdatedAug. 25, 2026, 1:28 p.m.
Changelog

Added

  • Screenshot des gerenderten Gantt-Charts als PNG, SVG oder PDF (Issue #116): ein neuer „Screenshot"-Eintrag in der Chart-Toolbar öffnet ein Menü mit den drei Formaten, derselbe Export steht im Bearbeiten-Dialog des Projekts neben JSON und CSV bereit (dort mit vorgeschalteter Formatwahl statt Menü, openScreenshotFormatDialog()). Exportiert wird stets das gesamte Chart, nicht nur der sichtbare Scroll-Ausschnitt — frappe-gantt rendert die volle Breite und Höhe ohnehin als eine SVG. PNG und SVG klonen die aktuell gerenderte SVG und schreiben getComputedStyle() als Inline-Attribute (löst auch CSS-Variablen auf), sodass die Datei ohne das App-Stylesheet eigenständig darstellbar ist; sie folgen dem eingestellten UI-Theme, ein hinterlegtes Rechteck in --color-main-background deckt dabei auch die Fläche unterhalb der Aufgabenzeilen (Workload-Chart, Leerraum) ab, die sonst transparent bliebe. PNG rastert per Canvas mit doppelter Auflösung
  • Zeitraum-Auswahl für den PDF-/Druck-Export (Issue #116): vor dem Drucken fragt ein Dialog, welchen Zeitraum die PDF abdecken soll — gesamter Projektzeitraum (Vorbelegung), laufendes Kalenderjahr, „aktives Quartal" (Vormonat bis einschließlich übernächstem Monat) oder ein frei eingegebener Bereich. Der Vorschlag ist der tatsächlich von Balken belegte Zeitraum, nicht gantt_start/gantt_end — frappe-gantt legt darum großzügig Puffer, der andernfalls mitgedruckt würde. Umgesetzt wird der Zuschnitt als viewBox auf dem SVG-Klon; die dafür nötige Skala (Chart-Start, Pixel pro Millisekunde) schreibt GanttChart.vue nach jedem Render als data-Attribute an den Chart-Container, sodass auch Aufrufer ohne Zugriff auf die frappe-Instanz (Bearbeiten-Dialog) sie lesen können. Die Zeitraum-Logik steckt isoliert testbar in src/gantt/printRange.js (src/gantt/printRange.test.js)

Changed

  • Der PDF-Export druckt nicht mehr den Live-Container, sondern einen gestylten, selbst gerasterten Klon des Charts (Issue #116). Bisher wurde das Inline-SVG direkt gedruckt — wie fein die Browser-Engine das rastert, ist nicht steuerbar und fiel je nach Browser sichtbar grob aus. Jetzt wird der Klon mit 300 dpi bezogen auf die bedruckbare Fläche (A4 quer, 10 mm Rand → 277 × 190 mm) in ein Canvas gerastert und als Bild mit fest vorgegebener physischer Größe in den Druck gegeben; schlägt die Rasterung fehl, bleibt der SVG-Klon als Druckinhalt übrig und skaliert per CSS auf die Seitenbreite. Der Klon wird immer im hellen Theme berechnet (Papier ist weiß, ein dunkles UI-Theme ergäbe sonst dunkelgraue Flächen und helle Schrift) — dafür genügt ein ausgelagerter Container mit gesetztem data-theme-light, da Nextcloud die Stylesheets aller Theme-Varianten lädt und nur über dieses Attribut umschaltet. Das Ausblenden der übrigen Seite läuft über display: none statt visibility: hidden, sonst blieb die Fläche der App als Leerseiten erhalten; Nextclouds Hintergrundbild auf html/body wird für den Druck abgeschaltet. Aufgeräumt wird über afterprint mit Timeout-Fallback für Browser, die das Event nicht feuern
  • Der Toolbar-Eintrag heißt „Screenshot" (Icon MonitorScreenshot) statt „Export" (Icon ExportVariant) — „Export" ist im Projekt bereits für den Daten-Export nach JSON/CSV belegt, der Chart-Export ist demgegenüber eine reine Abbildung der aktuellen Darstellung
  • Übersetzungen für alle sechs Sprachen ergänzt
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
Signaturek6kpvF53IR8tnkOFXyoSPFPGlwVzO/RUTgI1p1ppRcFmoFPUyFeIokOqWA1OtD9q
wdo+qkQQPyJGph0Uwp+hwP9oGmeKWErnlv8HMdA5I5iaXIN7afGvdfxWfKgqWetw
tGmCF7l2IzMmT2Ol/3pKgCnFkXTDPCVeNINNbUdi7EjeRpbcbOGWt3+5UVwZgVTX
Z9/kEEPvjMV98s1zT5yBXcFvyqZA7bhhezmBkfzaLa0DAIczFJbhyFtR06WuS7bP
CWBCs/xvSRuCwo61oyXRAWLohz/2GsrMSa5jvPvnEG1XEHpGPVorCijAvsBm4sX5
yAZTrBm3zbNYObqVE2idxTH/eqP8xXtE38JFjpG5IahpeFNb9V2EbH1nrjfRKYcH
NVvXcXFREAmvorShKDE0ymFN+/BCjvU1vnJCnRD5RMsoSGUzXSVHPrF1emtusLHj
ltP22eYOCpraEpZheTkFftqvpbiQKVXsZ+8wY4dJnh5Ymi71loxZhggoH8s/pmp+
WbSx6MZwSPBqJ0HOJWckFiZXur2OQYOVLfXt8uPYNU/EZSoYDqm81GVMglQXxyM6
YDh0xXqwJ7Qft7k0O/3Fa5YLJGP4B4RvjU29N3/WDsfjuk7PeCSTPhRlTOfV/Fbn
jS83WVBavmyklhu0rNYuCYfdYvUxemIO3VuGmYzFXgI=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.5.0
Release Details
UpdatedAug. 24, 2026, 5:26 p.m.
Changelog

Added

  • Editierbare Vorgänger-Spalte in der Tabellenansicht, Teilschritt F von #117 (Issue #124): eine neue, über das Spaltenmenü einblendbare Spalte „Vorgänger“ zeigt die Abhängigkeiten einer Aufgabe in der aus MS Project bekannten Kurznotation (12EA+2d = Vorgänger Aufgabe 12, Ende-Anfang, zwei Arbeitstage Abstand; mehrere durch ; getrennt) und nimmt sie dort auch entgegen. Typ (EA/AA/EE/AE, englische Kürzel FS/SS/FF/SF werden beim Eingeben mit akzeptiert) und Abstand sind optional, 12 allein bedeutet also EA ohne Abstand. Geprüft wird beim Verlassen der Zelle: Syntax, Existenz der Aufgabe im selben Projekt, Selbstbezug, Dubletten und Kreisschluss über die gesamte Abhängigkeitskette – bei einem Fehler bleibt die Zelle im Bearbeitungsmodus und nennt den Grund im Klartext, die bisherigen Abhängigkeiten bleiben unangetastet. Gespeichert wird über dieselbe PUT /tasks/{id}-Route wie der Detail-Dialog (inkl. dep_mode), sodass der Server die Termine anwendet und durch die Nachfolgerkette propagiert; Balken, Pfeile und Typ-Labels im Chart zeichnen sich mit der Antwort neu. Bewusst ohne Undo-Toast, da ein Zurücknehmen nur der Termine (nicht der Abhängigkeit) beim nächsten Neuberechnen wieder überschrieben würde. Nur Aufgaben haben eine editierbare Zelle – Meilensteine hängen über task_id an genau einer Aufgabe, Projekte und Teilprojekte sind berechnete Strukturebenen. Notation und Prüfungen stecken isoliert testbar in src/gantt/dependencyNotation.js (src/gantt/dependencyNotation.test.js)
  • Zeitabstand (Lag/Lead) je Aufgaben-Abhängigkeit (Issue #124): neue Spalte lag_days in nc_gantt_task_deps (Version1032; der Name vermeidet das seit MySQL 8.0 reservierte Wort LAG und nennt die Einheit). Der Abstand zählt in Arbeitstagen über den Ressourcenkalender wie die übrige Terminberechnung, wirkt bei allen vier Abhängigkeitstypen auf deren Ankertag und darf negativ sein (Lead: der Nachfolger beginnt früher). Bestandsabhängigkeiten bekommen 0 und verhalten sich unverändert. TaskService::update() weist eine gesendete Vorgängerliste jetzt vollständig zurück (invalid_dependency, dependency_cycle), bevor die bisherigen Abhängigkeiten dafür gelöscht werden, statt Vorgänger aus fremden Projekten oder Zyklen zu speichern – letztere hätten die topologische Sortierung in recalculateProject() stillschweigend Aufgaben überspringen lassen

  • Konfigurierbare Spalten in der Tabellenansicht, Teilschritt E von #117 (Issue #122): ein Menü in der Tabellenkopfzeile blendet Spalten ein und aus, die Reihenfolge lässt sich per Drag & Drop der Kopfzellen ändern (barrierefreie Alternative: Strg+Pfeil links/rechts auf der fokussierten Kopfzelle), die Breiten per Ziehen am Spaltentrenner (Doppelklick setzt auf die Standardbreite zurück). Zusätzlich zu den bisherigen Spalten (Titel, Start, Ende, Fortschritt) stehen ID, Dauer (Kalendertage inkl. Start- und Endtag, bei Meilensteinen 0), Status (Issue #78, nur Aufgaben/Teilprojekte) und Ressource (Issue #103, zugeordnete Ressourcennamen einer Aufgabe) bereit – alle vier als reine Anzeigespalten, die das Inline-Editing und die Tastaturnavigation daher überspringen. Auswahl, Reihenfolge und Breiten werden pro Nutzer über den in Teilschritt B angelegten UI-Preferences-Endpunkt persistiert (neuer Key gantt_table_columns, kein Schema- oder Endpunktwechsel nötig); eine gespeicherte Konfiguration wird beim Laden validiert, sodass entfernte Spalten oder unplausible Breiten die Ansicht nicht beschädigen können. Die Tastaturnavigation folgt jetzt der sichtbaren Spaltenreihenfolge statt einer festen Liste. Reicht die Tabellenbreite für die gewählten Spalten nicht aus, scrollen Kopfzeile und Zeilen gemeinsam horizontal; die Obergrenze des Splitters steigt dafür von 560 auf 900 px. Die Spaltenlogik steckt isoliert testbar in src/gantt/tableColumns.js (src/gantt/tableColumns.test.js). Ein Sortieren der Zeilen nach Spaltenwert ist bewusst nicht enthalten – es kollidiert mit der Hierarchie Projekt → Teilprojekt → Aufgabe und der sort_order, die das Chart für dieselbe Zeilenreihenfolge braucht

  • Tastatur-Navigation in der Tabellenansicht, Teilschritt D von #117 (Issue #121): Tab/Shift+Tab springen zur nächsten bzw. vorigen editierbaren Zelle (überspringt Zeilen ohne Schreibrecht, springt am Zeilenende zur nächsten/vorigen Zeile), Enter speichert und springt in dieselbe Spalte der nächsten Zeile, Esc bricht ab und stellt den vorigen Wert wieder her. Pfeiltasten bewegen die Zellauswahl, wenn gerade keine Zelle im Editiermodus ist (innerhalb einer Zelle bleiben sie für Cursor-/Wertänderung nutzbar). Die reine Traversierungslogik steckt isoliert testbar in src/gantt/tableNav.js (src/gantt/tableNav.test.js). Die ausgewählte Zelle bekommt einen sichtbaren Fokusrahmen und wird per scrollIntoView() ins Sichtfeld gescrollt – über den bestehenden bidirektionalen Scroll-Sync (Issue #119) folgt das Gantt-Chart automatisch mit. Neue ARIA-Rollen (grid/row/columnheader/gridcell, aria-readonly je Zelle) machen die Tabelle für Screenreader als Grid erkennbar
  • Read-only Tabellenansicht links vom Zeitstrahl, Teilschritt B von #117 (Issue #119): neue Komponente GanttTable.vue zeigt dieselben Zeilen wie das Chart (Titel mit Einrückung nach depth, Startdatum, Enddatum, Fortschritt), gespeist aus buildRowModel()/buildTableRows() (siehe Teilschritt A, Issue #118) – Reihenfolge, Filter-Dimming, kritischer Pfad und Projektfarben bleiben so exakt konsistent zum Chart. Zeilenhöhen sind fest an frappe-gantt gekoppelt (40 px = bar_height 22 + padding 18, Kopfzeile 52 px = header_height). Ein Toolbar-Button blendet die Tabelle ein/aus, ein Splitter macht die Breite verstellbar (160–560 px), vertikales Scrollen ist bidirektional mit .nc-gantt-container synchronisiert. Sichtbarkeit und Breite werden pro Nutzer über einen neuen, generischen UI-Preferences-Endpunkt (GET/PUT /api/v1/ui-preferences, SettingsController über IConfig::get/setUserValue() als JSON-Blob) persistiert, damit Teilschritt E (Editieren) das Schema nur noch um weitere Keys erweitern muss, ohne Endpunkt oder Speicherformat anzupassen. Rein lesend – Editieren folgt in Teilschritt C
  • Projekt-Export/-Import (JSON, format_version: 2) kann jetzt optional die von den Aufgaben referenzierten Ressourcen inkl. Gruppe, Wochenmodell, Arbeitskalender+Feiertagen und Tageskapazitäten mitnehmen, statt wie bisher nur die rohe resource_id in eine Zieldatei zu schreiben, die dort ungeprüft übernommen wurde – auf einem System ohne identische Ressourcen-IDs ging die Zuordnung damit verloren oder landete schlimmstenfalls bei einer fremden Ressource (Issue #115). Vor dem JSON-Download entscheidet ein Options-Dialog, was mitexportiert wird (Ressourcen / Verfügbarkeiten / Kalender, gestaffelt); vor dem JSON-Import zeigt ein passender Dialog, wie viele Ressourcen die Datei enthält, und lässt auswählen, was übernommen wird sowie ob bei Namensgleichheit die vorhandene Ressource wiederverwendet (Default) oder immer neu angelegt wird. Referenzen werden ausschließlich über eine Mapping-Tabelle aufgelöst und nie roh durchgereicht; nicht auflösbare Referenzen werden verworfen (Regressionsschutz zu #112/#113). Neu angelegte Ressourcen/Gruppen/Kapazitäten gehören stets dem importierenden Nutzer. Wochenmodelle/Kalender/Feiertage sind weiterhin admin-only anlegbar – ohne Adminrechte werden nur namensgleiche vorhandene Einträge gemappt. CSV bleibt unverändert aufgabenzentriert mit Ressourcen nur als ID-Referenz; der volle Ressourcenumfang ist JSON-only. Alt-Exporte ohne resources-Schlüssel importieren unverändert wie bisher, ohne Options-Dialog
  • Inline-Editing der Basisfelder in der Tabellenansicht, Teilschritt C von #117 (Issue #120): die Zellen für Titel, Start, Ende und Fortschritt sind direkt editierbar und aktualisieren die Balken im Chart sofort. Gespeichert wird über die bestehenden Store-Actions (updateTask/updateSubproject/updateMilestone/updateProject) inkl. Terminpropagation und dem vorhandenen Undo-Toast für Aufgaben (analog Drag & Drop im Chart); ein lokaler pendingEdits-Overlay zeigt die Änderung optimistisch an und rollt sie bei einem fehlgeschlagenen Request mit Fehlermeldung zurück. Die Schreibrechte löst tableRows.js je Feld über store.canEditProject() auf (titleEditable/startEditable/endEditable/progressEditable) – Projekte und Teilprojekte bleiben bei Datum und Fortschritt read-only, da diese Werte aus den Aufgaben berechnet werden. Geprüft wird: Titel nicht leer, Enddatum nicht vor Startdatum (Meilensteine ausgenommen, deren Start- und Endspalte dasselbe milestone_date zeigen) und Fortschritt 0–100. Im Simulationsmodus rufen Datumsänderungen an Aufgaben simulateTaskChange() statt einer echten Speicherung auf
  • Ressourcen-Überbuchungswarnung (Issue #82): die Kapazitätsplanung war bisher reine Datenerfassung ohne Abgleich mit dem tatsächlich verplanten Aufwand. Der neue Endpunkt GET /api/v1/resources/{id}/utilization?from=&to=&group= (ResourceUtilizationService/ResourceUtilizationController) stellt den geplanten Aufwand aus den Aufgaben-Ressourcen-Zuordnungen – gleichmäßig auf die Arbeitstage der Aufgabe verteilt, über WorkCalendarService mit ressourcenspezifischen Arbeitstagen und Feiertagen – der erfassten Tageskapazität gegenüber. Mehrfach-Ressourcen-Zuordnungen (Issue #103) samt Alt-Aufgaben werden berücksichtigt, ohne gepflegte Tageskapazität gilt ein Fallback von 1 PT/Tag. Im Ressourcen-Raster färben sich die Zellen nach Auslastungsgrad (> 90 % gelb, > 100 % rot) und der Tooltip zeigt geplante Stunden gegen Kapazität samt Aufschlüsselung nach Projekt und Aufgabe; das Workload-Chart im Gantt markiert überbuchte Perioden jetzt rot. Übersetzungen für alle sechs Sprachen ergänzt
  • Nachgetragen – beide Funktionen wurden bereits mit 1.4.2 ausgeliefert, waren dort aber nicht im Changelog dokumentiert:
  • Einheitliches Statusmodell für Teilprojekte und Aufgaben (Issue #78): neues Feld status (not_started/in_progress/blocked/done) auf nc_gantt_subprojects und nc_gantt_tasks ergänzend zum numerischen Fortschritt, Bestandsdaten leitet Migration Version1030 daraus ab. Validierung per StatusValidator (Whitelist analog ColorValidator), Status-Dropdown in den Detailformularen, blockierte Balken im Gantt schraffiert und mit Warnsymbol, blocked_count je Projekt im Rollup sowie ein Badge in der Projektliste
  • Mehrere Ressourcen mit individuellem Aufwand je Aufgabe (Issue #103): bis zu vier Ressourcen je Aufgabe mit eigener Aufwandsschätzung in der Einheit der Ressource (neue Tabelle nc_gantt_task_resources, Migration Version1029, Backfill aus den vorhandenen resource_id/work-Werten). Das manuelle Work-Feld entfällt: TaskService::resolveResources() summiert die Einzel-Aufwände zur Gesamt-Work (1 PT = 8 h) und übernimmt die erste Zuordnung als primäre resource_id, die weiterhin für Arbeitskalender und CPM maßgeblich ist. Ressourcen-Filter sowie CSV-/JSON-Export und -Import berücksichtigen alle Zuordnungen statt nur der primären

Changed

  • buildFrappeRows() (src/gantt/frappeRows.js) mischte bisher Datenmodell und frappe-gantt-Formatierung: die Einrückungstiefe steckte als Leerzeichen im name, Zustände (Dimming, kritischer Pfad, Summenbalken) hingen in custom_class – eine geplante Tabellenansicht neben dem Gantt-Chart (Issue #117) hätte dieselbe Logik duplizieren müssen (Teilschritt A, Issue #118). Neue Funktion buildRowModel() (src/gantt/rowModel.js) liefert dieselbe Zeilenliste jetzt als neutrales Modell (id, type, entityId, depth, title, start, end, progress, parentId, editable, matched, critical), resolveMilestoneDate() wandert mit ins Modell. buildFrappeRows() ist nur noch ein dünner Adapter darüber, der name/custom_class/dependencies wie bisher erzeugt. Reines Refactoring ohne sichtbare Änderung, per Fixture-Vergleich gegen die alte Implementierung geprüft; neue Unit-Tests (src/gantt/rowModel.test.js, npm run test:unit, via neu eingeführtem vitest) decken Hierarchie, Einrückungstiefe, Meilenstein-Datumsauflösung und Filter-Dimming ab

Fixed

  • Abhängigkeitsfortschreibung bei Aufgaben lieferte für alle vier Abhängigkeitstypen (EA/AA/EE/AE) einen um einen Arbeitstag zu langen Nachfolger-Termin – bei EA zusätzlich mit falschem Anschlusstag, da der Nachfolger auf dem Endtag des Vorgängers statt am folgenden Arbeitstag begann und dadurch mit dem Vorgänger überlappte (Issue #114). Ursache war eine Off-by-one-Vermischung zwischen der inklusiven Arbeitstagszählung und der exklusiven Datumsfortschreibung in TaskService::applyConstraint()/computeMultiConstraint(). Betroffen sind sowohl das direkte Setzen einer Abhängigkeit als auch die Terminpropagation nach Drag & Drop und die What-if-Simulation. Verhaltensänderung: Bestehende Pläne mit EA-Abhängigkeiten verschieben sich beim nächsten Speichern/Neuberechnen um einen Arbeitstag nach vorne (auf den korrekten Anschlusstag); bei AA/EE/AE verkürzt sich der zuvor fälschlich verlängerte Nachfolger-Zeitraum um einen Arbeitstag, die ursprüngliche Dauer bleibt jetzt garantiert erhalten. Abhängige Meilensteine (Dauer 0) sind unverändert korrekt und von diesem Fix nicht betroffen

  • Security: jeder Nutzer mit Schreibrecht auf ein eigenes Projekt konnte jede in der Instanz existierende resource_id einer eigenen Aufgabe zuordnen – unabhängig vom Eigentümer und ohne jede Share-Beziehung (Cross-Tenant-IDOR, Issue #112). TaskService::resolveResources() lud die vom Client gesendeten IDs bisher nur über ResourceMapper::findByIds(), also als reine Existenzprüfung ohne Berechtigungscheck; da die IDs fortlaufende Integer sind, waren sie trivial zu erraten. Der Listen- wie der Legacy-Einzelfeld-Pfad in create()/update() filtern referenzierte IDs jetzt über ResourceMapper::findVisibleToUser(), nicht sichtbare IDs werden wie unbekannte verworfen (Liste) bzw. nicht übernommen (Einzelfeld, der bestehende Wert bleibt erhalten). Regressionstests decken beide Pfade für create() und update() ab

  • Feiertage ließen sich nicht in mehreren Regionskalendern führen: POST /admin/holidays lief in einen HTTP 500, sobald dasselbe Datum in einem zweiten Kalender angelegt wurde, weil der Unique-Index aus Version1006 allein auf holiday_date lag, obwohl Feiertage seit Version1013 über calendar_id kalenderbezogen sind. Version1031 verschiebt den Index auf (holiday_date, calendar_id); die Eindeutigkeit globaler Feiertage sichert weiterhin der Upsert in HolidayAdminController::create(). Betroffen war neben der Feiertagsverwaltung auch der Kalender-Import aus einem Projekt-Export, wo der Fehler still verschluckt wurde. Zweitens war GET /admin/holidays admin-only, während work-calendars und work-schedules lesend für alle offen sind – für normale Nutzer blieb der Store deshalb immer leer (keine Feiertage im Projekt-Export, keine Markierung arbeitsfreier Tage im Ressourcen-Raster); der Lese-Endpunkt zieht nach dem Muster der übrigen Stammdaten auf /api/v1/holidays um (NoAdminRequired), Anlegen und Löschen bleiben unter /api/v1/admin/holidays. Ergänzend werden Feiertage jetzt mit ihrem Kalender gelöscht (WorkCalendarController::destroy(), HolidayMapper::deleteByCalendar()) und Bestandswaisen bereits gelöschter Kalender in Version1031 aufgeräumt – über verweisende Ressourcen haben sie bis dahin weiter als arbeitsfreie Tage gewirkt
  • Die Installation der Tabelle nc_gantt_task_resources scheiterte auf Instanzen, deren Tabellenpräfix die von Nextcloud aus Oracle-Kompatibilität erzwungene 30-Zeichen-Grenze für Bezeichner reizt: der von Doctrine automatisch vergebene Primary-Key-Name (Tabellenname + _pkey) wird mit Präfix oc_ zu lang (Issue #132). Die Primärschlüssel-Definition bekommt – analog zu den bestehenden Migrationen für nc_gantt_resource_capacity und nc_gantt_resource_groups – einen expliziten kurzen Namen
  • Der absolut positionierte Toggle-Button der Nextcloud-App-Navigation ragte in den App-Content hinein und verdeckte dort den Titel des ersten Eintrags in der Projekt-Rollup-Leiste (Issue #134); der linke Innenabstand reserviert jetzt Platz für den Button
  • Ein leerer Balken in Höhe der Kopfzeile erschien oberhalb des App-Inhalts, weil der CSS-Reset für den Nextcloud-Content-Wrapper an main#content.app-nc_gantt gekoppelt war (Issue #126). Ändert sich das Markup zwischen Nextcloud-Versionen (Klasse oder Tag), griff der Reset nicht mehr und der von NcContent (#content-vue) selbst gesetzte margin-top: var(--header-height) addierte sich zum Rand des Cores. Der Reset hängt jetzt robust an #content mit !important, ohne Abhängigkeit vom app-spezifischen Klassennamen
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturegO8Qmtdwg8LOBKr5G+mq4co374hsqqSN6oCIRV9atUx0KqogPxisJiIExkPoSIbu
e4EeIaUzg2alhZsKBLWOwR3Ab3fNm5vvd2DzJn6Hei7A70woz9m/LXkVlcx3vALW
bwolClUX1XCZOBVo/kSS5AOVwak3/huzT1iF9xH9BnpdzVuOjZ563LYwSayfb2Gq
SkWNfLZlKlDkTTF2PIszrNmaa89GmyqTbraxSP16/9nYdIj8ZU0zrhC3HtpihgAT
wya2lwnMf6I73HU0xxgT/+ITTmurx4eHwKWH80P4tjg/Y9mnHODIpAslbW3351mc
qdTHXIBW1MB1YGCQ+JPt11v1X2ccn29kEF9d1nkizKZ1+Du+LJQhPhwHiQxPWSwAUlTlQ4tj1lbe3+FJ1jsVTCqWbDUN02EoV7ZwtN15/HLJfM6w4drUi8ThJ1aLG3v6
cIwMJjYgWeepxR2gv4YgBf4u7txodm5bAWuzySk4rfAG1dBxmie/bfNnWmQ9LrYy
spwKc73gsPhg03ll+jp0b27fJfNF4IpOysz3i+r1ExVfeNIWWO91JPatO2pXZh4O
q0rczM8wYvctvcEkKQIL67HoIrKw4GVIXfqM9Oc8e/Ay7d/NPL+BjpCO6zvoFBY+
SRmjit15YmGW4EQwweQfKXuZ2YeySj+7OjhKHpKfvfg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.2
Release Details
UpdatedAug. 20, 2026, 4:41 a.m.
Changelog

Changed

  • release.sh fragt die Versionsnummer jetzt interaktiv ab, statt sie nur aus appinfo/info.xml zu lesen: zu Beginn werden die aktuelle Version und das letzte Release-Tag angezeigt und es wird gefragt, ob eine neue Nummer vergeben werden soll (Vorschlag: nächster Patch-Level). Die Eingabe wird auf das Format X.Y.Z, auf einen echten Versionssprung und auf ein noch nicht vergebenes Tag geprüft und dann nach appinfo/info.xml, package.json und package-lock.json geschrieben. Fehlt ein Changelog-Abschnitt zur neuen Version, warnt das Skript; anschließend bietet es an, den Bump als chore: Version X.Y.Z zu committen — nötig, weil der Tag-Name den Commit-Hash enthält. Für nicht-interaktive Läufe gibt es --version=X.Y.Z und --no-bump; ohne TTY wird nie gefragt, make release bleibt also unverändert nutzbar
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturepoVHZxteU3AR2cpRRV98jNfv4j7rWnLEd3kbpn5Dp4T1ubAI+0QlRf9jxi0BaYz1
+1dg+3Yt25uN6Slp1FHiw7kyqt5HJ60XgnOWo+Zt0YBHSnWqLDhF8zxPEnQCkF+v
nUvscpcoQY2bRiIBZhVWVgeAD592uvOA4gfRDrUyLVDKjOrzBu3ScbNvCqTqE9IC
vs8qZkRLIcaD6tKoSLT2pZqmwBoCdClvEw5D5v40WSvLq232BZnXpQ4ogG3QvJdL
mYO8HlPZKVOxANp0MGnWle4IFRMbMt22Ebpkjc18Q9Q4AIv/Sd3zTsc+uMONTM95
s0GXvLSm95o0UCFZ1CL7fCEq7NwlprjVrTeWGmlJNVjHJuIyRKHEz5DR12s0lAGm
/rKGWjoVP+Wvk3upRizR/5y2fpWoiwuDqCtz9gQN6+HHYLaqLvYqsAJ/SxmtLtP7
8B2rK61ClKowz4phbTZdrCaF8w3IWnFFBTFquIVFuN2jUoHvdCMtd0910sLAMY5M
Xs9nk0FVsP23zKXlQpOojq7ouCQagRhsnBJDLxWRR+NIq582f7SDgcrkBqxlGJX4
/Ql9V+2iFx61QANCTjdJIO3LbvbK60vGos+ency0GfvSDbPovDAVyJzt8ZUh5BNw
UXQ4XOHJZAagAr36rKttk1DQbZZ/2bUEq231c96m83g=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.1
Release Details
UpdatedAug. 17, 2026, 7:43 p.m.
Changelog

Fixed

  • Die Meilenstein-Raute hatte eine fest verdrahtete Farbe (#fbbf24) statt der Projektfarbe zu folgen, obwohl Balken für Aufgaben und Teilprojekte bereits korrekt eingefärbt wurden (Issue #102). Zusätzlich wurden die separaten – und wirkungslosen – Farbwähler bei Teilprojekt und Aufgabe entfernt, da deren Anzeige ohnehin immer der Projektfarbe folgt
  • „Meilenstein hinzufügen" im Drei-Punkte-Menü der Projektliste nutzte zwei aufeinanderfolgende window.prompt()-Dialoge (Name, dann Datum als Freitext) statt des regulären Detail-Dialogs (Issue #101). Der Klick emittiert jetzt add-milestone analog zu „Aufgabe hinzufügen" (Issue #91) und öffnet denselben Meilenstein-Dialog wie beim Bearbeiten, mit vorbelegtem Projekt
  • Im festen Datumsmodus des Aufgaben-Detaildialogs konnte ein Enddatum vor dem Startdatum eingegeben und gespeichert werden (Issue #100). Jetzt zieht ein über das Enddatum hinaus verschobenes Startdatum das Enddatum mit; ein zu frühes Enddatum erzeugt ein rotes Fehler-Icon am Feld, hält den Fokus dort und deaktiviert den Speichern-Button, bis ein gültiges Datum eingetragen ist (dateField() in fields.js mit neuer setError()-Methode)
  • Eine Aufgabe mit Abhängigkeit behielt trotzdem die manuell eingegebenen Start-/Enddatumsfelder, die beim Speichern durch die Abhängigkeitsberechnung überschrieben wurden – für Nutzer nicht nachvollziehbar (Issue #99). Der Aufgaben-Detaildialog hat jetzt denselben Fixed-date/Dependency-Umschalter wie Meilensteine (Issue #90): im Dependency-Modus ersetzt eine Dauer (Tage) die Datumsfelder, die Abhängigkeit bestimmt den Termin. Serverseitig wendet TaskService::create() neu hinzugefügte Abhängigkeiten sofort an (bisher nur update()), damit auch neu angelegte Aufgaben keinen die Abhängigkeit ignorierenden Platzhaltertermin behalten
  • Der Speichern-Button des Detail-Dialogs blieb nach dem Hinzufügen einer Abhängigkeit deaktiviert: die Gültigkeitsprüfung hing allein am input-Event, das bei einem <select> vor dem change-Event feuert – zu diesem Zeitpunkt war die Abhängigkeit noch nicht in der Liste. detailModal.js wertet jetzt zusätzlich change aus, der Abhängigkeits-Editor (fields.js) meldet jede Mutation (Hinzufügen, Entfernen, Typwechsel) und der Fixed-date/Dependency-Umschalter in taskForm.js den Moduswechsel über ein eigenes bubbelndes change-Event

Added

  • CONTRIBUTE.md und setup-dev.sh für neue Mitwirkende (Issue #98): Projekt-Leitlinien (FOSS, Datensparsamkeit, Security), VIBE Coding ausdrücklich erlaubt, sowie der komplette lokale Dev-Setup-Weg (Docker-only, Nextcloud + MariaDB unter localhost:8088, optionales E2E-Setup, Git-/Bot-Auth). setup-dev.sh automatisiert diese Schritte, konfigurierbar über --workdir/--no-e2e/--no-docker-install
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturetYcUvVtDEQrAakG32EQF1atSOmz91/8XudCUh60A9r/AxMv+AA5AX2rIvNYDkEn0
OuTmXA6Iz0DoVkdij11MPDhfLaUy0mXeLbwB5fj/xF/cFYcNeCFGKD3k2jyRwOTo
2n1sEp0r8sn0fitHzLfhcB/By0VJkZyhaO/NctP01XfcSQM7lGEOeY/uagtGaDTI
JnPLoQTHo0nUOv4y7jUMZuurEgBOc4yeUxTKv4A/m642hvNQIS0q6K9ON41JHKIO
UtI7DbDWSboVhct1Go3zoj5XcaAGG9ZiwSkJDt/BXT5tWH4SOnSn7mys0j1F6Y0g
vIJWjz6S2RlekwIrIxgyaeEVBOeQ4OM1ilJGWKXDyYJM/0IBxqnqRXDkAFlOopDo
xezlcFdWTkYL5OBKMFJhKuTy7dEQ9hxx5Nz4+vLbNV8gDJZBI+5yI5m8W+809WIH
iKUpIda1ShOblvYH6Ek5AzBdTmoFQ6T4f7iWT7j1i4uUP2DsgehjGVl8yDIT/wyx
nV9t7koXapu1WeXrunSa++mXsi3lceNpthdz5/cUEOe9yWvc1rrulRhiWzePga8H
zjVQYGAIPvwYkWa47U6Zh7HBsQaS+9yQ8NDnD49SS4jUWTGlwNqfM+q9fZ16/G9d
7l4chZUPPbBv+xWfdxmFw+MC5zdkAq27isHToTqnm+w=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.4.0
Release Details
UpdatedAug. 13, 2026, 6:41 a.m.
Changelog

Added

  • Meilensteine werden jetzt immer direkt unter ihrem übergeordneten Objekt angezeigt statt gesammelt am Projektende (Issue #90): Meilensteine lassen sich neu auch einer Aufgabe zuordnen (bisher nur Projekt/Teilprojekt, Spalte task_id war bereits vorhanden, es fehlte nur die UI), das Auswahlfeld im Detail-Dialog heißt entsprechend „Übergeordnetes Objekt" statt „Teilprojekt". Zusätzlich können Meilensteine ihr Datum jetzt statt eines fixen Werts aus einer Abhängigkeit zum übergeordneten Objekt beziehen (EA/AA/EE/AE, analog zu Aufgaben-Abhängigkeiten; neue Spalte dependency_type, Migration Version1028) – da ein Meilenstein keine Dauer hat, bestimmen EA/EE das Ende und AA/AE den Start des Zielobjekts. Die Berechnung übernimmt der neue MilestoneDateService (serverseitig live bei jedem Abruf sowie im ICS-Feed neu aufgelöst, nicht persistiert propagiert) sowie clientseitig in frappeRows.js für sofortige Reaktivität ohne Neuladen
  • Undo für Drag-&-Drop-Verschiebungen inkl. Propagationskette (Issue #85, Umsetzung der einstufigen Empfehlung: letzte eigene Operation, kein Redo, zunächst nur Drag & Drop): Migration Version1027Date20260810020000 ergänzt nc_gantt_change_events um op_id (UUID) – alle Change-Events einer zusammenhängenden Operation (Drag + gesamte Propagationskette B→C→D) erhalten dieselbe op_id, erzeugt in TaskService::update()/propagateDates() und über ChangeEventService::log() protokolliert (dieses Change-Tracking war bisher tote Scaffolding-Logik ohne Aufrufer). Neuer Endpoint POST /api/v1/changes/{opId}/revert (ChangeEventController::revert, ChangeEventService::revertOperation) wendet alle Change-Events der Operation invers an, in einer DB-Transaktion mit Rechteprüfung über AccessService::canEditOrOwn(). Konfliktschutz: Revert wird mit HTTP 409 abgelehnt, sobald der aktuelle Feldwert nicht mehr dem protokollierten new_value entspricht (jemand hat zwischenzeitlich erneut geändert); der Revert selbst erzeugt wiederum neue Change-Events, die Audit-Historie bleibt lückenlos. Frontend: nach jedem gespeicherten Drag zeigt GanttChart.vue einen Toast „N Termine verschoben — Rückgängig" (showUndo aus @nextcloud/dialogs, 10 s sichtbar) sowie ein globales Ctrl+Z für die letzte eigene Operation der Session (stores/projects.js: lastOperation, revertOperation(), undoLastOperation())
  • Kalender-Export als abonnierbarer ICS-Feed pro Projekt (Issue #83, Variante A aus dem Umsetzungsvorschlag): neue öffentliche Route GET /apps/nc_gantt/projects/{id}/calendar.ics?token=… (regulärer, unauthentifizierter CalendarFeedController, damit externe Clients wie Thunderbird, Nextcloud Kalender oder mobile Apps ohne Nextcloud-Login abonnieren können) liefert Meilensteine als ganztägige VEVENTs (◆-Präfix), den aus den Aufgaben abgeleiteten Projektzeitraum sowie optional Teilprojekt-Zeiträume (Schalter „Auch Teilprojekt-Zeiträume einschließen", standardmäßig aus, um den Feed bei großen Projekten nicht zu überladen). Zugriff erfolgt über einen pro Projekt generierten, 256-Bit-zufälligen Token in der URL (CalendarFeedService, neue Spalte calendar_token auf nc_gantt_projects, Migration Version1026), der über einen neuen „Kalender-Feed"-Bereich im Freigabe-Dialog angezeigt, kopiert und jederzeit widerrufen/neu generiert werden kann (CalendarTokenController, nur für Projekt-Admins/-Eigentümer). ICS-Generierung ohne neue Abhängigkeit, RFC-5545-konform (Escaping, Zeilenfaltung)
  • What-if-Simulationsmodus für Terminverschiebungen (Issue #84): neuer Endpunkt POST /api/v1/projects/{id}/simulate führt Terminpropagation und CPM rein im Speicher aus (kein DB-Schreibzugriff) – TaskService::simulate() nutzt die neu extrahierte, stateless propagateDatesCore()-Kernlogik, die auch der persistierende update()-Pfad verwendet, CriticalPathService::computeFromTasks() erlaubt CPM auf einem hypothetisch veränderten Task-Datensatz. Frontend: „Simulation"-Toggle in der Gantt-Toolbar mit Banner inkl. Projektende-Delta in Arbeitstagen, Geisterbalken für die Ist-Termine (analog Baseline-Overlay) sowie Verwerfen/Übernehmen. Drag & Drop ruft im Simulationsmodus simulate() statt einer echten Speicherung auf. Szenarien bleiben bewusst flüchtig (kein Speichern benannter Szenarien), „Übernehmen" wendet die Änderungen als einzelne Task-Updates an, Simulation läuft pro Projekt
  • Neuer Eintrag „Aufgabe hinzufügen" im Drei-Punkte-Menü der Projektliste (Issue #91): legt wie der „Neu"-Button eine Aufgabe an, belegt dabei aber das gewählte Projekt direkt vor, sodass der Projekt-Auswahlschritt in der Maske entfällt
  • Such-/Filterleiste für Projekte, Teilprojekte und Aufgaben (Issue #81, Stufe 1: rein clientseitig auf Basis der bereits geladenen Daten aktivierter Projekte): Freitextsuche im Titel, Risiko, „überfällig" (Ende < heute und Fortschritt < 100 %), „über Budget" (Ist-Kosten > geplantes Budget), Ressource und „ohne Deck-Verknüpfung". Neuer Pinia-Store stores/filters.js (Zustand pro Nutzer in localStorage persistiert) und reine Matching-Logik in gantt/filterMatch.js. Nicht passende Balken werden in GanttChart.vue gedimmt statt ausgeblendet (Abhängigkeitspfeile und Zeitkontext bleiben lesbar), Projekte/Teilprojekte gelten als Treffer, sobald ein Nachfahre passt. Dieselbe Filterleiste (components/FilterBar.vue) dimmt in ProjectList.vue nicht passende Projekte; für nicht aktivierte Projekte sind nur Titel-Treffer zuverlässig auswertbar, da deren Aufgaben/Teilprojekte nicht geladen sind

Changed

  • Freigabe für Nextcloud 34 (Issue #94): max-version in appinfo/info.xml von 33 auf 34 angehoben, unterstützter Bereich damit NC 28–34. Verifiziert gegen Nextcloud 34.0.2 mit PHP 8.5 und MariaDB 11.4 (148 Unit-Tests, 39 E2E-Tests)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignaturerdYQI+Semnk0cWtKqUZinVDHk0KpxyqB5Ox4NBL9pyf5nrpK8xZpe9lBxc6zhDfW
8adLfVIwQtX+bI7S1O05xm0hwrUHQQ2ii6ADD8mlAxnZjdhKSCAhZ0NcaqT7Em0X
nD7yRP2B8zAAQMJNjzeBsLbzveNrXMoGzFrvm/jFxskUIUS01ykfWEg2C5+5/IAf
7jBAM8x2Qj92b8zyBFpLjfJQw9aTE/trtCsmZXQFmkJjGS0anrJc9dXKfqnHxknX
8jmLOv4Yq4Qtk3UF35HTiIGcA2W0UKfVyFb4XcrIMeSmLXXcagJWjeH5nB18UUNn
JyCgE/qY4v/SnrNQU92mIdMOQGuvYg7xbaz226lgNdwM2HuM9U8nc2juqJxAO+Wp
xs85yl82qIviZ5YY39HK371MZ/ufMsaVsVy3Nfn3PjqqkzGLmtAL6KJDcKHZWdDw
ugEPwO61DxnERZsNGeFpXfzCxjMpZc/qwUIfGw85idmU2vdedtrxHBkDGoqu1IP7
xZOO8uSYrzbXM6g1gs3WPBOY51CmZLs5pIQTN1tVl5ifGw4uOuzDBUCKiR5oA2MI
352HbA7wwWFgwoN9W5t4MRTowmaYpM8AB+nh9f1A5q9S8SelfMpXOqGew9VZb2jd
AuiJK/LEPjRgs5s3/FfC4Sb1MSsXDr1N1jziaF4MomM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<35.0.0
Minimum Integer bits32
Gantt Projects 1.3.1
Release Details
UpdatedAug. 6, 2026, 12:57 p.m.
Changelog

Added

  • Regressionstests für die Rollentrennung Viewer/Editor/Admin: ForbiddenException-Tests für TaskService::create/update/delete/syncProgress und ProjectService::updateProject/deleteProject (inkl. „Editor darf nicht löschen") sowie erstmalige Testabdeckung für SubProjectService und MilestoneService

Fixed

  • CSV-/Formel-Injection im Aufgaben-Export (detail/io.js exportCsv): Task-Felder wie Titel oder Beschreibung, die mit =, +, - oder @ beginnen, wurden unverändert übernommen und von Excel/LibreOffice/Google Sheets als Formel ausgewertet. Die Absicherung aus ReportView.vue liegt jetzt in einer gemeinsamen Utility (src/utils/csv.js, escCsvCell) und wird an beiden Export-Stellen verwendet
  • Viewer sahen im Frontend dieselbe Oberfläche wie Editor/Admin (Add Task/Subproject/Milestone, Share, Delete Project, Drag/Resize im Gantt-Chart, volle Bearbeitungsformulare), obwohl das Backend Schreibversuche ohnehin korrekt ablehnte – kein Least-Privilege an der UI. ProjectService::getAccessibleProjects()/getProject()/createProject()/updateProject() liefern jetzt das effektive permission-Feld (AccessService::getEffectivePermission()) mit; ProjectList.vue, GanttChart.vue (inkl. Drag/Resize-Sperre je Projekt) und das Detail-Modal (detail/detailModal.js) blenden Schreib-Aktionen für Viewer aus bzw. schalten Formulare read-only
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureBUyDeI9y7iKD1wQJLHrW6IFq0zuXOJQ1Zv/x+0RryIIQ0fAGAUjwQyLDYLYpRzPg
6ITDPfY9bftVlwkB8w9LqZiFETkH8mgc6l5XB7F6NKzFrdaSnNw21CIVJjWyHpkR
mxDq+vQepZd+YtB9jMz39kbFk9BNHrpKIwIXXZVqEqj1U54NmT+5XnT892m9mY6o
RWQNf3Mn5WcH3vmoDi/UAOgDaO30GFCRJePryurVpK7stkub8K6fYsWIkS5NeJlP
YHdb4nBlkhUzQFjo0ZDSkCSiWjMdyCrtEanX6m6Ffhr+j0jpd+J9soMSjrzdzt4T
5mETo8xc61IQIVFWMD6fZ3/oCmbx2TFDZsJjRpFKxoPA08AkdC7xjxHoaKYZHvi4
+tzowxPzMfyjQ/hEj2dwq22doM2lXK1Y13VvV8kkaomrAQjB97gf+ODrYZNDgNvc
2ijU08B4cAB0HbEveDpDRjCJj8D5OwCMafRMa4Ul/nm0+RiBFOhIjDYdVAnCp7Ql
IEc4UQZM9WYLQlouwGPk83POpbqkbc8vKAucGBXvHAbxNpUSOGbssMYFGU/e9pOg
+oT2gcJrC3MYPGIWnS8iLn75PK1H063KFJv5yqC+h/rqwX/FxyTzKvofJi68GpLs
WtG3y67W8Ju4y1I9iQ9raJxxhFUJrStibGy9gc7L0DM=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.3.0
Release Details
UpdatedJuly 9, 2026, 5:23 a.m.
Changelog

Fixed

  • Baseline-Toggle im Gantt-Toolbar zeigte nie Baseline-Balken und PUT baseline/active warf einen SQL-Fehler: baseline_id auf nc_gantt_projects wurde mit der 3-Ebenen-Migration versehentlich zusammen mit den tatsächlich berechneten Spalten entfernt (Migration Version1025 stellt die Spalte wieder her). BaselineService::create() aktiviert eine frisch eingefrorene Baseline jetzt automatisch fürs Projekt; renderBaselineBars() (GanttChart.vue) und baselineOverlay.js lesen Baseline-Items dafür über task_id statt des seit der Umstellung auf das 3-Ebenen-Modell nicht mehr existierenden subproject_id-Felds
  • OCS-API-Fehlerantworten (400/404/500 …) lieferten für alle OCSController-Endpunkte einen leeren text/html-Body statt des erwarteten JSON-Envelopes mit message; ExceptionMiddleware wirft jetzt passende OCP\AppFramework\OCS\OCSException-Subklassen statt einer rohen DataResponse, die Nextcloud-Core nur für 401/403 automatisch formatiert. Betroffene Controller (ProjectController, ReportController, ShareController, ChangeEventController, WorkScheduleController, WorkCalendarController, HolidayAdminController, SettingsController) wurden zusätzlich auf die bestehende ApiException-Hierarchie umgestellt

Removed

  • Nie ausgewertete Gruppen-Freigabe-Option entfernt: Share::TYPE_GROUP, principal_type-Parameter in ShareController sowie die DB-Spalte principal_type (Migration Version1024) wurden gestrichen; Freigaben sind jetzt ausschließlich benutzerbezogen

Changed

  • Dashboard-Summary (DashboardController::summary) lädt Baselines und Aufgaben-KPIs jetzt über gruppierte SQL-Aggregat-Queries statt pro Projekt einzeln (N+1-Fix, gilt auch für den Admin-Fall mit allen Instanz-Projekten)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureNJfbRJEXzKGipUjUW9tUKayHoY6ltSU0ZeCQXcDx6XVDY/naLK0dVY9ciofXU7oy
40sJIOsjxcUZbalDwqncPn0vjGoqmk3BFUT7KotEpLLf8/ZmIjdHEbQbJHzEpdQV
y5YedjqRLKB7/Ul35JWVFxY8eutCsYkonfof7v6AGMGB/RjQ7GDB6jWunEEwqUGV
3Y1isvdq5N3oo9NDY/QR0C2C7jiMnB9Kon0KzZ2yp0oF5euOpA/qrtmqxG3L9PoZ
DW1ppwAuARWtRDu4tcDAAQAkF1Mrg5YyYgRoKm7uVazlKEP7DxX56g/g4RS2otEE
rKsA/ER9yYiHnNyGcZuK9Jkfe2Ua6/R7gzJilRvHZ7CBPkhY+XNPcrjlsHbCJEJJ
cX/b0B56p7FyAFy6XjMY9PYFhxy/ubHE7Sk8ixX88uxgM4vf/sSFQaN84CcG/qqA
Nq/OEytBjZP82TsFFYBRcPKEPskF4ejjhNVC4bJpiuCIpo51nIxjeFGtDpeQiDG7
0OWc/GCvsz5W3eG7ShmQsRf0DoivL8tjgDUlMWO9boTpUy2Vnm1rDMKu6nFGKZ8o
TWTZR6u4zGZW1OBERdlU5NQmqxqXrsuGlT6r6DuQnWsWiscjXOyIFeHz+EOKhQOk
wUuN6xN8QES1CDFB27PYV0Sz2KhYhM78GKLCc9B10Ic=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.2.0
Release Details
UpdatedJuly 5, 2026, 9:21 a.m.
Changelog

Added

  • Löschen-Buttons im Detail-Modal für Teilprojekt, Aufgabe und Meilenstein
  • Deutsche App-Beschreibung in info.xml ergänzt

Fixed

  • Baseline-Datum und Kommentar im Report werden untereinander angezeigt
  • Dashboard-Widget zeigt Baseline-Datum und KPI-Werte korrekt an
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureXuYTZMxTlGNJVrGvU/GcIjSlBTYreXdvF5FilCmsi+l7MBvFlyeW1DsnxAsao3pu
HyhvaKXRnkraVQNT2ULgX4BmjUnhsELROrK03gtY88Odzqzh7RIXYkNkRcy92ara
56HIP8BBRD3m1c8pYPfpZfLAzkuurAzVqbJ6qbxw1AebdlPCFEq2g6C+IOcTon0T
q9ZD2I2XJmWd42EXXqva+NMrcx1jfjmHWVtl7xHKhwPndFCtHPMMAdoDS0Xpbh1V
3Ns4h/DphDpC8/ZYZ5sbpMCBekHw4VTZDGxNAuXJ5DMPhkqg2MxVplkS19PuBHLx
r74sgsz611Gjg55gjKOqCb5ZZEvqZTrwKTEYIbu8HicWtq/HxKRZS3FZN9MThTTX
Hesv65vsq9fH0UhEM6MVJjE5Phnw8Wb5ivtAiaUA+NzPrDfrHZAsSfR9NFL3rhFC
7wOmnXtzK40NjmqyMWJu/UP9UVbz3SeEiHiQO9GwBc2krzmXzcmLZFgEjty4CGoJ
SzXAA1QGpUGO4n0b3Y/5zSY4UxogGWybW+mOs27KDLytI915wNYGvBtcdAKCj6Ws
1uVA98npwMid/WSJJ04BXHX8LbowrDeTezSFJyGh+u/KnhlzkA7fqEuu3nQGT5rA
a9qeMmd5kmLBIVcG9RVVmw1kc0AK0qIqZDmlk9dWGJs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1
Release Details
UpdatedMay 27, 2026, 6:43 p.m.
Changelog

Added

  • Glossar-Button in der Navigation (über den Einstellungen) mit interaktivem Nachschlagewerk
  • Dreispaltige Glossar-Tabelle: Begriff/Abkürzung, Bedeutung/Englisch, Erklärung mit Berechnungsformeln
  • 30 Einträge in 6 Abschnitten: Allgemein, Terminplanung, Baseline & Berichtswesen, Earned Value Management, Ressourcen & Kapazität, Budget

Fixed

  • Fehlende deutsche Übersetzungen für englische UI-Strings ergänzt (Add dependency, Baseline, Dependencies, Remove, Work, No projects yet, Open Gantt Projects, u.a.)
  • Leere Übersetzung für Deck-Fortschritt-Hinweis befüllt
  • de.js vollständig mit de.json synchronisiert (war ~70 Einträge veraltet)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF1RHJx15J/N8tWNrDlAX1MFE/9tsct4PViEcBDeo8U/V2qJ/tezL7Xk0BVmYxjWw
pB+J685mcHjikh93/aZUqG8C6y0mR3TTJfaa7dLCIRMc1034x6Xj9Nd7Q9YVH4yi
RLTcRhrG2pN1Iqc23kwId995GJXHnCBEyuQuzo9m1VtA7GoFa8W2nqhXVT94khDS
8ZHQrixDfv63d4GfA5N2x4+phaiyObdskvCmVSuKrmouYvCGpPevtKG5YxzOrHid
aL48Kod0YgOCAD8nAqOhEmioP0m+wP2e/NC4Z5svSKT3vdlE67W1M68ipQQfnhPI
txui+2X8zFvoe/M5sE3DaXbLnOVNGQ7YRpyXFI5GsQmQ0cPSSO/94Mw15hLZfwMf
TDOrrvzf+Vu05sUTZ6CFh4FrSl4e5Q6Xjv6uH4BZtyOpPxl+ajgW4OkZvQDbt4KC
8aTZodMcDpXlRMDdBSxT1bT9tD05KUKmEZYtpxHiARrHDuQQCRSuxtjitQD9lbla
CMxKrBiO0YgdUb3FuczYkWEH+y8gW5egU6TDMnjAVbuUecGuGZN1Cc6tarcFWRu2
+V7Qn+aNoXEwP5e/0TM4Ig8ETAYseZRiTPHxeWiSg3RnU3HuT6kPVWw8LERACDT4
6F4WU9t2+4kESMLMjeV0/2UBQSykR/42Idsg0PtvpyQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32
Gantt Projects 1.1.1 (nightly)
Release Details
UpdatedMay 27, 2026, 5:24 p.m.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEAzCCAusCAhNMMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYwNTI3MTAzNzU4WhcNMzYwOTAxMTAzNzU4WjATMREwDwYD
VQQDDAhuY19nYW50dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm9
oP6vDlGpTxTBBnAJjbz2Sv0wAf5dyLr73SLX2nXmidKACWjAkuq7CiU9qHfJaxJC
n5gXVjhV4QQKUTaK2Y5bOPGf9VBLwX9zDC1gwlVHFlG7HA+tlXZyL96Zw/qB1ghe
Ykk95n4Ahx7FNHvZwHbAqhoxejWQIo6mwJna9I+x3gyUsURfmaNKo0ZHckMP2fpj
4HKAwSTbzWqytYgbV8efQP6mAF6JWoBOIyi0Y/Z/zd2YIJ6ZVal0UmxpfTexSgfN
ZPoJWYLQGjqFxAZWLad4XTsJ3kHC4uSJf4LPE3Fde/B+/a15/Xi1uhkIzG38+I0L
EcOsobkGkgiVU8BFe5NF39Ld8oKTpV+DNaWTAXSzC+qxhM4O15etll/cN8hP1rSb
nxppA6diiiZ1D788feYvnU0bFMDjG1AxAtiO7su0m+VrqqQcKd/9oyNouZXAG0VH
LAsTL2kp78EW9yD3UxdgJ8C1j5XEApOAiILt4j0rsRK40u7AGVGD6zwKVPUoOMKZ
kdiKOHIU31AdMJj3K9ovgXk9ecIOaU+r2UHmP5mH7d7M8pHLwp5elj4rmgx+He69
2ShNAt0FECTm3yiay30qDuT2IIvRxVrn0qHUE8OWRgtzJnV0WXe2GMvASN86JQM9
DGhtam1lDw+X44c7LEFawVN/AUesvxv1BrbEezjLAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAGXZO3ifTRL+r9p3iZ8AmTlcdjfYeZLQT/VDio5Zo1fbu1vQRuVXZ4YJ
ye9PFBwLjCXU/Wr2bXdFoMi/L1+o/kZmfEiLmEv7GA6GP88OhozJeNx/+6myhUX6
0oLbVDJresaO8dguSwc1BkQ1TPO5P2Pihtg6Jz+SE1IG4fhoMCrKwMdmzySZkqeL
XTgBLp6GTaGGd8Z5jXZvvBTEDwRaVeNMte3UmR2ON2B4zsR8eVD+PAoDobM+HR7q
x9N8zOlBqkdJUcS/KodgNfXH0o/ZOgqzhHIbD5XnnQpMT1yG0qM98PtelqDURCi9
qzfIPzlCodnTkS/VWiq16iR9q4kwjRg=
-----END CERTIFICATE-----
SignatureF4W7QJB8rVJ2xARdnhUbEouzRZMKcowz/ToDwr0oVFrVUbInFvHAMqXFTOVD1a5N
kDTb0pOhv+hCStTh70bN+aapjbdtp7SdQ3SbP5VoXBKLhUii41OqN/i4yM7itdSs
M8d5PVo9TJYVnfaij2xwWghMqZEyzxgOw4Q98dYIysf2BxsjjCtxGEMo/pISS90G
OE2RaVEFbwUcvu1XKszLP5X/k/onOchengURYVuzHP7VgVuomZpPSZ9VEIRKITyL
GpgSDrdqm5AXdQc7l/2OqprW6G+MxsuVzChbyPGslIYYXYsP9HLc/FfwVxaCJjh2
Nsiqfd+dMwnf7f9yrrBuZjbNwNa5x++IynrwaU01jVISkl9pgkbUYRaPSMTGFDAd
lsEEqlgs9KdzGoTFjDXNdTwDOsUMiu/ag8tPo7YxsxLFbVGa7laRAUhlGUT9QGPr
IOAAWvKqivsYnU5iwTCY/5HGza3SDfZc/e5upPfB+lTQTYijp1aAYRBAsoOLmuga
f0aP45pnfnYBJkT1kMsthjbGMdlL46wLiNHKRct9OlFwx74IRIyZdcs5Q3pC0aNN
DSLvsCSmY8/9PtTLzcOJKXTqqDVupCqq82bUlGRn7NR6xsinzv74MuOVnGY21tBm
iB5ceYpgiEukUz6YtS2quIIiMTnYmM+HBFnkXgBV0/Y=
Signature digestsha512
Dependencies
Required Nextcloud versions >=28.0.0,<34.0.0
Minimum Integer bits32