summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/admin_controller.rb21
-rw-r--r--app/helpers/nodes_helper.rb12
-rw-r--r--app/models/node.rb6
-rw-r--r--app/views/admin/_menu.html.erb14
-rw-r--r--app/views/admin/index.html.erb210
-rw-r--r--test/controllers/admin_controller_test.rb20
-rw-r--r--test/controllers/nodes_controller_test.rb20
-rw-r--r--test/models/node_test.rb24
8 files changed, 134 insertions, 193 deletions
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index 8445997..37fd78b 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -5,25 +5,8 @@ class AdminController < ApplicationController
5 before_action :login_required 5 before_action :login_required
6 6
7 def index 7 def index
8 @drafts = Node.where("draft_id IS NOT NULL OR autosave_id IS NOT NULL") 8 @drafts = Node.drafts_and_autosaves(current_user_id: current_user.id).limit(5)
9 .limit(50).order("updated_at desc") 9 @recent_changes = Node.recently_changed.limit(5)
10
11 @drafts_count = Node.where("draft_id IS NOT NULL OR autosave_id IS NOT NULL").count
12
13 @recent_changes = Node.where(
14 "updated_at < ? AND updated_at > ? AND parent_id IS NOT NULL",
15 Time.now, Time.now - 14.days
16 ).limit(50).order("updated_at desc")
17
18 ordered_with_level = Node.root.self_and_descendants_ordered_with_level
19 @sitemap_depth = {}
20 ordered_with_level.each { |node, level| @sitemap_depth[node.id] = level }
21 @sitemap = ordered_with_level.map(&:first).reject(&:update?)
22
23 @mynodes = Node.joins(:pages)
24 .where("pages.user_id = ? or pages.editor_id = ?", current_user, current_user)
25 .order("updated_at desc")
26 .distinct.first(50)
27 end 10 end
28 11
29 def conventions 12 def conventions
diff --git a/app/helpers/nodes_helper.rb b/app/helpers/nodes_helper.rb
index 1268b63..5884c8c 100644
--- a/app/helpers/nodes_helper.rb
+++ b/app/helpers/nodes_helper.rb
@@ -28,6 +28,18 @@ module NodesHelper
28 User.all.map {|u| [u.login, u.id]} 28 User.all.map {|u| [u.login, u.id]}
29 end 29 end
30 30
31 def node_last_editor(node)
32 editor = node.draft&.editor || node.head&.editor
33 return nil unless editor
34 editor == current_user ? t("editor_self") : editor.login
35 end
36
37 def node_head_editor(node)
38 editor = node.head&.editor
39 return nil unless editor
40 editor == current_user ? t("publisher_self") : editor.login
41 end
42
31 DEFAULT_EVENT_TAG_BY_PAGE_TAG = { 43 DEFAULT_EVENT_TAG_BY_PAGE_TAG = {
32 'erfa-detail' => 'open-day', 44 'erfa-detail' => 'open-day',
33 'chaostreff-detail' => 'open-day' 45 'chaostreff-detail' => 'open-day'
diff --git a/app/models/node.rb b/app/models/node.rb
index aa2f7f3..1d0a089 100644
--- a/app/models/node.rb
+++ b/app/models/node.rb
@@ -377,10 +377,10 @@ class Node < ApplicationRecord
377 end 377 end
378 378
379 def self.recently_changed 379 def self.recently_changed
380 where( 380 includes(:head).where(
381 "nodes.updated_at < ? AND nodes.updated_at > ? AND nodes.parent_id IS NOT NULL", 381 "pages.updated_at < ? AND pages.updated_at > ? AND nodes.parent_id IS NOT NULL",
382 Time.now, Time.now - 14.days 382 Time.now, Time.now - 14.days
383 ) 383 ).order("pages.updated_at desc").references(:head)
384 end 384 end
385 385
386 protected 386 protected
diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb
index 4302987..970429d 100644
--- a/app/views/admin/_menu.html.erb
+++ b/app/views/admin/_menu.html.erb
@@ -1,9 +1,5 @@
1<%= language_selector %> 1<%= link_to icon("home", library: "tabler", "aria-hidden": true), admin_path, "aria-label": "Dashboard" %>
2<%= button_to 'Logout', logout_path, method: :delete %> 2<a href="#" onclick="admin_search.display_toggle(); return false;" aria-label="Search"><%= icon("search", library: "tabler", "aria-hidden": true) %></a>
3<%= link_to 'Overview', admin_path %> 3<%= button_to logout_path, method: :delete, aria: { label: "Log out" } do %>
4<a href="#" onclick="admin_search.display_toggle(); return false;">search</a> 4 <%= icon("logout", library: "tabler", "aria-hidden": true) %>
5<%= link_to 'Nodes', nodes_path, selected?('nodes') %> 5<% end %>
6<%= link_to 'Assets', assets_path, selected?('assets') %>
7<%= link_to 'Events', events_path, selected?('events') %>
8<%= link_to 'User', users_path, selected?('users') %>
9<%= link_to 'Navigation', menu_items_path, selected?('menu_items') %>
diff --git a/app/views/admin/index.html.erb b/app/views/admin/index.html.erb
index c67ccb3..bdb555e 100644
--- a/app/views/admin/index.html.erb
+++ b/app/views/admin/index.html.erb
@@ -1,155 +1,81 @@
1<div id="admin_wizard"> 1<h1>cccms dashboard</h1>
2 <h2>Quick links</h2> 2<div id="dashboard_search">
3 <div class="admin_wizard_button"><%= link_to 'Create post or page', new_node_path, :only_path => false %></div> 3 <%= form_tag admin_search_path, method: :get do %>
4 <div class="admin_wizard_button"><a href="#" id="admin_wizard_my_work" class="button">Continue my work</a></div> 4 <%= text_field_tag :search_term, nil, id: "dashboard_search_term", placeholder: "Search nodes and tags…", autocomplete: "off" %>
5 <div class="admin_wizard_button"><a href="#" id="admin_wizard_create_page" class="button">Add a page in the page tree</a></div> 5 <% end %>
6 <div class="admin_wizard_button"><%= link_to("Upload a new asset", new_asset_path, :only_path => false) %></div> 6 <div id="dashboard_search_results" class="search_results"></div>
7</div> 7</div>
8 8
9<p id="overview_toggle"> 9<div id="dashboard_signposts" class="button_row">
10 <a href="#" id="recent_changes_toggle" class="button">recent changes</a> 10 <%= link_to new_node_path, class: "action_button" do %>
11 <a href="#" id="my_work_toggle" class="button">my work</a> 11 <%= icon("plus", library: "tabler", "aria-hidden": true) %> Create post
12 <a href="#" id="current_drafts_toggle" class="button">current drafts (<%= @drafts_count %>)</a> 12 <% end %>
13 <a href="#" id="admin_sitemap_toggle" class="button">site map</a> 13 <%= link_to chapters_nodes_path, class: "action_button" do %>
14</p> 14 <%= icon("map-pin", library: "tabler", "aria-hidden": true) %> Find a chapter
15 <% end %>
16 <%= link_to new_asset_path, class: "action_button" do %>
17 <%= icon("upload", library: "tabler", "aria-hidden": true) %> Upload asset
18 <% end %>
19 <%= link_to sitemap_nodes_path, class: "action_button" do %>
20 <%= icon("list-tree", library: "tabler", "aria-hidden": true) %> Add a page in the page tree
21 <% end %>
22</div>
15 23
16<div id="recent_changes_table"> 24<div id="dashboard_widgets">
17 25 <div class="dashboard_widget">
18 <h2>Recent Changes</h2> 26 <h3>Drafts and autosaves</h3>
27 <ul>
28 <% @drafts.each do |node| %>
29 <li>
30 <div>
31 <%= link_to title_for_node(node), node_path(node) %>
32 <span class="field_hint"><%= link_to_path("#{node.unique_name} ↗", node.unique_name) %></span>
33 </div>
34 <span class="dashboard_widget_meta">
35 <% if (editor = node_last_editor(node)) %><%= editor %>, <% end %><%= relative_time_phrase(node.updated_at) %>
36 </span>
37 </li>
38 <% end %>
39 </ul>
40 <%= link_to "See all drafts →", drafts_nodes_path %>
41 </div>
19 42
20 <table class="node_table"> 43 <div class="dashboard_widget">
21 <tr class="header"> 44 <h3>Recent changes</h3>
22 <th class="node_id">ID</th> 45 <ul>
23 <th class="title">Title</th>
24 <th class="actions">Actions</th>
25 <th class="editor">Locked by</th>
26 <th class="revision">Rev.</th>
27 </tr>
28 <% @recent_changes.each do |node| %> 46 <% @recent_changes.each do |node| %>
29 <tr class="<%= cycle("even", "odd") %>"> 47 <li>
30 <td class="node_id"><%= node.id %></td> 48 <div>
31 <td class="title"> 49 <%= link_to title_for_node(node), node_path(node) %>
32 <h4><%= link_to title_for_node(node), node_path(node) %></h4> 50 <span class="field_hint"><%= link_to_path("#{node.unique_name} ↗", node.unique_name) %></span>
33 <p><%= link_to_path(node.unique_name, node.unique_name) %></p> 51 </div>
34 </td> 52 <span class="dashboard_widget_meta">
35 <td class="actions"> 53 <%= (editor = node_head_editor(node)) ? t("published_by", editor: editor) : t("published") %>, <%= relative_time_phrase(node.head.updated_at) %>
36 <%= link_to 'show', node_path(node) %> 54 </span>
37 <%= link_to 'Revisions', node_revisions_path(node) %> 55 </li>
38 </td>
39 <td>
40 <%= node.lock_owner.login if node.lock_owner %>
41 </td>
42 <td>
43 <%= link_to ( node.draft ? node.draft.revision : (node.head ? node.head.revision : "EMPTY" ) ), node_revisions_path(node) %>
44 </td>
45 </tr>
46 <% end %> 56 <% end %>
47 </table> 57 </ul>
58 <%= link_to "See all recent changes →", recent_nodes_path %>
59 </div>
48</div> 60</div>
49 61
50<div id="my_work_table"> 62<div id="dashboard_housekeeping">
51 63 <h3>Housekeeping</h3>
52 <h2>My Work</h2> 64 <div class="button_row">
53 65 <%= link_to nodes_path, class: "action_button" do %>
54 <table class="node_table"> 66 <%= icon("list", library: "tabler", "aria-hidden": true) %> Nodes
55 <tr class="header">
56 <th class="node_id">ID</th>
57 <th class="title">Title</th>
58 <th class="actions">Actions</th>
59 <th class="editor">Locked by</th>
60 <th class="revision">Rev.</th>
61 </tr>
62 <% @mynodes.each do |node| %>
63 <tr class="<%= cycle("even", "odd") %>">
64 <td class="node_id"><%= node.id %></td>
65 <td class="title">
66 <h4><%= link_to title_for_node(node), node_path(node) %></h4>
67 <p><%= link_to_path(node.unique_name, node.unique_name) %></p>
68 </td>
69 <td class="actions">
70 <%= link_to 'show', node_path(node) %>
71 <%= link_to 'Revisions', node_revisions_path(node) %>
72 </td>
73 <td>
74 <%= node.lock_owner.login if node.lock_owner %>
75 </td>
76 <td>
77 <%= link_to ( node.draft ? node.draft.revision : (node.head ? node.head.revision : "EMPTY" ) ), node_revisions_path(node) %>
78 </td>
79 </tr>
80 <% end %> 67 <% end %>
81 </table> 68 <%= link_to events_path, class: "action_button" do %>
82</div> 69 <%= icon("calendar", library: "tabler", "aria-hidden": true) %> Events
83
84<div id="current_drafts_table">
85
86 <h2>Current Drafts & Autosaves (<%= @drafts_count %>)</h2>
87
88 <table class="node_table">
89 <tr class="header">
90 <th class="node_id">ID</th>
91 <th class="title">Title</th>
92 <th class="actions">Actions</th>
93 <th class="editor">Locked by</th>
94 <th class="revision">Rev.</th>
95 <th class="autosave">Autosave</th>
96 </tr>
97 <% @drafts.each do |node| %>
98 <tr class="<%= cycle("even", "odd") %>">
99 <td class="node_id"><%= node.id %></td>
100 <td class="title">
101 <h4><%= link_to title_for_node(node), node_path(node) %></h4>
102 <p><%= link_to_path(node.unique_name, node.unique_name) %></p>
103 </td>
104 <td class="actions">
105 <%= link_to 'show', node_path(node) %>
106 <%= link_to 'Revisions', node_revisions_path(node) %>
107 <% if pair = node.available_layer_pairs.last %>
108 <%= link_to 'diff', diff_node_revisions_path(node, start_revision: pair.first, end_revision: pair.last) %>
109 <% end %>
110 </td>
111 <td>
112 <%= node.lock_owner.login if node.lock_owner %>
113 </td>
114 <td>
115 <%= link_to ( node.draft ? node.draft.revision : (node.head ? node.head.revision : "EMPTY" ) ), node_revisions_path(node) %>
116 </td>
117 <td class="autosave">
118 <%= node.autosave ? "unsaved changes" : "" %>
119 </td>
120 </tr>
121 <% end %> 70 <% end %>
122 </table> 71 <%= link_to assets_path, class: "action_button" do %>
123</div> 72 <%= icon("folder", library: "tabler", "aria-hidden": true) %> Assets
124 73 <% end %>
125<div id="admin_sitemap_table"> 74 <%= link_to users_path, class: "action_button" do %>
126 75 <%= icon("users", library: "tabler", "aria-hidden": true) %> Users
127 <h2>Sitemap</h2>
128
129 <table class="node_table">
130 <tr class="header">
131 <th class="node_id">ID</th>
132 <th class="title">Title</th>
133 <th class="actions">Actions</th>
134 <th class="editor">Locked by</th>
135 </tr>
136 <% @sitemap.each do |node| %>
137 <% if !node.nil? %>
138 <tr class="<%= cycle("even", "odd") %>">
139 <td class="node_id" style="padding-left: <%= @sitemap_depth[node.id] * 30 %>px;"><%= node.id %></td>
140 <td class="title" style="padding-left: <%= @sitemap_depth[node.id] * 30 %>px;">
141 <h4><%= link_to title_for_node(node), node_path(node) %></h4>
142 <p><%= link_to_path(node.unique_name, node.unique_name) %></p>
143 </td>
144 <td class="actions">
145 <%= link_to 'create subpage', new_node_path(:parent_id => node.id) %>
146 <%= link_to 'Revisions', node_revisions_path(node) %>
147 </td>
148 <td>
149 <%= node.lock_owner.login if node.lock_owner %>
150 </td>
151 </tr>
152 <% end %> 76 <% end %>
77 <%= link_to menu_items_path, class: "action_button" do %>
78 <%= icon("menu-2", library: "tabler", "aria-hidden": true) %> Navigation
153 <% end %> 79 <% end %>
154 </table> 80 </div>
155</div> 81</div>
diff --git a/test/controllers/admin_controller_test.rb b/test/controllers/admin_controller_test.rb
index 9beaf58..00a51e2 100644
--- a/test/controllers/admin_controller_test.rb
+++ b/test/controllers/admin_controller_test.rb
@@ -15,26 +15,6 @@ class AdminControllerTest < ActionController::TestCase
15 assert_includes assigns(:drafts), node 15 assert_includes assigns(:drafts), node
16 end 16 end
17 17
18 test "my work list shows each matching node only once, even with several revisions by the same user" do
19 login_as :quentin
20 user = User.find_by_login("quentin")
21 node = Node.root.children.create!(:slug => "dedup_test")
22 node.lock_for_editing!(user)
23 node.autosave!({:title => "v1"}, user)
24 node.save_draft!(user)
25 node.publish_draft!
26 node.lock_for_editing!(user)
27 node.autosave!({:title => "v2"}, user)
28 node.save_draft!(user)
29 node.publish_draft!
30 # three pages now exist on this node, all touched by quentin --
31 # without DISTINCT, the join would return this node three times
32
33 get :index
34 matches = assigns(:mynodes).select { |n| n.id == node.id }
35 assert_equal 1, matches.length
36 end
37
38 test "dashboard_search returns matching tags and nodes grouped separately" do 18 test "dashboard_search returns matching tags and nodes grouped separately" do
39 node = Node.root.children.create!(:slug => "dashboard_search_test") 19 node = Node.root.children.create!(:slug => "dashboard_search_test")
40 node.find_or_create_draft(User.find_by_login("aaron")) 20 node.find_or_create_draft(User.find_by_login("aaron"))
diff --git a/test/controllers/nodes_controller_test.rb b/test/controllers/nodes_controller_test.rb
index b43d2de..81e3f45 100644
--- a/test/controllers/nodes_controller_test.rb
+++ b/test/controllers/nodes_controller_test.rb
@@ -521,6 +521,26 @@ class NodesControllerTest < ActionController::TestCase
521 assert_response :success 521 assert_response :success
522 end 522 end
523 523
524 test "mine shows each matching node only once, even with several revisions by the same user" do
525 login_as :quentin
526 user = User.find_by_login("quentin")
527 node = Node.root.children.create!(:slug => "dedup_test")
528 node.lock_for_editing!(user)
529 node.autosave!({:title => "v1"}, user)
530 node.save_draft!(user)
531 node.publish_draft!
532 node.lock_for_editing!(user)
533 node.autosave!({:title => "v2"}, user)
534 node.save_draft!(user)
535 node.publish_draft!
536 # three pages now exist on this node, all touched by quentin --
537 # without DISTINCT, the join would return this node three times
538
539 get :mine
540 matches = assigns(:nodes).select { |n| n.id == node.id }
541 assert_equal 1, matches.length
542 end
543
524 test "chapters combined with a search term does not raise an ambiguous column error" do 544 test "chapters combined with a search term does not raise an ambiguous column error" do
525 login_as :quentin 545 login_as :quentin
526 get :chapters, params: { :q => "Zombies" } 546 get :chapters, params: { :q => "Zombies" }
diff --git a/test/models/node_test.rb b/test/models/node_test.rb
index de540f8..0bce222 100644
--- a/test/models/node_test.rb
+++ b/test/models/node_test.rb
@@ -561,4 +561,28 @@ class NodeTest < ActiveSupport::TestCase
561 result = Node.drafts_and_autosaves(current_user_id: @user1.id).to_a 561 result = Node.drafts_and_autosaves(current_user_id: @user1.id).to_a
562 assert result.index(mine) < result.index(someone_elses_newer) 562 assert result.index(mine) < result.index(someone_elses_newer)
563 end 563 end
564
565 test "recently_changed includes a node whose head was recently published" do
566 node = Node.root.children.create!(:slug => "recent_changed_published")
567 node.find_or_create_draft(@user1)
568 node.autosave!({:title => "v1"}, @user1)
569 node.save_draft!(@user1)
570 node.publish_draft!
571
572 assert_includes Node.recently_changed, node
573 end
574
575 test "recently_changed excludes a node only touched by locking or unlocking after an old publish" do
576 node = Node.root.children.create!(:slug => "recent_changed_lock_only")
577 node.find_or_create_draft(@user1)
578 node.autosave!({:title => "v1"}, @user1)
579 node.save_draft!(@user1)
580 node.publish_draft!
581 node.head.update_column(:updated_at, 20.days.ago)
582
583 node.lock_for_editing!(@user1)
584 node.unlock!
585
586 assert_not_includes Node.recently_changed, node
587 end
564end 588end