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 +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: bc35e337330b8a45d9a53897d1ec6f9377bce876
|
4
|
+
data.tar.gz: 7a100feddc7fb1094fc0dadadd1b6c9530d0e251
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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.8.
|
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-
|
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
|