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 +4 -4
- data/app/controllers/alchemy/admin/pages_controller.rb +90 -20
- data/app/models/alchemy/page.rb +19 -0
- data/app/models/alchemy/page/natures.rb +1 -1
- data/app/models/alchemy/tree_node.rb +4 -0
- data/app/views/alchemy/admin/pages/_page.html.erb +1 -1
- data/lib/alchemy/version.rb +1 -1
- data/spec/controllers/admin/pages_controller_spec.rb +89 -0
- data/spec/models/page_spec.rb +118 -0
- data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +8 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 491ad71e83bcd6aaaeb4f79f9c696982ea8c8112
|
4
|
+
data.tar.gz: 64c210c6d964efa046475945ede684ecf3e626ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
data/app/models/alchemy/page.rb
CHANGED
@@ -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?
|
@@ -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 %>
|
data/lib/alchemy/version.rb
CHANGED
@@ -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
|
|
data/spec/models/page_spec.rb
CHANGED
@@ -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
|
+
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-
|
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
|