summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/assets_controller.rb22
-rw-r--r--app/controllers/nodes_controller.rb16
-rw-r--r--app/models/asset.rb9
-rw-r--r--app/models/node.rb66
-rw-r--r--app/views/assets/edit.html.erb17
-rw-r--r--app/views/assets/new.html.erb17
-rw-r--r--app/views/assets/show.html.erb25
-rw-r--r--app/views/layouts/_flash.html.erb24
-rw-r--r--app/views/layouts/admin.html.erb17
-rw-r--r--app/views/nodes/_recent_change_item.html.erb9
-rw-r--r--app/views/nodes/edit.html.erb129
-rw-r--r--app/views/nodes/new.html.erb18
-rw-r--r--app/views/nodes/recent.html.erb7
-rw-r--r--app/views/nodes/show.html.erb29
14 files changed, 291 insertions, 114 deletions
diff --git a/app/controllers/assets_controller.rb b/app/controllers/assets_controller.rb
index becfe13..fbede0a 100644
--- a/app/controllers/assets_controller.rb
+++ b/app/controllers/assets_controller.rb
@@ -28,6 +28,7 @@ class AssetsController < ApplicationController
28 # GET /assets/new.xml 28 # GET /assets/new.xml
29 def new 29 def new
30 @asset = Asset.new 30 @asset = Asset.new
31 @attach_node = Node.not_in_trash.find_by(:id => params[:node_id]) if params[:node_id].present?
31 32
32 respond_to do |format| 33 respond_to do |format|
33 format.html # new.html.erb 34 format.html # new.html.erb
@@ -44,10 +45,12 @@ class AssetsController < ApplicationController
44 # POST /assets.xml 45 # POST /assets.xml
45 def create 46 def create
46 @asset = Asset.new(asset_params) 47 @asset = Asset.new(asset_params)
48 attach_node = Node.not_in_trash.find_by(:id => params[:node_id]) if params[:node_id].present?
47 49
48 respond_to do |format| 50 respond_to do |format|
49 if @asset.save 51 if @asset.save
50 flash[:notice] = 'Asset was successfully created.' 52 flash[:notice] = 'Asset was successfully created.'
53 attach_to(attach_node) if attach_node
51 format.html { redirect_to(@asset) } 54 format.html { redirect_to(@asset) }
52 format.xml { render :xml => @asset, :status => :created, :location => @asset } 55 format.xml { render :xml => @asset, :status => :created, :location => @asset }
53 else 56 else
@@ -91,4 +94,23 @@ class AssetsController < ApplicationController
91 def asset_params 94 def asset_params
92 params.require(:asset).permit(:name, :upload, :creator, :source_url, :license_key) 95 params.require(:asset).permit(:name, :upload, :creator, :source_url, :license_key)
93 end 96 end
97
98 def attach_to node
99 result = node.attach_asset!(@asset, :user => current_user,
100 :headline => params[:headline].present?)
101 flash[:notice] =
102 if result[:attached].zero?
103 "Asset saved — it was already attached to “#{node.title}”."
104 else
105 "Asset was successfully created and attached to “#{node.title}”."
106 end
107 case result[:headline]
108 when :set then flash[:notice] += " It is now the page's headline."
109 when :kept_existing then flash[:headline_kept_path] = node_path(node)
110 when :not_eligible then flash[:error] = "This asset type cannot be a headline."
111 end
112 rescue LockedByAnotherUser
113 flash[:locked_by] = node.lock_owner&.login
114 flash[:locked_node_path] = node_path(node)
115 end
94end 116end
diff --git a/app/controllers/nodes_controller.rb b/app/controllers/nodes_controller.rb
index 6caa827..ce8f053 100644
--- a/app/controllers/nodes_controller.rb
+++ b/app/controllers/nodes_controller.rb
@@ -30,6 +30,7 @@ class NodesController < ApplicationController
30 @node = Node.new node_params 30 @node = Node.new node_params
31 @selected_kind = CccConventions::NODE_KINDS.key?(params[:kind]) ? params[:kind] : "generic" 31 @selected_kind = CccConventions::NODE_KINDS.key?(params[:kind]) ? params[:kind] : "generic"
32 @parent = Node.find(params[:parent_id]) if params.has_key?(:parent_id) 32 @parent = Node.find(params[:parent_id]) if params.has_key?(:parent_id)
33 @attach_asset = Asset.find(params[:asset_id]) if params.has_key?(:asset_id)
33 end 34 end
34 35
35 def create 36 def create
@@ -51,10 +52,19 @@ class NodesController < ApplicationController
51 :action => "create", 52 :action => "create",
52 :title => params[:title], :path => @node.unique_name) 53 :title => params[:title], :path => @node.unique_name)
53 54
55 if params[:asset_id].present? && (asset = Asset.find(params[:asset_id]))
56 result = @node.attach_asset!(asset, :user => current_user,
57 :headline => params[:asset_headline].present?)
58 flash[:notice] = "Page created with “#{asset.name}” attached."
59 flash[:notice] += " It is the page's headline." if result[:headline] == :set
60 flash[:error] = "This asset type cannot be a headline." if result[:headline] == :not_eligible
61 end
62
54 redirect_to(edit_node_path(@node)) 63 redirect_to(edit_node_path(@node))
55 else 64 else
56 @selected_kind = CccConventions::NODE_KINDS.key?(params[:kind]) ? params[:kind] : "generic" 65 @selected_kind = CccConventions::NODE_KINDS.key?(params[:kind]) ? params[:kind] : "generic"
57 @parent = Node.find(params[:parent_id]) if params.has_key?(:parent_id) 66 @parent = Node.find(params[:parent_id]) if params.has_key?(:parent_id)
67 @attach_asset = Asset.find(params[:asset_id]) if params.has_key?(:asset_id)
58 render :new 68 render :new
59 end 69 end
60 end 70 end
@@ -77,7 +87,7 @@ class NodesController < ApplicationController
77 "This page has unsaved changes from a previous session, shown below. " \ 87 "This page has unsaved changes from a previous session, shown below. " \
78 "Save to keep them, or use \"Discard Autosave\" below to go back to the last saved version." 88 "Save to keep them, or use \"Discard Autosave\" below to go back to the last saved version."
79 elsif freshly_locked 89 elsif freshly_locked
80 flash.now[:notice] = "Node locked and ready to edit" 90 flash.now[:notice] ||= "Node locked and ready to edit"
81 end 91 end
82 rescue LockedByAnotherUser => e 92 rescue LockedByAnotherUser => e
83 flash[:error] = e.message 93 flash[:error] = e.message
@@ -223,10 +233,6 @@ class NodesController < ApplicationController
223 @nodes = index_matching(Node.drafts_and_autosaves) 233 @nodes = index_matching(Node.drafts_and_autosaves)
224 end 234 end
225 235
226 def recent
227 @nodes = index_matching(Node.recently_changed)
228 end
229
230 def mine 236 def mine
231 base = Node.joins(:pages) 237 base = Node.joins(:pages)
232 .where("pages.user_id = ? or pages.editor_id = ?", current_user, current_user) 238 .where("pages.user_id = ? or pages.editor_id = ?", current_user, current_user)
diff --git a/app/models/asset.rb b/app/models/asset.rb
index ba9c2f0..73970e2 100644
--- a/app/models/asset.rb
+++ b/app/models/asset.rb
@@ -31,4 +31,13 @@ class Asset < ApplicationRecord
31 def show_credit? 31 def show_credit?
32 image? && has_credit? 32 image? && has_credit?
33 end 33 end
34
35 # Nodes whose current lifecycle rows (head, draft or autosave) carry
36 # this asset. Historical revisions also hold RelatedAsset rows; they
37 # are excluded by construction here, since nothing points at them.
38 def attached_nodes
39 page_ids = related_assets.select(:page_id)
40 Node.where("head_id IN (:ids) OR draft_id IN (:ids) OR autosave_id IN (:ids)",
41 :ids => page_ids).distinct
42 end
34end 43end
diff --git a/app/models/node.rb b/app/models/node.rb
index a440c2f..a5a40d3 100644
--- a/app/models/node.rb
+++ b/app/models/node.rb
@@ -442,8 +442,65 @@ class Node < ApplicationRecord
442 end 442 end
443 end 443 end
444 444
445 # Attaches an asset to every current lifecycle row -- head, draft and
446 # autosave -- that does not already carry it. Attachments are page-
447 # scoped content (RelatedAsset belongs_to :page; drafts and autosaves
448 # are wholesale clones), so attaching to a single layer is how an
449 # attachment gets silently lost when another layer replaces it at
450 # publish or save. This is the out-of-band counterpart to the
451 # in-editor attach UI; it refuses when someone else holds the editing
452 # lock. Attaching to a head row changes the public page immediately,
453 # by design -- same reasoning as formalizing an already-existing
454 # editorial link.
455 #
456 # headline is a node-level decision: the flag is set on the newly
457 # created joins only when no current row has a headline yet and the
458 # asset is eligible; otherwise the asset is attached plain and the
459 # result says why, so the caller can point the editor at the star in
460 # the editor instead.
461 #
462 # Returns { :attached => n, :already => n,
463 # :headline => nil | :set | :kept_existing | :not_eligible }
464 def attach_asset! asset, user:, headline: false
465 if in_trash? || trash_node?
466 raise ActiveRecord::RecordInvalid.new(self), "Cannot attach assets to a node in the Trash"
467 end
468
469 if lock_owner && lock_owner != user
470 raise(
471 LockedByAnotherUser,
472 "Page is locked by another user who is working on it! " \
473 "Last modification: #{(self.autosave || self.draft || self.head).updated_at.to_fs(:db)}"
474 )
475 end
476
477 rows = [head, draft, autosave].compact
478 to_attach = rows.reject { |row| row.related_assets.exists?(:asset_id => asset.id) }
479
480 headline_state =
481 if headline && to_attach.any?
482 if !(asset.image? || asset.pdf?)
483 :not_eligible
484 elsif rows.any? { |row| row.headline_asset.present? }
485 :kept_existing
486 else
487 :set
488 end
489 end
490
491 ActiveRecord::Base.transaction do
492 to_attach.each do |row|
493 row.related_assets.create!(:asset => asset, :headline => headline_state == :set)
494 end
495 end
496
497 { :attached => to_attach.size,
498 :already => rows.size - to_attach.size,
499 :headline => headline_state }
500 end
501
445 def title 502 def title
446 head ? head.title : draft.title 503 editable_page&.title
447 end 504 end
448 505
449 def update_unique_names? 506 def update_unique_names?
@@ -533,13 +590,6 @@ class Node < ApplicationRecord
533 throw :abort 590 throw :abort
534 end 591 end
535 592
536 def self.recently_changed
537 includes(:head).where(
538 "pages.updated_at < ? AND pages.updated_at > ? AND nodes.parent_id IS NOT NULL",
539 Time.now, Time.now - 14.days
540 ).order("pages.updated_at desc").references(:head)
541 end
542
543 protected 593 protected
544 def lock_for! current_user 594 def lock_for! current_user
545 self.lock_owner = current_user 595 self.lock_owner = current_user
diff --git a/app/views/assets/edit.html.erb b/app/views/assets/edit.html.erb
index 3ef8fea..0c1dd8f 100644
--- a/app/views/assets/edit.html.erb
+++ b/app/views/assets/edit.html.erb
@@ -31,6 +31,23 @@
31 ) %> 31 ) %>
32 </div> 32 </div>
33 33
34 <div class="node_description">attach to page</div>
35 <div class="node_content">
36 <div class="restore_picker">
37 <%= text_field_tag :asset_node_search_term, @attach_node&.title,
38 :placeholder => "Search for a page…", :autocomplete => "off" %>
39 <div id="asset_node_search_results" class="search_results" style="display: none"></div>
40 </div>
41 <%= hidden_field_tag :node_id, @attach_node&.id %>
42 <span class="field_hint">Optional — attaches this asset to that page, all pending versions included.</span>
43 </div>
44
45 <div class="node_description"></div>
46 <div class="node_content">
47 <label><%= check_box_tag :headline, "1" %> attach as the page's headline</label>
48 <span class="field_hint">Applies only if the page has no headline yet.</span>
49 </div>
50
34 <div class="node_description">Actions</div> 51 <div class="node_description">Actions</div>
35 <div class="node_content node_info_group"> 52 <div class="node_content node_info_group">
36 <div class="node_info_group_items"> 53 <div class="node_info_group_items">
diff --git a/app/views/assets/new.html.erb b/app/views/assets/new.html.erb
index 2cf8865..7bded94 100644
--- a/app/views/assets/new.html.erb
+++ b/app/views/assets/new.html.erb
@@ -26,6 +26,23 @@
26 ) %> 26 ) %>
27 </div> 27 </div>
28 28
29 <div class="node_description">attach to page</div>
30 <div class="node_content">
31 <div class="restore_picker">
32 <%= text_field_tag :asset_node_search_term, @attach_node&.title,
33 :placeholder => "Search for a page…", :autocomplete => "off" %>
34 <div id="asset_node_search_results" class="search_results" style="display: none"></div>
35 </div>
36 <%= hidden_field_tag :node_id, @attach_node&.id %>
37 <span class="field_hint">Optional — attaches this asset to that page, all pending versions included.</span>
38 </div>
39
40 <div class="node_description"></div>
41 <div class="node_content">
42 <label><%= check_box_tag :headline, "1" %> attach as the page's headline</label>
43 <span class="field_hint">Applies only if the page has no headline yet.</span>
44 </div>
45
29 <div class="node_description">Actions</div> 46 <div class="node_description">Actions</div>
30 <div class="node_content node_info_group"> 47 <div class="node_content node_info_group">
31 <div class="node_info_group_items"> 48 <div class="node_info_group_items">
diff --git a/app/views/assets/show.html.erb b/app/views/assets/show.html.erb
index e551e35..2723821 100644
--- a/app/views/assets/show.html.erb
+++ b/app/views/assets/show.html.erb
@@ -21,6 +21,31 @@
21 <div class="node_content"><%= image_tag @asset.upload.url(:medium), style: "max-width: 300px; max-height: 300px;" %></div> 21 <div class="node_content"><%= image_tag @asset.upload.url(:medium), style: "max-width: 300px; max-height: 300px;" %></div>
22 <% end %> 22 <% end %>
23 23
24 <div class="node_description">Attached to</div>
25 <div class="node_content">
26 <% nodes = @asset.attached_nodes %>
27 <% if nodes.any? %>
28 <ul class="search_results_list">
29 <% nodes.each do |node| %>
30 <li>
31 <%= link_to node.title, node_path(node) %>
32 <% if node.editable_page&.headline_asset == @asset %>
33 <%= icon("star", library: "tabler", "aria-hidden": true) %>
34 <% end %>
35 <% unless node.head && node.head.related_assets.exists?(:asset_id => @asset.id) %>
36 <span class="field_hint">pending versions only</span>
37 <% end %>
38 </li>
39 <% end %>
40 </ul>
41 <% else %>
42 — not attached to any page —
43 <% end %>
44 <%= link_to new_node_path(:asset_id => @asset.id), :class => "action_button" do %>
45 <%= icon("file-plus", library: "tabler", "aria-hidden": true) %> New page with this attachment
46 <% end %>
47 </div>
48
24 <div class="node_description">Public Path</div> 49 <div class="node_description">Public Path</div>
25 <div class="node_content"> 50 <div class="node_content">
26 <% public_path = @asset.upload.url.sub(/\?\d+$/, "") %> 51 <% public_path = @asset.upload.url.sub(/\?\d+$/, "") %>
diff --git a/app/views/layouts/_flash.html.erb b/app/views/layouts/_flash.html.erb
new file mode 100644
index 0000000..a67fc86
--- /dev/null
+++ b/app/views/layouts/_flash.html.erb
@@ -0,0 +1,24 @@
1<% if flash.any? %>
2<div id="flash">
3 <%= flash[:notice] %>
4 <% if flash[:status_path] %>
5 <%= link_to 'Go to Status', flash[:status_path] %>
6 <% end %>
7 <% if flash[:stale_locale_path] %>
8 The <%= flash[:stale_locale] %> translation may be out of date —
9 <%= link_to 'review it', flash[:stale_locale_path] %> too.
10 <% end %>
11 <% if flash[:locked_node_path] %>
12 <span class="warning">The page is locked by <%= flash[:locked_by] %> —
13 <%= link_to 'unlock it there', flash[:locked_node_path] %> first,
14 then attach from this asset's page.</span>
15 <% end %>
16 <% if flash[:headline_kept_path] %>
17 <span class="warning">The page's existing headline was kept —
18 <%= link_to 'change it there', flash[:headline_kept_path] %> if needed.</span>
19 <% end %>
20 <% if flash[:error] %>
21 <span id="flash_error"><%= flash[:error] %></span>
22 <% end %>
23</div>
24<% end %>
diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb
index e220beb..89c9b55 100644
--- a/app/views/layouts/admin.html.erb
+++ b/app/views/layouts/admin.html.erb
@@ -37,25 +37,10 @@
37 </div> 37 </div>
38 </div> 38 </div>
39 <div class="admin_content_spacer"></div> 39 <div class="admin_content_spacer"></div>
40 <% if flash[:notice].present? || flash[:error].present? %> 40 <%= render "layouts/flash" %>
41 <div id="flash">
42 <%= flash[:notice] %>
43 <% if flash[:status_path] %>
44 <%= link_to 'Go to Status', flash[:status_path] %>
45 <% end %>
46 <% if flash[:stale_locale_path] %>
47 The <%= flash[:stale_locale] %> translation may be out of date —
48 <%= link_to 'review it', flash[:stale_locale_path] %> too.
49 <% end %>
50 <% if flash[:error] %>
51 <span id="flash_error"><%= flash[:error] %></span>
52 <% end %>
53 </div>
54 <% end %>
55 <div id="content"> 41 <div id="content">
56 <%= yield %> 42 <%= yield %>
57 </div> 43 </div>
58
59 <div id="results"></div> 44 <div id="results"></div>
60 </div> 45 </div>
61 </body> 46 </body>
diff --git a/app/views/nodes/_recent_change_item.html.erb b/app/views/nodes/_recent_change_item.html.erb
deleted file mode 100644
index 754a775..0000000
--- a/app/views/nodes/_recent_change_item.html.erb
+++ /dev/null
@@ -1,9 +0,0 @@
1<li>
2 <div>
3 <%= link_to title_for_node(node), node_path(node) %>
4 <span class="field_hint"><%= link_to_path("#{node.unique_name} ↗", node.unique_name) %></span>
5 </div>
6 <span class="dashboard_widget_meta">
7 <%= (editor = node_head_editor(node)) ? t("last_edited_by", editor: editor) : t("last_edited") %>, <%= relative_time_phrase(node.head.updated_at) %>
8 </span>
9</li>
diff --git a/app/views/nodes/edit.html.erb b/app/views/nodes/edit.html.erb
index 12dc000..fc1d18f 100644
--- a/app/views/nodes/edit.html.erb
+++ b/app/views/nodes/edit.html.erb
@@ -33,48 +33,17 @@
33 </div> 33 </div>
34 <% end %> 34 <% end %>
35 35
36 <details id="metadata_details"> 36 <%= fields_for @page do |d| %>
37 <summary>Metadata (slug, parent, tags, template, author, images)</summary> 37 <div id="edit_grid">
38 <div id="metadata">
39 <div class="node_description">Slug</div>
40 <div class="node_content">
41 <%= f.text_field(
42 :staged_slug, :value => @node.staged_slug || @node.slug
43 )
44 %>
45 </div>
46 38
47 <div class="node_description">parent</div> 39 <div id="main_fields">
48 <div class="node_content"> 40 <div class="node_description">Title</div>
49 <%= text_field_tag :move_to_search_term, @node.parent.title rescue "" %> 41 <div class="node_content"><%= d.text_field :title %></div>
50 <div id="move_to_search_results" class="search_results"></div>
51 <%= f.hidden_field(
52 :staged_parent_id,
53 :value => @node.staged_parent_id || @node.parent_id
54 )
55 %>
56 </div>
57 42
58 <%= fields_for @page do |d| %> 43 <div class="node_description">Abstract</div>
59 <div class="node_description">Tags - comma seperated</div> 44 <div class="node_content"><%= d.text_area :abstract %></div>
60 <div class="node_content"><%= text_field_tag :tag_list, @page.tag_list.join(', ') %></div>
61 45
62 <div class="node_description">Publish at</div> 46 <div class="node_description">Attachments</div>
63 <div class="node_content"><%= d.datetime_select :published_at, :value => @page.published_at %></div>
64
65 <div class="node_description">Template</div>
66 <div class="node_content">
67 <%= d.select :template_name, custom_page_templates, {:prompt => 'Select Template'} %>
68 <span class="field_hint">Set automatically based on how this node was created - change it if needed.</span>
69 </div>
70
71 <div class="node_description">Author</div>
72 <div class="node_content">
73 <%= d.select :user_id, user_list,
74 :selected => @page.user_id || @node.draft&.user_id || @node.head&.user_id %>
75 </div>
76
77 <div class="node_description">Images</div>
78 <div class="node_content"> 47 <div class="node_content">
79 <div id="related_assets" 48 <div id="related_assets"
80 data-search-url="<%= search_node_related_assets_path(@node) %>" 49 data-search-url="<%= search_node_related_assets_path(@node) %>"
@@ -120,33 +89,73 @@
120 </button> 89 </button>
121 </li> 90 </li>
122 </template> 91 </template>
123
124 </div> 92 </div>
125 </details>
126 93
127 <div id="content"> 94 <details id="metadata_details">
128 <div class="node_description">Title</div> 95 <summary>Metadata (slug, parent, tags, template, author, images)</summary>
129 <div class="node_content"><%= d.text_field :title %></div> 96 <div id="metadata">
97 <div class="node_description">Slug</div>
98 <div class="node_content">
99 <%= f.text_field(
100 :staged_slug, :value => @node.staged_slug || @node.slug
101 )
102 %>
103 </div>
104
105 <div class="node_description">parent</div>
106 <div class="node_content">
107 <%= text_field_tag :move_to_search_term, @node.parent.title rescue "" %>
108 <p class="field_hint">Start typing to find a new parent for this node.</p>
109 <div id="move_to_search_results" class="search_results"></div>
110 <%= f.hidden_field(
111 :staged_parent_id,
112 :value => @node.staged_parent_id || @node.parent_id
113 )
114 %>
115 </div>
130 116
131 <div class="node_description">Abstract</div> 117 <div class="node_description">Tags</div>
132 <div class="node_content"><%= d.text_area :abstract %></div> 118 <div class="node_content">
119 <%= text_field_tag :tag_list, @page.tag_list.join(', ') %>
120 <span class="field_hint">Comma separated.</span>
121 </div>
133 122
134 <div class="node_description">Body</div> 123 <div class="node_description">Publish at</div>
135 <div class="body_toolbar_row"> 124 <div class="node_content"><%= d.datetime_select :published_at, :value => @page.published_at %></div>
136 <button type="button" id="preview_toggle" class="view_toggle" aria-pressed="false" aria-label="Toggle live preview" title="Toggle live preview">
137 <%= icon("layout-sidebar-right", library: "tabler", "aria-hidden": true) %> <span>Live preview</span>
138 </button>
139 <button type="button" id="preview_force_render" class="view_toggle" style="display:none" aria-label="Force preview render" title="Force preview render">
140 <%= icon("refresh", library: "tabler", "aria-hidden": true) %> <span>Force refresh</span>
141 </button>
142 </div>
143 125
144 <div id="editor_and_preview"> 126 <div class="node_description">Template</div>
145 <div class="preview_content"> 127 <div class="node_content">
146 <div class="node_content"><%= d.text_area :body, :class => 'with_editor' %></div> 128 <%= d.select :template_name, custom_page_templates, {:prompt => 'Select Template'} %>
129 <span class="field_hint">Set automatically based on how this node was created - change it if needed.</span>
130 </div>
131
132 <div class="node_description">Author</div>
133 <div class="node_content">
134 <%= d.select :user_id, user_list,
135 :selected => @page.user_id || @node.draft&.user_id || @node.head&.user_id %>
136 </div>
137
138 </div>
139 </details>
140
141 <div id="content">
142 <div class="node_description">Body</div>
143 <div class="body_toolbar_row">
144 <button type="button" id="preview_toggle" class="view_toggle" aria-pressed="false" aria-label="Toggle live preview" title="Toggle live preview">
145 <%= icon("layout-sidebar-right", library: "tabler", "aria-hidden": true) %> <span>Live preview</span>
146 </button>
147 <button type="button" id="preview_force_render" class="view_toggle" style="display:none" aria-label="Force preview render" title="Force preview render">
148 <%= icon("refresh", library: "tabler", "aria-hidden": true) %> <span>Force refresh</span>
149 </button>
147 </div> 150 </div>
148 <div id="preview_panel" style="display:none"> 151
149 <iframe id="live_preview_iframe" title="Live preview" data-src="<%= preview_page_path(@page, :locale => I18n.default_locale) %>"></iframe> 152 <div id="editor_and_preview">
153 <div class="preview_content">
154 <div class="node_content"><%= d.text_area :body, :class => 'with_editor' %></div>
155 </div>
156 <div id="preview_panel" style="display:none">
157 <iframe id="live_preview_iframe" title="Live preview" data-src="<%= preview_page_path(@page, :locale => I18n.default_locale) %>"></iframe>
158 </div>
150 </div> 159 </div>
151 </div> 160 </div>
152 </div> 161 </div>
diff --git a/app/views/nodes/new.html.erb b/app/views/nodes/new.html.erb
index 805fbc9..1303e9e 100644
--- a/app/views/nodes/new.html.erb
+++ b/app/views/nodes/new.html.erb
@@ -48,6 +48,24 @@
48 <span class="field_hint">This preview updates as you type. The final path can still be changed afterward by editing the title (for the last segment) or moving the node to a different parent (for everything before it).</span> 48 <span class="field_hint">This preview updates as you type. The final path can still be changed afterward by editing the title (for the last segment) or moving the node to a different parent (for everything before it).</span>
49 </div> 49 </div>
50 50
51 <% if @attach_asset %>
52 <div class="node_description">attachment</div>
53 <div class="node_content">
54 <ul class="thumbnail_list">
55 <li>
56 <% if @attach_asset.has_variant?(:thumb) %>
57 <%= image_tag @attach_asset.upload.url(:thumb) %>
58 <% end %>
59 <%= link_to @attach_asset.name, asset_path(@attach_asset),
60 :target => "_blank", :rel => "noopener" %>
61 </li>
62 </ul>
63 <span class="field_hint">This asset will automatically be attached to the new page.</span>
64 <label><%= check_box_tag :asset_headline, "1" %> as the page's headline</label>
65 <%= hidden_field_tag :asset_id, @attach_asset.id %>
66 </div>
67 <% end %>
68
51 <div class="node_description"></div> 69 <div class="node_description"></div>
52 <div class="node_content"><%= submit_tag "Create" %></div> 70 <div class="node_content"><%= submit_tag "Create" %></div>
53 71
diff --git a/app/views/nodes/recent.html.erb b/app/views/nodes/recent.html.erb
deleted file mode 100644
index d256253..0000000
--- a/app/views/nodes/recent.html.erb
+++ /dev/null
@@ -1,7 +0,0 @@
1<h1>Recently changed</h1>
2
3<%= will_paginate @nodes %>
4<ul id="recent_changes_full_list">
5 <%= render partial: "nodes/recent_change_item", collection: @nodes, as: :node %>
6</ul>
7<%= will_paginate @nodes %>
diff --git a/app/views/nodes/show.html.erb b/app/views/nodes/show.html.erb
index adf79dd..b3987c6 100644
--- a/app/views/nodes/show.html.erb
+++ b/app/views/nodes/show.html.erb
@@ -253,10 +253,10 @@
253 <% end %> 253 <% end %>
254 </div> 254 </div>
255 255
256 <% if @page.assets.any? %> 256 <div class="node_description">Attachments</div>
257 <% headline_asset_id = @page.headline_asset&.id %> 257 <div class="node_content node_info_group">
258 <div class="node_description">Images</div> 258 <% if @page.assets.any? %>
259 <div class="node_content node_info_group"> 259 <% headline_asset_id = @page.headline_asset&.id %>
260 <ul class="thumbnail_list"> 260 <ul class="thumbnail_list">
261 <% @page.assets.each do |asset| %> 261 <% @page.assets.each do |asset| %>
262 <li class="<%= "is_headline" if asset.id == headline_asset_id %>"> 262 <li class="<%= "is_headline" if asset.id == headline_asset_id %>">
@@ -269,8 +269,14 @@
269 </li> 269 </li>
270 <% end %> 270 <% end %>
271 </ul> 271 </ul>
272 </div> 272 <% end %>
273 <% end %> 273 <p class="add_attachments">
274 <%= link_to new_asset_path(:node_id => @node.id), class: 'action_button' do %>
275 <%= icon("paperclip", library: "tabler", "aria-hidden": true) %> Upload new attachment
276 <% end %>
277 <span class="field_hint">To add or remove existing attachments, edit this node instead.</span>
278 </p>
279 </div>
274 280
275 <div class="node_description">Events</div> 281 <div class="node_description">Events</div>
276 <div class="node_content node_info_group"> 282 <div class="node_content node_info_group">
@@ -284,7 +290,11 @@
284 <% end %> 290 <% end %>
285 </ul> 291 </ul>
286 <% mapping = default_event_tag_mapping(@page) %> 292 <% mapping = default_event_tag_mapping(@page) %>
287 <%= link_to 'add event', new_event_path(node_id: @node.id, tag_list: mapping&.last, auto_tag_source: mapping&.first, return_to: request.path) %> 293 <p class="add_events">
294 <%= link_to new_event_path(node_id: @node.id, tag_list: mapping&.last, auto_tag_source: mapping&.first, return_to: request.path), class: 'action_button' do %>
295 <%= icon("calendar-plus", library: "tabler", "aria-hidden": true) %> Add event
296 <% end %>
297 </p>
288 </div> 298 </div>
289 299
290 <% matches = matching_node_kinds(@node) %> 300 <% matches = matching_node_kinds(@node) %>
@@ -304,10 +314,11 @@
304 <% if matches.any? %> 314 <% if matches.any? %>
305 <p class="add_child_links"> 315 <p class="add_child_links">
306 <% matches.each_with_index do |(kind, config), index| %> 316 <% matches.each_with_index do |(kind, config), index| %>
307 <%= " &middot; ".html_safe if index > 0 %>
308 <% link_params = { kind: kind } %> 317 <% link_params = { kind: kind } %>
309 <% link_params[:parent_id] = @node.id if kind == "generic" %> 318 <% link_params[:parent_id] = @node.id if kind == "generic" %>
310 <%= link_to "add '#{resolve_kind_text(config[:label])}' child", new_node_path(link_params) %> 319 <%= link_to new_node_path(link_params), class: 'action_button' do %>
320 <%= icon("text-plus", library: "tabler", "aria-hidden": true) %> Add child type <%= resolve_kind_text(config[:label]) %>
321 <% end %>
311 <% end %> 322 <% end %>
312 </p> 323 </p>
313 <% end %> 324 <% end %>