summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/views/events/_rrule_builder.html.erb33
-rw-r--r--app/views/events/edit.html.erb34
-rw-r--r--app/views/events/new.html.erb5
-rw-r--r--lib/tasks/node_actions.rake61
-rw-r--r--public/javascripts/admin_search.js30
-rw-r--r--public/javascripts/related_assets.js9
6 files changed, 114 insertions, 58 deletions
diff --git a/app/views/events/_rrule_builder.html.erb b/app/views/events/_rrule_builder.html.erb
new file mode 100644
index 0000000..1ffec0a
--- /dev/null
+++ b/app/views/events/_rrule_builder.html.erb
@@ -0,0 +1,33 @@
1<div id="rrule_builder">
2 <p>
3 <label><%= radio_button_tag "rrule_freq", "weekly", true %> Weekly</label>
4 <label><%= radio_button_tag "rrule_freq", "monthly" %> Monthly</label>
5 </p>
6 <div id="rrule_weekly_options">
7 <p><label><%= check_box_tag "rrule_biweekly" %> Every 2 weeks</label></p>
8 <p>
9 <% %w[MO TU WE TH FR SA SU].each do |code| %>
10 <label><%= check_box_tag "rrule_byday_#{code}" %> <%= RruleHumanizer::WEEKDAY_NAMES_ABBR[:de][code] %></label>
11 <% end %>
12 </p>
13 </div>
14 <div id="rrule_monthly_options" style="display: none;">
15 <p><label><%= check_box_tag "rrule_monthly_ordinal" %> On a specific weekday each month</label></p>
16 <p id="rrule_ordinal_fields" style="display: none;">
17 <%= select_tag "rrule_ordinal", options_for_select([["1.", 1], ["2.", 2], ["3.", 3], ["4.", 4], ["letzter", -1], ["vorletzter", -2], ["Selected weeks", "custom"]]) %>
18 <%= select_tag "rrule_ordinal_day", options_for_select(%w[MO TU WE TH FR SA SU].map { |c| [RruleHumanizer::WEEKDAY_NAMES[:de][c], c] }) %>
19 </p>
20 <p id="rrule_custom_ordinal_fields" style="display: none;">
21 Weeks of the month:
22 <% (1..5).each do |ordinal| %>
23 <label><%= check_box_tag "rrule_custom_ordinal_#{ordinal}" %> <%= ordinal %>.</label>
24 <% end %>
25 </p>
26 </div>
27 <p>
28 <label><%= check_box_tag "rrule_exclude_month" %> Except in one month</label>
29 <%= select_tag "rrule_excluded_month", options_for_select((1..12).map { |m| [RruleHumanizer::MONTH_NAMES[:de][m-1], m] }), style: "display: none;" %>
30 </p>
31 <span class="field_hint">Builds the field below automatically. If your pattern is more complex than this covers, just edit it directly below - these controls won't touch it unless you use them.</span>
32</div>
33<%= f.text_field :rrule, id: "event_rrule" %>
diff --git a/app/views/events/edit.html.erb b/app/views/events/edit.html.erb
index bf8cb35..4532ff2 100644
--- a/app/views/events/edit.html.erb
+++ b/app/views/events/edit.html.erb
@@ -40,39 +40,7 @@
40 40
41 <div class="node_description">Recurrence</div> 41 <div class="node_description">Recurrence</div>
42 <div class="node_content"> 42 <div class="node_content">
43 <div id="rrule_builder"> 43 <%= render "rrule_builder", f: f %>
44 <p>
45 <label><%= radio_button_tag "rrule_freq", "weekly", true %> Weekly</label>
46 <label><%= radio_button_tag "rrule_freq", "monthly" %> Monthly</label>
47 </p>
48 <div id="rrule_weekly_options">
49 <p><label><%= check_box_tag "rrule_biweekly" %> Every 2 weeks</label></p>
50 <p>
51 <% %w[MO TU WE TH FR SA SU].each do |code| %>
52 <label><%= check_box_tag "rrule_byday_#{code}" %> <%= RruleHumanizer::WEEKDAY_NAMES_ABBR[:de][code] %></label>
53 <% end %>
54 </p>
55 </div>
56 <div id="rrule_monthly_options" style="display: none;">
57 <p><label><%= check_box_tag "rrule_monthly_ordinal" %> On a specific weekday each month</label></p>
58 <p id="rrule_ordinal_fields" style="display: none;">
59 <%= select_tag "rrule_ordinal", options_for_select([["1.", 1], ["2.", 2], ["3.", 3], ["4.", 4], ["letzter", -1], ["vorletzter", -2], ["Selected weeks", "custom"]]) %>
60 <%= select_tag "rrule_ordinal_day", options_for_select(%w[MO TU WE TH FR SA SU].map { |c| [RruleHumanizer::WEEKDAY_NAMES[:de][c], c] }) %>
61 </p>
62 <p id="rrule_custom_ordinal_fields" style="display: none;">
63 Weeks of the month:
64 <% (1..5).each do |ordinal| %>
65 <label><%= check_box_tag "rrule_custom_ordinal_#{ordinal}" %> <%= ordinal %>.</label>
66 <% end %>
67 </p>
68 </div>
69 <p>
70 <label><%= check_box_tag "rrule_exclude_month" %> Except in one month</label>
71 <%= select_tag "rrule_excluded_month", options_for_select((1..12).map { |m| [RruleHumanizer::MONTH_NAMES[:de][m-1], m] }), style: "display: none;" %>
72 </p>
73 <span class="field_hint">Builds the field below automatically. If your pattern is more complex than this covers, just edit it directly below - these controls won't touch it unless you use them.</span>
74 </div>
75 <%= f.text_field :rrule, id: "event_rrule" %>
76 </div> 44 </div>
77 45
78 <div class="node_description">Title</div> 46 <div class="node_description">Title</div>
diff --git a/app/views/events/new.html.erb b/app/views/events/new.html.erb
index 7a1ee7a..028fce7 100644
--- a/app/views/events/new.html.erb
+++ b/app/views/events/new.html.erb
@@ -21,8 +21,8 @@
21 <div class="node_description">End time</div> 21 <div class="node_description">End time</div>
22 <div class="node_content"><%= f.datetime_select :end_time %></div> 22 <div class="node_content"><%= f.datetime_select :end_time %></div>
23 23
24 <div class="node_description">Rrule</div> 24 <div class="node_description">Recurrence</div>
25 <div class="node_content"><%= f.text_field :rrule %></div> 25 <div class="node_content"><%= render "rrule_builder", f: f %></div>
26 26
27 <div class="node_description">Title</div> 27 <div class="node_description">Title</div>
28 <div class="node_content"> 28 <div class="node_content">
@@ -53,4 +53,3 @@
53 </div> 53 </div>
54 </div> 54 </div>
55<% end %> 55<% end %>
56
diff --git a/lib/tasks/node_actions.rake b/lib/tasks/node_actions.rake
new file mode 100644
index 0000000..fdd286c
--- /dev/null
+++ b/lib/tasks/node_actions.rake
@@ -0,0 +1,61 @@
1namespace :node_actions do
2 desc "Rebuild inferred NodeAction entries from existing revisions and " \
3 "node timestamps. Witnessed entries (inferred_from IS NULL) are " \
4 "never touched; previously inferred ones are deleted and " \
5 "regenerated, so the task is idempotent. Scope with NODE_ID=n."
6 task :backfill => :environment do
7 scope = Node.where.not(:parent_id => nil)
8 scope = scope.where(:id => ENV["NODE_ID"]) if ENV["NODE_ID"]
9
10 created = 0
11
12 ActiveRecord::Base.transaction do
13 stale = NodeAction.where.not(:inferred_from => nil)
14 stale = stale.where(:node_id => ENV["NODE_ID"]) if ENV["NODE_ID"]
15 puts "Removing #{stale.count} previously inferred entries"
16 stale.delete_all
17
18 witnessed_creates = NodeAction.where(:action => "create", :inferred_from => nil).pluck(:node_id).to_set
19 witnessed_promotes = NodeAction.where(:action => "publish", :inferred_from => nil).pluck(:page_id).to_set
20
21 scope.find_each do |node|
22 # Every page row except the current draft was once promoted to
23 # head. Autosaves never carry node_id, so they cannot appear.
24 revisions = node.pages.to_a.reject { |page| page.id == node.draft_id }
25 first_page = node.pages.first
26
27 unless witnessed_creates.include?(node.id)
28 NodeAction.record!(
29 :node => node, :page => first_page, :user => first_page&.editor,
30 :action => "create",
31 :occurred_at => node.created_at,
32 :inferred_from => "from_node_created_at",
33 :title => first_page&.translations&.find_by(:locale => I18n.default_locale)&.title,
34 :path => node.unique_name,
35 :human_readable_node_name =>
36 first_page&.translations&.find_by(:locale => I18n.default_locale)&.title)
37 created += 1
38 end
39
40 previous = nil
41 revisions.each do |page|
42 unless witnessed_promotes.include?(page.id)
43 diff = NodeAction.head_diff(previous, page)
44 NodeAction.record!(
45 :node => node, :page => page, :user => page.editor,
46 :action => "publish",
47 :occurred_at => page.updated_at,
48 :inferred_from => "from_page_revision",
49 :via => "draft",
50 :human_readable_node_name => diff[:title]["to"],
51 **diff)
52 created += 1
53 end
54 previous = page
55 end
56 end
57 end
58
59 puts "Created #{created} inferred entries"
60 end
61end
diff --git a/public/javascripts/admin_search.js b/public/javascripts/admin_search.js
index 7604bb7..d135503 100644
--- a/public/javascripts/admin_search.js
+++ b/public/javascripts/admin_search.js
@@ -72,24 +72,20 @@ function initSearchPicker(options) {
72 found = false; 72 found = false;
73 for (var i = 0; i < data.length; i++) { 73 for (var i = 0; i < data.length; i++) {
74 (function(node) { 74 (function(node) {
75 var link; 75 var anchor = $("<a>").attr("href", onSelect ? "#" : node.node_path);
76 anchor.append(document.createTextNode(node.title || ""));
77 anchor.append($("<span>", { "class": "result_path" }).text(node.unique_name || ""));
78 var link = $("<p>").append(anchor);
79
76 if (onSelect) { 80 if (onSelect) {
77 link = $( 81 anchor.bind("click", function() {
78 "<p><a href='#'>" + node.title +
79 "<span class='result_path'>" + node.unique_name + "</span></a></p>"
80 );
81 link.find("a").bind("click", function() {
82 onSelect(node); 82 onSelect(node);
83 results.slideUp(); 83 results.slideUp();
84 results.empty(); 84 results.empty();
85 return false; 85 return false;
86 }); 86 });
87 } else {
88 link = $(
89 "<p><a href='" + node.node_path + "'>" + node.title +
90 "<span class='result_path'>" + node.unique_name + "</span></a></p>"
91 );
92 } 87 }
88
93 results.append(link); 89 results.append(link);
94 found = true; 90 found = true;
95 })(data[i]); 91 })(data[i]);
@@ -150,7 +146,9 @@ dashboard_search = {
150 results.append("<p class='search_group_label'>Tags</p>"); 146 results.append("<p class='search_group_label'>Tags</p>");
151 var tag_row = $("<div class='search_tag_row'></div>"); 147 var tag_row = $("<div class='search_tag_row'></div>");
152 data.tags.forEach(function(tag) { 148 data.tags.forEach(function(tag) {
153 tag_row.append("<a class='search_tag_pill' href='" + tag.tag_path + "'>" + tag.name + "</a>"); 149 tag_row.append(
150 $("<a>", { "class": "search_tag_pill" }).attr("href", tag.tag_path).text(tag.name || "")
151 );
154 found = true; 152 found = true;
155 }); 153 });
156 results.append(tag_row); 154 results.append(tag_row);
@@ -159,10 +157,10 @@ dashboard_search = {
159 if (data.nodes.length) { 157 if (data.nodes.length) {
160 results.append("<p class='search_group_label'>Pages</p>"); 158 results.append("<p class='search_group_label'>Pages</p>");
161 data.nodes.forEach(function(node) { 159 data.nodes.forEach(function(node) {
162 results.append( 160 var anchor = $("<a>").attr("href", node.node_path);
163 "<p><a href='" + node.node_path + "'>" + node.title + 161 anchor.append(document.createTextNode(node.title || ""));
164 "<span class='result_path'>" + node.unique_name + "</span></a></p>" 162 anchor.append($("<span>", { "class": "result_path" }).text(node.unique_name || ""));
165 ); 163 results.append($("<p>").append(anchor));
166 found = true; 164 found = true;
167 }); 165 });
168 } 166 }
diff --git a/public/javascripts/related_assets.js b/public/javascripts/related_assets.js
index 803332f..3340fa7 100644
--- a/public/javascripts/related_assets.js
+++ b/public/javascripts/related_assets.js
@@ -15,12 +15,9 @@ related_assets = {
15 renderResults: function(data, results) { 15 renderResults: function(data, results) {
16 var found = false; 16 var found = false;
17 data.forEach(function(asset) { 17 data.forEach(function(asset) {
18 var item = $( 18 var item = $("<a>", { href: "#", "class": "related_asset_result" });
19 "<a href='#' class='related_asset_result'>" + 19 item.append($("<img>", { src: asset.thumb_url, alt: "" }));
20 "<img src='" + asset.thumb_url + "' alt=''>" + 20 item.append($("<span>").text(asset.name || ""));
21 "<span>" + asset.name + "</span>" +
22 "</a>"
23 );
24 item.bind("click", function() { 21 item.bind("click", function() {
25 related_assets.attach(asset.id, createUrl, list); 22 related_assets.attach(asset.id, createUrl, list);
26 return false; 23 return false;