alchemy_cms 2.8.2 → 2.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c340c7f6e2a90471d1b5772227b1498b37859249
4
- data.tar.gz: 450b8d1a217f9deaecabab868405f5c4a9457df7
3
+ metadata.gz: bc35e337330b8a45d9a53897d1ec6f9377bce876
4
+ data.tar.gz: 7a100feddc7fb1094fc0dadadd1b6c9530d0e251
5
5
  SHA512:
6
- metadata.gz: fa4fec08b37e3fe38aa3c3b0ddc10458007b1621299734c8ad8071849de679b0a08b5d33c0989198cff1321106c94be6cebe2560ceabb6ea721b5fb15c5750d2
7
- data.tar.gz: adbccc24de430ef03733e08d179f2528f7cb947cdb363c17a8b66b6aa7dd5a0bd2e1961aedf205f249d74cc00d7f1f64c519395d2d733029dc3456b078232fd3
6
+ metadata.gz: b8c7b30d537f2b948f7bc9af5ef0fd1e9830b2046fc474a22b4c0c228de101871ac183ec79f414eb00b35aa2c179c49449568db1d40b177af8d7f7ec748211db
7
+ data.tar.gz: 63502e6354cdeae1f48e4f5aa47ccdc9c40152fdf862bfa5f94831bc0c1b1f93099d23d3a2e6afa7480b5101c678b58e2f8821f95ad45d6aa5e69ea48f10df74
@@ -200,17 +200,19 @@ module Alchemy
200
200
  @sorting = true
201
201
  end
202
202
 
203
+ # Receives a JSON object representing a language tree to be ordered
204
+ # and updates all pages in that language structure to their correct indexes
203
205
  def order
204
- @page_root = Page.language_root_for(session[:language_id])
205
-
206
- # Taken from https://github.com/matenia/jQuery-Awesome-Nested-Set-Drag-and-Drop
207
206
  neworder = JSON.parse(params[:set])
208
- prev_item = nil
209
- neworder.each do |item|
210
- dbitem = Page.find(item['id'])
211
- prev_item.nil? ? dbitem.move_to_child_of(@page_root) : dbitem.move_to_right_of(prev_item)
212
- sort_children(item, dbitem) unless item['children'].nil?
213
- prev_item = dbitem.reload
207
+ rootpage = Page.language_root_for(session[:language_id])
208
+
209
+ tree = create_tree(neworder, rootpage)
210
+
211
+ Alchemy::Page.transaction do
212
+ tree.each do |key, node|
213
+ dbitem = Page.find(key)
214
+ dbitem.update_node!(node)
215
+ end
214
216
  end
215
217
 
216
218
  flash[:notice] = _t("Pages order saved")
@@ -240,6 +242,85 @@ module Alchemy
240
242
 
241
243
  private
242
244
 
245
+ # Returns the current left index and the aggregated hash of tree nodes indexed by page id visited so far
246
+ #
247
+ # Visits a batch of children nodes, assigns them the correct ordering indexes and spuns recursively the same
248
+ # procedure on their children, if any
249
+ #
250
+ # @param [Array]
251
+ # An array of children nodes to be visited
252
+ # @param [Integer]
253
+ # The lft attribute that should be given to the first node in the array
254
+ # @param [Integer]
255
+ # The page id of the parent of this batch of children nodes
256
+ # @param [Integer]
257
+ # The depth at which these children reside
258
+ # @param [Hash]
259
+ # A Hash of TreeNode's indexed by their page ids
260
+ # @param [String]
261
+ # The url for the parent node of these children
262
+ # @param [Boolean]
263
+ # Whether these children reside in a restricted branch according to their ancestors
264
+ #
265
+ def visit_nodes(nodes, my_left, parent, depth, tree, url, restricted)
266
+ nodes.each do |item|
267
+ my_right = my_left + 1
268
+ my_restricted = item['restricted'] || restricted
269
+ urls = process_url(url, item)
270
+
271
+ if item['children']
272
+ my_right, tree = visit_nodes(item['children'], my_left + 1, item['id'], depth + 1, tree, urls[:children_path], my_restricted)
273
+ end
274
+
275
+ tree[item['id']] = TreeNode.new(my_left, my_right, parent, depth, urls[:my_urlname], my_restricted)
276
+ my_left = my_right + 1
277
+ end
278
+
279
+ [my_left, tree]
280
+ end
281
+
282
+ # Returns a Hash of TreeNode's indexed by their page ids
283
+ #
284
+ # Grabs the array representing a tree structure of pages passed as a parameter,
285
+ # visits it and creates a map of TreeNodes indexed by page id featuring Nested Set
286
+ # ordering information consisting of the left, right, depth and parent_id indexes as
287
+ # well as a node's url and restricted status
288
+ #
289
+ # @param [Array]
290
+ # An Array representing a tree of Alchemy::Page's
291
+ # @param [Alchemy::Page]
292
+ # The root page for the language being ordered
293
+ #
294
+ def create_tree(items, rootpage)
295
+ _, tree = visit_nodes(items, rootpage.lft + 1, rootpage.id, rootpage.depth + 1, {}, "", rootpage.restricted)
296
+ tree
297
+ end
298
+
299
+ # Returns a pair, the path that a given tree node should take, and the path its children should take
300
+ #
301
+ # This function will add a node's own slug into their ancestor's path
302
+ # in order to create the full URL of a node
303
+ #
304
+ # NOTE: external and invisible pages are not part of the full path of their children
305
+ #
306
+ # @param [String]
307
+ # The node's ancestors path
308
+ # @param [Hash]
309
+ # A children node
310
+ #
311
+ def process_url(ancestors_path, item)
312
+ default_urlname = (ancestors_path.blank? ? "" : "#{ancestors_path}/") + item['slug']
313
+
314
+ pair = {my_urlname: default_urlname, children_path: default_urlname}
315
+
316
+ if item['external'] == true || item['visible'] == false
317
+ # children ignore an ancestor in their path if external or invisible
318
+ pair[:children_path] = ancestors_path
319
+ end
320
+
321
+ pair
322
+ end
323
+
243
324
  def load_page
244
325
  @page = Page.find(params[:id])
245
326
  end
@@ -248,17 +329,6 @@ module Alchemy
248
329
  request.raw_post.split('&').map { |i| i = {i.split('=')[0].gsub(/[^0-9]/, '') => i.split('=')[1]} }
249
330
  end
250
331
 
251
- # Taken from https://github.com/matenia/jQuery-Awesome-Nested-Set-Drag-and-Drop
252
- def sort_children(element, dbitem)
253
- prevchild = nil
254
- element['children'].each do |child|
255
- childitem = Page.find(child['id'])
256
- prevchild.nil? ? childitem.move_to_child_of(dbitem) : childitem.move_to_right_of(prevchild)
257
- sort_children(child, childitem) unless child['children'].nil?
258
- prevchild = childitem
259
- end
260
- end
261
-
262
332
  def redirect_path_after_page_create
263
333
  if @page.redirects_to_external?
264
334
  admin_pages_path
@@ -298,6 +298,25 @@ module Alchemy
298
298
  set_language_code
299
299
  end
300
300
 
301
+ # Updates an Alchemy::Page based on a new ordering to be applied to it
302
+ #
303
+ # Note: Page's urls should not be updated (and a legacy URL created) if nesting is OFF
304
+ # or if a page is external or if the URL is the same
305
+ #
306
+ # @param [TreeNode]
307
+ # A tree node with new lft, rgt, depth, url, parent_id and restricted indexes to be updated
308
+ #
309
+ def update_node!(node)
310
+ hash = {lft: node.left, rgt: node.right, parent_id: node.parent, depth: node.depth, restricted: node.restricted}
311
+
312
+ if Config.get(:url_nesting) && !self.redirects_to_external? && self.urlname != node.url
313
+ LegacyPageUrl.create(page_id: self.id, urlname: self.urlname)
314
+ hash.merge!(urlname: node.url)
315
+ end
316
+
317
+ self.class.update_all(hash, {id: self.id})
318
+ end
319
+
301
320
  private
302
321
 
303
322
  # Returns the next or previous page on the same level or nil.
@@ -27,7 +27,7 @@ module Alchemy
27
27
 
28
28
  # Returns true or false if the pages layout_description for config/alchemy/page_layouts.yml contains redirects_to_external: true
29
29
  def redirects_to_external?
30
- definition["redirects_to_external"]
30
+ !!definition["redirects_to_external"]
31
31
  end
32
32
 
33
33
  def has_controller?
@@ -0,0 +1,4 @@
1
+ module Alchemy
2
+ class TreeNode < Struct.new(:left, :right, :parent, :depth, :url, :restricted)
3
+ end
4
+ end
@@ -1,4 +1,4 @@
1
- <li id="page_<%= page.id %>" class="page_level_<%= "#{page.level} #{page.page_layout}" %>">
1
+ <li id="page_<%= page.id %>" class="page_level_<%= "#{page.level} #{page.page_layout}" %>" data-slug="<%= page.slug %>" data-restricted="<%= page.restricted? %>" data-visible="<%= page.visible? %>" data-external="<%= page.redirects_to_external? %>">
2
2
  <div class="sitemap_page<%= page.locked ? ' locked' : '' %>" name="<%= page.name %>">
3
3
  <div class="sitemap_left_images">
4
4
  <%= sitemap_folder_link(page) unless page.level == 1 || page.children.blank? || @sorting %>
@@ -1,6 +1,6 @@
1
1
  module Alchemy
2
2
 
3
- VERSION = "2.8.2"
3
+ VERSION = "2.8.3"
4
4
 
5
5
  def self.version
6
6
  VERSION
@@ -55,6 +55,95 @@ module Alchemy
55
55
  end
56
56
  end
57
57
 
58
+ describe '#order' do
59
+ let(:page_1) { FactoryGirl.create(:page, visible: true) }
60
+ let(:page_2) { FactoryGirl.create(:page, visible: true) }
61
+ let(:page_3) { FactoryGirl.create(:page, visible: true) }
62
+ let(:page_item_1) { {id: page_1.id, slug: page_1.slug, restricted: false, external: page_1.redirects_to_external?, visible: page_1.visible?, children: [page_item_2]} }
63
+ let(:page_item_2) { {id: page_2.id, slug: page_2.slug, restricted: false, external: page_2.redirects_to_external?, visible: page_2.visible?, children: [page_item_3]} }
64
+ let(:page_item_3) { {id: page_3.id, slug: page_3.slug, restricted: false, external: page_3.redirects_to_external?, visible: page_3.visible? } }
65
+ let(:set_of_pages) { [page_item_1] }
66
+
67
+ it "stores the new order" do
68
+ xhr :post, :order, set: set_of_pages.to_json
69
+ page_1.reload
70
+ expect(page_1.descendants).to eq([page_2, page_3])
71
+ end
72
+
73
+ context 'with url nesting enabled' do
74
+ before { Alchemy::Config.stub(get: true) }
75
+
76
+ it "updates the pages urlnames" do
77
+ xhr :post, :order, set: set_of_pages.to_json
78
+ [page_1, page_2, page_3].map(&:reload)
79
+ expect(page_1.urlname).to eq("#{page_1.slug}")
80
+ expect(page_2.urlname).to eq("#{page_1.slug}/#{page_2.slug}")
81
+ expect(page_3.urlname).to eq("#{page_1.slug}/#{page_2.slug}/#{page_3.slug}")
82
+ end
83
+
84
+ context 'with invisible page in tree' do
85
+ let(:page_item_2) do
86
+ {
87
+ id: page_2.id,
88
+ slug: page_2.slug,
89
+ children: [page_item_3],
90
+ visible: false
91
+ }
92
+ end
93
+
94
+ it "does not use this pages slug in urlnames of descendants" do
95
+ xhr :post, :order, set: set_of_pages.to_json
96
+ [page_1, page_2, page_3].map(&:reload)
97
+ expect(page_1.urlname).to eq("#{page_1.slug}")
98
+ expect(page_2.urlname).to eq("#{page_1.slug}/#{page_2.slug}")
99
+ expect(page_3.urlname).to eq("#{page_1.slug}/#{page_3.slug}")
100
+ end
101
+ end
102
+
103
+ context 'with external page in tree' do
104
+ let(:page_item_2) do
105
+ {
106
+ id: page_2.id,
107
+ slug: page_2.slug,
108
+ children: [page_item_3],
109
+ external: true
110
+ }
111
+ end
112
+
113
+ it "does not use this pages slug in urlnames of descendants" do
114
+ xhr :post, :order, set: set_of_pages.to_json
115
+ [page_1, page_2, page_3].map(&:reload)
116
+ expect(page_3.urlname).to eq("#{page_1.slug}/#{page_3.slug}")
117
+ end
118
+ end
119
+
120
+ context 'with restricted page in tree' do
121
+ let(:page_2) { FactoryGirl.create(:page, restricted: true) }
122
+ let(:page_item_2) do
123
+ {
124
+ id: page_2.id,
125
+ slug: page_2.slug,
126
+ children: [page_item_3],
127
+ restricted: true
128
+ }
129
+ end
130
+
131
+ it "updates restricted status of descendants" do
132
+ xhr :post, :order, set: set_of_pages.to_json
133
+ page_3.reload
134
+ expect(page_3.restricted).to be_true
135
+ end
136
+ end
137
+
138
+ it "creates legacy urls" do
139
+ xhr :post, :order, set: set_of_pages.to_json
140
+ [page_2, page_3].map(&:reload)
141
+ expect(page_2.legacy_urls.size).to eq(1)
142
+ expect(page_3.legacy_urls.size).to eq(1)
143
+ end
144
+ end
145
+ end
146
+
58
147
  describe "#configure" do
59
148
  render_views
60
149
 
@@ -1117,6 +1117,124 @@ module Alchemy
1117
1117
  end
1118
1118
  end
1119
1119
 
1120
+ describe "#update_node!" do
1121
+
1122
+ let(:original_url) { "sample-url" }
1123
+ let(:page) { FactoryGirl.create(:page, :language => language, :parent_id => language_root.id, :urlname => original_url, restricted: false) }
1124
+ let(:node) { TreeNode.new(10, 11, 12, 13, "another-url", true) }
1125
+
1126
+ context "when nesting is enabled" do
1127
+ before { Alchemy::Config.stub(:get).with(:url_nesting) { true } }
1128
+
1129
+ context "when page is not external" do
1130
+
1131
+ before { page.stub(redirects_to_external?: false)}
1132
+
1133
+ it "should update all attributes" do
1134
+ page.update_node!(node)
1135
+ page.reload
1136
+ expect(page.lft).to eq(node.left)
1137
+ expect(page.rgt).to eq(node.right)
1138
+ expect(page.parent_id).to eq(node.parent)
1139
+ expect(page.depth).to eq(node.depth)
1140
+ expect(page.urlname).to eq(node.url)
1141
+ expect(page.restricted).to eq(node.restricted)
1142
+ end
1143
+
1144
+ context "when url is the same" do
1145
+ let(:node) { TreeNode.new(10, 11, 12, 13, original_url, true) }
1146
+
1147
+ it "should not create a legacy url" do
1148
+ page.update_node!(node)
1149
+ page.reload
1150
+ expect(page.legacy_urls.size).to eq(0)
1151
+ end
1152
+ end
1153
+
1154
+ context "when url is not the same" do
1155
+ it "should create a legacy url" do
1156
+ page.update_node!(node)
1157
+ page.reload
1158
+ expect(page.legacy_urls.size).to eq(1)
1159
+ end
1160
+ end
1161
+ end
1162
+
1163
+ context "when page is external" do
1164
+
1165
+ before { page.stub(redirects_to_external?: true) }
1166
+
1167
+ it "should update all attributes except url" do
1168
+ page.update_node!(node)
1169
+ page.reload
1170
+ expect(page.lft).to eq(node.left)
1171
+ expect(page.rgt).to eq(node.right)
1172
+ expect(page.parent_id).to eq(node.parent)
1173
+ expect(page.depth).to eq(node.depth)
1174
+ expect(page.urlname).to eq(original_url)
1175
+ expect(page.restricted).to eq(node.restricted)
1176
+ end
1177
+
1178
+ it "should not create a legacy url" do
1179
+ page.update_node!(node)
1180
+ page.reload
1181
+ expect(page.legacy_urls.size).to eq(0)
1182
+ end
1183
+ end
1184
+ end
1185
+
1186
+ context "when nesting is disabled" do
1187
+ before { Alchemy::Config.stub(:get).with(:url_nesting) { false } }
1188
+
1189
+ context "when page is not external" do
1190
+
1191
+ before { page.stub(redirects_to_external?: false)}
1192
+
1193
+ it "should update all attributes except url" do
1194
+ page.update_node!(node)
1195
+ page.reload
1196
+ expect(page.lft).to eq(node.left)
1197
+ expect(page.rgt).to eq(node.right)
1198
+ expect(page.parent_id).to eq(node.parent)
1199
+ expect(page.depth).to eq(node.depth)
1200
+ expect(page.urlname).to eq(original_url)
1201
+ expect(page.restricted).to eq(node.restricted)
1202
+ end
1203
+
1204
+ it "should not create a legacy url" do
1205
+ page.update_node!(node)
1206
+ page.reload
1207
+ expect(page.legacy_urls.size).to eq(0)
1208
+ end
1209
+
1210
+ end
1211
+
1212
+ context "when page is external" do
1213
+
1214
+ before { page.stub(redirects_to_external?: true) }
1215
+
1216
+ before { Alchemy::Config.stub(get: true) }
1217
+
1218
+ it "should update all attributes except url" do
1219
+ page.update_node!(node)
1220
+ page.reload
1221
+ expect(page.lft).to eq(node.left)
1222
+ expect(page.rgt).to eq(node.right)
1223
+ expect(page.parent_id).to eq(node.parent)
1224
+ expect(page.depth).to eq(node.depth)
1225
+ expect(page.urlname).to eq(original_url)
1226
+ expect(page.restricted).to eq(node.restricted)
1227
+ end
1228
+
1229
+ it "should not create a legacy url" do
1230
+ page.update_node!(node)
1231
+ page.reload
1232
+ expect(page.legacy_urls.size).to eq(0)
1233
+ end
1234
+ end
1235
+ end
1236
+ end
1237
+
1120
1238
  describe '#slug' do
1121
1239
  context "with parents path saved in urlname" do
1122
1240
  let(:page) { FactoryGirl.build(:page, urlname: 'root/parent/my-name')}
@@ -255,8 +255,14 @@
255
255
 
256
256
  function _recursiveItems(li) {
257
257
  var id = ($(li).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
258
+
259
+ var restricted = $(li).data("restricted");
260
+ var slug = $(li).data("slug");
261
+ var external = $(li).data("external");
262
+ var visible = $(li).data("visible");
263
+
258
264
  if (id) {
259
- var item = {"id":id[2]};
265
+ var item = {"id": id[2], "slug": slug, "restricted": restricted, "external": external, "visible": visible};
260
266
  if ($(li).children(o.listType).children('li').length > 0) {
261
267
  item.children = [];
262
268
  $(li).children(o.listType).children('li').each(function () {
@@ -391,4 +397,4 @@
391
397
  }));
392
398
 
393
399
  $.ui.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.ui.nestedSortable.prototype.options);
394
- })(jQuery);
400
+ })(jQuery);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.2
4
+ version: 2.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2014-05-26 00:00:00.000000000 Z
15
+ date: 2014-06-12 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: rails
@@ -551,6 +551,7 @@ files:
551
551
  - app/models/alchemy/site.rb
552
552
  - app/models/alchemy/site/layout.rb
553
553
  - app/models/alchemy/tag.rb
554
+ - app/models/alchemy/tree_node.rb
554
555
  - app/models/alchemy/user.rb
555
556
  - app/sweepers/alchemy/content_sweeper.rb
556
557
  - app/sweepers/alchemy/pages_sweeper.rb