Skip to content

Feature: Publish Collective as Static Website - #2769

Draft
code4candy wants to merge 4 commits into
mainfrom
feature/collectives-publish
Draft

code4candy wants to merge 4 commits into
mainfrom
feature/collectives-publish

Conversation

@code4candy

@code4candy code4candy commented Sep 10, 2026 •

Copy link
Copy Markdown

📝 Summary

This is the first step towards the publishing feature, that we implement as part of the funding program Prototype Fund (get more information here: https://github.com/nextcloud-publish). This is not going to be merged after a first review, but works as intermediate working tree for the feature.

This PR contains the following Issues:

(Check off once the review has been completed and all feedback has been incorporated.)

WIP

@mejo- mejo- left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks great 😊 I have some comments on the backend part, the frontend looks fine from reading the code.

Comment thread lib/AppInfo/Application.php Outdated
Comment thread lib/Service/PublishSettings.php Outdated
Comment thread playwright/e2e/collective-publish.spec.ts Outdated
Comment thread playwright/e2e/collective-publish.spec.ts Outdated
@kjoscha
kjoscha force-pushed the feature/collectives-publish branch 2 times, most recently from 7cebf56 to 63fc23e Compare September 22, 2026 08:55
@kjoscha

kjoscha commented Sep 22, 2026 •

Copy link
Copy Markdown
image

Since the settings area is not in the Collectives scope, we should prefix the header with collective or BETTER move both settings under a "Collectives" heading

@kjoscha
kjoscha force-pushed the feature/collectives-publish branch 2 times, most recently from 13eabe3 to 20f507c Compare September 22, 2026 12:40
@github-actions

Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

@mejo- mejo- left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes in this PR look good now. Ideally you could merge the three commits into one and then it would be ready to be used as the base for future PRs to be merged into it from my side 😊

@code4candy
code4candy force-pushed the feature/collectives-publish branch from 9ca1098 to 37ee4ef Compare October 7, 2026 21:03
@kjoscha

kjoscha commented Oct 13, 2026

Copy link
Copy Markdown

Important

  • 9fd6602 Stable35 setting in workflow file must be reverted before merge!

@mejo-

mejo- commented Oct 14, 2026

Copy link
Copy Markdown
Member

I asked Claude to do a review and here's the most important findings. From a quick code walkthrough, at least the security/permission problems seem true.

Security and permissions

  1. Archive ignores the page selection (lib/Service/StaticSiteService.php:164). buildArchive() calls contentCollector->collect($collectiveId), which collects every file in the collective folder. The pageIds are stored but never used to filter the archive. A page the user unticked, such as "HR/Salaries", still ends up in the tar.gz along with all attachments. Commit e285da7 says it builds "an archive containing selected pages and assets", which isn't what the code does.
  2. The backend allows any editor, but the UI treats publishing as admin-only (StaticSiteService.php:271). The service checks canEdit(), while the frontend and the e2e test use isCollectiveAdmin. With default permissions, a regular member can call POST or DELETE /static-sites directly, publishing the collective or deleting an admin's site. It should check getLevel() >= Member::LEVEL_ADMIN, or whatever permission is intended.
  3. The publish_enabled setting is only checked in the frontend (lib/Controller/StaticSiteController.php:87). No controller or service checks it. Even with the feature disabled (the default), editors can call the API and build archives of up to 100 MB in appdata.

Correctness

  1. The page list can be empty (src/components/Nav/CollectivePublishModal.vue:153). created() reads allPageIds once from the pages store and never loads pages itself. If you open the modal for a collective that isn't the current one, or before its pages have loaded, the tree is empty. Publish then only fails with "select at least one page".
  2. Ticking a subpage doesn't select its parents (CollectivePublishModal.vue:173). Toggling a page only affects its subtree. You can untick the root, tick one deep subpage, and the backend accepts a site whose pages have no parent and no landing page.
  3. A failed retry restarts the 15-minute lock (StaticSiteService.php:126). If an update takes over a timed-out "pending" site and the build then fails, restoreStatus() writes "pending" back through updateStatus(), which sets updated_at to now. Every retry is then refused as "already in progress" for another 15 minutes, although nothing is running.
  4. The feature defaults to enabled in the frontend (src/CollectivesApp.vue:152). loadState(..., 'publish_enabled', true) should default to false, and the === 'true' check is unnecessary. On the backend, getValueBool('collectives', 'publish_enabled') should replace the copied getValueString(...) === 'true' checks in BeforeTemplateRenderedListener and Settings/Admin.

Design

  1. The build runs synchronously in the request (StaticSiteService.php:105). The service copies, tars and gzips up to 100 MB inside the OCS request. The PENDING_TIMEOUT and optimistic-lock logic only exists to work around request timeouts. A queued background job (IJobList) would make that logic unnecessary and avoid timeouts on object storage.

@code4candy
code4candy force-pushed the feature/collectives-publish branch from 7c64523 to 090efb9 Compare October 15, 2026 11:44
Melpo added 3 commits October 15, 2026 17:08
Signed-off-by: kayoshi <jo5cha@web.de>
Signed-off-by: Melpo <melpomene@posteo.net>
Assisted-by: ClaudeCode:claude-sonnet-5
- create and save static site metadata to db
- add update and delete route, add state lock on publish
- add symfony/uid and symfony/polyfill-uuid to composer.json
- add title and slug input fields to modal

Signed-off-by: Melpo <melpomene@posteo.net>
Assisted-by: ClaudeCode:claude-sonnet-5
- create and save static site metadata to db
- add archiver to store static sites as tar.gz in appdata
- build archive containing selected pages and assets

Signed-off-by: Melpo <melpomene@posteo.net>
Assisted-by: ClaudeCode:claude-sonnet-5
@code4candy
code4candy force-pushed the feature/collectives-publish branch from 090efb9 to 6ac7363 Compare October 15, 2026 15:11
- add static site generator settings and connection test
- add public download endpoint to stream the archive to the external ssg service
- notify static site generator when the provided archive is ready
- regenerate OpenAPI specs (adds administration scope)

Signed-off-by: Melpo <melpomene@posteo.net>
Assisted-by: ClaudeCode:claude-opus-5-5

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants