alchemy_cms 2.6.2.1 → 2.6.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/alchemy_cms.gemspec +0 -1
- data/app/assets/stylesheets/alchemy/flash.scss +1 -0
- data/app/controllers/alchemy/admin/pages_controller.rb +90 -21
- data/app/controllers/alchemy/admin/pictures_controller.rb +1 -1
- data/app/controllers/alchemy/admin/users_controller.rb +1 -11
- data/app/controllers/alchemy/users_controller.rb +0 -3
- data/app/models/alchemy/page.rb +31 -10
- data/app/models/alchemy/tree_node.rb +4 -0
- data/app/models/alchemy/user.rb +16 -0
- data/app/views/alchemy/admin/pages/_page.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_date.html.erb +8 -0
- data/app/views/alchemy/admin/resources/_datetime.html.erb +1 -2
- data/app/views/alchemy/admin/users/_table.html.erb +2 -2
- data/config/locales/alchemy.de.yml +1 -1
- data/config/locales/alchemy.en.yml +1 -0
- data/lib/alchemy/version.rb +1 -1
- data/spec/controllers/admin/pages_controller_spec.rb +89 -0
- data/spec/controllers/admin/users_controller_spec.rb +66 -30
- data/spec/controllers/users_controller_spec.rb +1 -2
- data/spec/libraries/resource_spec.rb +1 -3
- data/spec/libraries/resources_helper_spec.rb +1 -4
- data/spec/models/page_spec.rb +143 -9
- data/spec/models/user_spec.rb +40 -0
- data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +8 -2
- metadata +63 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c1c44138b3e7be76b6a952789f4b54045aa8f8a
|
4
|
+
data.tar.gz: c8cd62e435145ef96d4b874ad68b840cbdf8524c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f3bde0e560a5c53c7305a6371851e31b2a980dae1b49781e98ec3c1129dcfc2542ae8686cb5e02d13378881e50edbdbf0b315e211236bc23d4407867088bb8b
|
7
|
+
data.tar.gz: 6194a45096869a6ae0faed7e4fdad33c88a326e1505bc716c886388ea33efb6ea0e265dbc0275a76fb2973e21138a17fe577cb135b5472c2c283019311b17f44
|
data/alchemy_cms.gemspec
CHANGED
@@ -57,7 +57,6 @@ POST_INSTALL
|
|
57
57
|
s.add_development_dependency %q<capybara>, ['~> 2.0.3']
|
58
58
|
s.add_development_dependency %q<factory_girl_rails>
|
59
59
|
s.add_development_dependency %q<rspec-rails>, ['~> 2.13.1']
|
60
|
-
s.add_development_dependency %q<sqlite3>
|
61
60
|
s.add_development_dependency %q<yard>
|
62
61
|
s.add_development_dependency %q<redcarpet>
|
63
62
|
|
@@ -208,17 +208,19 @@ module Alchemy
|
|
208
208
|
@sorting = true
|
209
209
|
end
|
210
210
|
|
211
|
+
# Receives a JSON object representing a language tree to be ordered
|
212
|
+
# and updates all pages in that language structure to their correct indexes
|
211
213
|
def order
|
212
|
-
@page_root = Page.language_root_for(session[:language_id])
|
213
|
-
|
214
|
-
# Taken from https://github.com/matenia/jQuery-Awesome-Nested-Set-Drag-and-Drop
|
215
214
|
neworder = JSON.parse(params[:set])
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
215
|
+
rootpage = Page.language_root_for(session[:language_id])
|
216
|
+
|
217
|
+
tree = create_tree(neworder, rootpage)
|
218
|
+
|
219
|
+
Alchemy::Page.transaction do
|
220
|
+
tree.each do |key, node|
|
221
|
+
dbitem = Page.find(key)
|
222
|
+
dbitem.update_node!(node)
|
223
|
+
end
|
222
224
|
end
|
223
225
|
|
224
226
|
flash[:notice] = _t("Pages order saved")
|
@@ -248,25 +250,92 @@ module Alchemy
|
|
248
250
|
|
249
251
|
private
|
250
252
|
|
251
|
-
|
252
|
-
|
253
|
+
# Returns the current left index and the aggregated hash of tree nodes indexed by page id visited so far
|
254
|
+
#
|
255
|
+
# Visits a batch of children nodes, assigns them the correct ordering indexes and spuns recursively the same
|
256
|
+
# procedure on their children, if any
|
257
|
+
#
|
258
|
+
# @param [Array]
|
259
|
+
# An array of children nodes to be visited
|
260
|
+
# @param [Integer]
|
261
|
+
# The lft attribute that should be given to the first node in the array
|
262
|
+
# @param [Integer]
|
263
|
+
# The page id of the parent of this batch of children nodes
|
264
|
+
# @param [Integer]
|
265
|
+
# The depth at which these children reside
|
266
|
+
# @param [Hash]
|
267
|
+
# A Hash of TreeNode's indexed by their page ids
|
268
|
+
# @param [String]
|
269
|
+
# The url for the parent node of these children
|
270
|
+
# @param [Boolean]
|
271
|
+
# Whether these children reside in a restricted branch according to their ancestors
|
272
|
+
#
|
273
|
+
def visit_nodes(nodes, my_left, parent, depth, tree, url, restricted)
|
274
|
+
nodes.each do |item|
|
275
|
+
my_right = my_left + 1
|
276
|
+
my_restricted = item['restricted'] || restricted
|
277
|
+
urls = process_url(url, item)
|
278
|
+
|
279
|
+
if item['children']
|
280
|
+
my_right, tree = visit_nodes(item['children'], my_left + 1, item['id'], depth + 1, tree, urls[:children_path], my_restricted)
|
281
|
+
end
|
282
|
+
|
283
|
+
tree[item['id']] = TreeNode.new(my_left, my_right, parent, depth, urls[:my_urlname], my_restricted)
|
284
|
+
my_left = my_right + 1
|
285
|
+
end
|
286
|
+
|
287
|
+
[my_left, tree]
|
253
288
|
end
|
254
289
|
|
255
|
-
|
256
|
-
|
290
|
+
# Returns a Hash of TreeNode's indexed by their page ids
|
291
|
+
#
|
292
|
+
# Grabs the array representing a tree structure of pages passed as a parameter,
|
293
|
+
# visits it and creates a map of TreeNodes indexed by page id featuring Nested Set
|
294
|
+
# ordering information consisting of the left, right, depth and parent_id indexes as
|
295
|
+
# well as a node's url and restricted status
|
296
|
+
#
|
297
|
+
# @param [Array]
|
298
|
+
# An Array representing a tree of Alchemy::Page's
|
299
|
+
# @param [Alchemy::Page]
|
300
|
+
# The root page for the language being ordered
|
301
|
+
#
|
302
|
+
def create_tree(items, rootpage)
|
303
|
+
_, tree = visit_nodes(items, rootpage.lft + 1, rootpage.id, rootpage.depth + 1, {}, "", rootpage.restricted)
|
304
|
+
tree
|
257
305
|
end
|
258
306
|
|
259
|
-
#
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
307
|
+
# Returns a pair, the path that a given tree node should take, and the path its children should take
|
308
|
+
#
|
309
|
+
# This function will add a node's own slug into their ancestor's path
|
310
|
+
# in order to create the full URL of a node
|
311
|
+
#
|
312
|
+
# NOTE: external and invisible pages are not part of the full path of their children
|
313
|
+
#
|
314
|
+
# @param [String]
|
315
|
+
# The node's ancestors path
|
316
|
+
# @param [Hash]
|
317
|
+
# A children node
|
318
|
+
#
|
319
|
+
def process_url(ancestors_path, item)
|
320
|
+
default_urlname = (ancestors_path.blank? ? "" : "#{ancestors_path}/") + item['slug']
|
321
|
+
|
322
|
+
pair = {my_urlname: default_urlname, children_path: default_urlname}
|
323
|
+
|
324
|
+
if item['external'] == true || item['visible'] == false
|
325
|
+
# children ignore an ancestor in their path if external or invisible
|
326
|
+
pair[:children_path] = ancestors_path
|
267
327
|
end
|
328
|
+
|
329
|
+
pair
|
330
|
+
end
|
331
|
+
|
332
|
+
def load_page
|
333
|
+
@page = Page.find(params[:id])
|
268
334
|
end
|
269
335
|
|
336
|
+
def pages_from_raw_request
|
337
|
+
request.raw_post.split('&').map { |i| i = {i.split('=')[0].gsub(/[^0-9]/, '') => i.split('=')[1]} }
|
338
|
+
end
|
270
339
|
end
|
271
340
|
end
|
272
341
|
end
|
@@ -164,7 +164,7 @@ module Alchemy
|
|
164
164
|
def pictures_per_page_for_size(size)
|
165
165
|
case size
|
166
166
|
when 'small'
|
167
|
-
per_page = in_overlay? ?
|
167
|
+
per_page = in_overlay? ? 25 : (per_page_value_for_screen_size * 2.9).floor
|
168
168
|
when 'large'
|
169
169
|
per_page = in_overlay? ? 4 : (per_page_value_for_screen_size / 1.7).floor + 1
|
170
170
|
else
|
@@ -27,14 +27,7 @@ module Alchemy
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def create
|
30
|
-
@user = User.
|
31
|
-
if @user.save
|
32
|
-
if @user.role == "registered" && params[:send_credentials]
|
33
|
-
Notifications.registered_user_created(@user).deliver
|
34
|
-
elsif params[:send_credentials]
|
35
|
-
Notifications.admin_user_created(@user).deliver
|
36
|
-
end
|
37
|
-
end
|
30
|
+
@user = User.create(params[:user])
|
38
31
|
render_errors_or_redirect(
|
39
32
|
@user,
|
40
33
|
admin_users_path,
|
@@ -50,9 +43,6 @@ module Alchemy
|
|
50
43
|
else
|
51
44
|
@user.update_without_password(params[:user])
|
52
45
|
end
|
53
|
-
if params[:send_credentials]
|
54
|
-
Notifications.admin_user_created(@user).deliver
|
55
|
-
end
|
56
46
|
render_errors_or_redirect(
|
57
47
|
@user,
|
58
48
|
admin_users_path,
|
@@ -18,9 +18,6 @@ module Alchemy
|
|
18
18
|
def create
|
19
19
|
@user = User.new(params[:user])
|
20
20
|
if @user.save
|
21
|
-
if params[:send_credentials]
|
22
|
-
Notifications.admin_user_created(@user).deliver
|
23
|
-
end
|
24
21
|
flash[:notice] = _t('Successfully signup admin user')
|
25
22
|
sign_in :user, @user
|
26
23
|
redirect_to admin_dashboard_path
|
data/app/models/alchemy/page.rb
CHANGED
@@ -54,8 +54,8 @@ module Alchemy
|
|
54
54
|
attr_accessor :do_not_validate_language
|
55
55
|
|
56
56
|
before_save :set_language_code, :unless => :systempage?
|
57
|
-
before_save :
|
58
|
-
|
57
|
+
before_save :inherit_restricted_status, if: -> { parent && parent.restricted? }, unless: :systempage?
|
58
|
+
after_update :set_restrictions_to_child_pages, unless: :systempage?
|
59
59
|
after_update :create_legacy_url, :if => :urlname_changed?, :unless => :redirects_to_external?
|
60
60
|
|
61
61
|
scope :language_roots, where(:language_root => true)
|
@@ -305,14 +305,10 @@ module Alchemy
|
|
305
305
|
self.public_was != self.public
|
306
306
|
end
|
307
307
|
|
308
|
+
# Sets my restricted value to all child pages
|
309
|
+
#
|
308
310
|
def set_restrictions_to_child_pages
|
309
|
-
descendants.
|
310
|
-
child.update_attributes(:restricted => self.restricted?)
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
def inherit_restricted_status
|
315
|
-
self.restricted = parent.restricted?
|
311
|
+
descendants.update_all(restricted: self.restricted?)
|
316
312
|
end
|
317
313
|
|
318
314
|
def contains_feed?
|
@@ -321,7 +317,7 @@ module Alchemy
|
|
321
317
|
|
322
318
|
# Returns true or false if the pages layout_description for config/alchemy/page_layouts.yml contains redirects_to_external: true
|
323
319
|
def redirects_to_external?
|
324
|
-
definition["redirects_to_external"]
|
320
|
+
!!definition["redirects_to_external"]
|
325
321
|
end
|
326
322
|
|
327
323
|
# Returns the first published child
|
@@ -377,6 +373,25 @@ module Alchemy
|
|
377
373
|
self.save
|
378
374
|
end
|
379
375
|
|
376
|
+
# Updates an Alchemy::Page based on a new ordering to be applied to it
|
377
|
+
#
|
378
|
+
# Note: Page's urls should not be updated (and a legacy URL created) if nesting is OFF
|
379
|
+
# or if a page is external or if the URL is the same
|
380
|
+
#
|
381
|
+
# @param [TreeNode]
|
382
|
+
# A tree node with new lft, rgt, depth, url, parent_id and restricted indexes to be updated
|
383
|
+
#
|
384
|
+
def update_node!(node)
|
385
|
+
hash = {lft: node.left, rgt: node.right, parent_id: node.parent, depth: node.depth, restricted: node.restricted}
|
386
|
+
|
387
|
+
if Config.get(:url_nesting) && !self.redirects_to_external? && self.urlname != node.url
|
388
|
+
LegacyPageUrl.create(page_id: self.id, urlname: self.urlname)
|
389
|
+
hash.merge!(urlname: node.url)
|
390
|
+
end
|
391
|
+
|
392
|
+
self.class.update_all(hash, {id: self.id})
|
393
|
+
end
|
394
|
+
|
380
395
|
private
|
381
396
|
|
382
397
|
def next_or_previous(direction = :next, options = {})
|
@@ -407,5 +422,11 @@ module Alchemy
|
|
407
422
|
legacy_urls.find_or_create_by_urlname(:urlname => urlname_was)
|
408
423
|
end
|
409
424
|
|
425
|
+
# Sets my restricted status to parent's restricted status
|
426
|
+
#
|
427
|
+
def inherit_restricted_status
|
428
|
+
self.restricted = parent.restricted?
|
429
|
+
end
|
430
|
+
|
410
431
|
end
|
411
432
|
end
|
data/app/models/alchemy/user.rb
CHANGED
@@ -18,11 +18,15 @@ module Alchemy
|
|
18
18
|
:password,
|
19
19
|
:password_confirmation,
|
20
20
|
:roles,
|
21
|
+
:send_credentials,
|
21
22
|
:tag_list
|
22
23
|
)
|
23
24
|
|
25
|
+
attr_accessor :send_credentials
|
26
|
+
|
24
27
|
has_many :folded_pages
|
25
28
|
|
29
|
+
validates_uniqueness_of :login
|
26
30
|
validates_presence_of :roles
|
27
31
|
|
28
32
|
# Unlock all locked pages before destroy and before the user gets logged out.
|
@@ -33,6 +37,8 @@ module Alchemy
|
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
40
|
+
after_save :deliver_welcome_mail, if: -> { send_credentials }
|
41
|
+
|
36
42
|
scope :admins, where(arel_table[:roles].matches("%admin%")) # not pleased with that approach
|
37
43
|
# mysql regexp word matching would be much nicer, but it's not included in SQLite functions per se.
|
38
44
|
# scope :admins, where("#{table_name}.roles REGEXP '[[:<:]]admin[[:>:]]'")
|
@@ -153,5 +159,15 @@ module Alchemy
|
|
153
159
|
self.class.logged_in_timeout
|
154
160
|
end
|
155
161
|
|
162
|
+
# Delivers a welcome mail depending from user's role.
|
163
|
+
#
|
164
|
+
def deliver_welcome_mail
|
165
|
+
if has_role?('author') || has_role?('editor') || has_role?('admin')
|
166
|
+
Notifications.admin_user_created(self).deliver
|
167
|
+
else
|
168
|
+
Notifications.registered_user_created(self).deliver
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
156
172
|
end
|
157
173
|
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
|
<%= sitemapFolderLink(page) unless page.children.blank? || @sorting %>
|
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
<td class="input"><%= f.text_field attribute[:name], :type => :date -%></td>
|
1
|
+
<%= render 'date', attribute: attribute, f: f %>
|
@@ -57,8 +57,8 @@
|
|
57
57
|
<tr>
|
58
58
|
<td> </td>
|
59
59
|
<td class="checkbox long">
|
60
|
-
<%=
|
61
|
-
<%=
|
60
|
+
<%= f.check_box(:send_credentials, checked: @user.new_record?) %>
|
61
|
+
<%= f.label(:send_credentials) %>
|
62
62
|
</td>
|
63
63
|
</tr>
|
64
64
|
<tr>
|
@@ -280,7 +280,6 @@ de:
|
|
280
280
|
"Select all": "Alle auswählen"
|
281
281
|
"Select an content": "Wählen Sie einen Inhalt aus"
|
282
282
|
"Select style": "Stilvorlage"
|
283
|
-
"Send email with credentials": "Sende Benutzerdaten per Email"
|
284
283
|
"Send reset instructions": "Anweisungen senden"
|
285
284
|
"Show Elements Window": "Elementeliste anzeigen"
|
286
285
|
"Show Preview Window": "Seitenvorschau anzeigen"
|
@@ -940,6 +939,7 @@ de:
|
|
940
939
|
password_confirmation: "Passwort Bestätigung"
|
941
940
|
roles: "Benutzerrollen"
|
942
941
|
tag_list: Tags
|
942
|
+
send_credentials: "Sende Benutzerdaten per E-Mail"
|
943
943
|
|
944
944
|
alchemy/site:
|
945
945
|
name: "Bezeichnung"
|
data/lib/alchemy/version.rb
CHANGED
@@ -37,6 +37,95 @@ module Alchemy
|
|
37
37
|
|
38
38
|
end
|
39
39
|
|
40
|
+
describe '#order' do
|
41
|
+
let(:page_1) { FactoryGirl.create(:page, visible: true) }
|
42
|
+
let(:page_2) { FactoryGirl.create(:page, visible: true) }
|
43
|
+
let(:page_3) { FactoryGirl.create(:page, visible: true) }
|
44
|
+
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]} }
|
45
|
+
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]} }
|
46
|
+
let(:page_item_3) { {id: page_3.id, slug: page_3.slug, restricted: false, external: page_3.redirects_to_external?, visible: page_3.visible? } }
|
47
|
+
let(:set_of_pages) { [page_item_1] }
|
48
|
+
|
49
|
+
it "stores the new order" do
|
50
|
+
xhr :post, :order, set: set_of_pages.to_json
|
51
|
+
page_1.reload
|
52
|
+
expect(page_1.descendants).to eq([page_2, page_3])
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with url nesting enabled' do
|
56
|
+
before { Alchemy::Config.stub(get: true) }
|
57
|
+
|
58
|
+
it "updates the pages urlnames" do
|
59
|
+
xhr :post, :order, set: set_of_pages.to_json
|
60
|
+
[page_1, page_2, page_3].map(&:reload)
|
61
|
+
expect(page_1.urlname).to eq("#{page_1.slug}")
|
62
|
+
expect(page_2.urlname).to eq("#{page_1.slug}/#{page_2.slug}")
|
63
|
+
expect(page_3.urlname).to eq("#{page_1.slug}/#{page_2.slug}/#{page_3.slug}")
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with invisible page in tree' do
|
67
|
+
let(:page_item_2) do
|
68
|
+
{
|
69
|
+
id: page_2.id,
|
70
|
+
slug: page_2.slug,
|
71
|
+
children: [page_item_3],
|
72
|
+
visible: false
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
it "does not use this pages slug in urlnames of descendants" 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_3.slug}")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with external page in tree' do
|
86
|
+
let(:page_item_2) do
|
87
|
+
{
|
88
|
+
id: page_2.id,
|
89
|
+
slug: page_2.slug,
|
90
|
+
children: [page_item_3],
|
91
|
+
external: true
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
it "does not use this pages slug in urlnames of descendants" do
|
96
|
+
xhr :post, :order, set: set_of_pages.to_json
|
97
|
+
[page_1, page_2, page_3].map(&:reload)
|
98
|
+
expect(page_3.urlname).to eq("#{page_1.slug}/#{page_3.slug}")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'with restricted page in tree' do
|
103
|
+
let(:page_2) { FactoryGirl.create(:page, restricted: true) }
|
104
|
+
let(:page_item_2) do
|
105
|
+
{
|
106
|
+
id: page_2.id,
|
107
|
+
slug: page_2.slug,
|
108
|
+
children: [page_item_3],
|
109
|
+
restricted: true
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
it "updates restricted status of descendants" do
|
114
|
+
xhr :post, :order, set: set_of_pages.to_json
|
115
|
+
page_3.reload
|
116
|
+
expect(page_3.restricted).to be_true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "creates legacy urls" do
|
121
|
+
xhr :post, :order, set: set_of_pages.to_json
|
122
|
+
[page_2, page_3].map(&:reload)
|
123
|
+
expect(page_2.legacy_urls.size).to eq(1)
|
124
|
+
expect(page_3.legacy_urls.size).to eq(1)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
40
129
|
describe "#configure" do
|
41
130
|
render_views
|
42
131
|
|