alchemy_cms 2.7.4 → 2.7.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec39e6f9f8433170bdf6cfa12566eded64cc8970
4
- data.tar.gz: 79ae67d762d97a743c584a0bd5e1d2df47ff56a0
3
+ metadata.gz: 491ad71e83bcd6aaaeb4f79f9c696982ea8c8112
4
+ data.tar.gz: 64c210c6d964efa046475945ede684ecf3e626ed
5
5
  SHA512:
6
- metadata.gz: 36f7ce8de540fdb2d2830be679ac0215e6d16115342653ad83cb8ae84a0589d3b78b3e46d01ad3da7c413bb6cd4a78d6d326402a4e2001e57515510c7e6ba304
7
- data.tar.gz: 3c1d7a9aab4b031126c5113deb889d7f7c12ac54e2d22e422bb0761bb7538f914a7b7983df534bb0ed454d4b9f460e518dfb24ca70a548e2da0639bbb721ae4d
6
+ metadata.gz: be1fe2cef675009c89d8f1a70f6b8b90ded20c81e91e74c0619347e9d7047d73da29683391d8512281147a7a71d0a88b3dbd840b94c5a0427814ada8eb9fb1a0
7
+ data.tar.gz: 91beddf874b48a4f4b761a5952c73a18059e5b355d0e28b417d8a34c1a4c61fda2f20e18369ee13488c2521555b3ddd8f9fb1c139b09f14d750d6402e6aba36c
@@ -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,5 +1,5 @@
1
1
  module Alchemy
2
- VERSION = "2.7.4"
2
+ VERSION = "2.7.5"
3
3
 
4
4
  def self.version
5
5
  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.7.4
4
+ version: 2.7.5
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-27 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