From 43e7743ebff1b1f41bbb5ae39bcc5bed7d8982e3 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Wed, 8 Jul 2026 16:24:05 +0200 Subject: Autosave posts to its own endpoint surface lock loss immediately Previously piggybacked on the main form's action and relied on a .js.erb response to update #flash; now posts to the dedicated autosave route with an explicit _method=put override, and reacts to a 423 by stopping the timer and showing a persistent banner rather than letting the next Save discover the conflict on its own. --- app/views/nodes/edit.html.erb | 2 +- public/javascripts/admin_interface.js | 38 ++++++++++++++++++++++++++++++++--- public/stylesheets/admin.css | 5 +++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/app/views/nodes/edit.html.erb b/app/views/nodes/edit.html.erb index 4693569..1c6cc3a 100644 --- a/app/views/nodes/edit.html.erb +++ b/app/views/nodes/edit.html.erb @@ -5,7 +5,7 @@ <% end %>
-<%= form_for(@node) do |f| %> + <%= form_for(@node, html: { data: { autosave_url: autosave_node_path(@node), show_url: node_path(@node) } }) do |f| %> <% if @node.errors.any? %>
    <% @node.errors.full_messages.each do |msg| %>
  • <%= msg %>
  • <% end %>
diff --git a/public/javascripts/admin_interface.js b/public/javascripts/admin_interface.js index 41699a1..4107ce8 100644 --- a/public/javascripts/admin_interface.js +++ b/public/javascripts/admin_interface.js @@ -159,13 +159,45 @@ cccms = { page.cached_abstract = elements.abstract.val(); page.cached_body = elements.body.html(); - $("#flash").append(""); - $.post(this.attr("action"), $(this).serialize(), null, "script"); + var data = this.serializeArray().filter(function(field) { + return field.name !== "_method"; + }); + data.push({ name: "_method", value: "put" }); + + $.ajax({ + type: "POST", + url: this.attr("data-autosave-url"), + data: data, + error: function(xhr) { + if (xhr.status === 423) { + clearInterval(cccms.autosave_timer); + cccms.report_lock_lost($("#page_editor > form").attr("data-show-url")); + } + // any other failure: quietly retried on the next tick + } + }); } }; - setInterval('$("#page_editor > form").submitWithAjax()', 7000); + cccms.autosave_timer = setInterval(function() { + $("#page_editor > form").submitWithAjax(); + }, 7000); + }, + + report_lock_lost : function(show_url) { + var $flash = $("#flash"); + if ($flash.length === 0) { + $flash = $("
", { id: "flash" }).insertAfter(".admin_content_spacer"); + } + + var $banner = $("", { "class": "warning" }).text( + "This page is now locked by someone else — your changes here can no longer be saved. Copy anything you need, then " + ); + $banner.append($("", { href: show_url }).text("return to the published page")); + $banner.append("."); + + $flash.html($banner); } } diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index 0c0779f..7ae374c 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css @@ -200,6 +200,11 @@ span#flash_error, span.warning { padding-bottom: 1px; } +span.warning a { + color: inherit; + text-decoration: underline; +} + /* ============================================================ Pagination ============================================================ */ -- cgit v1.3