Skip to content

Desktop: Publish a notebook#15276

Open
Rygaa wants to merge 7 commits into
laurent22:devfrom
Rygaa:issue360
Open

Desktop: Publish a notebook#15276
Rygaa wants to merge 7 commits into
laurent22:devfrom
Rygaa:issue360

Conversation

@Rygaa
Copy link
Copy Markdown
Collaborator

@Rygaa Rygaa commented May 6, 2026

Summary

This change lets a user publish a full notebook with all the descendent notebooks/notes

The feature reuses the current public note page where possible. Published notebooks also use a different share type than normal shared notebooks.

It uses Wunderbaum to render the notebook tree with the descendent notebooks/notes

Server

I added sharePublishedFolder to create a public notebook share:

ShareModel.sharePublishedFolder(owner, folderId)

It still uses the existing share endpoint:

POST /api/shares

When the request has folder_id and type: 4, the server treat is as publishing a notebook. Henry recommended using 4 for this new share type instead of 2.

For a public notebook share, the server builds the tree from the published root notebook. The tree includes child notebooks and notes.

joplinUtils

Most of the public notebook rendering is handled in joplinUtils.ts.

This file used to render one published note and its resources. Now it can also render a published notebook.

I added a buildFolderTree function. It starts from the published root notebook and loads its child notebooks and notes. For each note, it creates a link like this:

/shares/<share_id>?note_id=<note_id>

It also keeps a list of allowed note IDs. This list is used to check that the requested note is really inside the published notebook.

I also changed renderNote so it can receive a notebook tree. If there is a tree, the note is rendered inside the notebook page.

There is also a shared renderNotePage function. It renders both:

  • a normal published note;
  • a note inside a published notebook.

When a notebook tree is present, renderNotePage loads the Wunderbaum files and sends the tree data to the Mustache template.

Finally, renderItem now has a folder case. If the shared item is a folder, it renders the notebook tree first. Then it either shows the root notebook page, a selected note, or a linked resource from that selected note.

Security

In joplinUtils.ts, buildFolderTree now keeps a list of allowed note IDs for the published notebook.

When renderItem handles a published notebook:

  • it rejects a resource_id request if there is no note_id or wrong note_id;
  • it rejects a note_id if it is not in the allowed note ID list;

E2EE

Folder.updateAllShareIds(resourceService, activeShares)

It now handle e2ee by:

  • finds active PublishedFolder shares;
  • finds all folders inside those published notebooks;
  • finds directly published notes;
  • marks folders inside a published notebook as shared;
  • marks notes as shared if they are directly published or inside a published notebook;

Test Coverage

The tests cover the main parts of the change.

They check that Joplin can create a public notebook share and that it does not mix it with a normal shared notebook.

They also check the public page:

  • the notebook root page is rendered;
  • a note can be opened with ?note_id=;
  • notes outside the notebook are rejected;
  • linked resources can be opened;
  • resources without note_id are rejected;
  • resources not linked from the selected note are rejected.

The tests also cover E2EE. They check that published notebook content is readable after sync, and that folders, notes, and resources get the right is_shared state before sync.

@coderabbitai coderabbitai Bot added desktop All desktop platforms Sharing labels May 6, 2026
Comment thread packages/app-desktop/gui/NoteList/NoteList2.tsx Outdated
Comment thread packages/app-desktop/gui/PublishFolderDialog.tsx
Comment thread packages/lib/services/noteList/defaultLeftToRightListRenderer.ts
Comment thread packages/lib/services/noteList/defaultListRenderer.ts Outdated
Comment thread packages/lib/services/noteList/defaultMultiColumnsRenderer.ts Outdated
Comment thread packages/lib/services/share/ShareService.ts
@github-actions github-actions Bot deleted a comment from coderabbitai Bot May 6, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

@Rygaa the pull request title does not match the required format.

Please prefix the title with the area you are targeting, then add the issue you are addressing. For example:

  • Desktop: Resolves #123: Added new setting to change font
  • Mobile, Desktop: Fixes #456: Fixed config screen error
  • All: Resolves #777: Made synchronisation faster

See the pull request template for the list of valid prefixes and the full specification.

This PR has been closed automatically. Please update the title and reopen it, or open a new pull request.

@github-actions github-actions Bot closed this May 6, 2026
@github-actions github-actions Bot deleted a comment from coderabbitai Bot May 6, 2026
@Rygaa Rygaa reopened this May 6, 2026
@coderabbitai coderabbitai Bot added enhancement Feature requests and code enhancements and removed desktop All desktop platforms Sharing labels May 6, 2026
@github-actions github-actions Bot deleted a comment from coderabbitai Bot May 6, 2026
@coderabbitai coderabbitai Bot added desktop All desktop platforms Sharing and removed enhancement Feature requests and code enhancements labels May 7, 2026
@Rygaa Rygaa self-assigned this May 7, 2026
@github-actions github-actions Bot deleted a comment from coderabbitai Bot May 7, 2026
@laurent22 laurent22 marked this pull request as draft May 10, 2026 13:55
@laurent22
Copy link
Copy Markdown
Owner

Note: all server code is missing

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds PublishedFolder share support end-to-end: server/models/API, share service, client selectors/renderers, sidebar published markers and context menu, a Publish Folder dialog with command/window integration, public site folder-tree UI/assets, and related tests and ignore/dictionary updates.

Changes

Folder publication feature

Layer / File(s) Summary
Share foundation & server API/model changes
packages/lib/services/share/reducer.ts, packages/lib/services/share/ShareService.ts, packages/lib/services/share/ShareService.test.ts, packages/server/src/models/ShareModel.ts, packages/server/src/models/ShareModel.test.ts, packages/server/src/routes/api/shares.ts, packages/server/src/routes/api/utils/types.ts, packages/server/src/services/database/types.ts
Introduce ShareType.PublishedFolder, extend ApiShare shape, add publishFolder client method, typed server-side sharing methods (sharePublishedFolder), widen GET share unauthenticated access to PublishedFolder, and add tests covering published-folder behaviour.
Folder model and indexing
packages/lib/models/Folder.ts, packages/server/src/models/ItemModel.ts
Update Folder.updateAllShareIds to propagate published-folder roots to descendants and update note/folder is_shared flags; add ItemModel.loadByJopParentId.
Command context and when-clauses
packages/lib/services/commands/stateToWhenClauseContext.ts
Add folderIsPublished to WhenClauseContext and compute from isFolderPublished for selected folder.
Note publication state and renderers
packages/app-desktop/gui/NoteList/..., packages/app-desktop/gui/NoteListItem/..., packages/lib/services/noteList/*, packages/lib/services/plugins/api/noteListType.ts
Selector for published note IDs; pass isPublished to NoteListItem; propagate into useRenderedNote/prepareViewProps; add note.is_published dependency to renderers and apply published CSS classes.
Sidebar published-folder display
packages/app-desktop/gui/Sidebar/*
Provide shares to FolderAndTagList; compute publishedFolderIds (including ancestors) in hook; pass isPublished to FolderItem; render published icon/title and adjust styles/opacity; add "Publish Folder" context-menu entry.
Publish folder dialog and command integration
packages/app-desktop/gui/PublishFolderDialog.tsx, packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showPublishFolderDialog.ts, packages/app-desktop/gui/WindowCommandsAndDialogs/WindowCommandsAndDialogs.tsx, packages/app-desktop/gui/WindowCommandsAndDialogs/types.ts, packages/app-desktop/gui/WindowCommandsAndDialogs/utils/useSyncDialogState.ts, packages/app-desktop/gui/WindowCommandsAndDialogs/commands/index.ts
New PublishFolderDialog component that counts notes, publishes or reuses a share and copies URL to clipboard; showPublishFolderDialog command added and wired into WindowCommandsAndDialogs with dialog-state syncing.
Public site folder-tree rendering and assets
packages/server/public/js/items/folderTree.js, packages/server/public/css/items/note.css, packages/server/src/utils/joplinUtils.ts, packages/server/src/views/index/items/note.mustache, packages/server/package.json, packages/server/src/routes/index/shares.link.test.ts
Add Wunderbaum assets and dependency, folder-tree JS init, CSS for tree layout and responsive behaviour, server rendering changes to build folderTree and include note_id in resource URLs, Mustache template changes for folder-tree, and tests for published-folder link and resource serving.
Configuration and dictionary
.eslintignore, .gitignore, packages/tools/cspell/dictionary3.txt
Ignore generated GUI files in lint/VCS and add dictionary entries (wunderbaum, YYYYMMDDTHH).

Sequence Diagram(s):
(none generated — the changes are spread across many components and include multiple flows; no single simple 3+ actor sequence met the explicit diagram criteria.)

Possibly related PRs:

Suggested labels: desktop, Sharing

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Desktop: Publish a notebook' accurately reflects the main change: adding desktop UI functionality to publish notebooks via a context menu and dialog.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description is directly related to the changeset. It explains the feature of publishing full notebooks with descendant notebooks/notes, the server-side changes using sharePublishedFolder, the joplinUtils rendering logic, security/E2EE handling, and test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added server Issues related to Joplin Server and removed desktop All desktop platforms Sharing labels May 18, 2026
@coderabbitai coderabbitai Bot added desktop All desktop platforms Sharing and removed server Issues related to Joplin Server labels May 18, 2026
Comment on lines -440 to -441
const bannerInfo = getDefaultBannerInfo();
return renderNote(share, itemToRender, resourceInfos, linkedItemInfos, bannerInfo);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

As discussed, this should remain here, so please pass around the banner info

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/lib/models/Folder.ts`:
- Around line 792-800: The current guard skips fetching notes when
noteIsSharedSql is empty, leaving notes with is_shared=1 stale; change the logic
around noteIsSharedSql and the notesToUpdate lookup so you always query notes
that need flipping: if noteIsSharedSql is non-empty run the existing SELECT that
finds rows where is_shared differs from the predicate, and if noteIsSharedSql is
empty run a SELECT that returns notes with is_shared = 1 (so they can be set to
0). Update the code around notesToUpdate, db().selectAll(…), and NoteEntity
handling to cover both branches (or remove the if and build a single SQL that
handles an empty predicate) so published state is cleared when there are no
active published-note predicates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 22c8bb4b-d604-45bc-b2fc-b8969dc03873

📥 Commits

Reviewing files that changed from the base of the PR and between fcbd704 and 1ac5081.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (42)
  • .eslintignore
  • .gitignore
  • packages/app-desktop/gui/NoteList/NoteList2.tsx
  • packages/app-desktop/gui/NoteList/utils/types.ts
  • packages/app-desktop/gui/NoteListItem/NoteListItem.tsx
  • packages/app-desktop/gui/NoteListItem/utils/prepareViewProps.ts
  • packages/app-desktop/gui/NoteListItem/utils/useRenderedNote.ts
  • packages/app-desktop/gui/PublishFolderDialog.tsx
  • packages/app-desktop/gui/Sidebar/FolderAndTagList.tsx
  • packages/app-desktop/gui/Sidebar/hooks/useOnRenderItem.tsx
  • packages/app-desktop/gui/Sidebar/listItemComponents/FolderItem.tsx
  • packages/app-desktop/gui/Sidebar/styles/index.ts
  • packages/app-desktop/gui/WindowCommandsAndDialogs/WindowCommandsAndDialogs.tsx
  • packages/app-desktop/gui/WindowCommandsAndDialogs/commands/index.ts
  • packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showPublishFolderDialog.ts
  • packages/app-desktop/gui/WindowCommandsAndDialogs/types.ts
  • packages/app-desktop/gui/WindowCommandsAndDialogs/utils/useSyncDialogState.ts
  • packages/lib/models/Folder.ts
  • packages/lib/services/commands/stateToWhenClauseContext.ts
  • packages/lib/services/noteList/defaultLeftToRightListRenderer.ts
  • packages/lib/services/noteList/defaultListRenderer.ts
  • packages/lib/services/noteList/defaultMultiColumnsRenderer.ts
  • packages/lib/services/plugins/api/noteListType.ts
  • packages/lib/services/share/ShareService.test.ts
  • packages/lib/services/share/ShareService.ts
  • packages/lib/services/share/reducer.ts
  • packages/server/package.json
  • packages/server/public/css/items/note.css
  • packages/server/public/js/items/folderTree.js
  • packages/server/src/models/ItemModel.ts
  • packages/server/src/models/ShareModel.test.ts
  • packages/server/src/models/ShareModel.ts
  • packages/server/src/routes/api/shares.test.ts
  • packages/server/src/routes/api/shares.ts
  • packages/server/src/routes/api/utils/types.ts
  • packages/server/src/routes/default.ts
  • packages/server/src/routes/index/shares.link.test.ts
  • packages/server/src/routes/index/shares.ts
  • packages/server/src/services/database/types.ts
  • packages/server/src/utils/joplinUtils.ts
  • packages/server/src/views/index/items/note.mustache
  • packages/tools/cspell/dictionary3.txt
✅ Files skipped from review due to trivial changes (3)
  • .gitignore
  • .eslintignore
  • packages/tools/cspell/dictionary3.txt
🚧 Files skipped from review as they are similar to previous changes (24)
  • packages/app-desktop/gui/NoteListItem/NoteListItem.tsx
  • packages/app-desktop/gui/WindowCommandsAndDialogs/commands/index.ts
  • packages/app-desktop/gui/WindowCommandsAndDialogs/types.ts
  • packages/app-desktop/gui/NoteList/utils/types.ts
  • packages/app-desktop/gui/WindowCommandsAndDialogs/utils/useSyncDialogState.ts
  • packages/lib/services/share/ShareService.test.ts
  • packages/server/src/services/database/types.ts
  • packages/lib/services/noteList/defaultLeftToRightListRenderer.ts
  • packages/app-desktop/gui/NoteListItem/utils/prepareViewProps.ts
  • packages/app-desktop/gui/NoteListItem/utils/useRenderedNote.ts
  • packages/app-desktop/gui/WindowCommandsAndDialogs/WindowCommandsAndDialogs.tsx
  • packages/lib/services/share/reducer.ts
  • packages/lib/services/plugins/api/noteListType.ts
  • packages/app-desktop/gui/Sidebar/listItemComponents/FolderItem.tsx
  • packages/lib/services/noteList/defaultMultiColumnsRenderer.ts
  • packages/app-desktop/gui/NoteList/NoteList2.tsx
  • packages/lib/services/commands/stateToWhenClauseContext.ts
  • packages/app-desktop/gui/Sidebar/styles/index.ts
  • packages/app-desktop/gui/Sidebar/hooks/useOnRenderItem.tsx
  • packages/lib/services/share/ShareService.ts
  • packages/lib/services/noteList/defaultListRenderer.ts
  • packages/app-desktop/gui/Sidebar/FolderAndTagList.tsx
  • packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showPublishFolderDialog.ts
  • packages/app-desktop/gui/PublishFolderDialog.tsx

Comment thread packages/lib/models/Folder.ts
@laurent22
Copy link
Copy Markdown
Owner

laurent22 commented May 18, 2026

This is hard to review without any context. @Rygaa, please provide a detailed spec of what the code is doing:

  • First at a high level
  • Then some more technical details if relevant

Please use proper formatting. And if using an LLM please re-read it and trim it down if needed to keep it readable.

For now add that spec to the top post and later we can add it to a spec document under /readme

@Rygaa
Copy link
Copy Markdown
Collaborator Author

Rygaa commented May 19, 2026

part_1.1.mp4
part_2.1.mp4

@Rygaa Rygaa marked this pull request as ready for review May 19, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop All desktop platforms Sharing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants