ddcore 0.23
0.23.5 — 2026-10-01
Changed
- Desk addresses carry a DocType, a workspace or a report without the spaces of its name:
/app/Training%20Classis now/app/TrainingClass, and/app/<Workspace>/report/Open%20Tasksis/app/<Workspace>/report/OpenTasks. Nothing to change in an app: the name as it is still opens and is redirected, so aroute:, addcore.route(...)call and a link in a mail already sent keep working. Record ids and/apipaths are untouched. See "Navigation" inform-api. ddcore.docUrl(doctype, id)returns the address in that form (…/app/SalesOrder/SO-1).- Two DocTypes, two workspaces or two reports whose names differ only by spaces are refused when the site loads, naming both: they would share a Desk address. (Two such DocTypes already shared a table.)
0.23.4 — 2026-10-01
Fixed
- The short route keeps its query string and its hash when the Desk redirects it to the workspace route.
/app/<DocType>/new?field=valueopened the new form with the field empty and/app/<DocType>?field=valueopened the list unfiltered, because the redirect was built from the path alone.form-apinow documentsddcore.route,ddcore.setRoute, the short route and the prefill (#57). frm.addFieldButtonon aTable,ReportorHTMLfield renders its button. The call was accepted and nothing appeared, because only a field with an input was given its buttons. On aTableor aReportfield the button sits in the grid's toolbar, to the right; on anHTMLfield, under the content. AReportfield shows it once the document is saved (#56).
0.23.3 — 2026-10-01
Fixed
- Ctrl+S on a form saves the document with Caps Lock on. Windows reports the key in upper case then, the Desk missed it, and the browser opened its own "Save page as" dialog instead.
0.23.2 — 2026-10-01
Added
- The Desk's two side bars collapse to a rail of icons, each with a button of its own. The navigation sidebar keeps the bell and To-Do icons with their counters as badges; the form's right column keeps one icon per section (assignments, shares, comments, history) with its count, and a click on any of them opens it again. The choice is kept per browser.
ddcore.jsontakes an optionaltitlethat names the site — the sidebar's heading, the browser tab, the e-mails,/health— ahead of every app's title. Left out, the site is named by its own app as before. It changes the name only:desk.homeanddesk.logostay with the apps. Seei18n→ "The site's name is the app's title" (#54).- The three notifications the core writes itself — a document assigned, a document shared, a task due today — now also go out by email, in the recipient's language, with a button that opens the document. Each person turns each kind off on their profile ("Email notifications"); the inbox notification is written either way. The choices are three opt-out fields on
User(mute_assignment_email,mute_share_email,mute_due_email), reachable throughprofile.updateMyProfile({emailNotifications: {assignment?, share?, due?}}). Emails of an app's own notification rules are not affected by them. Seenotifications→ "Email for the core's own notifications". ddcore.siteUrl()andddcore.docUrl(doctype, id)in server code: the site's public address and the absolute desk address of a document, for a link in a mail template or a webhook payload. Seemail.- Mail template
core.notification({title, message, doctype, id}), the message behind the emails above. - A comment on a form can be edited and deleted from the form's right column. Its author edits it in place and may delete it; a System Manager may delete anybody's. An edited comment says so next to its date, and
GET /api/comments/{doctype}/{id}now returnsmodifiedso a client can tell.
Changed
- Users start receiving email on upgrade. A site with a real mail transport (
DDCORE_MAIL_TRANSPORT=smtpormethod) mails assignees, share recipients and the owners of tasks due today from the first request after the upgrade; nothing was mailed for these before. Runddcore migrateto add the threeUsercolumns. To keep a user silent, tick the three "No email when…" fields on their User record, or let them do it on their profile. - A timeline entry — the
Commentan assignment or a workflow transition writes in the user's name, anycomment_typeother thanComment— can no longer be edited or deleted by its owner throughPUT/DELETE /api/resource/Comment/{id}; a System Manager still can. Server code that rewrote such an entry as an ordinary user now gets aPermissionError.
Fixed
- A line break in a mail subject no longer reaches the
Subjectheader: it is replaced by a space, so a subject built from text somebody typed cannot add headers to the message.
0.23.1 — 2026-10-01
Fixed
doc.flagsset beforeinsert(),save(),submit(),cancel()ordelete()reaches the hooks of that write. Every hook used to get an emptydoc.flagsof its own, so server code could not tellvalidatethat a write was the system's, and a notevalidateleft was gone byonUpdate. The flags now travel with the write: the hooks share them, and what they set is back on the caller'sdoc.flagswhen the call returns. They stay server-side and per document, and their values must be JSON. Seecontroller-api→ "doc.flags: context for one write" (#55).
0.23.0 — 2026-10-01
Breaking
- An app that imports a server file of another app by relative path now gets that app's own module instead of a private copy bundled into the importer: module state is shared, a test can replace an export the owner's controllers call, and the file's top-level code runs once. The importer has to declare the owner — a site where it does not refuses to load with
app b imports a module of app a: add "a" to requires in its ddcore.app.ts. Upgrade: add the app torequiresindefineApp; code that relied on its own copy of the other app's module state now shares it. Seeconventions→ "Calling another app's server code" (#47).
Fixed
- The site is named by its own app, not by a library that app
requires: a required app loads first, so it used to take the Desk's title,desk.homeanddesk.logo. The apps nobody requires now come first for all three, and/api/bootcarries the result assite.homeandsite.logo(#54). - A
beforeEach,afterEachorbeforeAllwritten outside anydescribeapplies to the tests of its own file. It used to run around every test of every file of the site, so a fake installed by one app's test file was in place during another app's tests. Tests run file by file as a result: a file's top-level tests, then itsdescribeblocks (#48).