Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4d1b6af
Size sections relative to thumbnail viewport
tf Oct 22, 2026
8afed86
Number chapters in outline by index
tf Oct 22, 2026
f7796ba
Dim outline items while they are being deleted
tf Oct 16, 2026
a055a26
Add role column to scrolled storylines
tf Oct 16, 2026
c13b566
Detect main storyline by role
tf Oct 23, 2026
9d185cb
Hide trash storyline from revision queries
tf Oct 16, 2026
40b8254
Keep content trash out of publication and duplication
tf Oct 16, 2026
9a67dd0
Move deleted sections to the content trash
tf Oct 16, 2026
9adba67
Add editor endpoint listing content trash items
tf Oct 16, 2026
d0fc8ff
Restore sections from the content trash
tf Oct 16, 2026
424ed87
Add content trash items collection to editor entry
tf Oct 16, 2026
6f050f5
Add content trash view to editor sidebar
tf Oct 16, 2026
54a6c71
Add content trash button to outline toolbar
tf Oct 16, 2026
9ff3f03
Move deleted chapters to the content trash
tf Oct 22, 2026
957c4ad
Restore chapters from the content trash
tf Oct 22, 2026
c6f8f76
Restore chapters from the content trash in the editor
tf Oct 22, 2026
699b13c
Label trashed chapters with title and section count
tf Oct 22, 2026
e732f25
Move content elements deleted via batch to the content trash
tf Oct 22, 2026
fece7e2
Accept late updates of trashed content elements
tf Oct 22, 2026
cd7f1e1
Restore content elements from the content trash
tf Oct 22, 2026
dedba64
Send content elements deleted by the user to the content trash
tf Oct 22, 2026
df2f2d8
Restore content elements from the content trash in the editor
tf Oct 22, 2026
e55cb35
Label trashed content elements with types and origin section
tf Oct 22, 2026
5eda5b0
Mention the content trash in delete confirmations
tf Oct 22, 2026
ade05fd
Permanently delete content trash items
tf Oct 22, 2026
faf0e8a
Permanently delete content trash items in the editor
tf Oct 22, 2026
8a22e1e
Offer emptying the trash and deleting single trash items
tf Oct 22, 2026
1c52822
Nudge content trash button after deletions
tf Oct 22, 2026
14e54f5
Render trashed content elements on checkerboard backdrop
tf Oct 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/models/pageflow/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def publish(options = {}) # rubocop:todo Metrics/AbcSize
revisions.depublish_all
association(:published_revision).reset

draft.copy do |revision|
draft.copy(purpose: :publish) do |revision|
revision.creator = options[:creator]
revision.frozen_at = Time.now
revision.published_at = Time.now
Expand All @@ -102,7 +102,7 @@ def publish(options = {}) # rubocop:todo Metrics/AbcSize

def snapshot(options)
transaction do
draft.copy do |revision|
draft.copy(purpose: :snapshot) do |revision|
revision.creator = options[:creator]
revision.frozen_at = Time.now
revision.snapshot_type = options.fetch(:type, 'auto')
Expand All @@ -116,7 +116,7 @@ def restore(options)
draft.update!(frozen_at: Time.now, creator: options[:creator],
snapshot_type: 'before_restore')

restored_revision.copy do |revision|
restored_revision.copy(purpose: :restore) do |revision|
revision.restored_from = restored_revision
revision.frozen_at = nil
revision.snapshot_type = nil
Expand Down
3 changes: 2 additions & 1 deletion app/models/pageflow/revision.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def created_with
# Public interface for copying a revision
#
# @since 15.1
def copy
def copy(purpose: :duplicate)
revision = dup

yield(revision) if block_given?
Expand All @@ -190,6 +190,7 @@ def copy
[FileFolder, FileUsage]
)

Pageflow.config_for(revision.entry).revision_components.after_copy(revision, purpose:)
revision
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddRoleToPageflowScrolledStorylines < ActiveRecord::Migration[7.2]
def change
add_column :pageflow_scrolled_storylines, :role, :string
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def update

def destroy
chapter = Chapter.all_for_revision(@entry.draft).find(params[:id])
chapter.destroy
Trash.new(@entry.draft).add_chapter(chapter)

head :no_content
rescue ActiveRecord::RecordNotFound
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def batch
section = Section.all_for_revision(@entry.draft).find(params[:section_id])

items = params.require(:content_elements).map do |item|
item.transform_keys(&:underscore).permit(:id, :type_name, :_delete,
item.transform_keys(&:underscore).permit(:id, :type_name, :_delete, :_trash,
configuration: {},
migrate_comment_threads: [])
end
Expand All @@ -31,7 +31,8 @@ def batch
end

def update
content_element = ContentElement.all_for_revision(@entry.draft).find(params[:id])
content_element =
ContentElement.all_for_revision_including_trash(@entry.draft).find(params[:id])

ContentElement.transaction do
content_element.update(content_element_params)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def update

def destroy
section = Section.all_for_revision(@entry.draft).find(params[:id])
section.destroy
Trash.new(@entry.draft).add_section(section)

head :no_content
rescue ActiveRecord::RecordNotFound
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module PageflowScrolled
module Editor
# @api private
class TrashItemsController < ActionController::Base
include Pageflow::EditorController

def index
@items = Trash.new(@entry.draft).items
end

def restore
restored = Trash.new(@entry.draft).find_item(params[:id]).restore

case restored
when Chapter
render partial: 'pageflow_scrolled/editor/chapters/chapter_with_sections',
locals: {chapter: restored}
when Section
render partial: 'pageflow_scrolled/editor/sections/section_with_content_elements',
locals: {section: restored}
else
render partial: 'pageflow_scrolled/editor/trash_items/content_elements',
locals: {content_elements: restored}
end
rescue ActiveRecord::RecordNotFound
head :not_found
rescue Trash::DestinationMissing
head :unprocessable_entity
end

def destroy
Trash.new(@entry.draft).find_item(params[:id]).destroy

head :no_content
rescue ActiveRecord::RecordNotFound
head :not_found
end

def empty
Trash.new(@entry.draft).empty

head :no_content
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def scrolled_theme_stylesheet_pack_tags(theme)
end)
end

def scrolled_theme_properties_style_tag(theme)
def scrolled_theme_properties_style_tag(theme, include_section_thumbnail_rules: false)
declarations = [
scrolled_theme_deep_declarations(theme.options.fetch(:font_family, {}), 'font_family'),
scrolled_theme_deep_declarations(theme.options.fetch(:colors, {}), 'color')
Expand All @@ -38,7 +38,7 @@ def scrolled_theme_properties_style_tag(theme)
}

#{scrolled_theme_typography_rules(theme)}
#{scrolled_theme_properties_rules(theme)}
#{scrolled_theme_properties_rules(theme, include_section_thumbnail_rules:)}
CSS
end

Expand All @@ -63,9 +63,27 @@ def scrolled_theme_typography_rules(theme)
RuleSet.new(prefix: 'typography').generate(theme.options.fetch(:typography, {}))
end

def scrolled_theme_properties_rules(theme)
RuleSet.new(prefix: 'scope',
custom_properties: true).generate(theme.options.fetch(:properties, {}))
def scrolled_theme_properties_rules(theme, include_section_thumbnail_rules: false)
properties = theme.options.fetch(:properties, {})
rules = RuleSet.new(prefix: 'scope', custom_properties: true).generate(properties)
return rules unless include_section_thumbnail_rules

# Section thumbnails set viewport unit custom properties on an
# element with this attribute. Root properties using them only
# resolve against the thumbnail size if declared there again.
viewport_dependent_root_properties = properties.fetch(:root, {}).select do |_, value|
value.is_a?(String) && value.match?(/var\(--s?vh\b/)
end

return rules if viewport_dependent_root_properties.empty?

[
rules,
RuleSet.new(prefix: 'scope',
custom_properties: true,
root_selector: '[data-section-thumbnail]')
.generate(root: viewport_dependent_root_properties)
].join("\n")
end

private
Expand Down Expand Up @@ -232,9 +250,10 @@ def descriptors

# @api private
class RuleSet
def initialize(prefix:, custom_properties: false)
def initialize(prefix:, custom_properties: false, root_selector: ':root')
@prefix = prefix
@property_prefix = custom_properties ? '--theme-' : ''
@root_selector = root_selector
end

def generate(rules)
Expand Down Expand Up @@ -280,7 +299,7 @@ def generate_rule(rule_name, declarations)
end

def selector(rule_name)
return ':root' if rule_name == :root
return @root_selector if rule_name == :root

".#{@prefix}-#{rule_name.to_s.camelize(:lower)}"
end
Expand Down
8 changes: 8 additions & 0 deletions entry_types/scrolled/app/models/pageflow_scrolled/chapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,17 @@ def duplicate_section(section)
end
end

def insert_section(section, position:)
transaction do
shift_section_positions(from: position)
section.update!(chapter: self, position:)
end
end

def self.all_for_revision(revision)
joins(storyline: :revision)
.where(pageflow_scrolled_storylines: {revision_id: revision})
.merge(Storyline.excluding_trash)
.order(POSITION_ORDER)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ def entry_for_auto_generated_perma_id
end

def self.all_for_revision(revision)
all_for_revision_including_trash(revision).merge(Storyline.excluding_trash)
end

def self.all_for_revision_including_trash(revision)
joins(section: {chapter: {storyline: :revision}})
.where(pageflow_scrolled_storylines: {revision_id: revision})
.order(POSITION_ORDER)
Expand All @@ -42,9 +46,11 @@ def initialize(section, items, comment_thread_subject_ranges: nil)

def save!
ContentElement.transaction do
trash_content_elements

result = @items.map.with_index { |item, index|
if item[:_delete]
@section.content_elements.delete(item[:id])
@section.content_elements.delete(item[:id]) unless item[:_trash]
nil
else
create_or_update(item, index)
Expand All @@ -59,6 +65,33 @@ def save!

private

def trash_content_elements
trashed_ids = item_ids { |item| item[:_trash] }
return if trashed_ids.empty?

content_elements = ContentElement.where(section: @section).order(:position).to_a
trashed = content_elements.select { |ce| trashed_ids.include?(ce.id) }
raise ActiveRecord::RecordNotFound if trashed.size < trashed_ids.size

Trash.new(@storyline.revision).add_content_elements(
trashed,
origin_section: @section,
origin_position: count_remaining_before(content_elements, trashed_ids)
)
end

def count_remaining_before(content_elements, trashed_ids)
deleted_ids = item_ids { |item| item[:_delete] }

content_elements
.take_while { |ce| trashed_ids.exclude?(ce.id) }
.count { |ce| deleted_ids.exclude?(ce.id) }
end

def item_ids(&)
@items.select(&).map { |item| item[:id].to_i }
end

def create_or_update(item, index)
attributes = {
section_id: @section.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def chapters_by_revision_id
PageflowScrolled::Chapter
.joins(:storyline)
.where(pageflow_scrolled_storylines: {revision_id: revision_ids})
.merge(PageflowScrolled::Storyline.excluding_trash)
.order(PageflowScrolled::Chapter::POSITION_ORDER)
.select('pageflow_scrolled_chapters.*', 'pageflow_scrolled_storylines.revision_id')
.group_by(&:revision_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def storylines
end

def main_storyline
storylines.detect { |storyline| storyline.configuration['main'] }
storylines.detect { |storyline| storyline.role == 'main' }
end

def chapters
Expand Down
13 changes: 13 additions & 0 deletions entry_types/scrolled/app/models/pageflow_scrolled/section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,26 @@ class Section < Pageflow::ApplicationRecord # rubocop:todo Style/Documentation

nested_revision_components :content_elements

def insert_content_elements(inserted, position:)
transaction do
content_elements
.where('position >= ?', position)
.update_all(['position = position + ?', inserted.size])

inserted.each_with_index do |content_element, index|
content_element.update!(section: self, position: position + index)
end
end
end

def entry_for_auto_generated_perma_id
chapter.storyline.revision.entry
end

def self.all_for_revision(revision)
joins(chapter: {storyline: :revision})
.where(pageflow_scrolled_storylines: {revision_id: revision})
.merge(Storyline.excluding_trash)
.order(POSITION_ORDER)
end
end
Expand Down
36 changes: 34 additions & 2 deletions entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,45 @@ class Storyline < Pageflow::ApplicationRecord # rubocop:todo Style/Documentation

nested_revision_components :chapters

scope :excluding_trash, -> { where.not(role: 'trash').or(where(role: nil)) }

def role
super || (configuration['main'] ? 'main' : 'excursions')
end

def insert_chapter(chapter, position:)
transaction do
chapters.where('position >= ?', position).update_all('position = position + 1')
chapter.update!(storyline: self, position:)
end
end

def sections_before_cutoff_section
sections_before(cutoff_section)
end

def self.create_defaults(draft)
create!(revision: draft, configuration: {main: true})
create!(revision: draft, position: 1)
create!(revision: draft, role: 'main')
create!(revision: draft, role: 'excursions', position: 1)
end

def self.all_for_revision(revision)
super.excluding_trash
end

def self.find_or_create_trash_for(revision)
revision.with_lock do
where(revision:, role: 'trash').first ||
create!(revision:,
role: 'trash',
position: where(revision:).maximum(:position).to_i + 1)
end
end

def self.after_copy(revision, purpose:)
return if %i[snapshot restore].include?(purpose)

where(revision:, role: 'trash').destroy_all
end

private
Expand Down
Loading
Loading