summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/admin_controller.rb4
-rw-r--r--app/models/node.rb3
-rw-r--r--app/views/layouts/admin.html.erb2
-rw-r--r--app/views/nodes/new.html.erb6
-rw-r--r--config/locales/de.yml2
-rw-r--r--config/locales/en.yml2
-rw-r--r--public/javascripts/admin_search.js8
-rw-r--r--public/stylesheets/admin.css11
-rw-r--r--test/models/node_test.rb23
9 files changed, 58 insertions, 3 deletions
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index 3b8b3582..c23cec04 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -67,7 +67,9 @@ class AdminController < ApplicationController
67 67
68 format.js do 68 format.js do
69 render( :json => @results.map do |node| 69 render( :json => @results.map do |node|
70 {:node_id => node.id, :title => node.title, :unique_name => node.unique_name, :node_path => node_path(node)} 70 { :node_id => node.id, :title => node.title,
71 :unique_name => node.unique_name, :node_path => node_path(node),
72 :needs_redaktion => !current_user.may_change_live?(node) }
71 end 73 end
72 ) 74 )
73 75
diff --git a/app/models/node.rb b/app/models/node.rb
index 188b6c17..d6fe32ff 100644
--- a/app/models/node.rb
+++ b/app/models/node.rb
@@ -375,6 +375,9 @@ class Node < ApplicationRecord
375 raise ActiveRecord::RecordInvalid.new(self) 375 raise ActiveRecord::RecordInvalid.new(self)
376 end 376 end
377 377
378 guard_live_change!(current_user,
379 :target_path => [new_parent.unique_name.presence, slug].compact.join("/"))
380
378 ActiveRecord::Base.transaction do 381 ActiveRecord::Base.transaction do
379 path_before = unique_name 382 path_before = unique_name
380 move_to_child_of(new_parent) 383 move_to_child_of(new_parent)
diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb
index 97117710..43f09b96 100644
--- a/app/views/layouts/admin.html.erb
+++ b/app/views/layouts/admin.html.erb
@@ -18,7 +18,7 @@
18 <%= javascript_tag nonce: true do %> 18 <%= javascript_tag nonce: true do %>
19 var ADMIN_SEARCH_URL = "<%= admin_search_path %>"; 19 var ADMIN_SEARCH_URL = "<%= admin_search_path %>";
20 var ADMIN_MENU_SEARCH_URL = "<%= admin_menu_search_path %>"; 20 var ADMIN_MENU_SEARCH_URL = "<%= admin_menu_search_path %>";
21 var ADMIN_STRINGS = <%= raw({ :lock_lost_prefix => t(".lock_lost_prefix"), :lock_lost_link => t(".lock_lost_link"), :insert_image => t(".insert_image"), :insert_image_tooltip => t(".insert_image_tooltip"), :copied => t(".copied") }.to_json) %>; 21 var ADMIN_STRINGS = <%= raw({ :lock_lost_prefix => t(".lock_lost_prefix"), :lock_lost_link => t(".lock_lost_link"), :insert_image => t(".insert_image"), :insert_image_tooltip => t(".insert_image_tooltip"), :copied => t(".copied"), :needs_redaktion => t(".needs_redaktion") }.to_json) %>;
22 var PARAMETERIZE_PREVIEW_URL = "<%= parameterize_preview_nodes_path %>"; 22 var PARAMETERIZE_PREVIEW_URL = "<%= parameterize_preview_nodes_path %>";
23 var DASHBOARD_SEARCH_URL = "<%= admin_dashboard_search_path %>"; 23 var DASHBOARD_SEARCH_URL = "<%= admin_dashboard_search_path %>";
24 <% end %> 24 <% end %>
diff --git a/app/views/nodes/new.html.erb b/app/views/nodes/new.html.erb
index b56bbeed..883909a6 100644
--- a/app/views/nodes/new.html.erb
+++ b/app/views/nodes/new.html.erb
@@ -12,13 +12,17 @@
12 <div class="layout_row_label"><%= t(".type") %></div> 12 <div class="layout_row_label"><%= t(".type") %></div>
13 <div class="layout_row_content"> 13 <div class="layout_row_content">
14 <% CccConventions::NODE_KINDS.each do |kind, config| %> 14 <% CccConventions::NODE_KINDS.each do |kind, config| %>
15 <% prefix = resolve_kind_text(config[:path_prefix]) %>
15 <p> 16 <p>
16 <%= radio_button_tag :kind, kind, kind == @selected_kind, 17 <%= radio_button_tag :kind, kind, kind == @selected_kind,
17 data: { path_prefix: resolve_kind_text(config[:path_prefix]) } %> 18 data: { path_prefix: prefix } %>
18 <%= resolve_kind_text(config[:label]) %> 19 <%= resolve_kind_text(config[:label]) %>
19 <% if config[:hint] %> 20 <% if config[:hint] %>
20 <span class="field_hint"><%= resolve_kind_text(config[:hint]) %></span> 21 <span class="field_hint"><%= resolve_kind_text(config[:hint]) %></span>
21 <% end %> 22 <% end %>
23 <% unless current_user.may_change_live_at?(prefix) %>
24 <span class="field_hint restricted_kind_hint"><%= t(".restricted_kind") %></span>
25 <% end %>
22 </p> 26 </p>
23 <% end %> 27 <% end %>
24 </div> 28 </div>
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 746aa803..74f6e8ae 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -382,6 +382,7 @@ de:
382 attachment: "Anhang" 382 attachment: "Anhang"
383 attach_hint: "Dieses Asset wird der neuen Seite automatisch angehängt." 383 attach_hint: "Dieses Asset wird der neuen Seite automatisch angehängt."
384 as_headline: "als Aufmacher der Seite" 384 as_headline: "als Aufmacher der Seite"
385 restricted_kind: "Veröffentlichen hier ist der Redaktion vorbehalten. Anlegen und schreiben kannst du aber."
385 edit: 386 edit:
386 save_draft: "Entwurf speichern" 387 save_draft: "Entwurf speichern"
387 save_unlock_exit: "Speichern + Entsperren + Verlassen" 388 save_unlock_exit: "Speichern + Entsperren + Verlassen"
@@ -743,6 +744,7 @@ de:
743 insert_image: "Bild einfügen" 744 insert_image: "Bild einfügen"
744 insert_image_tooltip: "Eines der angehängten Bilder dieser Seite einfügen" 745 insert_image_tooltip: "Eines der angehängten Bilder dieser Seite einfügen"
745 copied: "Kopiert!" 746 copied: "Kopiert!"
747 needs_redaktion: "Redaktion nötig"
746 nav_controls: 748 nav_controls:
747 dashboard: "Dashboard" 749 dashboard: "Dashboard"
748 search: "Suche" 750 search: "Suche"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 22429c9e..c23f2c33 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -325,6 +325,7 @@ en:
325 attachment: "attachment" 325 attachment: "attachment"
326 attach_hint: "This asset will automatically be attached to the new page." 326 attach_hint: "This asset will automatically be attached to the new page."
327 as_headline: "as the page's headline" 327 as_headline: "as the page's headline"
328 restricted_kind: "Publishing here is reserved for Redaktion. You can create and write freely, though."
328 edit: 329 edit:
329 save_draft: "Save Draft" 330 save_draft: "Save Draft"
330 save_unlock_exit: "Save + Unlock + Exit" 331 save_unlock_exit: "Save + Unlock + Exit"
@@ -686,6 +687,7 @@ en:
686 insert_image: "Insert image" 687 insert_image: "Insert image"
687 insert_image_tooltip: "Insert one of this page's attached images" 688 insert_image_tooltip: "Insert one of this page's attached images"
688 copied: "Copied!" 689 copied: "Copied!"
690 needs_redaktion: "Redaktion required"
689 nav_controls: 691 nav_controls:
690 dashboard: "Dashboard" 692 dashboard: "Dashboard"
691 search: "Search" 693 search: "Search"
diff --git a/public/javascripts/admin_search.js b/public/javascripts/admin_search.js
index 792849f7..37b696e4 100644
--- a/public/javascripts/admin_search.js
+++ b/public/javascripts/admin_search.js
@@ -46,6 +46,7 @@ function initSearchPicker(options) {
46 var isActive = options.isActive; 46 var isActive = options.isActive;
47 var resultsHeaderHtml = options.resultsHeaderHtml; 47 var resultsHeaderHtml = options.resultsHeaderHtml;
48 var renderResults = options.renderResults; 48 var renderResults = options.renderResults;
49 var showRestricted = options.showRestricted;
49 var loadOnFocus = options.loadOnFocus; // optional, fires an initial search on focus with no term typed yet 50 var loadOnFocus = options.loadOnFocus; // optional, fires an initial search on focus with no term typed yet
50 var requestId = 0; 51 var requestId = 0;
51 var timeout; 52 var timeout;
@@ -75,6 +76,10 @@ function initSearchPicker(options) {
75 var anchor = $("<a>").attr("href", onSelect ? "#" : node.node_path); 76 var anchor = $("<a>").attr("href", onSelect ? "#" : node.node_path);
76 anchor.append(document.createTextNode(node.title || "")); 77 anchor.append(document.createTextNode(node.title || ""));
77 anchor.append($("<span>", { "class": "result_path" }).text(node.unique_name || "")); 78 anchor.append($("<span>", { "class": "result_path" }).text(node.unique_name || ""));
79 if (showRestricted && node.needs_redaktion) {
80 anchor.append($("<span>", { "class": "restricted_result" })
81 .text(ADMIN_STRINGS.needs_redaktion));
82 }
78 var link = $("<p>").append(anchor); 83 var link = $("<p>").append(anchor);
79 84
80 if (onSelect) { 85 if (onSelect) {
@@ -192,6 +197,7 @@ parent_search = {
192 initSearchPicker({ 197 initSearchPicker({
193 inputSelector: "#parent_search_term", 198 inputSelector: "#parent_search_term",
194 resultsSelector: "#parent_search_results", 199 resultsSelector: "#parent_search_results",
200 showRestricted: true,
195 onSelect: function(node) { 201 onSelect: function(node) {
196 $("#parent_search_term").val(node.title); 202 $("#parent_search_term").val(node.title);
197 $("#parent_id").val(node.node_id).attr("data-unique-name", node.unique_name); 203 $("#parent_id").val(node.node_id).attr("data-unique-name", node.unique_name);
@@ -248,6 +254,7 @@ move_to_search = {
248 initSearchPicker({ 254 initSearchPicker({
249 inputSelector: "#move_to_search_term", 255 inputSelector: "#move_to_search_term",
250 resultsSelector: "#move_to_search_results", 256 resultsSelector: "#move_to_search_results",
257 showRestricted: true,
251 onSelect: function(node) { 258 onSelect: function(node) {
252 $("#move_to_search_term").val(node.title); 259 $("#move_to_search_term").val(node.title);
253 $("#node_staged_parent_id").val(node.node_id); 260 $("#node_staged_parent_id").val(node.node_id);
@@ -261,6 +268,7 @@ restore_search = {
261 initSearchPicker({ 268 initSearchPicker({
262 inputSelector: "#restore_search_term", 269 inputSelector: "#restore_search_term",
263 resultsSelector: "#restore_search_results", 270 resultsSelector: "#restore_search_results",
271 showRestricted: true,
264 onSelect: function(node) { 272 onSelect: function(node) {
265 $("#restore_search_term").val(node.title); 273 $("#restore_search_term").val(node.title);
266 $("#parent_id").val(node.node_id); 274 $("#parent_id").val(node.node_id);
diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css
index 55fdf15d..7f286705 100644
--- a/public/stylesheets/admin.css
+++ b/public/stylesheets/admin.css
@@ -889,6 +889,7 @@ div.layout_row_content {
889 font-weight: bold; 889 font-weight: bold;
890 color: var(--text-muted); 890 color: var(--text-muted);
891 cursor: not-allowed; 891 cursor: not-allowed;
892 line-height: 1.25rem;
892} 893}
893 894
894.info_group .disabled_action svg { 895.info_group .disabled_action svg {
@@ -973,6 +974,10 @@ form.button_to button[type="submit"] {
973 padding-bottom: 4px; 974 padding-bottom: 4px;
974} 975}
975 976
977.restricted_kind_hint {
978 color: var(--accent);
979}
980
976.action_button + .field_hint { 981.action_button + .field_hint {
977 display: inline; 982 display: inline;
978 margin-left: 0.5rem; 983 margin-left: 0.5rem;
@@ -1459,6 +1464,12 @@ input#menu_item_title {
1459 padding-bottom: 4px; 1464 padding-bottom: 4px;
1460} 1465}
1461 1466
1467.search_results .restricted_result {
1468 display: block;
1469 font-size: 0.85rem;
1470 color: var(--accent);
1471}
1472
1462.search_results p.search_more { 1473.search_results p.search_more {
1463 margin: 0; 1474 margin: 0;
1464 padding: 6px 4px; 1475 padding: 6px 4px;
diff --git a/test/models/node_test.rb b/test/models/node_test.rb
index aa714427..a34094a0 100644
--- a/test/models/node_test.rb
+++ b/test/models/node_test.rb
@@ -1049,4 +1049,27 @@ class NodeTest < ActiveSupport::TestCase
1049 assert_not squatter.valid? 1049 assert_not squatter.valid?
1050 assert squatter.errors[:slug].any? 1050 assert squatter.errors[:slug].any?
1051 end 1051 end
1052
1053 test "restoring into a restricted subtree is refused" do
1054 editor = User.create!(:login => "restore_editor", :email => "rs@example.com",
1055 :password => "secret", :password_confirmation => "secret")
1056 updates = Node.root.children.create!(:slug => "updates")
1057 node = Node.root.children.create!(:slug => "restorable")
1058 node.reload.trash!
1059
1060 assert_raises(ActiveRecord::RecordInvalid) { node.restore_from_trash!(updates, editor) }
1061 assert node.reload.in_trash?
1062 end
1063
1064 test "restoring into unrestricted space needs no role" do
1065 editor = User.create!(:login => "restore_free", :email => "rf@example.com",
1066 :password => "secret", :password_confirmation => "secret")
1067 club = Node.root.children.create!(:slug => "club")
1068 node = Node.root.children.create!(:slug => "restorable_free")
1069 node.reload.trash!
1070
1071 node.restore_from_trash!(club, editor)
1072 assert_not node.reload.in_trash?
1073 assert_equal club.id, node.parent_id
1074 end
1052end 1075end