Skip to content

[3.0] Theme split (wave 1, part 5) — Autosave drafts from an SCEditor plugin - #9337

Merged
live627 merged 3 commits into
SimpleMachines:release-3.0from
albertlast:3.0/theme-drafts
Aug 2, 2026
Merged

[3.0] Theme split (wave 1, part 5) — Autosave drafts from an SCEditor plugin#9337
live627 merged 3 commits into
SimpleMachines:release-3.0from
albertlast:3.0/theme-drafts

Conversation

@albertlast

Copy link
Copy Markdown
Collaborator

Wave 1, part 5 of breaking up #7933.

Draft autosave is currently dead

Before anything else: on release-3.0 today, draft autosave never saves anything. The console says

Uncaught TypeError: sceditor.instance(...).getText is not a function

drafts.js calls getText(), which the bundled SCEditor no longer has. I confirmed it on a clean checkout: type a subject and a body, wait past the autosave interval, and smf_user_drafts stays empty and id_draft stays 0. So this PR is less a refactor than a repair.

What changes

smf_DraftAutoSave reached into the editor from outside — waited for window.load hoping SCEditor had built its iframe by then, dug that iframe out with document.getElementsByTagName('iframe')[0], and attached blur and focus handlers by hand. Every caller then repeated its configuration in an inline <script>.

It becomes a plugin, so SCEditor says when it is ready and when focus moves rather than the code guessing:

  • drafts decides when to save.
  • messageDrafts / pmDrafts decide what to send.

That splits the old bPM flag and its branches into two small objects, and configuration moves into the editor's draftOptions, which Editor::setSCEditorOptions() already supports. The two inline <script> blocks go away.

Fixes on top of #7933

The version there does not work for personal messages. Three independent faults:

  1. getRecipient() and getFormData() refer to form and editor, which are declared const inside this.init rather than in the plugin closure — ReferenceError on every call.
  2. It posts id_pm_draft, but the server reads $_POST['id_draft'], so every autosave would create another draft instead of updating one.
  3. It posts recipient_to[] repeated, making $_POST['recipient_to'] an array, while Draft::setProperties() does explode(',', $_POST['recipient_to']) — a TypeError, fatal on every PM autosave.

Also dropped: a leftover console.log(XMLDoc); a draft_section lookup that assumes an element only the posting page has; a signalBlurEvent that cleared the interval without clearing the handle, so signalFocusEvent could never restart it; and php_to8bit() on FormData fields, which double-encodes now that the browser does the encoding.

One more found while testing, in both the old code and #7933: recipients were collected through form.elements['recipient_to[]'], which returns a RadioNodeList as soon as there is more than one. A RadioNodeList has a value property of its own, so a 'value' in el test cannot tell one recipient from several — with two selected it took the single-value branch and posted NaN. Collected by selector instead.

Testing

  • Posting: autosave creates the draft, id_draft comes back and is reused, the "last saved" note updates, the throbber clears. Verified in smf_user_drafts.
  • Personal messages: draft saved with type = 1 and to_list of {"to":[2,3],"bcc":[0]} — the server parsed both recipients from the joined value, which cannot happen with any of the three faults above.
  • Recipient serialisation checked with none, one and two selected: "", "2", "2,3".
  • No console errors on either page. 108/108 unit tests pass.

Unrelated bug found, not fixed here

Updating an existing draft does not write. The client sends the right request (id_draft set, new body) and the server answers with a fresh "last saved" time, but the row never changes. I traced it into Draft::saveToDatabase(): in the same request the DB layer can SELECT the row by that id, yet the UPDATE immediately afterwards reports pg_affected_rows() == 0 and the row keeps its old body and poster_time. The same statement run by hand in psql updates the row fine. saveToDatabase() returns true regardless, so nothing surfaces.

That is server-side and independent of this change — it just was not observable before, because autosave never got as far as saving a first draft. Happy to open it separately.

🤖 Generated with Claude Code

albertlast and others added 2 commits August 1, 2026 22:02
… plugin

smf_DraftAutoSave reached into the editor from outside: it waited for
window.load in the hope that SCEditor had built its iframe by then, dug that
iframe out of the document by index, and hung blur and focus handlers on it by
hand. Each caller then repeated its configuration in an inline <script>.

Rewrite it as a plugin, so SCEditor tells it when the editor is ready and when
focus moves, instead of it guessing. Deciding *when* to save lives in one
place; *what* to send comes from a second plugin, messageDrafts or pmDrafts,
so the post and personal message cases no longer share a bPM flag and a pile
of branches. Configuration moves into the editor's draftOptions.

Wave 1, part 5 of breaking up SimpleMachines#7933.

Co-Authored-By: John Rayes <live627@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
form.elements['recipient_to[]'] returns a RadioNodeList once there is more
than one recipient, and a RadioNodeList carries a value property of its own,
so testing for one cannot tell a single recipient apart from several. With
two recipients selected it took the single-value branch and posted NaN.
@albertlast

Copy link
Copy Markdown
Collaborator Author

One thing to flag for whoever merges: this overlaps #9335 textually. Both add to the editor's plugins option in Post::loadEditor() and Display::loadEditor(), so whichever lands second needs a conflict resolved. It is a union — keep both if blocks appending to $plugins, and keep the single 'plugins' => $plugins, 'options' => $options pair:

$plugins = [];
$options = [ /* autofocus, on Post only */ ];

if (Utils::$context['drafts_autosave']) {
	$plugins[] = 'drafts';
	$plugins[] = 'messageDrafts';
	$options['draftOptions'] = [ /* ... */ ];
}

if (!empty(Config::$modSettings['enable_mentions']) && User::$me->allowedTo('mention')) {
	$plugins[] = 'mentions';
}

I have built and tested that combination locally — the editor comes up with plugins: "smf,drafts,messageDrafts,mentions,autolinker", and drafts and mentions both work alongside each other. No other part of wave 1 conflicts with either.

@albertlast

albertlast commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

The Draft::saveToDatabase() problem described above now has its own home: #9339. It turned out to be entirely server-side and to touch no theme file, so it is independent of this series and of #7933.

Root cause was a copy-paste slip in Draft::__construct()poster_time was assigned to $this->type, so every loaded draft carried a unix timestamp as its type and wrote that back into a tinyint column on the next save. On PostgreSQL that is a smallint, so the whole UPDATE failed with "smallint out of range" while save() still returned true.

That also explains part of why the branch's pmDrafts plugin never worked: $this->type never equalled 1, so the PM branch of the id_pm_draft expression was unreachable and the wrong context key was being filled in by accident.

@jdarwood007 jdarwood007 added this to the 3.0 Alpha 5 milestone Aug 2, 2026
Comment thread Themes/default/scripts/sceditor.plugins.drafts.js Outdated
Review feedback on SimpleMachines#9337: multi-line comments should be the
/* * ... */ variant rather than stacked // lines.
albertlast added a commit to albertlast/SMF2.1 that referenced this pull request Aug 2, 2026
Review feedback on SimpleMachines#9337: multi-line comments should be the
/* * ... */ variant rather than stacked // lines.
albertlast added a commit to albertlast/SMF2.1 that referenced this pull request Aug 2, 2026
Review feedback on SimpleMachines#9337: multi-line comments should be the
/* * ... */ variant rather than stacked // lines.
@live627
live627 merged commit 44939b6 into SimpleMachines:release-3.0 Aug 2, 2026
4 checks passed
albertlast added a commit to albertlast/SMF2.1 that referenced this pull request Aug 2, 2026
Review feedback on SimpleMachines#9337: multi-line comments should be the
/* * ... */ variant rather than stacked // lines.
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