Skip to content

FIX: composer save validation and tag assignment persistence#80

Open
jlzlt wants to merge 2 commits into
paviliondev:mainfrom
jlzlt:fix/composer-timing-and-tag-chooser
Open

FIX: composer save validation and tag assignment persistence#80
jlzlt wants to merge 2 commits into
paviliondev:mainfrom
jlzlt:fix/composer-timing-and-tag-chooser

Conversation

@jlzlt

@jlzlt jlzlt commented Jun 5, 2026

Copy link
Copy Markdown

Closes #79

Two independent bugs fixed in this PR.

Bug 1 - Composer save validation silently broken on multi-plugin sites

modifyClass("service:composer", ...) was being called inside withPluginApi to intercept the composer's save() method and validate that all checked ratings have a value before submitting.

On sites with multiple plugins, service:composer is a singleton that gets initialized early in the boot process - before the ratings initializer runs. modifyClass detects this and silently bails out, so the validation and the _shouldSaveDraft observer were never applied. The composer would allow submitting with empty ratings, sending a 0 value to the server.

Console message I received that triggered this PR:
c5feee90a4c892bb0d6508fac4090a23

Fix: Move the save validation into the existing modifyClass("model:composer") block. model:composer is instantiated fresh each time the composer opens, so the patch always applies regardless of initialization order. The _shouldSaveDraft observer is dropped - draft saves for ratings already work via serializeToDraft.

Bug 2 - Tag rating assignments not saved through admin UI

This is in reference to Issue #79.

When adding a tag assignment in the ratings admin panel, the save silently fails every time - meaning tag assignments have never persisted through the admin UI on Discourse versions from February 2026 onward.

The root cause: Discourse core commit 9e99066b changed TagChooser's _onChange to pass full tag objects to the callback instead of plain name strings. updateTag was written for the old API and assumed tags[0] was a string. After the Discourse change, tags[0] became a full object, which got sent to the server as the tag name. Rails silently rejected it, causing the save
to fail with no error shown to the user.

Fix: Extract the tag name string from the TagChooser object before storing it. The fix handles both old and new Discourse versions - if tags[0] is already a string it is used directly, otherwise name is extracted from the object.

jlzlt added 2 commits June 5, 2026 15:13
service:composer is a singleton that gets initialized early in the boot
process. On sites with multiple plugins, modifyClass("service:composer")
silently fails because the service is already cached by the time the
ratings initializer runs, so the save validation and draft observer were
never applied.

Moving the save validation into the existing modifyClass("model:composer")
block fixes the timing issue — model:composer is instantiated fresh each
time the composer opens, so the patch always applies. The _shouldSaveDraft
observer is dropped entirely; draft saves for ratings already work via the
serializeToDraft registration.
TagChooser's onChange passes full tag objects to the callback, not plain
strings. updateTag was storing the raw object as obj.name, which got
serialized as a nested hash in the POST body. Rails rejected it via strong
parameters, so object_params[:name] came out nil, the blank check in
Object.create returned false, and the save silently failed.

Extract the name string from the tag object before storing it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Ratings tags don't stick

1 participant