diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-07-23 19:45:47 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-07-23 19:45:47 +0200 |
| commit | aedf5778aba87c4e8d036dde2a0b6ec79cf8b342 (patch) | |
| tree | 059a8954c332d0a7c3e48e44906a4104d4338007 /app | |
| parent | f993853db3e233f05a55de5ba2a87b77acf041aa (diff) | |
Keep in-editor asset curation off the head, layering it like every edit
ensure_autosave! gives body keystrokes and asset curation one shared
layer, so head is never mutated in place and every curation change
surfaces in the publish delta. Stale rendered join ids are mapped
across the clone via asset_id. Curation now requires holding the
lock; a missing lock answers 423, matching the autosave endpoint.
Diffstat (limited to 'app')
| -rw-r--r-- | app/controllers/related_assets_controller.rb | 18 | ||||
| -rw-r--r-- | app/models/node.rb | 12 |
2 files changed, 19 insertions, 11 deletions
diff --git a/app/controllers/related_assets_controller.rb b/app/controllers/related_assets_controller.rb index ca894f2f..3aa6d3db 100644 --- a/app/controllers/related_assets_controller.rb +++ b/app/controllers/related_assets_controller.rb | |||
| @@ -2,6 +2,10 @@ class RelatedAssetsController < ApplicationController | |||
| 2 | before_action :login_required | 2 | before_action :login_required |
| 3 | before_action :find_node | 3 | before_action :find_node |
| 4 | 4 | ||
| 5 | rescue_from LockedByAnotherUser do | ||
| 6 | head :locked | ||
| 7 | end | ||
| 8 | |||
| 5 | def search | 9 | def search |
| 6 | term = params[:search_term].to_s.strip | 10 | term = params[:search_term].to_s.strip |
| 7 | attached_ids = @node.editable_page.related_assets.pluck(:asset_id) | 11 | attached_ids = @node.editable_page.related_assets.pluck(:asset_id) |
| @@ -19,8 +23,9 @@ class RelatedAssetsController < ApplicationController | |||
| 19 | end | 23 | end |
| 20 | 24 | ||
| 21 | def create | 25 | def create |
| 26 | page = @node.ensure_autosave!(current_user) | ||
| 22 | asset = Asset.find(params[:asset_id]) | 27 | asset = Asset.find(params[:asset_id]) |
| 23 | related = @node.editable_page.related_assets.find_or_create_by!(asset: asset) | 28 | related = page.related_assets.find_or_create_by!(asset: asset) |
| 24 | 29 | ||
| 25 | render json: { | 30 | render json: { |
| 26 | id: related.id, | 31 | id: related.id, |
| @@ -35,22 +40,25 @@ class RelatedAssetsController < ApplicationController | |||
| 35 | end | 40 | end |
| 36 | 41 | ||
| 37 | def destroy | 42 | def destroy |
| 38 | @node.editable_page.related_assets.find(params[:id]).destroy | 43 | page = @node.ensure_autosave!(current_user) |
| 44 | source = RelatedAsset.find(params[:id]) | ||
| 45 | page.related_assets.find_by!(:asset_id => source.asset_id).destroy | ||
| 39 | head :ok | 46 | head :ok |
| 40 | end | 47 | end |
| 41 | 48 | ||
| 42 | def update | 49 | def update |
| 43 | related = @node.editable_page.related_assets.find(params[:id]) | 50 | page = @node.ensure_autosave!(current_user) |
| 51 | source = RelatedAsset.find(params[:id]) | ||
| 52 | related = page.related_assets.find_by!(:asset_id => source.asset_id) | ||
| 44 | 53 | ||
| 45 | if params.key?(:headline) | 54 | if params.key?(:headline) |
| 46 | RelatedAsset.transaction do | 55 | RelatedAsset.transaction do |
| 47 | @node.editable_page.related_assets.update_all(headline: false) | 56 | page.related_assets.update_all(headline: false) |
| 48 | related.update!(headline: true) if params[:headline] == "true" | 57 | related.update!(headline: true) if params[:headline] == "true" |
| 49 | end | 58 | end |
| 50 | else | 59 | else |
| 51 | related.insert_at(params[:position].to_i) | 60 | related.insert_at(params[:position].to_i) |
| 52 | end | 61 | end |
| 53 | |||
| 54 | head :ok | 62 | head :ok |
| 55 | end | 63 | end |
| 56 | 64 | ||
diff --git a/app/models/node.rb b/app/models/node.rb index 274b2f94..02502f6c 100644 --- a/app/models/node.rb +++ b/app/models/node.rb | |||
| @@ -112,18 +112,18 @@ class Node < ApplicationRecord | |||
| 112 | end | 112 | end |
| 113 | end | 113 | end |
| 114 | 114 | ||
| 115 | # Creates or updates the autosave buffer from the given attributes. | 115 | def ensure_autosave! current_user |
| 116 | # Autosave rows are never associated to the node via node_id -- they | ||
| 117 | # must never appear in self.pages / the revisions list, which is the | ||
| 118 | # whole reason autosave exists as a separate, unversioned layer. | ||
| 119 | def autosave! attributes, current_user | ||
| 120 | assert_locked_by! current_user | 116 | assert_locked_by! current_user |
| 121 | |||
| 122 | unless self.autosave | 117 | unless self.autosave |
| 123 | self.autosave = Page.create!(:editor => current_user) | 118 | self.autosave = Page.create!(:editor => current_user) |
| 124 | self.autosave.clone_attributes_from(self.draft || self.head) if self.draft || self.head | 119 | self.autosave.clone_attributes_from(self.draft || self.head) if self.draft || self.head |
| 125 | self.save! | 120 | self.save! |
| 126 | end | 121 | end |
| 122 | self.autosave | ||
| 123 | end | ||
| 124 | |||
| 125 | def autosave! attributes, current_user | ||
| 126 | ensure_autosave!(current_user) | ||
| 127 | self.autosave.assign_attributes(attributes) | 127 | self.autosave.assign_attributes(attributes) |
| 128 | self.autosave.save! | 128 | self.autosave.save! |
| 129 | self.autosave | 129 | self.autosave |
