diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-07-13 17:09:31 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-07-13 17:09:31 +0200 |
| commit | aa14587b052a0aa7884aaf27183981975168ab14 (patch) | |
| tree | 2b043061a06474929bbfb9fe79a7985267ccef17 | |
| parent | 0b6ae26f3b4c3c8278e569fa6644a544e996c25d (diff) | |
Wire real autosave into translation editing
The shared TinyMCE setup initializes cccms.setup_autosave() on any
page with a textarea.with_editor, unconditionally starting a
7-second interval that submits to the form's data-autosave-url --
which the translation edit form never set, so the interval PUT the
current page URL itself and 404'd on a nonexistent route.
Fixed by actually giving it something to talk to, rather than
suppressing it: a real autosave endpoint, and update now goes
through Node#autosave!/#save_draft! -- the same pipeline the
primary editor uses, fixed earlier this session for exactly this
kind of cross-locale carryover. This was the autosave-buffer parity
already flagged as due after the proof of concept; the shared JS
just forced the timing.
| -rw-r--r-- | app/controllers/page_translations_controller.rb | 17 | ||||
| -rw-r--r-- | app/views/page_translations/edit.html.erb | 3 | ||||
| -rw-r--r-- | config/routes.rb | 7 | ||||
| -rw-r--r-- | test/controllers/page_translations_controller_test.rb | 19 |
4 files changed, 40 insertions, 6 deletions
diff --git a/app/controllers/page_translations_controller.rb b/app/controllers/page_translations_controller.rb index 47f01f7..9446cd2 100644 --- a/app/controllers/page_translations_controller.rb +++ b/app/controllers/page_translations_controller.rb | |||
| @@ -3,7 +3,7 @@ class PageTranslationsController < ApplicationController | |||
| 3 | 3 | ||
| 4 | before_action :login_required | 4 | before_action :login_required |
| 5 | before_action :find_node | 5 | before_action :find_node |
| 6 | before_action :find_locale, :only => [:show, :edit, :update, :destroy] | 6 | before_action :find_locale, :only => [:show, :edit, :update, :autosave, :destroy] |
| 7 | 7 | ||
| 8 | def index | 8 | def index |
| 9 | page = @node.draft || @node.head | 9 | page = @node.draft || @node.head |
| @@ -26,8 +26,8 @@ class PageTranslationsController < ApplicationController | |||
| 26 | end | 26 | end |
| 27 | 27 | ||
| 28 | def update | 28 | def update |
| 29 | draft = ensure_editable_draft | 29 | Globalize.with_locale(@locale) { @node.autosave!(translation_params, current_user) } |
| 30 | Globalize.with_locale(@locale) { draft.update!(translation_params) } | 30 | @node.save_draft!(current_user) |
| 31 | flash[:notice] = "#{@locale.upcase} translation saved. Publish the draft to make it live." | 31 | flash[:notice] = "#{@locale.upcase} translation saved. Publish the draft to make it live." |
| 32 | 32 | ||
| 33 | if params[:commit] == "Save + Unlock + Exit" | 33 | if params[:commit] == "Save + Unlock + Exit" |
| @@ -41,6 +41,17 @@ class PageTranslationsController < ApplicationController | |||
| 41 | redirect_to node_path(@node) | 41 | redirect_to node_path(@node) |
| 42 | end | 42 | end |
| 43 | 43 | ||
| 44 | def autosave | ||
| 45 | Globalize.with_locale(@locale) { @node.autosave!(translation_params, current_user) } | ||
| 46 | head :ok | ||
| 47 | rescue LockedByAnotherUser => e | ||
| 48 | render plain: e.message, status: :locked | ||
| 49 | rescue ActiveRecord::RecordInvalid => e | ||
| 50 | render plain: e.message, status: :unprocessable_entity | ||
| 51 | rescue StandardError => e | ||
| 52 | render plain: "Autosave failed", status: :internal_server_error | ||
| 53 | end | ||
| 54 | |||
| 44 | def destroy | 55 | def destroy |
| 45 | base = @node.draft || @node.head | 56 | base = @node.draft || @node.head |
| 46 | unless base && base.translated_locales.include?(@locale) | 57 | unless base && base.translated_locales.include?(@locale) |
diff --git a/app/views/page_translations/edit.html.erb b/app/views/page_translations/edit.html.erb index 7371a42..89b594e 100644 --- a/app/views/page_translations/edit.html.erb +++ b/app/views/page_translations/edit.html.erb | |||
| @@ -18,7 +18,8 @@ | |||
| 18 | </div> | 18 | </div> |
| 19 | 19 | ||
| 20 | <div id="page_editor"> | 20 | <div id="page_editor"> |
| 21 | <%= form_with url: node_translation_path(@node, @locale), method: :patch, local: true, id: "translation_edit_form" do |f| %> | 21 | <%= form_with url: node_translation_path(@node, @locale), method: :patch, local: true, id: "translation_edit_form", |
| 22 | data: { autosave_url: autosave_node_translation_path(@node, @locale), show_url: node_path(@node) } do |f| %> | ||
| 22 | <div id="content"> | 23 | <div id="content"> |
| 23 | <div class="node_description">Title</div> | 24 | <div class="node_description">Title</div> |
| 24 | <div class="node_content"> | 25 | <div class="node_content"> |
diff --git a/config/routes.rb b/config/routes.rb index 38a497d..826bb18 100644 --- a/config/routes.rb +++ b/config/routes.rb | |||
| @@ -58,8 +58,11 @@ Cccms::Application.routes.draw do | |||
| 58 | resources :translations, controller: 'page_translations', | 58 | resources :translations, controller: 'page_translations', |
| 59 | param: :translation_locale, | 59 | param: :translation_locale, |
| 60 | constraints: { translation_locale: /en/ }, | 60 | constraints: { translation_locale: /en/ }, |
| 61 | only: [:index, :show, :edit, :update, :destroy] | 61 | only: [:index, :show, :edit, :update, :destroy] do |
| 62 | 62 | member do | |
| 63 | put :autosave | ||
| 64 | end | ||
| 65 | end | ||
| 63 | 66 | ||
| 64 | resources :related_assets, only: [:create, :destroy, :update] do | 67 | resources :related_assets, only: [:create, :destroy, :update] do |
| 65 | collection do | 68 | collection do |
diff --git a/test/controllers/page_translations_controller_test.rb b/test/controllers/page_translations_controller_test.rb index d84ac45..8c899fd 100644 --- a/test/controllers/page_translations_controller_test.rb +++ b/test/controllers/page_translations_controller_test.rb | |||
| @@ -18,6 +18,7 @@ class PageTranslationsControllerTest < ActionController::TestCase | |||
| 18 | node = Node.root.children.create!(:slug => "translations_create_test") | 18 | node = Node.root.children.create!(:slug => "translations_create_test") |
| 19 | Globalize.with_locale(:de) { node.draft.update!(:title => "Deutscher Titel") } | 19 | Globalize.with_locale(:de) { node.draft.update!(:title => "Deutscher Titel") } |
| 20 | node.publish_draft! | 20 | node.publish_draft! |
| 21 | node.lock_for_editing!(users(:quentin)) | ||
| 21 | 22 | ||
| 22 | patch :update, params: { :node_id => node.id, :translation_locale => "en", :page => { :title => "English Title" } } | 23 | patch :update, params: { :node_id => node.id, :translation_locale => "en", :page => { :title => "English Title" } } |
| 23 | 24 | ||
| @@ -39,6 +40,7 @@ class PageTranslationsControllerTest < ActionController::TestCase | |||
| 39 | test "update with Save + Unlock + Exit unlocks the node and redirects to nodes#show" do | 40 | test "update with Save + Unlock + Exit unlocks the node and redirects to nodes#show" do |
| 40 | login_as :quentin | 41 | login_as :quentin |
| 41 | node = Node.root.children.create!(:slug => "translations_exit_test") | 42 | node = Node.root.children.create!(:slug => "translations_exit_test") |
| 43 | node.lock_for_editing!(users(:quentin)) | ||
| 42 | 44 | ||
| 43 | patch :update, params: { :node_id => node.id, :translation_locale => "en", :page => { :title => "x" }, :commit => "Save + Unlock + Exit" } | 45 | patch :update, params: { :node_id => node.id, :translation_locale => "en", :page => { :title => "x" }, :commit => "Save + Unlock + Exit" } |
| 44 | 46 | ||
| @@ -50,6 +52,7 @@ class PageTranslationsControllerTest < ActionController::TestCase | |||
| 50 | login_as :quentin | 52 | login_as :quentin |
| 51 | node = Node.root.children.create!(:slug => "translations_update_test") | 53 | node = Node.root.children.create!(:slug => "translations_update_test") |
| 52 | Globalize.with_locale(:en) { node.draft.update!(:title => "Original") } | 54 | Globalize.with_locale(:en) { node.draft.update!(:title => "Original") } |
| 55 | node.lock_for_editing!(users(:quentin)) | ||
| 53 | 56 | ||
| 54 | patch :update, params: { :node_id => node.id, :translation_locale => "en", :page => { :title => "Revised" } } | 57 | patch :update, params: { :node_id => node.id, :translation_locale => "en", :page => { :title => "Revised" } } |
| 55 | 58 | ||
| @@ -75,4 +78,20 @@ class PageTranslationsControllerTest < ActionController::TestCase | |||
| 75 | 78 | ||
| 76 | assert_match(/No EN translation exists/, flash[:error]) | 79 | assert_match(/No EN translation exists/, flash[:error]) |
| 77 | end | 80 | end |
| 81 | |||
| 82 | test "autosave writes the translation without creating a new revision or touching the draft" do | ||
| 83 | login_as :quentin | ||
| 84 | node = Node.root.children.create!(:slug => "translations_autosave_test") | ||
| 85 | node.publish_draft! | ||
| 86 | node.lock_for_editing!(users(:quentin)) | ||
| 87 | page_count_before = node.pages.count | ||
| 88 | |||
| 89 | put :autosave, params: { :node_id => node.id, :translation_locale => "en", :page => { :title => "in progress" } } | ||
| 90 | |||
| 91 | assert_response :success | ||
| 92 | node.reload | ||
| 93 | assert_not_nil node.autosave | ||
| 94 | assert_equal page_count_before, node.pages.count | ||
| 95 | assert_equal "in progress", Globalize.with_locale(:en) { node.autosave.title } | ||
| 96 | end | ||
| 78 | end | 97 | end |
