Skip to main content

Collectives Todos - Releases

← App details

Nextcloud 34

Collectives Todos 0.1.0
Release Details
UpdatedOct. 7, 2026, 6:31 p.m.
Changelog

Added

  • Initial release of Collectives Todos: event-driven aggregation of markdown checkboxes across all pages of a Collectives into an auto-maintained Todos page, using Nextcloud core file events, a JSON cache for efficient updates and a CLI command (occ collectives_todos:init <collectives-id>) to build the page for existing collectives. Unit tests cover all core services and listeners.

  • App store release packaging: a GitHub Action (.github/workflows/release.yml) builds a store-conformant collectives_todos-vX.Y.Z.tar.gz (single top-level folder named after the app id, dev files excluded) on every v* tag, signs it with the app certificate and attaches archive and signature to the GitHub release. Tag and app version must match, and appinfo/info.xml is validated against the app store schema. With the APPSTORE_TOKEN secret set, the release is also published to the Nextcloud app store automatically.

  • Per-collective Enable/Disable button in the settings page's per-collective table. Disabling deletes the collective's Todos page (to trash) and stops generating or reverse-syncing it; the checkbox cache keeps being maintained, so enabling regenerates a consistent page immediately. Enabled is the default.
  • Global Enable/Disable button in the settings page's Defaults section: flips the instance-wide default. Enabling resets every collective to enabled - it clears all per-collective enabled opt-outs and regenerates their Todos pages, since opting into the app means wanting it everywhere; disabling single collectives stays the manual choice. Disabling globally deletes the Todos page of every collective that is not already opted out.
  • The per-collective Enable button clears the collective's enabled override instead of writing a sticky enabled value: a sticky override would shield the collective from the global default toggle forever, so a global disable reliably deletes every Todos page of collectives that were not explicitly disabled. The per-row Enable buttons render as inactive while the global default is disabled. Deleting the Todos page on disable now also removes its collectives_pages row (the delete is a hard delete on the appdata path, which the Collectives trash does not handle; the managed page's content is fully derived, so nothing of value is lost). The row is resolved untrashed and deleted directly: Collectives' PageMapper::deleteByFileId only removes rows that were moved to trash and silently ignored the hard-deleted page's row, leaking one on every disable cycle.
  • Todos page emoji config item (default none, per-collective override): the emoji is written to the Todos page's collectives_pages row like the emoji of any other Collectives page and rendered in the page tree. Validated with the Collectives app's own emoji validation; the settings page ships a curated emoji picker built with plain HTML/JS (no new dependencies), and any emoji can be pasted directly. The emoji is enforced on every Todos page regeneration and on settings save.
  • Admin settings page (Settings → Admin → Collectives Todos) with instance-wide defaults and per-collective overrides for three config items: the Todos page name (default Todos), the position of the Todos page in the Collectives page tree (always on top / always on bottom / alphabetically, default always on top) and the maximum number of cached checkboxes per collective (0 = unlimited, default 0). An empty override field uses the default. Invalid input is rejected without storing anything.
  • The tree position is enforced by writing the landing page's collectives_pages.subpage_order (the Collectives frontend sorts listed pages by array order, unlisted pages alphabetically), on every Todos page regeneration and on settings save. Manual ordering of the other pages is preserved; an already-open Collectives tab needs a reload to pick up the new order.
  • Changing the Todos page name renames the existing Todos page file; if a page with the new name already exists in a collective, the rename is skipped and reported on the settings page.
  • When the checkbox limit is reached, further checkboxes are not cached and the Todos page renders a *(list truncated by the settings limit)* note under the affected page's section.
  • Bidirectional checkbox sync: ticking or unticking a checkbox on the Todos page now also updates the checkbox on the source page it was aggregated from. Sections of the Todos page are resolved to source pages by the page file id in the heading link URL (title as fallback), and checkboxes are matched by text within a section. Only checked-state changes are synced; text edits on the Todos page are discarded with the next regeneration. If the cached line number of a checkbox has drifted, the checkbox is located by a unique text match, otherwise the change is skipped and a warning is logged.
  • Page headings on the Todos page are now links to the corresponding page, in the same URL format the Collectives app itself uses (/apps/collectives/<collectives>/<slug>-<pageId>, with a ?fileId= fallback for pages without a slug). If the URL cannot be resolved, the plain heading is kept.
  • Regenerating the Todos page while the Text editor had it open caused stale content to be shown and "Error saving the document" on manual saves: the Text app keeps per-file session state (etag, checksum, editor steps) that our background write invalidated. The Text document state of the Todos page is now reset on every regeneration, so open editors reload the fresh content. Unsaved manual edits to the Todos page are discarded by design.

Changed

  • Minimum Nextcloud version raised from 25 to 27 (the admin settings controller uses the AuthorizedAdminSetting attribute introduced in 27).
  • appinfo/info.xml brought into the form the Nextcloud app store validates: added the required <summary>, renamed <license> to <licence> and <depends> to <dependencies>, and dropped the <app>collectives</app> element (not part of the store or server schema, so it was never read; the Collectives dependency is enforced at runtime via IAppManager). No runtime behavior changes on servers already running the app.

Fixed

  • Reverse-syncing a tick made in the Collectives editor: the Text editor strips trailing whitespace on save, so a Todos page checkbox whose source line ends in spaces no longer matched the cached source text and the tick was silently dropped. Checkbox texts are now trimmed when parsed, and the reverse sync also tolerates untrimmed legacy cache entries.
  • Deleting the Todos page recreates it immediately. Collectives moves deleted pages to trash; the trash move is complete before the delete event fires and collectives paths are never locked, so no delayed recreation is needed.
  • The emoji of a freshly (re)created Todos page is now really written: inserting the missing collectives_pages row failed because its last_user_id column is NOT NULL. The app-created row now carries an empty user id (the frontend hides the "last edited" info for such pages), like untouched pages.
  • Deleting a page did not clean up the Todos page: the deleted page's section and checkboxes stayed listed. A Collectives page is a folder (the index page is its Readme.md, subpages are further files in it), and deleting it only emits a filesystem event for the folder itself - the markdown files inside vanish without their own events. A new listener for BeforeNodeDeletedEvent now collects the markdown pages under a folder before it is deleted, removes them from the cache and regenerates the Todos page. Plain .md file deletes keep their previous cleanup path. Restoring a page from the Collectives trash does not re-add it to the Todos page until the page is saved again or collectives_todos:init runs.
  • After ticking a checkbox on the Todos page, the source page briefly showed the tick and then reverted to its old content, with the editor refusing saves ("File changed in the meantime from outside"): the Text app keeps per-file session state (etag, checksum, steps) also for source pages, and the reverse sync's external write desynced it. The Text document state of a source page is now reset when the reverse sync writes it, so the page loads with the synced state and saves work. Unsaved keystrokes in an editing session open on that page at that moment are discarded (same contract the Todos page already has).
  • Checking a checkbox on the Todos page and saving broke the editing session: the reverse sync's regeneration of the Todos page ran nested inside the save and rewrote the page with a fresh "Last updated" timestamp, changing the etag under the open editor ("Error saving the document", conflict diff prompts). The Todos page is now only rewritten when its content actually changes (checkbox states, pages, titles); an update that would only refresh the "Last updated" timestamp leaves the file - and open editing sessions - untouched.
  • App bootstrap was silently skipped: info.xml declared the full namespace (OCA\CollectiveTodos), which Nextcloud prefixes with OCA\ itself, so no class of the app was autoloadable. The namespace is now CollectiveTodos.
  • Application.php moved from appinfo/ to lib/AppInfo/ (PSR-4 location that the bootstrap looks up).
  • CLI command is now registered via info.xml <commands> (previously the occ command never showed up).
  • Todos page is now written to the collectives root folder (previously it was written next to the edited page).
  • Page titles for Readme.md pages now use the page folder name (Collectives stores a page as folder with a Readme.md inside).
  • The listener skips its own Todos.md writes to avoid an infinite write-event loop, and logs failures instead of breaking saves.
  • collectives_todos:init now scans all pages of a collectives and accepts the collectives id as used by the Collectives app.
Licenses GNU Affero General Public License v3.0 or later
Certificate-----BEGIN CERTIFICATE-----
MIIEDDCCAvQCAhRDMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYxMDA3MTQ0NDAwWhcNMzcwMTEyMTQ0NDAwWjAcMRowGAYD
VQQDDBFjb2xsZWN0aXZlc190b2RvczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBALdQghokF+S+krqy/gMTVV+G7kw1vlwkBdVWroUoXrJk2U19znosxLwN
Hx/lOsSGjn9Ln0Y4/PF1aDCNITmzRKnUo3JcuC+dT6RPuG4XtGvcDPe2mBGmrY4J
wkqwWN5N1cic7crNXUqnI8cFUg+U8dAlkYah6Han5QkXf4ZuO/lH0cIRPsRaMw8j
5BDHAcdLvniw2Exa9y/atNe/7i1NDkgtpxN8yA2hoXUM4YUMcnlzUAiDx+fO556B
iMcSb8WJNblb/EpQjVKetk3h5N/oVopUR/Oo0st/tgWh/jiO7TMoCYx7ktFpQQJN
j9H7rLjsVswG3cn7xSTL3wfm0stFB1tSnTXP4zf0pp4d2QilI0GxgSdeAOPskFTN
pJGzSghDZ7/+tw4kZ5Y8gWj3aME9O10rXinBWaYV05cme569ZMXzbigLgNFTA/sT
0L/rHT3RZkrJ9cUIJCRm3yruqAQIml69onikKOl8XNM95GGAT/oF10ebhU0ITTNk
WafdGlqT2iPloHCvaRqR2BMPxR+My55IQ6+VI2nvgLztA524/Cud60PFhyrCXUZU
9O+c+5oz7MEZF0HPJfvg65PiXD/LvXi22Q17fY6fhlVQVYn8vQoMnoc0I6bkw8Al
PdK/d/kxNIpeWDNDh2gyEyBIiOxS7rOSyFmump1qliF8qf47PFEDAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBACh749xaQoj6Y4w24L7C4kiuYBMfo6JbAEqtijdZUQRC
XdV3pnonKnJ+mMV/803o41PxQ2xmRzvEaDWm2WKG2zzNssKwObcJg8TnpdPTcHjB
brU6pxkJXIHmgEjMHc5x6XJ3RR2uA9Wayaf5PFl+omfd52TzHQpMbGXM+u6TYckx
uEe0D9gnyg/Jw2ESnsOABk3n/Mkbv1IzUVs0Cxb3Je+dwJDMDBwW3DaSby/kKSjc
fSZBA9FsAJU77/yg7dnsFV3rhBXdJ5sQbVJNTH7gzcHhCiLffuEC0BmIPF+D5/80
yT7k9b7NAK212mhbleE90EiOwpPtKirDr+jKLL1zT1g=
-----END CERTIFICATE-----
SignaturetOPFL+uJkvXf4IYYON0b0T+MahYmk+Sga4NLaCPjTbv+nUklqi4DBjxNPKZEFLSHW+e7b6A2wR+uL+ugBCy9IWjJHfe35JVt1g/06s28dnXkm6nrod3AtPk4DgrZ2HHrDgPo86K4iIW2KIeW+IMbT1iKhYLQ9mGq3YcBPPtDgWzNjbpLid+PcGtxcPn8+E6GuOkrN6YbKuNkkdhL6UGuuqlHFYw7bVzDN41shzSaMYrUBmZUXXl/5ZnbnewEgiGZf6wsobuxQZ6uLqNjl9yKBvA85quuhBcnEHL0L+aFZn0xTuX8YFxvVmrx9iO8m24luzzSys35L/PL9W2l7m+w4MOX5zTIKkx+q8ExR8PtZBYOZqhaUPcZZwYLHFvJUlGL80LOPil51hn0qrsOUdJCd5yZqcoLoZxbBFKj1njokd7cTb8ZNPbw/lYsMnp8JCp4ldTTVLE/+P3U4p7aG1lbOIP33zMzOz6R+oF3w3xuNlT6QFRjvztbXzLohaj2fuTx3rVuraaztowQk9tLSqFiuRbMh5KHv3n9BFAuijyR9mfaPwJpEnKD0u0nKJmb2oIAkrwviHjYJR1xUbNsiZHbEQOpW2UMy0+5wvE++zGGCX+c3p9lRFqN00+KO1RNosmEOnmd/st12hZowjR+oayERlBKLUBXXxgO7h4/MIUHqYs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=27.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 33

Collectives Todos 0.1.0
Release Details
UpdatedOct. 7, 2026, 6:31 p.m.
Changelog

Added

  • Initial release of Collectives Todos: event-driven aggregation of markdown checkboxes across all pages of a Collectives into an auto-maintained Todos page, using Nextcloud core file events, a JSON cache for efficient updates and a CLI command (occ collectives_todos:init <collectives-id>) to build the page for existing collectives. Unit tests cover all core services and listeners.

  • App store release packaging: a GitHub Action (.github/workflows/release.yml) builds a store-conformant collectives_todos-vX.Y.Z.tar.gz (single top-level folder named after the app id, dev files excluded) on every v* tag, signs it with the app certificate and attaches archive and signature to the GitHub release. Tag and app version must match, and appinfo/info.xml is validated against the app store schema. With the APPSTORE_TOKEN secret set, the release is also published to the Nextcloud app store automatically.

  • Per-collective Enable/Disable button in the settings page's per-collective table. Disabling deletes the collective's Todos page (to trash) and stops generating or reverse-syncing it; the checkbox cache keeps being maintained, so enabling regenerates a consistent page immediately. Enabled is the default.
  • Global Enable/Disable button in the settings page's Defaults section: flips the instance-wide default. Enabling resets every collective to enabled - it clears all per-collective enabled opt-outs and regenerates their Todos pages, since opting into the app means wanting it everywhere; disabling single collectives stays the manual choice. Disabling globally deletes the Todos page of every collective that is not already opted out.
  • The per-collective Enable button clears the collective's enabled override instead of writing a sticky enabled value: a sticky override would shield the collective from the global default toggle forever, so a global disable reliably deletes every Todos page of collectives that were not explicitly disabled. The per-row Enable buttons render as inactive while the global default is disabled. Deleting the Todos page on disable now also removes its collectives_pages row (the delete is a hard delete on the appdata path, which the Collectives trash does not handle; the managed page's content is fully derived, so nothing of value is lost). The row is resolved untrashed and deleted directly: Collectives' PageMapper::deleteByFileId only removes rows that were moved to trash and silently ignored the hard-deleted page's row, leaking one on every disable cycle.
  • Todos page emoji config item (default none, per-collective override): the emoji is written to the Todos page's collectives_pages row like the emoji of any other Collectives page and rendered in the page tree. Validated with the Collectives app's own emoji validation; the settings page ships a curated emoji picker built with plain HTML/JS (no new dependencies), and any emoji can be pasted directly. The emoji is enforced on every Todos page regeneration and on settings save.
  • Admin settings page (Settings → Admin → Collectives Todos) with instance-wide defaults and per-collective overrides for three config items: the Todos page name (default Todos), the position of the Todos page in the Collectives page tree (always on top / always on bottom / alphabetically, default always on top) and the maximum number of cached checkboxes per collective (0 = unlimited, default 0). An empty override field uses the default. Invalid input is rejected without storing anything.
  • The tree position is enforced by writing the landing page's collectives_pages.subpage_order (the Collectives frontend sorts listed pages by array order, unlisted pages alphabetically), on every Todos page regeneration and on settings save. Manual ordering of the other pages is preserved; an already-open Collectives tab needs a reload to pick up the new order.
  • Changing the Todos page name renames the existing Todos page file; if a page with the new name already exists in a collective, the rename is skipped and reported on the settings page.
  • When the checkbox limit is reached, further checkboxes are not cached and the Todos page renders a *(list truncated by the settings limit)* note under the affected page's section.
  • Bidirectional checkbox sync: ticking or unticking a checkbox on the Todos page now also updates the checkbox on the source page it was aggregated from. Sections of the Todos page are resolved to source pages by the page file id in the heading link URL (title as fallback), and checkboxes are matched by text within a section. Only checked-state changes are synced; text edits on the Todos page are discarded with the next regeneration. If the cached line number of a checkbox has drifted, the checkbox is located by a unique text match, otherwise the change is skipped and a warning is logged.
  • Page headings on the Todos page are now links to the corresponding page, in the same URL format the Collectives app itself uses (/apps/collectives/<collectives>/<slug>-<pageId>, with a ?fileId= fallback for pages without a slug). If the URL cannot be resolved, the plain heading is kept.
  • Regenerating the Todos page while the Text editor had it open caused stale content to be shown and "Error saving the document" on manual saves: the Text app keeps per-file session state (etag, checksum, editor steps) that our background write invalidated. The Text document state of the Todos page is now reset on every regeneration, so open editors reload the fresh content. Unsaved manual edits to the Todos page are discarded by design.

Changed

  • Minimum Nextcloud version raised from 25 to 27 (the admin settings controller uses the AuthorizedAdminSetting attribute introduced in 27).
  • appinfo/info.xml brought into the form the Nextcloud app store validates: added the required <summary>, renamed <license> to <licence> and <depends> to <dependencies>, and dropped the <app>collectives</app> element (not part of the store or server schema, so it was never read; the Collectives dependency is enforced at runtime via IAppManager). No runtime behavior changes on servers already running the app.

Fixed

  • Reverse-syncing a tick made in the Collectives editor: the Text editor strips trailing whitespace on save, so a Todos page checkbox whose source line ends in spaces no longer matched the cached source text and the tick was silently dropped. Checkbox texts are now trimmed when parsed, and the reverse sync also tolerates untrimmed legacy cache entries.
  • Deleting the Todos page recreates it immediately. Collectives moves deleted pages to trash; the trash move is complete before the delete event fires and collectives paths are never locked, so no delayed recreation is needed.
  • The emoji of a freshly (re)created Todos page is now really written: inserting the missing collectives_pages row failed because its last_user_id column is NOT NULL. The app-created row now carries an empty user id (the frontend hides the "last edited" info for such pages), like untouched pages.
  • Deleting a page did not clean up the Todos page: the deleted page's section and checkboxes stayed listed. A Collectives page is a folder (the index page is its Readme.md, subpages are further files in it), and deleting it only emits a filesystem event for the folder itself - the markdown files inside vanish without their own events. A new listener for BeforeNodeDeletedEvent now collects the markdown pages under a folder before it is deleted, removes them from the cache and regenerates the Todos page. Plain .md file deletes keep their previous cleanup path. Restoring a page from the Collectives trash does not re-add it to the Todos page until the page is saved again or collectives_todos:init runs.
  • After ticking a checkbox on the Todos page, the source page briefly showed the tick and then reverted to its old content, with the editor refusing saves ("File changed in the meantime from outside"): the Text app keeps per-file session state (etag, checksum, steps) also for source pages, and the reverse sync's external write desynced it. The Text document state of a source page is now reset when the reverse sync writes it, so the page loads with the synced state and saves work. Unsaved keystrokes in an editing session open on that page at that moment are discarded (same contract the Todos page already has).
  • Checking a checkbox on the Todos page and saving broke the editing session: the reverse sync's regeneration of the Todos page ran nested inside the save and rewrote the page with a fresh "Last updated" timestamp, changing the etag under the open editor ("Error saving the document", conflict diff prompts). The Todos page is now only rewritten when its content actually changes (checkbox states, pages, titles); an update that would only refresh the "Last updated" timestamp leaves the file - and open editing sessions - untouched.
  • App bootstrap was silently skipped: info.xml declared the full namespace (OCA\CollectiveTodos), which Nextcloud prefixes with OCA\ itself, so no class of the app was autoloadable. The namespace is now CollectiveTodos.
  • Application.php moved from appinfo/ to lib/AppInfo/ (PSR-4 location that the bootstrap looks up).
  • CLI command is now registered via info.xml <commands> (previously the occ command never showed up).
  • Todos page is now written to the collectives root folder (previously it was written next to the edited page).
  • Page titles for Readme.md pages now use the page folder name (Collectives stores a page as folder with a Readme.md inside).
  • The listener skips its own Todos.md writes to avoid an infinite write-event loop, and logs failures instead of breaking saves.
  • collectives_todos:init now scans all pages of a collectives and accepts the collectives id as used by the Collectives app.
Licenses GNU Affero General Public License v3.0 or later
Certificate-----BEGIN CERTIFICATE-----
MIIEDDCCAvQCAhRDMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYxMDA3MTQ0NDAwWhcNMzcwMTEyMTQ0NDAwWjAcMRowGAYD
VQQDDBFjb2xsZWN0aXZlc190b2RvczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBALdQghokF+S+krqy/gMTVV+G7kw1vlwkBdVWroUoXrJk2U19znosxLwN
Hx/lOsSGjn9Ln0Y4/PF1aDCNITmzRKnUo3JcuC+dT6RPuG4XtGvcDPe2mBGmrY4J
wkqwWN5N1cic7crNXUqnI8cFUg+U8dAlkYah6Han5QkXf4ZuO/lH0cIRPsRaMw8j
5BDHAcdLvniw2Exa9y/atNe/7i1NDkgtpxN8yA2hoXUM4YUMcnlzUAiDx+fO556B
iMcSb8WJNblb/EpQjVKetk3h5N/oVopUR/Oo0st/tgWh/jiO7TMoCYx7ktFpQQJN
j9H7rLjsVswG3cn7xSTL3wfm0stFB1tSnTXP4zf0pp4d2QilI0GxgSdeAOPskFTN
pJGzSghDZ7/+tw4kZ5Y8gWj3aME9O10rXinBWaYV05cme569ZMXzbigLgNFTA/sT
0L/rHT3RZkrJ9cUIJCRm3yruqAQIml69onikKOl8XNM95GGAT/oF10ebhU0ITTNk
WafdGlqT2iPloHCvaRqR2BMPxR+My55IQ6+VI2nvgLztA524/Cud60PFhyrCXUZU
9O+c+5oz7MEZF0HPJfvg65PiXD/LvXi22Q17fY6fhlVQVYn8vQoMnoc0I6bkw8Al
PdK/d/kxNIpeWDNDh2gyEyBIiOxS7rOSyFmump1qliF8qf47PFEDAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBACh749xaQoj6Y4w24L7C4kiuYBMfo6JbAEqtijdZUQRC
XdV3pnonKnJ+mMV/803o41PxQ2xmRzvEaDWm2WKG2zzNssKwObcJg8TnpdPTcHjB
brU6pxkJXIHmgEjMHc5x6XJ3RR2uA9Wayaf5PFl+omfd52TzHQpMbGXM+u6TYckx
uEe0D9gnyg/Jw2ESnsOABk3n/Mkbv1IzUVs0Cxb3Je+dwJDMDBwW3DaSby/kKSjc
fSZBA9FsAJU77/yg7dnsFV3rhBXdJ5sQbVJNTH7gzcHhCiLffuEC0BmIPF+D5/80
yT7k9b7NAK212mhbleE90EiOwpPtKirDr+jKLL1zT1g=
-----END CERTIFICATE-----
SignaturetOPFL+uJkvXf4IYYON0b0T+MahYmk+Sga4NLaCPjTbv+nUklqi4DBjxNPKZEFLSHW+e7b6A2wR+uL+ugBCy9IWjJHfe35JVt1g/06s28dnXkm6nrod3AtPk4DgrZ2HHrDgPo86K4iIW2KIeW+IMbT1iKhYLQ9mGq3YcBPPtDgWzNjbpLid+PcGtxcPn8+E6GuOkrN6YbKuNkkdhL6UGuuqlHFYw7bVzDN41shzSaMYrUBmZUXXl/5ZnbnewEgiGZf6wsobuxQZ6uLqNjl9yKBvA85quuhBcnEHL0L+aFZn0xTuX8YFxvVmrx9iO8m24luzzSys35L/PL9W2l7m+w4MOX5zTIKkx+q8ExR8PtZBYOZqhaUPcZZwYLHFvJUlGL80LOPil51hn0qrsOUdJCd5yZqcoLoZxbBFKj1njokd7cTb8ZNPbw/lYsMnp8JCp4ldTTVLE/+P3U4p7aG1lbOIP33zMzOz6R+oF3w3xuNlT6QFRjvztbXzLohaj2fuTx3rVuraaztowQk9tLSqFiuRbMh5KHv3n9BFAuijyR9mfaPwJpEnKD0u0nKJmb2oIAkrwviHjYJR1xUbNsiZHbEQOpW2UMy0+5wvE++zGGCX+c3p9lRFqN00+KO1RNosmEOnmd/st12hZowjR+oayERlBKLUBXXxgO7h4/MIUHqYs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=27.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 32

Collectives Todos 0.1.0
Release Details
UpdatedOct. 7, 2026, 6:31 p.m.
Changelog

Added

  • Initial release of Collectives Todos: event-driven aggregation of markdown checkboxes across all pages of a Collectives into an auto-maintained Todos page, using Nextcloud core file events, a JSON cache for efficient updates and a CLI command (occ collectives_todos:init <collectives-id>) to build the page for existing collectives. Unit tests cover all core services and listeners.

  • App store release packaging: a GitHub Action (.github/workflows/release.yml) builds a store-conformant collectives_todos-vX.Y.Z.tar.gz (single top-level folder named after the app id, dev files excluded) on every v* tag, signs it with the app certificate and attaches archive and signature to the GitHub release. Tag and app version must match, and appinfo/info.xml is validated against the app store schema. With the APPSTORE_TOKEN secret set, the release is also published to the Nextcloud app store automatically.

  • Per-collective Enable/Disable button in the settings page's per-collective table. Disabling deletes the collective's Todos page (to trash) and stops generating or reverse-syncing it; the checkbox cache keeps being maintained, so enabling regenerates a consistent page immediately. Enabled is the default.
  • Global Enable/Disable button in the settings page's Defaults section: flips the instance-wide default. Enabling resets every collective to enabled - it clears all per-collective enabled opt-outs and regenerates their Todos pages, since opting into the app means wanting it everywhere; disabling single collectives stays the manual choice. Disabling globally deletes the Todos page of every collective that is not already opted out.
  • The per-collective Enable button clears the collective's enabled override instead of writing a sticky enabled value: a sticky override would shield the collective from the global default toggle forever, so a global disable reliably deletes every Todos page of collectives that were not explicitly disabled. The per-row Enable buttons render as inactive while the global default is disabled. Deleting the Todos page on disable now also removes its collectives_pages row (the delete is a hard delete on the appdata path, which the Collectives trash does not handle; the managed page's content is fully derived, so nothing of value is lost). The row is resolved untrashed and deleted directly: Collectives' PageMapper::deleteByFileId only removes rows that were moved to trash and silently ignored the hard-deleted page's row, leaking one on every disable cycle.
  • Todos page emoji config item (default none, per-collective override): the emoji is written to the Todos page's collectives_pages row like the emoji of any other Collectives page and rendered in the page tree. Validated with the Collectives app's own emoji validation; the settings page ships a curated emoji picker built with plain HTML/JS (no new dependencies), and any emoji can be pasted directly. The emoji is enforced on every Todos page regeneration and on settings save.
  • Admin settings page (Settings → Admin → Collectives Todos) with instance-wide defaults and per-collective overrides for three config items: the Todos page name (default Todos), the position of the Todos page in the Collectives page tree (always on top / always on bottom / alphabetically, default always on top) and the maximum number of cached checkboxes per collective (0 = unlimited, default 0). An empty override field uses the default. Invalid input is rejected without storing anything.
  • The tree position is enforced by writing the landing page's collectives_pages.subpage_order (the Collectives frontend sorts listed pages by array order, unlisted pages alphabetically), on every Todos page regeneration and on settings save. Manual ordering of the other pages is preserved; an already-open Collectives tab needs a reload to pick up the new order.
  • Changing the Todos page name renames the existing Todos page file; if a page with the new name already exists in a collective, the rename is skipped and reported on the settings page.
  • When the checkbox limit is reached, further checkboxes are not cached and the Todos page renders a *(list truncated by the settings limit)* note under the affected page's section.
  • Bidirectional checkbox sync: ticking or unticking a checkbox on the Todos page now also updates the checkbox on the source page it was aggregated from. Sections of the Todos page are resolved to source pages by the page file id in the heading link URL (title as fallback), and checkboxes are matched by text within a section. Only checked-state changes are synced; text edits on the Todos page are discarded with the next regeneration. If the cached line number of a checkbox has drifted, the checkbox is located by a unique text match, otherwise the change is skipped and a warning is logged.
  • Page headings on the Todos page are now links to the corresponding page, in the same URL format the Collectives app itself uses (/apps/collectives/<collectives>/<slug>-<pageId>, with a ?fileId= fallback for pages without a slug). If the URL cannot be resolved, the plain heading is kept.
  • Regenerating the Todos page while the Text editor had it open caused stale content to be shown and "Error saving the document" on manual saves: the Text app keeps per-file session state (etag, checksum, editor steps) that our background write invalidated. The Text document state of the Todos page is now reset on every regeneration, so open editors reload the fresh content. Unsaved manual edits to the Todos page are discarded by design.

Changed

  • Minimum Nextcloud version raised from 25 to 27 (the admin settings controller uses the AuthorizedAdminSetting attribute introduced in 27).
  • appinfo/info.xml brought into the form the Nextcloud app store validates: added the required <summary>, renamed <license> to <licence> and <depends> to <dependencies>, and dropped the <app>collectives</app> element (not part of the store or server schema, so it was never read; the Collectives dependency is enforced at runtime via IAppManager). No runtime behavior changes on servers already running the app.

Fixed

  • Reverse-syncing a tick made in the Collectives editor: the Text editor strips trailing whitespace on save, so a Todos page checkbox whose source line ends in spaces no longer matched the cached source text and the tick was silently dropped. Checkbox texts are now trimmed when parsed, and the reverse sync also tolerates untrimmed legacy cache entries.
  • Deleting the Todos page recreates it immediately. Collectives moves deleted pages to trash; the trash move is complete before the delete event fires and collectives paths are never locked, so no delayed recreation is needed.
  • The emoji of a freshly (re)created Todos page is now really written: inserting the missing collectives_pages row failed because its last_user_id column is NOT NULL. The app-created row now carries an empty user id (the frontend hides the "last edited" info for such pages), like untouched pages.
  • Deleting a page did not clean up the Todos page: the deleted page's section and checkboxes stayed listed. A Collectives page is a folder (the index page is its Readme.md, subpages are further files in it), and deleting it only emits a filesystem event for the folder itself - the markdown files inside vanish without their own events. A new listener for BeforeNodeDeletedEvent now collects the markdown pages under a folder before it is deleted, removes them from the cache and regenerates the Todos page. Plain .md file deletes keep their previous cleanup path. Restoring a page from the Collectives trash does not re-add it to the Todos page until the page is saved again or collectives_todos:init runs.
  • After ticking a checkbox on the Todos page, the source page briefly showed the tick and then reverted to its old content, with the editor refusing saves ("File changed in the meantime from outside"): the Text app keeps per-file session state (etag, checksum, steps) also for source pages, and the reverse sync's external write desynced it. The Text document state of a source page is now reset when the reverse sync writes it, so the page loads with the synced state and saves work. Unsaved keystrokes in an editing session open on that page at that moment are discarded (same contract the Todos page already has).
  • Checking a checkbox on the Todos page and saving broke the editing session: the reverse sync's regeneration of the Todos page ran nested inside the save and rewrote the page with a fresh "Last updated" timestamp, changing the etag under the open editor ("Error saving the document", conflict diff prompts). The Todos page is now only rewritten when its content actually changes (checkbox states, pages, titles); an update that would only refresh the "Last updated" timestamp leaves the file - and open editing sessions - untouched.
  • App bootstrap was silently skipped: info.xml declared the full namespace (OCA\CollectiveTodos), which Nextcloud prefixes with OCA\ itself, so no class of the app was autoloadable. The namespace is now CollectiveTodos.
  • Application.php moved from appinfo/ to lib/AppInfo/ (PSR-4 location that the bootstrap looks up).
  • CLI command is now registered via info.xml <commands> (previously the occ command never showed up).
  • Todos page is now written to the collectives root folder (previously it was written next to the edited page).
  • Page titles for Readme.md pages now use the page folder name (Collectives stores a page as folder with a Readme.md inside).
  • The listener skips its own Todos.md writes to avoid an infinite write-event loop, and logs failures instead of breaking saves.
  • collectives_todos:init now scans all pages of a collectives and accepts the collectives id as used by the Collectives app.
Licenses GNU Affero General Public License v3.0 or later
Certificate-----BEGIN CERTIFICATE-----
MIIEDDCCAvQCAhRDMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYxMDA3MTQ0NDAwWhcNMzcwMTEyMTQ0NDAwWjAcMRowGAYD
VQQDDBFjb2xsZWN0aXZlc190b2RvczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBALdQghokF+S+krqy/gMTVV+G7kw1vlwkBdVWroUoXrJk2U19znosxLwN
Hx/lOsSGjn9Ln0Y4/PF1aDCNITmzRKnUo3JcuC+dT6RPuG4XtGvcDPe2mBGmrY4J
wkqwWN5N1cic7crNXUqnI8cFUg+U8dAlkYah6Han5QkXf4ZuO/lH0cIRPsRaMw8j
5BDHAcdLvniw2Exa9y/atNe/7i1NDkgtpxN8yA2hoXUM4YUMcnlzUAiDx+fO556B
iMcSb8WJNblb/EpQjVKetk3h5N/oVopUR/Oo0st/tgWh/jiO7TMoCYx7ktFpQQJN
j9H7rLjsVswG3cn7xSTL3wfm0stFB1tSnTXP4zf0pp4d2QilI0GxgSdeAOPskFTN
pJGzSghDZ7/+tw4kZ5Y8gWj3aME9O10rXinBWaYV05cme569ZMXzbigLgNFTA/sT
0L/rHT3RZkrJ9cUIJCRm3yruqAQIml69onikKOl8XNM95GGAT/oF10ebhU0ITTNk
WafdGlqT2iPloHCvaRqR2BMPxR+My55IQ6+VI2nvgLztA524/Cud60PFhyrCXUZU
9O+c+5oz7MEZF0HPJfvg65PiXD/LvXi22Q17fY6fhlVQVYn8vQoMnoc0I6bkw8Al
PdK/d/kxNIpeWDNDh2gyEyBIiOxS7rOSyFmump1qliF8qf47PFEDAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBACh749xaQoj6Y4w24L7C4kiuYBMfo6JbAEqtijdZUQRC
XdV3pnonKnJ+mMV/803o41PxQ2xmRzvEaDWm2WKG2zzNssKwObcJg8TnpdPTcHjB
brU6pxkJXIHmgEjMHc5x6XJ3RR2uA9Wayaf5PFl+omfd52TzHQpMbGXM+u6TYckx
uEe0D9gnyg/Jw2ESnsOABk3n/Mkbv1IzUVs0Cxb3Je+dwJDMDBwW3DaSby/kKSjc
fSZBA9FsAJU77/yg7dnsFV3rhBXdJ5sQbVJNTH7gzcHhCiLffuEC0BmIPF+D5/80
yT7k9b7NAK212mhbleE90EiOwpPtKirDr+jKLL1zT1g=
-----END CERTIFICATE-----
SignaturetOPFL+uJkvXf4IYYON0b0T+MahYmk+Sga4NLaCPjTbv+nUklqi4DBjxNPKZEFLSHW+e7b6A2wR+uL+ugBCy9IWjJHfe35JVt1g/06s28dnXkm6nrod3AtPk4DgrZ2HHrDgPo86K4iIW2KIeW+IMbT1iKhYLQ9mGq3YcBPPtDgWzNjbpLid+PcGtxcPn8+E6GuOkrN6YbKuNkkdhL6UGuuqlHFYw7bVzDN41shzSaMYrUBmZUXXl/5ZnbnewEgiGZf6wsobuxQZ6uLqNjl9yKBvA85quuhBcnEHL0L+aFZn0xTuX8YFxvVmrx9iO8m24luzzSys35L/PL9W2l7m+w4MOX5zTIKkx+q8ExR8PtZBYOZqhaUPcZZwYLHFvJUlGL80LOPil51hn0qrsOUdJCd5yZqcoLoZxbBFKj1njokd7cTb8ZNPbw/lYsMnp8JCp4ldTTVLE/+P3U4p7aG1lbOIP33zMzOz6R+oF3w3xuNlT6QFRjvztbXzLohaj2fuTx3rVuraaztowQk9tLSqFiuRbMh5KHv3n9BFAuijyR9mfaPwJpEnKD0u0nKJmb2oIAkrwviHjYJR1xUbNsiZHbEQOpW2UMy0+5wvE++zGGCX+c3p9lRFqN00+KO1RNosmEOnmd/st12hZowjR+oayERlBKLUBXXxgO7h4/MIUHqYs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=27.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 31

Collectives Todos 0.1.0
Release Details
UpdatedOct. 7, 2026, 6:31 p.m.
Changelog

Added

  • Initial release of Collectives Todos: event-driven aggregation of markdown checkboxes across all pages of a Collectives into an auto-maintained Todos page, using Nextcloud core file events, a JSON cache for efficient updates and a CLI command (occ collectives_todos:init <collectives-id>) to build the page for existing collectives. Unit tests cover all core services and listeners.

  • App store release packaging: a GitHub Action (.github/workflows/release.yml) builds a store-conformant collectives_todos-vX.Y.Z.tar.gz (single top-level folder named after the app id, dev files excluded) on every v* tag, signs it with the app certificate and attaches archive and signature to the GitHub release. Tag and app version must match, and appinfo/info.xml is validated against the app store schema. With the APPSTORE_TOKEN secret set, the release is also published to the Nextcloud app store automatically.

  • Per-collective Enable/Disable button in the settings page's per-collective table. Disabling deletes the collective's Todos page (to trash) and stops generating or reverse-syncing it; the checkbox cache keeps being maintained, so enabling regenerates a consistent page immediately. Enabled is the default.
  • Global Enable/Disable button in the settings page's Defaults section: flips the instance-wide default. Enabling resets every collective to enabled - it clears all per-collective enabled opt-outs and regenerates their Todos pages, since opting into the app means wanting it everywhere; disabling single collectives stays the manual choice. Disabling globally deletes the Todos page of every collective that is not already opted out.
  • The per-collective Enable button clears the collective's enabled override instead of writing a sticky enabled value: a sticky override would shield the collective from the global default toggle forever, so a global disable reliably deletes every Todos page of collectives that were not explicitly disabled. The per-row Enable buttons render as inactive while the global default is disabled. Deleting the Todos page on disable now also removes its collectives_pages row (the delete is a hard delete on the appdata path, which the Collectives trash does not handle; the managed page's content is fully derived, so nothing of value is lost). The row is resolved untrashed and deleted directly: Collectives' PageMapper::deleteByFileId only removes rows that were moved to trash and silently ignored the hard-deleted page's row, leaking one on every disable cycle.
  • Todos page emoji config item (default none, per-collective override): the emoji is written to the Todos page's collectives_pages row like the emoji of any other Collectives page and rendered in the page tree. Validated with the Collectives app's own emoji validation; the settings page ships a curated emoji picker built with plain HTML/JS (no new dependencies), and any emoji can be pasted directly. The emoji is enforced on every Todos page regeneration and on settings save.
  • Admin settings page (Settings → Admin → Collectives Todos) with instance-wide defaults and per-collective overrides for three config items: the Todos page name (default Todos), the position of the Todos page in the Collectives page tree (always on top / always on bottom / alphabetically, default always on top) and the maximum number of cached checkboxes per collective (0 = unlimited, default 0). An empty override field uses the default. Invalid input is rejected without storing anything.
  • The tree position is enforced by writing the landing page's collectives_pages.subpage_order (the Collectives frontend sorts listed pages by array order, unlisted pages alphabetically), on every Todos page regeneration and on settings save. Manual ordering of the other pages is preserved; an already-open Collectives tab needs a reload to pick up the new order.
  • Changing the Todos page name renames the existing Todos page file; if a page with the new name already exists in a collective, the rename is skipped and reported on the settings page.
  • When the checkbox limit is reached, further checkboxes are not cached and the Todos page renders a *(list truncated by the settings limit)* note under the affected page's section.
  • Bidirectional checkbox sync: ticking or unticking a checkbox on the Todos page now also updates the checkbox on the source page it was aggregated from. Sections of the Todos page are resolved to source pages by the page file id in the heading link URL (title as fallback), and checkboxes are matched by text within a section. Only checked-state changes are synced; text edits on the Todos page are discarded with the next regeneration. If the cached line number of a checkbox has drifted, the checkbox is located by a unique text match, otherwise the change is skipped and a warning is logged.
  • Page headings on the Todos page are now links to the corresponding page, in the same URL format the Collectives app itself uses (/apps/collectives/<collectives>/<slug>-<pageId>, with a ?fileId= fallback for pages without a slug). If the URL cannot be resolved, the plain heading is kept.
  • Regenerating the Todos page while the Text editor had it open caused stale content to be shown and "Error saving the document" on manual saves: the Text app keeps per-file session state (etag, checksum, editor steps) that our background write invalidated. The Text document state of the Todos page is now reset on every regeneration, so open editors reload the fresh content. Unsaved manual edits to the Todos page are discarded by design.

Changed

  • Minimum Nextcloud version raised from 25 to 27 (the admin settings controller uses the AuthorizedAdminSetting attribute introduced in 27).
  • appinfo/info.xml brought into the form the Nextcloud app store validates: added the required <summary>, renamed <license> to <licence> and <depends> to <dependencies>, and dropped the <app>collectives</app> element (not part of the store or server schema, so it was never read; the Collectives dependency is enforced at runtime via IAppManager). No runtime behavior changes on servers already running the app.

Fixed

  • Reverse-syncing a tick made in the Collectives editor: the Text editor strips trailing whitespace on save, so a Todos page checkbox whose source line ends in spaces no longer matched the cached source text and the tick was silently dropped. Checkbox texts are now trimmed when parsed, and the reverse sync also tolerates untrimmed legacy cache entries.
  • Deleting the Todos page recreates it immediately. Collectives moves deleted pages to trash; the trash move is complete before the delete event fires and collectives paths are never locked, so no delayed recreation is needed.
  • The emoji of a freshly (re)created Todos page is now really written: inserting the missing collectives_pages row failed because its last_user_id column is NOT NULL. The app-created row now carries an empty user id (the frontend hides the "last edited" info for such pages), like untouched pages.
  • Deleting a page did not clean up the Todos page: the deleted page's section and checkboxes stayed listed. A Collectives page is a folder (the index page is its Readme.md, subpages are further files in it), and deleting it only emits a filesystem event for the folder itself - the markdown files inside vanish without their own events. A new listener for BeforeNodeDeletedEvent now collects the markdown pages under a folder before it is deleted, removes them from the cache and regenerates the Todos page. Plain .md file deletes keep their previous cleanup path. Restoring a page from the Collectives trash does not re-add it to the Todos page until the page is saved again or collectives_todos:init runs.
  • After ticking a checkbox on the Todos page, the source page briefly showed the tick and then reverted to its old content, with the editor refusing saves ("File changed in the meantime from outside"): the Text app keeps per-file session state (etag, checksum, steps) also for source pages, and the reverse sync's external write desynced it. The Text document state of a source page is now reset when the reverse sync writes it, so the page loads with the synced state and saves work. Unsaved keystrokes in an editing session open on that page at that moment are discarded (same contract the Todos page already has).
  • Checking a checkbox on the Todos page and saving broke the editing session: the reverse sync's regeneration of the Todos page ran nested inside the save and rewrote the page with a fresh "Last updated" timestamp, changing the etag under the open editor ("Error saving the document", conflict diff prompts). The Todos page is now only rewritten when its content actually changes (checkbox states, pages, titles); an update that would only refresh the "Last updated" timestamp leaves the file - and open editing sessions - untouched.
  • App bootstrap was silently skipped: info.xml declared the full namespace (OCA\CollectiveTodos), which Nextcloud prefixes with OCA\ itself, so no class of the app was autoloadable. The namespace is now CollectiveTodos.
  • Application.php moved from appinfo/ to lib/AppInfo/ (PSR-4 location that the bootstrap looks up).
  • CLI command is now registered via info.xml <commands> (previously the occ command never showed up).
  • Todos page is now written to the collectives root folder (previously it was written next to the edited page).
  • Page titles for Readme.md pages now use the page folder name (Collectives stores a page as folder with a Readme.md inside).
  • The listener skips its own Todos.md writes to avoid an infinite write-event loop, and logs failures instead of breaking saves.
  • collectives_todos:init now scans all pages of a collectives and accepts the collectives id as used by the Collectives app.
Licenses GNU Affero General Public License v3.0 or later
Certificate-----BEGIN CERTIFICATE-----
MIIEDDCCAvQCAhRDMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYxMDA3MTQ0NDAwWhcNMzcwMTEyMTQ0NDAwWjAcMRowGAYD
VQQDDBFjb2xsZWN0aXZlc190b2RvczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBALdQghokF+S+krqy/gMTVV+G7kw1vlwkBdVWroUoXrJk2U19znosxLwN
Hx/lOsSGjn9Ln0Y4/PF1aDCNITmzRKnUo3JcuC+dT6RPuG4XtGvcDPe2mBGmrY4J
wkqwWN5N1cic7crNXUqnI8cFUg+U8dAlkYah6Han5QkXf4ZuO/lH0cIRPsRaMw8j
5BDHAcdLvniw2Exa9y/atNe/7i1NDkgtpxN8yA2hoXUM4YUMcnlzUAiDx+fO556B
iMcSb8WJNblb/EpQjVKetk3h5N/oVopUR/Oo0st/tgWh/jiO7TMoCYx7ktFpQQJN
j9H7rLjsVswG3cn7xSTL3wfm0stFB1tSnTXP4zf0pp4d2QilI0GxgSdeAOPskFTN
pJGzSghDZ7/+tw4kZ5Y8gWj3aME9O10rXinBWaYV05cme569ZMXzbigLgNFTA/sT
0L/rHT3RZkrJ9cUIJCRm3yruqAQIml69onikKOl8XNM95GGAT/oF10ebhU0ITTNk
WafdGlqT2iPloHCvaRqR2BMPxR+My55IQ6+VI2nvgLztA524/Cud60PFhyrCXUZU
9O+c+5oz7MEZF0HPJfvg65PiXD/LvXi22Q17fY6fhlVQVYn8vQoMnoc0I6bkw8Al
PdK/d/kxNIpeWDNDh2gyEyBIiOxS7rOSyFmump1qliF8qf47PFEDAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBACh749xaQoj6Y4w24L7C4kiuYBMfo6JbAEqtijdZUQRC
XdV3pnonKnJ+mMV/803o41PxQ2xmRzvEaDWm2WKG2zzNssKwObcJg8TnpdPTcHjB
brU6pxkJXIHmgEjMHc5x6XJ3RR2uA9Wayaf5PFl+omfd52TzHQpMbGXM+u6TYckx
uEe0D9gnyg/Jw2ESnsOABk3n/Mkbv1IzUVs0Cxb3Je+dwJDMDBwW3DaSby/kKSjc
fSZBA9FsAJU77/yg7dnsFV3rhBXdJ5sQbVJNTH7gzcHhCiLffuEC0BmIPF+D5/80
yT7k9b7NAK212mhbleE90EiOwpPtKirDr+jKLL1zT1g=
-----END CERTIFICATE-----
SignaturetOPFL+uJkvXf4IYYON0b0T+MahYmk+Sga4NLaCPjTbv+nUklqi4DBjxNPKZEFLSHW+e7b6A2wR+uL+ugBCy9IWjJHfe35JVt1g/06s28dnXkm6nrod3AtPk4DgrZ2HHrDgPo86K4iIW2KIeW+IMbT1iKhYLQ9mGq3YcBPPtDgWzNjbpLid+PcGtxcPn8+E6GuOkrN6YbKuNkkdhL6UGuuqlHFYw7bVzDN41shzSaMYrUBmZUXXl/5ZnbnewEgiGZf6wsobuxQZ6uLqNjl9yKBvA85quuhBcnEHL0L+aFZn0xTuX8YFxvVmrx9iO8m24luzzSys35L/PL9W2l7m+w4MOX5zTIKkx+q8ExR8PtZBYOZqhaUPcZZwYLHFvJUlGL80LOPil51hn0qrsOUdJCd5yZqcoLoZxbBFKj1njokd7cTb8ZNPbw/lYsMnp8JCp4ldTTVLE/+P3U4p7aG1lbOIP33zMzOz6R+oF3w3xuNlT6QFRjvztbXzLohaj2fuTx3rVuraaztowQk9tLSqFiuRbMh5KHv3n9BFAuijyR9mfaPwJpEnKD0u0nKJmb2oIAkrwviHjYJR1xUbNsiZHbEQOpW2UMy0+5wvE++zGGCX+c3p9lRFqN00+KO1RNosmEOnmd/st12hZowjR+oayERlBKLUBXXxgO7h4/MIUHqYs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=27.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 30

Collectives Todos 0.1.0
Release Details
UpdatedOct. 7, 2026, 6:31 p.m.
Changelog

Added

  • Initial release of Collectives Todos: event-driven aggregation of markdown checkboxes across all pages of a Collectives into an auto-maintained Todos page, using Nextcloud core file events, a JSON cache for efficient updates and a CLI command (occ collectives_todos:init <collectives-id>) to build the page for existing collectives. Unit tests cover all core services and listeners.

  • App store release packaging: a GitHub Action (.github/workflows/release.yml) builds a store-conformant collectives_todos-vX.Y.Z.tar.gz (single top-level folder named after the app id, dev files excluded) on every v* tag, signs it with the app certificate and attaches archive and signature to the GitHub release. Tag and app version must match, and appinfo/info.xml is validated against the app store schema. With the APPSTORE_TOKEN secret set, the release is also published to the Nextcloud app store automatically.

  • Per-collective Enable/Disable button in the settings page's per-collective table. Disabling deletes the collective's Todos page (to trash) and stops generating or reverse-syncing it; the checkbox cache keeps being maintained, so enabling regenerates a consistent page immediately. Enabled is the default.
  • Global Enable/Disable button in the settings page's Defaults section: flips the instance-wide default. Enabling resets every collective to enabled - it clears all per-collective enabled opt-outs and regenerates their Todos pages, since opting into the app means wanting it everywhere; disabling single collectives stays the manual choice. Disabling globally deletes the Todos page of every collective that is not already opted out.
  • The per-collective Enable button clears the collective's enabled override instead of writing a sticky enabled value: a sticky override would shield the collective from the global default toggle forever, so a global disable reliably deletes every Todos page of collectives that were not explicitly disabled. The per-row Enable buttons render as inactive while the global default is disabled. Deleting the Todos page on disable now also removes its collectives_pages row (the delete is a hard delete on the appdata path, which the Collectives trash does not handle; the managed page's content is fully derived, so nothing of value is lost). The row is resolved untrashed and deleted directly: Collectives' PageMapper::deleteByFileId only removes rows that were moved to trash and silently ignored the hard-deleted page's row, leaking one on every disable cycle.
  • Todos page emoji config item (default none, per-collective override): the emoji is written to the Todos page's collectives_pages row like the emoji of any other Collectives page and rendered in the page tree. Validated with the Collectives app's own emoji validation; the settings page ships a curated emoji picker built with plain HTML/JS (no new dependencies), and any emoji can be pasted directly. The emoji is enforced on every Todos page regeneration and on settings save.
  • Admin settings page (Settings → Admin → Collectives Todos) with instance-wide defaults and per-collective overrides for three config items: the Todos page name (default Todos), the position of the Todos page in the Collectives page tree (always on top / always on bottom / alphabetically, default always on top) and the maximum number of cached checkboxes per collective (0 = unlimited, default 0). An empty override field uses the default. Invalid input is rejected without storing anything.
  • The tree position is enforced by writing the landing page's collectives_pages.subpage_order (the Collectives frontend sorts listed pages by array order, unlisted pages alphabetically), on every Todos page regeneration and on settings save. Manual ordering of the other pages is preserved; an already-open Collectives tab needs a reload to pick up the new order.
  • Changing the Todos page name renames the existing Todos page file; if a page with the new name already exists in a collective, the rename is skipped and reported on the settings page.
  • When the checkbox limit is reached, further checkboxes are not cached and the Todos page renders a *(list truncated by the settings limit)* note under the affected page's section.
  • Bidirectional checkbox sync: ticking or unticking a checkbox on the Todos page now also updates the checkbox on the source page it was aggregated from. Sections of the Todos page are resolved to source pages by the page file id in the heading link URL (title as fallback), and checkboxes are matched by text within a section. Only checked-state changes are synced; text edits on the Todos page are discarded with the next regeneration. If the cached line number of a checkbox has drifted, the checkbox is located by a unique text match, otherwise the change is skipped and a warning is logged.
  • Page headings on the Todos page are now links to the corresponding page, in the same URL format the Collectives app itself uses (/apps/collectives/<collectives>/<slug>-<pageId>, with a ?fileId= fallback for pages without a slug). If the URL cannot be resolved, the plain heading is kept.
  • Regenerating the Todos page while the Text editor had it open caused stale content to be shown and "Error saving the document" on manual saves: the Text app keeps per-file session state (etag, checksum, editor steps) that our background write invalidated. The Text document state of the Todos page is now reset on every regeneration, so open editors reload the fresh content. Unsaved manual edits to the Todos page are discarded by design.

Changed

  • Minimum Nextcloud version raised from 25 to 27 (the admin settings controller uses the AuthorizedAdminSetting attribute introduced in 27).
  • appinfo/info.xml brought into the form the Nextcloud app store validates: added the required <summary>, renamed <license> to <licence> and <depends> to <dependencies>, and dropped the <app>collectives</app> element (not part of the store or server schema, so it was never read; the Collectives dependency is enforced at runtime via IAppManager). No runtime behavior changes on servers already running the app.

Fixed

  • Reverse-syncing a tick made in the Collectives editor: the Text editor strips trailing whitespace on save, so a Todos page checkbox whose source line ends in spaces no longer matched the cached source text and the tick was silently dropped. Checkbox texts are now trimmed when parsed, and the reverse sync also tolerates untrimmed legacy cache entries.
  • Deleting the Todos page recreates it immediately. Collectives moves deleted pages to trash; the trash move is complete before the delete event fires and collectives paths are never locked, so no delayed recreation is needed.
  • The emoji of a freshly (re)created Todos page is now really written: inserting the missing collectives_pages row failed because its last_user_id column is NOT NULL. The app-created row now carries an empty user id (the frontend hides the "last edited" info for such pages), like untouched pages.
  • Deleting a page did not clean up the Todos page: the deleted page's section and checkboxes stayed listed. A Collectives page is a folder (the index page is its Readme.md, subpages are further files in it), and deleting it only emits a filesystem event for the folder itself - the markdown files inside vanish without their own events. A new listener for BeforeNodeDeletedEvent now collects the markdown pages under a folder before it is deleted, removes them from the cache and regenerates the Todos page. Plain .md file deletes keep their previous cleanup path. Restoring a page from the Collectives trash does not re-add it to the Todos page until the page is saved again or collectives_todos:init runs.
  • After ticking a checkbox on the Todos page, the source page briefly showed the tick and then reverted to its old content, with the editor refusing saves ("File changed in the meantime from outside"): the Text app keeps per-file session state (etag, checksum, steps) also for source pages, and the reverse sync's external write desynced it. The Text document state of a source page is now reset when the reverse sync writes it, so the page loads with the synced state and saves work. Unsaved keystrokes in an editing session open on that page at that moment are discarded (same contract the Todos page already has).
  • Checking a checkbox on the Todos page and saving broke the editing session: the reverse sync's regeneration of the Todos page ran nested inside the save and rewrote the page with a fresh "Last updated" timestamp, changing the etag under the open editor ("Error saving the document", conflict diff prompts). The Todos page is now only rewritten when its content actually changes (checkbox states, pages, titles); an update that would only refresh the "Last updated" timestamp leaves the file - and open editing sessions - untouched.
  • App bootstrap was silently skipped: info.xml declared the full namespace (OCA\CollectiveTodos), which Nextcloud prefixes with OCA\ itself, so no class of the app was autoloadable. The namespace is now CollectiveTodos.
  • Application.php moved from appinfo/ to lib/AppInfo/ (PSR-4 location that the bootstrap looks up).
  • CLI command is now registered via info.xml <commands> (previously the occ command never showed up).
  • Todos page is now written to the collectives root folder (previously it was written next to the edited page).
  • Page titles for Readme.md pages now use the page folder name (Collectives stores a page as folder with a Readme.md inside).
  • The listener skips its own Todos.md writes to avoid an infinite write-event loop, and logs failures instead of breaking saves.
  • collectives_todos:init now scans all pages of a collectives and accepts the collectives id as used by the Collectives app.
Licenses GNU Affero General Public License v3.0 or later
Certificate-----BEGIN CERTIFICATE-----
MIIEDDCCAvQCAhRDMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYxMDA3MTQ0NDAwWhcNMzcwMTEyMTQ0NDAwWjAcMRowGAYD
VQQDDBFjb2xsZWN0aXZlc190b2RvczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBALdQghokF+S+krqy/gMTVV+G7kw1vlwkBdVWroUoXrJk2U19znosxLwN
Hx/lOsSGjn9Ln0Y4/PF1aDCNITmzRKnUo3JcuC+dT6RPuG4XtGvcDPe2mBGmrY4J
wkqwWN5N1cic7crNXUqnI8cFUg+U8dAlkYah6Han5QkXf4ZuO/lH0cIRPsRaMw8j
5BDHAcdLvniw2Exa9y/atNe/7i1NDkgtpxN8yA2hoXUM4YUMcnlzUAiDx+fO556B
iMcSb8WJNblb/EpQjVKetk3h5N/oVopUR/Oo0st/tgWh/jiO7TMoCYx7ktFpQQJN
j9H7rLjsVswG3cn7xSTL3wfm0stFB1tSnTXP4zf0pp4d2QilI0GxgSdeAOPskFTN
pJGzSghDZ7/+tw4kZ5Y8gWj3aME9O10rXinBWaYV05cme569ZMXzbigLgNFTA/sT
0L/rHT3RZkrJ9cUIJCRm3yruqAQIml69onikKOl8XNM95GGAT/oF10ebhU0ITTNk
WafdGlqT2iPloHCvaRqR2BMPxR+My55IQ6+VI2nvgLztA524/Cud60PFhyrCXUZU
9O+c+5oz7MEZF0HPJfvg65PiXD/LvXi22Q17fY6fhlVQVYn8vQoMnoc0I6bkw8Al
PdK/d/kxNIpeWDNDh2gyEyBIiOxS7rOSyFmump1qliF8qf47PFEDAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBACh749xaQoj6Y4w24L7C4kiuYBMfo6JbAEqtijdZUQRC
XdV3pnonKnJ+mMV/803o41PxQ2xmRzvEaDWm2WKG2zzNssKwObcJg8TnpdPTcHjB
brU6pxkJXIHmgEjMHc5x6XJ3RR2uA9Wayaf5PFl+omfd52TzHQpMbGXM+u6TYckx
uEe0D9gnyg/Jw2ESnsOABk3n/Mkbv1IzUVs0Cxb3Je+dwJDMDBwW3DaSby/kKSjc
fSZBA9FsAJU77/yg7dnsFV3rhBXdJ5sQbVJNTH7gzcHhCiLffuEC0BmIPF+D5/80
yT7k9b7NAK212mhbleE90EiOwpPtKirDr+jKLL1zT1g=
-----END CERTIFICATE-----
SignaturetOPFL+uJkvXf4IYYON0b0T+MahYmk+Sga4NLaCPjTbv+nUklqi4DBjxNPKZEFLSHW+e7b6A2wR+uL+ugBCy9IWjJHfe35JVt1g/06s28dnXkm6nrod3AtPk4DgrZ2HHrDgPo86K4iIW2KIeW+IMbT1iKhYLQ9mGq3YcBPPtDgWzNjbpLid+PcGtxcPn8+E6GuOkrN6YbKuNkkdhL6UGuuqlHFYw7bVzDN41shzSaMYrUBmZUXXl/5ZnbnewEgiGZf6wsobuxQZ6uLqNjl9yKBvA85quuhBcnEHL0L+aFZn0xTuX8YFxvVmrx9iO8m24luzzSys35L/PL9W2l7m+w4MOX5zTIKkx+q8ExR8PtZBYOZqhaUPcZZwYLHFvJUlGL80LOPil51hn0qrsOUdJCd5yZqcoLoZxbBFKj1njokd7cTb8ZNPbw/lYsMnp8JCp4ldTTVLE/+P3U4p7aG1lbOIP33zMzOz6R+oF3w3xuNlT6QFRjvztbXzLohaj2fuTx3rVuraaztowQk9tLSqFiuRbMh5KHv3n9BFAuijyR9mfaPwJpEnKD0u0nKJmb2oIAkrwviHjYJR1xUbNsiZHbEQOpW2UMy0+5wvE++zGGCX+c3p9lRFqN00+KO1RNosmEOnmd/st12hZowjR+oayERlBKLUBXXxgO7h4/MIUHqYs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=27.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 29

Collectives Todos 0.1.0
Release Details
UpdatedOct. 7, 2026, 6:31 p.m.
Changelog

Added

  • Initial release of Collectives Todos: event-driven aggregation of markdown checkboxes across all pages of a Collectives into an auto-maintained Todos page, using Nextcloud core file events, a JSON cache for efficient updates and a CLI command (occ collectives_todos:init <collectives-id>) to build the page for existing collectives. Unit tests cover all core services and listeners.

  • App store release packaging: a GitHub Action (.github/workflows/release.yml) builds a store-conformant collectives_todos-vX.Y.Z.tar.gz (single top-level folder named after the app id, dev files excluded) on every v* tag, signs it with the app certificate and attaches archive and signature to the GitHub release. Tag and app version must match, and appinfo/info.xml is validated against the app store schema. With the APPSTORE_TOKEN secret set, the release is also published to the Nextcloud app store automatically.

  • Per-collective Enable/Disable button in the settings page's per-collective table. Disabling deletes the collective's Todos page (to trash) and stops generating or reverse-syncing it; the checkbox cache keeps being maintained, so enabling regenerates a consistent page immediately. Enabled is the default.
  • Global Enable/Disable button in the settings page's Defaults section: flips the instance-wide default. Enabling resets every collective to enabled - it clears all per-collective enabled opt-outs and regenerates their Todos pages, since opting into the app means wanting it everywhere; disabling single collectives stays the manual choice. Disabling globally deletes the Todos page of every collective that is not already opted out.
  • The per-collective Enable button clears the collective's enabled override instead of writing a sticky enabled value: a sticky override would shield the collective from the global default toggle forever, so a global disable reliably deletes every Todos page of collectives that were not explicitly disabled. The per-row Enable buttons render as inactive while the global default is disabled. Deleting the Todos page on disable now also removes its collectives_pages row (the delete is a hard delete on the appdata path, which the Collectives trash does not handle; the managed page's content is fully derived, so nothing of value is lost). The row is resolved untrashed and deleted directly: Collectives' PageMapper::deleteByFileId only removes rows that were moved to trash and silently ignored the hard-deleted page's row, leaking one on every disable cycle.
  • Todos page emoji config item (default none, per-collective override): the emoji is written to the Todos page's collectives_pages row like the emoji of any other Collectives page and rendered in the page tree. Validated with the Collectives app's own emoji validation; the settings page ships a curated emoji picker built with plain HTML/JS (no new dependencies), and any emoji can be pasted directly. The emoji is enforced on every Todos page regeneration and on settings save.
  • Admin settings page (Settings → Admin → Collectives Todos) with instance-wide defaults and per-collective overrides for three config items: the Todos page name (default Todos), the position of the Todos page in the Collectives page tree (always on top / always on bottom / alphabetically, default always on top) and the maximum number of cached checkboxes per collective (0 = unlimited, default 0). An empty override field uses the default. Invalid input is rejected without storing anything.
  • The tree position is enforced by writing the landing page's collectives_pages.subpage_order (the Collectives frontend sorts listed pages by array order, unlisted pages alphabetically), on every Todos page regeneration and on settings save. Manual ordering of the other pages is preserved; an already-open Collectives tab needs a reload to pick up the new order.
  • Changing the Todos page name renames the existing Todos page file; if a page with the new name already exists in a collective, the rename is skipped and reported on the settings page.
  • When the checkbox limit is reached, further checkboxes are not cached and the Todos page renders a *(list truncated by the settings limit)* note under the affected page's section.
  • Bidirectional checkbox sync: ticking or unticking a checkbox on the Todos page now also updates the checkbox on the source page it was aggregated from. Sections of the Todos page are resolved to source pages by the page file id in the heading link URL (title as fallback), and checkboxes are matched by text within a section. Only checked-state changes are synced; text edits on the Todos page are discarded with the next regeneration. If the cached line number of a checkbox has drifted, the checkbox is located by a unique text match, otherwise the change is skipped and a warning is logged.
  • Page headings on the Todos page are now links to the corresponding page, in the same URL format the Collectives app itself uses (/apps/collectives/<collectives>/<slug>-<pageId>, with a ?fileId= fallback for pages without a slug). If the URL cannot be resolved, the plain heading is kept.
  • Regenerating the Todos page while the Text editor had it open caused stale content to be shown and "Error saving the document" on manual saves: the Text app keeps per-file session state (etag, checksum, editor steps) that our background write invalidated. The Text document state of the Todos page is now reset on every regeneration, so open editors reload the fresh content. Unsaved manual edits to the Todos page are discarded by design.

Changed

  • Minimum Nextcloud version raised from 25 to 27 (the admin settings controller uses the AuthorizedAdminSetting attribute introduced in 27).
  • appinfo/info.xml brought into the form the Nextcloud app store validates: added the required <summary>, renamed <license> to <licence> and <depends> to <dependencies>, and dropped the <app>collectives</app> element (not part of the store or server schema, so it was never read; the Collectives dependency is enforced at runtime via IAppManager). No runtime behavior changes on servers already running the app.

Fixed

  • Reverse-syncing a tick made in the Collectives editor: the Text editor strips trailing whitespace on save, so a Todos page checkbox whose source line ends in spaces no longer matched the cached source text and the tick was silently dropped. Checkbox texts are now trimmed when parsed, and the reverse sync also tolerates untrimmed legacy cache entries.
  • Deleting the Todos page recreates it immediately. Collectives moves deleted pages to trash; the trash move is complete before the delete event fires and collectives paths are never locked, so no delayed recreation is needed.
  • The emoji of a freshly (re)created Todos page is now really written: inserting the missing collectives_pages row failed because its last_user_id column is NOT NULL. The app-created row now carries an empty user id (the frontend hides the "last edited" info for such pages), like untouched pages.
  • Deleting a page did not clean up the Todos page: the deleted page's section and checkboxes stayed listed. A Collectives page is a folder (the index page is its Readme.md, subpages are further files in it), and deleting it only emits a filesystem event for the folder itself - the markdown files inside vanish without their own events. A new listener for BeforeNodeDeletedEvent now collects the markdown pages under a folder before it is deleted, removes them from the cache and regenerates the Todos page. Plain .md file deletes keep their previous cleanup path. Restoring a page from the Collectives trash does not re-add it to the Todos page until the page is saved again or collectives_todos:init runs.
  • After ticking a checkbox on the Todos page, the source page briefly showed the tick and then reverted to its old content, with the editor refusing saves ("File changed in the meantime from outside"): the Text app keeps per-file session state (etag, checksum, steps) also for source pages, and the reverse sync's external write desynced it. The Text document state of a source page is now reset when the reverse sync writes it, so the page loads with the synced state and saves work. Unsaved keystrokes in an editing session open on that page at that moment are discarded (same contract the Todos page already has).
  • Checking a checkbox on the Todos page and saving broke the editing session: the reverse sync's regeneration of the Todos page ran nested inside the save and rewrote the page with a fresh "Last updated" timestamp, changing the etag under the open editor ("Error saving the document", conflict diff prompts). The Todos page is now only rewritten when its content actually changes (checkbox states, pages, titles); an update that would only refresh the "Last updated" timestamp leaves the file - and open editing sessions - untouched.
  • App bootstrap was silently skipped: info.xml declared the full namespace (OCA\CollectiveTodos), which Nextcloud prefixes with OCA\ itself, so no class of the app was autoloadable. The namespace is now CollectiveTodos.
  • Application.php moved from appinfo/ to lib/AppInfo/ (PSR-4 location that the bootstrap looks up).
  • CLI command is now registered via info.xml <commands> (previously the occ command never showed up).
  • Todos page is now written to the collectives root folder (previously it was written next to the edited page).
  • Page titles for Readme.md pages now use the page folder name (Collectives stores a page as folder with a Readme.md inside).
  • The listener skips its own Todos.md writes to avoid an infinite write-event loop, and logs failures instead of breaking saves.
  • collectives_todos:init now scans all pages of a collectives and accepts the collectives id as used by the Collectives app.
Licenses GNU Affero General Public License v3.0 or later
Certificate-----BEGIN CERTIFICATE-----
MIIEDDCCAvQCAhRDMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYxMDA3MTQ0NDAwWhcNMzcwMTEyMTQ0NDAwWjAcMRowGAYD
VQQDDBFjb2xsZWN0aXZlc190b2RvczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBALdQghokF+S+krqy/gMTVV+G7kw1vlwkBdVWroUoXrJk2U19znosxLwN
Hx/lOsSGjn9Ln0Y4/PF1aDCNITmzRKnUo3JcuC+dT6RPuG4XtGvcDPe2mBGmrY4J
wkqwWN5N1cic7crNXUqnI8cFUg+U8dAlkYah6Han5QkXf4ZuO/lH0cIRPsRaMw8j
5BDHAcdLvniw2Exa9y/atNe/7i1NDkgtpxN8yA2hoXUM4YUMcnlzUAiDx+fO556B
iMcSb8WJNblb/EpQjVKetk3h5N/oVopUR/Oo0st/tgWh/jiO7TMoCYx7ktFpQQJN
j9H7rLjsVswG3cn7xSTL3wfm0stFB1tSnTXP4zf0pp4d2QilI0GxgSdeAOPskFTN
pJGzSghDZ7/+tw4kZ5Y8gWj3aME9O10rXinBWaYV05cme569ZMXzbigLgNFTA/sT
0L/rHT3RZkrJ9cUIJCRm3yruqAQIml69onikKOl8XNM95GGAT/oF10ebhU0ITTNk
WafdGlqT2iPloHCvaRqR2BMPxR+My55IQ6+VI2nvgLztA524/Cud60PFhyrCXUZU
9O+c+5oz7MEZF0HPJfvg65PiXD/LvXi22Q17fY6fhlVQVYn8vQoMnoc0I6bkw8Al
PdK/d/kxNIpeWDNDh2gyEyBIiOxS7rOSyFmump1qliF8qf47PFEDAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBACh749xaQoj6Y4w24L7C4kiuYBMfo6JbAEqtijdZUQRC
XdV3pnonKnJ+mMV/803o41PxQ2xmRzvEaDWm2WKG2zzNssKwObcJg8TnpdPTcHjB
brU6pxkJXIHmgEjMHc5x6XJ3RR2uA9Wayaf5PFl+omfd52TzHQpMbGXM+u6TYckx
uEe0D9gnyg/Jw2ESnsOABk3n/Mkbv1IzUVs0Cxb3Je+dwJDMDBwW3DaSby/kKSjc
fSZBA9FsAJU77/yg7dnsFV3rhBXdJ5sQbVJNTH7gzcHhCiLffuEC0BmIPF+D5/80
yT7k9b7NAK212mhbleE90EiOwpPtKirDr+jKLL1zT1g=
-----END CERTIFICATE-----
SignaturetOPFL+uJkvXf4IYYON0b0T+MahYmk+Sga4NLaCPjTbv+nUklqi4DBjxNPKZEFLSHW+e7b6A2wR+uL+ugBCy9IWjJHfe35JVt1g/06s28dnXkm6nrod3AtPk4DgrZ2HHrDgPo86K4iIW2KIeW+IMbT1iKhYLQ9mGq3YcBPPtDgWzNjbpLid+PcGtxcPn8+E6GuOkrN6YbKuNkkdhL6UGuuqlHFYw7bVzDN41shzSaMYrUBmZUXXl/5ZnbnewEgiGZf6wsobuxQZ6uLqNjl9yKBvA85quuhBcnEHL0L+aFZn0xTuX8YFxvVmrx9iO8m24luzzSys35L/PL9W2l7m+w4MOX5zTIKkx+q8ExR8PtZBYOZqhaUPcZZwYLHFvJUlGL80LOPil51hn0qrsOUdJCd5yZqcoLoZxbBFKj1njokd7cTb8ZNPbw/lYsMnp8JCp4ldTTVLE/+P3U4p7aG1lbOIP33zMzOz6R+oF3w3xuNlT6QFRjvztbXzLohaj2fuTx3rVuraaztowQk9tLSqFiuRbMh5KHv3n9BFAuijyR9mfaPwJpEnKD0u0nKJmb2oIAkrwviHjYJR1xUbNsiZHbEQOpW2UMy0+5wvE++zGGCX+c3p9lRFqN00+KO1RNosmEOnmd/st12hZowjR+oayERlBKLUBXXxgO7h4/MIUHqYs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=27.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 28

Collectives Todos 0.1.0
Release Details
UpdatedOct. 7, 2026, 6:31 p.m.
Changelog

Added

  • Initial release of Collectives Todos: event-driven aggregation of markdown checkboxes across all pages of a Collectives into an auto-maintained Todos page, using Nextcloud core file events, a JSON cache for efficient updates and a CLI command (occ collectives_todos:init <collectives-id>) to build the page for existing collectives. Unit tests cover all core services and listeners.

  • App store release packaging: a GitHub Action (.github/workflows/release.yml) builds a store-conformant collectives_todos-vX.Y.Z.tar.gz (single top-level folder named after the app id, dev files excluded) on every v* tag, signs it with the app certificate and attaches archive and signature to the GitHub release. Tag and app version must match, and appinfo/info.xml is validated against the app store schema. With the APPSTORE_TOKEN secret set, the release is also published to the Nextcloud app store automatically.

  • Per-collective Enable/Disable button in the settings page's per-collective table. Disabling deletes the collective's Todos page (to trash) and stops generating or reverse-syncing it; the checkbox cache keeps being maintained, so enabling regenerates a consistent page immediately. Enabled is the default.
  • Global Enable/Disable button in the settings page's Defaults section: flips the instance-wide default. Enabling resets every collective to enabled - it clears all per-collective enabled opt-outs and regenerates their Todos pages, since opting into the app means wanting it everywhere; disabling single collectives stays the manual choice. Disabling globally deletes the Todos page of every collective that is not already opted out.
  • The per-collective Enable button clears the collective's enabled override instead of writing a sticky enabled value: a sticky override would shield the collective from the global default toggle forever, so a global disable reliably deletes every Todos page of collectives that were not explicitly disabled. The per-row Enable buttons render as inactive while the global default is disabled. Deleting the Todos page on disable now also removes its collectives_pages row (the delete is a hard delete on the appdata path, which the Collectives trash does not handle; the managed page's content is fully derived, so nothing of value is lost). The row is resolved untrashed and deleted directly: Collectives' PageMapper::deleteByFileId only removes rows that were moved to trash and silently ignored the hard-deleted page's row, leaking one on every disable cycle.
  • Todos page emoji config item (default none, per-collective override): the emoji is written to the Todos page's collectives_pages row like the emoji of any other Collectives page and rendered in the page tree. Validated with the Collectives app's own emoji validation; the settings page ships a curated emoji picker built with plain HTML/JS (no new dependencies), and any emoji can be pasted directly. The emoji is enforced on every Todos page regeneration and on settings save.
  • Admin settings page (Settings → Admin → Collectives Todos) with instance-wide defaults and per-collective overrides for three config items: the Todos page name (default Todos), the position of the Todos page in the Collectives page tree (always on top / always on bottom / alphabetically, default always on top) and the maximum number of cached checkboxes per collective (0 = unlimited, default 0). An empty override field uses the default. Invalid input is rejected without storing anything.
  • The tree position is enforced by writing the landing page's collectives_pages.subpage_order (the Collectives frontend sorts listed pages by array order, unlisted pages alphabetically), on every Todos page regeneration and on settings save. Manual ordering of the other pages is preserved; an already-open Collectives tab needs a reload to pick up the new order.
  • Changing the Todos page name renames the existing Todos page file; if a page with the new name already exists in a collective, the rename is skipped and reported on the settings page.
  • When the checkbox limit is reached, further checkboxes are not cached and the Todos page renders a *(list truncated by the settings limit)* note under the affected page's section.
  • Bidirectional checkbox sync: ticking or unticking a checkbox on the Todos page now also updates the checkbox on the source page it was aggregated from. Sections of the Todos page are resolved to source pages by the page file id in the heading link URL (title as fallback), and checkboxes are matched by text within a section. Only checked-state changes are synced; text edits on the Todos page are discarded with the next regeneration. If the cached line number of a checkbox has drifted, the checkbox is located by a unique text match, otherwise the change is skipped and a warning is logged.
  • Page headings on the Todos page are now links to the corresponding page, in the same URL format the Collectives app itself uses (/apps/collectives/<collectives>/<slug>-<pageId>, with a ?fileId= fallback for pages without a slug). If the URL cannot be resolved, the plain heading is kept.
  • Regenerating the Todos page while the Text editor had it open caused stale content to be shown and "Error saving the document" on manual saves: the Text app keeps per-file session state (etag, checksum, editor steps) that our background write invalidated. The Text document state of the Todos page is now reset on every regeneration, so open editors reload the fresh content. Unsaved manual edits to the Todos page are discarded by design.

Changed

  • Minimum Nextcloud version raised from 25 to 27 (the admin settings controller uses the AuthorizedAdminSetting attribute introduced in 27).
  • appinfo/info.xml brought into the form the Nextcloud app store validates: added the required <summary>, renamed <license> to <licence> and <depends> to <dependencies>, and dropped the <app>collectives</app> element (not part of the store or server schema, so it was never read; the Collectives dependency is enforced at runtime via IAppManager). No runtime behavior changes on servers already running the app.

Fixed

  • Reverse-syncing a tick made in the Collectives editor: the Text editor strips trailing whitespace on save, so a Todos page checkbox whose source line ends in spaces no longer matched the cached source text and the tick was silently dropped. Checkbox texts are now trimmed when parsed, and the reverse sync also tolerates untrimmed legacy cache entries.
  • Deleting the Todos page recreates it immediately. Collectives moves deleted pages to trash; the trash move is complete before the delete event fires and collectives paths are never locked, so no delayed recreation is needed.
  • The emoji of a freshly (re)created Todos page is now really written: inserting the missing collectives_pages row failed because its last_user_id column is NOT NULL. The app-created row now carries an empty user id (the frontend hides the "last edited" info for such pages), like untouched pages.
  • Deleting a page did not clean up the Todos page: the deleted page's section and checkboxes stayed listed. A Collectives page is a folder (the index page is its Readme.md, subpages are further files in it), and deleting it only emits a filesystem event for the folder itself - the markdown files inside vanish without their own events. A new listener for BeforeNodeDeletedEvent now collects the markdown pages under a folder before it is deleted, removes them from the cache and regenerates the Todos page. Plain .md file deletes keep their previous cleanup path. Restoring a page from the Collectives trash does not re-add it to the Todos page until the page is saved again or collectives_todos:init runs.
  • After ticking a checkbox on the Todos page, the source page briefly showed the tick and then reverted to its old content, with the editor refusing saves ("File changed in the meantime from outside"): the Text app keeps per-file session state (etag, checksum, steps) also for source pages, and the reverse sync's external write desynced it. The Text document state of a source page is now reset when the reverse sync writes it, so the page loads with the synced state and saves work. Unsaved keystrokes in an editing session open on that page at that moment are discarded (same contract the Todos page already has).
  • Checking a checkbox on the Todos page and saving broke the editing session: the reverse sync's regeneration of the Todos page ran nested inside the save and rewrote the page with a fresh "Last updated" timestamp, changing the etag under the open editor ("Error saving the document", conflict diff prompts). The Todos page is now only rewritten when its content actually changes (checkbox states, pages, titles); an update that would only refresh the "Last updated" timestamp leaves the file - and open editing sessions - untouched.
  • App bootstrap was silently skipped: info.xml declared the full namespace (OCA\CollectiveTodos), which Nextcloud prefixes with OCA\ itself, so no class of the app was autoloadable. The namespace is now CollectiveTodos.
  • Application.php moved from appinfo/ to lib/AppInfo/ (PSR-4 location that the bootstrap looks up).
  • CLI command is now registered via info.xml <commands> (previously the occ command never showed up).
  • Todos page is now written to the collectives root folder (previously it was written next to the edited page).
  • Page titles for Readme.md pages now use the page folder name (Collectives stores a page as folder with a Readme.md inside).
  • The listener skips its own Todos.md writes to avoid an infinite write-event loop, and logs failures instead of breaking saves.
  • collectives_todos:init now scans all pages of a collectives and accepts the collectives id as used by the Collectives app.
Licenses GNU Affero General Public License v3.0 or later
Certificate-----BEGIN CERTIFICATE-----
MIIEDDCCAvQCAhRDMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYxMDA3MTQ0NDAwWhcNMzcwMTEyMTQ0NDAwWjAcMRowGAYD
VQQDDBFjb2xsZWN0aXZlc190b2RvczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBALdQghokF+S+krqy/gMTVV+G7kw1vlwkBdVWroUoXrJk2U19znosxLwN
Hx/lOsSGjn9Ln0Y4/PF1aDCNITmzRKnUo3JcuC+dT6RPuG4XtGvcDPe2mBGmrY4J
wkqwWN5N1cic7crNXUqnI8cFUg+U8dAlkYah6Han5QkXf4ZuO/lH0cIRPsRaMw8j
5BDHAcdLvniw2Exa9y/atNe/7i1NDkgtpxN8yA2hoXUM4YUMcnlzUAiDx+fO556B
iMcSb8WJNblb/EpQjVKetk3h5N/oVopUR/Oo0st/tgWh/jiO7TMoCYx7ktFpQQJN
j9H7rLjsVswG3cn7xSTL3wfm0stFB1tSnTXP4zf0pp4d2QilI0GxgSdeAOPskFTN
pJGzSghDZ7/+tw4kZ5Y8gWj3aME9O10rXinBWaYV05cme569ZMXzbigLgNFTA/sT
0L/rHT3RZkrJ9cUIJCRm3yruqAQIml69onikKOl8XNM95GGAT/oF10ebhU0ITTNk
WafdGlqT2iPloHCvaRqR2BMPxR+My55IQ6+VI2nvgLztA524/Cud60PFhyrCXUZU
9O+c+5oz7MEZF0HPJfvg65PiXD/LvXi22Q17fY6fhlVQVYn8vQoMnoc0I6bkw8Al
PdK/d/kxNIpeWDNDh2gyEyBIiOxS7rOSyFmump1qliF8qf47PFEDAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBACh749xaQoj6Y4w24L7C4kiuYBMfo6JbAEqtijdZUQRC
XdV3pnonKnJ+mMV/803o41PxQ2xmRzvEaDWm2WKG2zzNssKwObcJg8TnpdPTcHjB
brU6pxkJXIHmgEjMHc5x6XJ3RR2uA9Wayaf5PFl+omfd52TzHQpMbGXM+u6TYckx
uEe0D9gnyg/Jw2ESnsOABk3n/Mkbv1IzUVs0Cxb3Je+dwJDMDBwW3DaSby/kKSjc
fSZBA9FsAJU77/yg7dnsFV3rhBXdJ5sQbVJNTH7gzcHhCiLffuEC0BmIPF+D5/80
yT7k9b7NAK212mhbleE90EiOwpPtKirDr+jKLL1zT1g=
-----END CERTIFICATE-----
SignaturetOPFL+uJkvXf4IYYON0b0T+MahYmk+Sga4NLaCPjTbv+nUklqi4DBjxNPKZEFLSHW+e7b6A2wR+uL+ugBCy9IWjJHfe35JVt1g/06s28dnXkm6nrod3AtPk4DgrZ2HHrDgPo86K4iIW2KIeW+IMbT1iKhYLQ9mGq3YcBPPtDgWzNjbpLid+PcGtxcPn8+E6GuOkrN6YbKuNkkdhL6UGuuqlHFYw7bVzDN41shzSaMYrUBmZUXXl/5ZnbnewEgiGZf6wsobuxQZ6uLqNjl9yKBvA85quuhBcnEHL0L+aFZn0xTuX8YFxvVmrx9iO8m24luzzSys35L/PL9W2l7m+w4MOX5zTIKkx+q8ExR8PtZBYOZqhaUPcZZwYLHFvJUlGL80LOPil51hn0qrsOUdJCd5yZqcoLoZxbBFKj1njokd7cTb8ZNPbw/lYsMnp8JCp4ldTTVLE/+P3U4p7aG1lbOIP33zMzOz6R+oF3w3xuNlT6QFRjvztbXzLohaj2fuTx3rVuraaztowQk9tLSqFiuRbMh5KHv3n9BFAuijyR9mfaPwJpEnKD0u0nKJmb2oIAkrwviHjYJR1xUbNsiZHbEQOpW2UMy0+5wvE++zGGCX+c3p9lRFqN00+KO1RNosmEOnmd/st12hZowjR+oayERlBKLUBXXxgO7h4/MIUHqYs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=27.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 27

Collectives Todos 0.1.0
Release Details
UpdatedOct. 7, 2026, 6:31 p.m.
Changelog

Added

  • Initial release of Collectives Todos: event-driven aggregation of markdown checkboxes across all pages of a Collectives into an auto-maintained Todos page, using Nextcloud core file events, a JSON cache for efficient updates and a CLI command (occ collectives_todos:init <collectives-id>) to build the page for existing collectives. Unit tests cover all core services and listeners.

  • App store release packaging: a GitHub Action (.github/workflows/release.yml) builds a store-conformant collectives_todos-vX.Y.Z.tar.gz (single top-level folder named after the app id, dev files excluded) on every v* tag, signs it with the app certificate and attaches archive and signature to the GitHub release. Tag and app version must match, and appinfo/info.xml is validated against the app store schema. With the APPSTORE_TOKEN secret set, the release is also published to the Nextcloud app store automatically.

  • Per-collective Enable/Disable button in the settings page's per-collective table. Disabling deletes the collective's Todos page (to trash) and stops generating or reverse-syncing it; the checkbox cache keeps being maintained, so enabling regenerates a consistent page immediately. Enabled is the default.
  • Global Enable/Disable button in the settings page's Defaults section: flips the instance-wide default. Enabling resets every collective to enabled - it clears all per-collective enabled opt-outs and regenerates their Todos pages, since opting into the app means wanting it everywhere; disabling single collectives stays the manual choice. Disabling globally deletes the Todos page of every collective that is not already opted out.
  • The per-collective Enable button clears the collective's enabled override instead of writing a sticky enabled value: a sticky override would shield the collective from the global default toggle forever, so a global disable reliably deletes every Todos page of collectives that were not explicitly disabled. The per-row Enable buttons render as inactive while the global default is disabled. Deleting the Todos page on disable now also removes its collectives_pages row (the delete is a hard delete on the appdata path, which the Collectives trash does not handle; the managed page's content is fully derived, so nothing of value is lost). The row is resolved untrashed and deleted directly: Collectives' PageMapper::deleteByFileId only removes rows that were moved to trash and silently ignored the hard-deleted page's row, leaking one on every disable cycle.
  • Todos page emoji config item (default none, per-collective override): the emoji is written to the Todos page's collectives_pages row like the emoji of any other Collectives page and rendered in the page tree. Validated with the Collectives app's own emoji validation; the settings page ships a curated emoji picker built with plain HTML/JS (no new dependencies), and any emoji can be pasted directly. The emoji is enforced on every Todos page regeneration and on settings save.
  • Admin settings page (Settings → Admin → Collectives Todos) with instance-wide defaults and per-collective overrides for three config items: the Todos page name (default Todos), the position of the Todos page in the Collectives page tree (always on top / always on bottom / alphabetically, default always on top) and the maximum number of cached checkboxes per collective (0 = unlimited, default 0). An empty override field uses the default. Invalid input is rejected without storing anything.
  • The tree position is enforced by writing the landing page's collectives_pages.subpage_order (the Collectives frontend sorts listed pages by array order, unlisted pages alphabetically), on every Todos page regeneration and on settings save. Manual ordering of the other pages is preserved; an already-open Collectives tab needs a reload to pick up the new order.
  • Changing the Todos page name renames the existing Todos page file; if a page with the new name already exists in a collective, the rename is skipped and reported on the settings page.
  • When the checkbox limit is reached, further checkboxes are not cached and the Todos page renders a *(list truncated by the settings limit)* note under the affected page's section.
  • Bidirectional checkbox sync: ticking or unticking a checkbox on the Todos page now also updates the checkbox on the source page it was aggregated from. Sections of the Todos page are resolved to source pages by the page file id in the heading link URL (title as fallback), and checkboxes are matched by text within a section. Only checked-state changes are synced; text edits on the Todos page are discarded with the next regeneration. If the cached line number of a checkbox has drifted, the checkbox is located by a unique text match, otherwise the change is skipped and a warning is logged.
  • Page headings on the Todos page are now links to the corresponding page, in the same URL format the Collectives app itself uses (/apps/collectives/<collectives>/<slug>-<pageId>, with a ?fileId= fallback for pages without a slug). If the URL cannot be resolved, the plain heading is kept.
  • Regenerating the Todos page while the Text editor had it open caused stale content to be shown and "Error saving the document" on manual saves: the Text app keeps per-file session state (etag, checksum, editor steps) that our background write invalidated. The Text document state of the Todos page is now reset on every regeneration, so open editors reload the fresh content. Unsaved manual edits to the Todos page are discarded by design.

Changed

  • Minimum Nextcloud version raised from 25 to 27 (the admin settings controller uses the AuthorizedAdminSetting attribute introduced in 27).
  • appinfo/info.xml brought into the form the Nextcloud app store validates: added the required <summary>, renamed <license> to <licence> and <depends> to <dependencies>, and dropped the <app>collectives</app> element (not part of the store or server schema, so it was never read; the Collectives dependency is enforced at runtime via IAppManager). No runtime behavior changes on servers already running the app.

Fixed

  • Reverse-syncing a tick made in the Collectives editor: the Text editor strips trailing whitespace on save, so a Todos page checkbox whose source line ends in spaces no longer matched the cached source text and the tick was silently dropped. Checkbox texts are now trimmed when parsed, and the reverse sync also tolerates untrimmed legacy cache entries.
  • Deleting the Todos page recreates it immediately. Collectives moves deleted pages to trash; the trash move is complete before the delete event fires and collectives paths are never locked, so no delayed recreation is needed.
  • The emoji of a freshly (re)created Todos page is now really written: inserting the missing collectives_pages row failed because its last_user_id column is NOT NULL. The app-created row now carries an empty user id (the frontend hides the "last edited" info for such pages), like untouched pages.
  • Deleting a page did not clean up the Todos page: the deleted page's section and checkboxes stayed listed. A Collectives page is a folder (the index page is its Readme.md, subpages are further files in it), and deleting it only emits a filesystem event for the folder itself - the markdown files inside vanish without their own events. A new listener for BeforeNodeDeletedEvent now collects the markdown pages under a folder before it is deleted, removes them from the cache and regenerates the Todos page. Plain .md file deletes keep their previous cleanup path. Restoring a page from the Collectives trash does not re-add it to the Todos page until the page is saved again or collectives_todos:init runs.
  • After ticking a checkbox on the Todos page, the source page briefly showed the tick and then reverted to its old content, with the editor refusing saves ("File changed in the meantime from outside"): the Text app keeps per-file session state (etag, checksum, steps) also for source pages, and the reverse sync's external write desynced it. The Text document state of a source page is now reset when the reverse sync writes it, so the page loads with the synced state and saves work. Unsaved keystrokes in an editing session open on that page at that moment are discarded (same contract the Todos page already has).
  • Checking a checkbox on the Todos page and saving broke the editing session: the reverse sync's regeneration of the Todos page ran nested inside the save and rewrote the page with a fresh "Last updated" timestamp, changing the etag under the open editor ("Error saving the document", conflict diff prompts). The Todos page is now only rewritten when its content actually changes (checkbox states, pages, titles); an update that would only refresh the "Last updated" timestamp leaves the file - and open editing sessions - untouched.
  • App bootstrap was silently skipped: info.xml declared the full namespace (OCA\CollectiveTodos), which Nextcloud prefixes with OCA\ itself, so no class of the app was autoloadable. The namespace is now CollectiveTodos.
  • Application.php moved from appinfo/ to lib/AppInfo/ (PSR-4 location that the bootstrap looks up).
  • CLI command is now registered via info.xml <commands> (previously the occ command never showed up).
  • Todos page is now written to the collectives root folder (previously it was written next to the edited page).
  • Page titles for Readme.md pages now use the page folder name (Collectives stores a page as folder with a Readme.md inside).
  • The listener skips its own Todos.md writes to avoid an infinite write-event loop, and logs failures instead of breaking saves.
  • collectives_todos:init now scans all pages of a collectives and accepts the collectives id as used by the Collectives app.
Licenses GNU Affero General Public License v3.0 or later
Certificate-----BEGIN CERTIFICATE-----
MIIEDDCCAvQCAhRDMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjYxMDA3MTQ0NDAwWhcNMzcwMTEyMTQ0NDAwWjAcMRowGAYD
VQQDDBFjb2xsZWN0aXZlc190b2RvczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBALdQghokF+S+krqy/gMTVV+G7kw1vlwkBdVWroUoXrJk2U19znosxLwN
Hx/lOsSGjn9Ln0Y4/PF1aDCNITmzRKnUo3JcuC+dT6RPuG4XtGvcDPe2mBGmrY4J
wkqwWN5N1cic7crNXUqnI8cFUg+U8dAlkYah6Han5QkXf4ZuO/lH0cIRPsRaMw8j
5BDHAcdLvniw2Exa9y/atNe/7i1NDkgtpxN8yA2hoXUM4YUMcnlzUAiDx+fO556B
iMcSb8WJNblb/EpQjVKetk3h5N/oVopUR/Oo0st/tgWh/jiO7TMoCYx7ktFpQQJN
j9H7rLjsVswG3cn7xSTL3wfm0stFB1tSnTXP4zf0pp4d2QilI0GxgSdeAOPskFTN
pJGzSghDZ7/+tw4kZ5Y8gWj3aME9O10rXinBWaYV05cme569ZMXzbigLgNFTA/sT
0L/rHT3RZkrJ9cUIJCRm3yruqAQIml69onikKOl8XNM95GGAT/oF10ebhU0ITTNk
WafdGlqT2iPloHCvaRqR2BMPxR+My55IQ6+VI2nvgLztA524/Cud60PFhyrCXUZU
9O+c+5oz7MEZF0HPJfvg65PiXD/LvXi22Q17fY6fhlVQVYn8vQoMnoc0I6bkw8Al
PdK/d/kxNIpeWDNDh2gyEyBIiOxS7rOSyFmump1qliF8qf47PFEDAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBACh749xaQoj6Y4w24L7C4kiuYBMfo6JbAEqtijdZUQRC
XdV3pnonKnJ+mMV/803o41PxQ2xmRzvEaDWm2WKG2zzNssKwObcJg8TnpdPTcHjB
brU6pxkJXIHmgEjMHc5x6XJ3RR2uA9Wayaf5PFl+omfd52TzHQpMbGXM+u6TYckx
uEe0D9gnyg/Jw2ESnsOABk3n/Mkbv1IzUVs0Cxb3Je+dwJDMDBwW3DaSby/kKSjc
fSZBA9FsAJU77/yg7dnsFV3rhBXdJ5sQbVJNTH7gzcHhCiLffuEC0BmIPF+D5/80
yT7k9b7NAK212mhbleE90EiOwpPtKirDr+jKLL1zT1g=
-----END CERTIFICATE-----
SignaturetOPFL+uJkvXf4IYYON0b0T+MahYmk+Sga4NLaCPjTbv+nUklqi4DBjxNPKZEFLSHW+e7b6A2wR+uL+ugBCy9IWjJHfe35JVt1g/06s28dnXkm6nrod3AtPk4DgrZ2HHrDgPo86K4iIW2KIeW+IMbT1iKhYLQ9mGq3YcBPPtDgWzNjbpLid+PcGtxcPn8+E6GuOkrN6YbKuNkkdhL6UGuuqlHFYw7bVzDN41shzSaMYrUBmZUXXl/5ZnbnewEgiGZf6wsobuxQZ6uLqNjl9yKBvA85quuhBcnEHL0L+aFZn0xTuX8YFxvVmrx9iO8m24luzzSys35L/PL9W2l7m+w4MOX5zTIKkx+q8ExR8PtZBYOZqhaUPcZZwYLHFvJUlGL80LOPil51hn0qrsOUdJCd5yZqcoLoZxbBFKj1njokd7cTb8ZNPbw/lYsMnp8JCp4ldTTVLE/+P3U4p7aG1lbOIP33zMzOz6R+oF3w3xuNlT6QFRjvztbXzLohaj2fuTx3rVuraaztowQk9tLSqFiuRbMh5KHv3n9BFAuijyR9mfaPwJpEnKD0u0nKJmb2oIAkrwviHjYJR1xUbNsiZHbEQOpW2UMy0+5wvE++zGGCX+c3p9lRFqN00+KO1RNosmEOnmd/st12hZowjR+oayERlBKLUBXXxgO7h4/MIUHqYs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=27.0.0,<35.0.0
Minimum Integer bits32