alchemy_cms 2.6.3 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (246) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +14 -0
  3. data/.travis.yml +1 -1
  4. data/Gemfile +7 -6
  5. data/README.md +15 -5
  6. data/alchemy_cms.gemspec +3 -2
  7. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +9 -17
  8. data/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +70 -0
  9. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +80 -0
  10. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +43 -19
  11. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +3 -1
  12. data/app/assets/javascripts/alchemy/alchemy.js +4 -2
  13. data/app/assets/javascripts/alchemy/alchemy.onload.js.coffee +1 -1
  14. data/app/assets/javascripts/alchemy/alchemy.spinner.js.coffee +14 -0
  15. data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee.erb +96 -0
  16. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +22 -0
  17. data/app/assets/javascripts/alchemy/alchemy.windows.js.coffee +28 -17
  18. data/app/assets/stylesheets/alchemy/base.scss +6 -0
  19. data/app/assets/stylesheets/alchemy/elements.scss +2 -28
  20. data/app/assets/stylesheets/alchemy/errors.scss +1 -1
  21. data/app/assets/stylesheets/alchemy/menubar.css.scss +2 -0
  22. data/app/assets/stylesheets/alchemy/sitemap.scss +21 -34
  23. data/app/assets/stylesheets/alchemy/tables.scss +13 -3
  24. data/app/controllers/alchemy/admin/attachments_controller.rb +10 -5
  25. data/app/controllers/alchemy/admin/base_controller.rb +19 -0
  26. data/app/controllers/alchemy/admin/contents_controller.rb +1 -4
  27. data/app/controllers/alchemy/admin/dashboard_controller.rb +2 -1
  28. data/app/controllers/alchemy/admin/elements_controller.rb +1 -1
  29. data/app/controllers/alchemy/admin/essence_files_controller.rb +1 -1
  30. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +70 -56
  31. data/app/controllers/alchemy/admin/pages_controller.rb +37 -114
  32. data/app/controllers/alchemy/admin/pictures_controller.rb +5 -12
  33. data/app/controllers/alchemy/admin/resources_controller.rb +3 -1
  34. data/app/controllers/alchemy/admin/trash_controller.rb +1 -1
  35. data/app/controllers/alchemy/attachments_controller.rb +1 -1
  36. data/app/controllers/alchemy/base_controller.rb +3 -15
  37. data/app/controllers/alchemy/messages_controller.rb +4 -10
  38. data/app/controllers/alchemy/pages_controller.rb +6 -6
  39. data/app/controllers/alchemy/passwords_controller.rb +1 -1
  40. data/app/controllers/alchemy/user_sessions_controller.rb +1 -1
  41. data/app/helpers/alchemy/admin/base_helper.rb +49 -230
  42. data/app/helpers/alchemy/admin/contents_helper.rb +5 -1
  43. data/app/helpers/alchemy/admin/elements_helper.rb +19 -47
  44. data/app/helpers/alchemy/admin/essences_helper.rb +59 -17
  45. data/app/helpers/alchemy/admin/navigation_helper.rb +204 -0
  46. data/app/helpers/alchemy/admin/pages_helper.rb +22 -79
  47. data/app/helpers/alchemy/admin/pictures_helper.rb +1 -1
  48. data/app/helpers/alchemy/admin/tags_helper.rb +42 -0
  49. data/app/helpers/alchemy/base_helper.rb +0 -11
  50. data/app/helpers/alchemy/elements_helper.rb +48 -25
  51. data/app/helpers/alchemy/essences_helper.rb +0 -20
  52. data/app/helpers/alchemy/pages_helper.rb +18 -14
  53. data/app/helpers/alchemy/url_helper.rb +1 -0
  54. data/app/mailers/alchemy/messages.rb +4 -6
  55. data/app/models/alchemy/attachment.rb +3 -0
  56. data/app/models/alchemy/cell.rb +33 -35
  57. data/app/models/alchemy/content.rb +20 -111
  58. data/app/models/alchemy/content/factory.rb +188 -0
  59. data/app/models/alchemy/element.rb +51 -200
  60. data/app/models/alchemy/element/definitions.rb +52 -0
  61. data/app/models/alchemy/element/presenters.rb +87 -0
  62. data/app/models/alchemy/essence_date.rb +1 -1
  63. data/app/models/alchemy/essence_file.rb +6 -7
  64. data/app/models/alchemy/essence_picture.rb +19 -4
  65. data/app/models/alchemy/message.rb +18 -14
  66. data/app/models/alchemy/page.rb +120 -214
  67. data/app/models/alchemy/page/elements.rb +145 -36
  68. data/app/models/alchemy/page/natures.rb +90 -0
  69. data/app/models/alchemy/page/scopes.rb +93 -0
  70. data/app/models/alchemy/page/users.rb +25 -0
  71. data/app/models/alchemy/picture.rb +15 -0
  72. data/app/models/alchemy/site.rb +15 -1
  73. data/app/models/alchemy/site/layout.rb +38 -0
  74. data/app/models/alchemy/user.rb +13 -3
  75. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +7 -7
  76. data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +8 -8
  77. data/app/views/alchemy/admin/attachments/_tag_list.html.erb +1 -16
  78. data/app/views/alchemy/admin/attachments/destroy.js.erb +1 -4
  79. data/app/views/alchemy/admin/contents/create.js.erb +1 -1
  80. data/app/views/alchemy/admin/dashboard/index.html.erb +14 -13
  81. data/app/views/alchemy/admin/elements/_element_head.html.erb +7 -7
  82. data/app/views/alchemy/admin/elements/_refresh_editor.js.erb +10 -0
  83. data/app/views/alchemy/admin/elements/create.js.erb +44 -44
  84. data/app/views/alchemy/admin/elements/fold.js.erb +22 -26
  85. data/app/views/alchemy/admin/elements/trash.js.erb +1 -1
  86. data/app/views/alchemy/admin/elements/update.js.erb +22 -25
  87. data/app/views/alchemy/admin/essence_files/assign.js.erb +8 -3
  88. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +14 -12
  89. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +22 -39
  90. data/app/views/alchemy/admin/pages/_page.html.erb +73 -80
  91. data/app/views/alchemy/admin/pages/destroy.js.erb +2 -2
  92. data/app/views/alchemy/admin/pages/edit.html.erb +21 -18
  93. data/app/views/alchemy/admin/pages/fold.js.erb +1 -0
  94. data/app/views/alchemy/admin/pages/info.html.erb +32 -0
  95. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +11 -13
  96. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +20 -20
  97. data/app/views/alchemy/admin/partials/_sub_navigation.html.erb +8 -0
  98. data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +25 -0
  99. data/app/views/alchemy/admin/partials/_upload_form.html.erb +15 -15
  100. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +39 -39
  101. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +10 -10
  102. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -16
  103. data/app/views/alchemy/admin/resources/destroy.js.erb +1 -1
  104. data/app/views/alchemy/base/500.html.erb +1 -1
  105. data/app/views/alchemy/base/permission_denied.js.erb +1 -1
  106. data/app/views/alchemy/base/redirect.js.erb +1 -1
  107. data/app/views/alchemy/essences/_essence_link_editor.html.erb +1 -1
  108. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +1 -1
  109. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +1 -1
  110. data/app/views/alchemy/essences/_essence_text_editor.html.erb +1 -1
  111. data/app/views/alchemy/essences/{_essence_picture_tools.html.erb → shared/_essence_picture_tools.html.erb} +5 -5
  112. data/app/views/alchemy/essences/{_linkable_essence_tools.html.erb → shared/_linkable_essence_tools.html.erb} +0 -0
  113. data/app/views/alchemy/messages/contact_form_mail.de.text.erb +12 -0
  114. data/app/views/alchemy/messages/contact_form_mail.en.text.erb +12 -0
  115. data/app/views/alchemy/notifications/reset_password_instructions.de.text.erb +1 -1
  116. data/app/views/alchemy/notifications/reset_password_instructions.en.text.erb +2 -2
  117. data/app/views/alchemy/pages/sitemap.xml.erb +3 -5
  118. data/app/views/alchemy/user_sessions/leave.html.erb +1 -1
  119. data/app/views/layouts/alchemy/admin.html.erb +4 -2
  120. data/app/views/layouts/alchemy/sitemap.xml.erb +1 -1
  121. data/bin/alchemy +7 -13
  122. data/config/alchemy/config.yml +1 -0
  123. data/config/authorization_rules.rb +2 -3
  124. data/config/initializers/dragonfly.rb +2 -0
  125. data/config/locales/alchemy.de.yml +8 -9
  126. data/config/locales/alchemy.en.yml +7 -4
  127. data/config/routes.rb +3 -0
  128. data/db/migrate/{20130214233001_alchemy_two_point_five.rb → 20130827094554_alchemy_two_point_six.rb} +29 -6
  129. data/lib/alchemy/auth/engine.rb +9 -0
  130. data/lib/alchemy/capistrano.rb +37 -12
  131. data/lib/alchemy/config.rb +48 -35
  132. data/lib/alchemy/engine.rb +35 -6
  133. data/lib/alchemy/essence.rb +25 -29
  134. data/lib/alchemy/ferret/search.rb +86 -0
  135. data/lib/alchemy/{scoped_pagination_url_helper.rb → kaminari/scoped_pagination_url_helper.rb} +0 -0
  136. data/lib/alchemy/logger.rb +3 -4
  137. data/lib/alchemy/page_layout.rb +124 -55
  138. data/lib/alchemy/resource.rb +0 -10
  139. data/lib/alchemy/resources_helper.rb +0 -5
  140. data/lib/alchemy/seeder.rb +1 -32
  141. data/lib/alchemy/shell.rb +6 -1
  142. data/lib/alchemy/tinymce.rb +41 -32
  143. data/lib/alchemy/upgrader.rb +3 -1
  144. data/lib/alchemy/upgrader/two_point_five.rb +15 -8
  145. data/lib/alchemy/upgrader/two_point_one.rb +10 -10
  146. data/lib/alchemy/upgrader/two_point_two.rb +96 -51
  147. data/lib/alchemy/version.rb +1 -1
  148. data/lib/alchemy_cms.rb +5 -46
  149. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +1 -1
  150. data/lib/rails/generators/alchemy/devise/devise_generator.rb +9 -4
  151. data/lib/rails/generators/alchemy/essence/essence_generator.rb +7 -6
  152. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +1 -1
  153. data/lib/rails/generators/alchemy/scaffold/files/_standard.html.erb +1 -0
  154. data/lib/rails/generators/alchemy/scaffold/scaffold_generator.rb +1 -0
  155. data/lib/rails/generators/alchemy/site_layouts/site_layouts_generator.rb +23 -0
  156. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.erb +1 -0
  157. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.haml +1 -0
  158. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.slim +1 -0
  159. data/lib/rails/templates/alchemy.rb +2 -2
  160. data/lib/tasks/alchemy/db.rake +3 -1
  161. data/lib/tasks/alchemy/tidy.rake +82 -0
  162. data/lib/tasks/alchemy/upgrade.rake +2 -1
  163. data/spec/controllers/admin/attachments_controller_spec.rb +124 -0
  164. data/spec/controllers/admin/base_controller_spec.rb +35 -0
  165. data/spec/controllers/admin/clipboard_controller_spec.rb +1 -1
  166. data/spec/controllers/admin/contents_controller_spec.rb +17 -26
  167. data/spec/controllers/admin/dashboard_controller_spec.rb +121 -0
  168. data/spec/controllers/admin/elements_controller_spec.rb +1 -1
  169. data/spec/controllers/admin/essence_files_controller_spec.rb +67 -0
  170. data/spec/controllers/admin/essence_pictures_controller_spec.rb +161 -0
  171. data/spec/controllers/admin/languages_controller_spec.rb +1 -1
  172. data/spec/controllers/admin/layoutpages_controller_spec.rb +28 -0
  173. data/spec/controllers/admin/pages_controller_spec.rb +164 -118
  174. data/spec/controllers/admin/pictures_controller_spec.rb +89 -0
  175. data/spec/controllers/admin/trash_controller_spec.rb +21 -31
  176. data/spec/controllers/admin/users_controller_spec.rb +114 -85
  177. data/spec/controllers/attachments_controller_spec.rb +6 -2
  178. data/spec/controllers/base_controller_spec.rb +22 -0
  179. data/spec/controllers/elements_controller_spec.rb +1 -1
  180. data/spec/controllers/messages_controller_spec.rb +200 -0
  181. data/spec/controllers/pictures_controller_spec.rb +1 -1
  182. data/spec/controllers/user_sessions_controller_spec.rb +7 -6
  183. data/spec/controllers/users_controller_spec.rb +2 -2
  184. data/spec/dummy/config/alchemy/cells.yml +2 -0
  185. data/spec/dummy/config/application.rb +19 -8
  186. data/spec/dummy/db/migrate/{20130214233001_alchemy_two_point_five.rb → 20130827094554_alchemy_two_point_six.rb} +29 -6
  187. data/spec/dummy/db/schema.rb +1 -1
  188. data/spec/fast_specs.rb +15 -0
  189. data/spec/helpers/admin/base_helper_spec.rb +53 -34
  190. data/spec/helpers/admin/contents_helper_spec.rb +15 -7
  191. data/spec/helpers/admin/elements_helper_spec.rb +79 -34
  192. data/spec/helpers/admin/essences_helper_spec.rb +45 -31
  193. data/spec/helpers/admin/navigation_helper_spec.rb +204 -0
  194. data/spec/helpers/admin/pages_helper_spec.rb +25 -15
  195. data/spec/helpers/admin/tags_helper_spec.rb +62 -2
  196. data/spec/helpers/elements_helper_spec.rb +202 -138
  197. data/spec/helpers/pages_helper_spec.rb +48 -0
  198. data/spec/helpers/url_helper_spec.rb +7 -0
  199. data/spec/libraries/config_spec.rb +110 -3
  200. data/spec/libraries/essence_spec.rb +29 -9
  201. data/spec/libraries/page_layout_spec.rb +134 -0
  202. data/spec/libraries/resource_spec.rb +3 -16
  203. data/spec/libraries/resources_helper_spec.rb +4 -8
  204. data/spec/libraries/shell_spec.rb +1 -0
  205. data/spec/libraries/tinymce_spec.rb +61 -0
  206. data/spec/mailers/messages_spec.rb +23 -0
  207. data/spec/models/attachment_spec.rb +45 -0
  208. data/spec/models/cell_spec.rb +62 -9
  209. data/spec/models/content_spec.rb +110 -28
  210. data/spec/models/element_spec.rb +275 -253
  211. data/spec/models/essence_date_spec.rb +25 -0
  212. data/spec/models/essence_file_spec.rb +23 -0
  213. data/spec/models/essence_html_spec.rb +13 -0
  214. data/spec/models/essence_picture_spec.rb +16 -0
  215. data/spec/models/essence_text_spec.rb +29 -0
  216. data/spec/models/language_spec.rb +34 -0
  217. data/spec/models/message_spec.rb +43 -0
  218. data/spec/models/page_spec.rb +726 -567
  219. data/spec/models/picture_spec.rb +98 -0
  220. data/spec/models/site_spec.rb +60 -2
  221. data/spec/models/tag_spec.rb +31 -0
  222. data/spec/models/user_spec.rb +4 -4
  223. data/spec/spec_helper.rb +49 -58
  224. data/spec/support/alchemy/controller_helpers.rb +35 -0
  225. data/spec/support/alchemy/{specs_helpers.rb → integration_helpers.rb} +4 -8
  226. data/spec/{factories.rb → support/factories.rb} +11 -1
  227. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +2 -8
  228. metadata +166 -106
  229. data/Guardfile +0 -16
  230. data/app/assets/javascripts/alchemy/alchemy.dirty.js +0 -93
  231. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +0 -122
  232. data/app/models/alchemy/tree_node.rb +0 -4
  233. data/app/views/alchemy/admin/pages/_page_infos.html.erb +0 -3
  234. data/app/views/alchemy/admin/partials/_sub_navigation_tab.html.erb +0 -8
  235. data/app/views/alchemy/messages/contact_form_mail.text.erb +0 -12
  236. data/config/initializers/kaminari_config.rb +0 -9
  237. data/db/migrate/20130221200514_migrate_attachments_to_dragonfly.rb +0 -21
  238. data/db/migrate/20130312205327_change_alchemy_users_role_to_roles.rb +0 -11
  239. data/lib/alchemy/auth_engine.rb +0 -7
  240. data/lib/alchemy/authentication_helpers.rb +0 -9
  241. data/lib/alchemy/ferret_search.rb +0 -84
  242. data/lib/extensions/array.rb +0 -25
  243. data/lib/extensions/hash.rb +0 -34
  244. data/spec/dummy/db/migrate/20130221200514_migrate_attachments_to_dragonfly.rb +0 -21
  245. data/spec/dummy/db/migrate/20130312205327_change_alchemy_users_role_to_roles.rb +0 -11
  246. data/spec/models/page_layout_spec.rb +0 -60
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module Alchemy
4
+ describe EssenceDate do
5
+ let(:essence_date) { EssenceDate.new }
6
+
7
+ describe '#preview_text' do
8
+
9
+ context "if no date set" do
10
+ it "should return an empty string" do
11
+ expect(essence_date.preview_text).to eq("")
12
+ end
13
+ end
14
+
15
+ context "if date set" do
16
+ it "should format the date by i18n" do
17
+ essence_date.date = Date.today
18
+ ::I18n.should_receive(:l).with(essence_date.date, format: :date)
19
+ essence_date.preview_text
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module Alchemy
4
+ describe EssenceFile do
5
+
6
+ describe '#preview_text' do
7
+ let(:attachment) { mock_model(Attachment, name: 'File') }
8
+ let(:essence) { EssenceFile.new }
9
+
10
+ it "returns the attachment's name as preview text" do
11
+ essence.stub(:attachment).and_return(attachment)
12
+ essence.preview_text.should == 'File'
13
+ end
14
+
15
+ context "with no attachment assigned" do
16
+ it "returns empty string" do
17
+ essence.preview_text.should == ''
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ module Alchemy
4
+ describe EssenceHtml do
5
+ describe '#preview_text' do
6
+ let (:essence_html) { EssenceHtml.new(source: '<p>hello!</p>') }
7
+
8
+ it "should return html escaped source code" do
9
+ expect(essence_html.preview_text).to eq('&lt;p&gt;hello!&lt;/p&gt;')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -23,5 +23,21 @@ module Alchemy
23
23
  essence.caption.should == "hello<br/>kitty"
24
24
  end
25
25
 
26
+ describe '#preview_text' do
27
+ let(:picture) { mock_model(Picture, name: 'Cute Cat Kittens')}
28
+ let(:essence) { EssencePicture.new }
29
+
30
+ it "should return the pictures name as preview text" do
31
+ essence.stub(:picture).and_return(picture)
32
+ essence.preview_text.should == 'Cute Cat Kittens'
33
+ end
34
+
35
+ context "with no picture assigned" do
36
+ it "returns empty string" do
37
+ essence.preview_text.should == ''
38
+ end
39
+ end
40
+ end
41
+
26
42
  end
27
43
  end
@@ -37,5 +37,34 @@ module Alchemy
37
37
  end
38
38
  end
39
39
 
40
+ context "acts_as_essence methods" do
41
+
42
+ describe '#preview_text' do
43
+ let(:essence) { EssenceText.new(body: 'Lorem ipsum') }
44
+
45
+ it "should return a preview text" do
46
+ essence.preview_text.should == 'Lorem ipsum'
47
+ end
48
+
49
+ context "with maxlength of 2" do
50
+ it "should return very short preview text" do
51
+ essence.preview_text(2).should == 'Lo'
52
+ end
53
+ end
54
+
55
+ context "with another preview_text_column defined" do
56
+ before {
57
+ essence.stub(:title).and_return('Title column')
58
+ essence.stub(:preview_text_column).and_return(:title)
59
+ }
60
+
61
+ it "should use this column as preview text method" do
62
+ essence.preview_text.should == 'Title column'
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+
40
69
  end
41
70
  end
@@ -117,5 +117,39 @@ module Alchemy
117
117
 
118
118
  end
119
119
 
120
+ describe '.all_codes_for_published' do
121
+ it "should return all codes of published languages" do
122
+ Language.stub!(:published).and_return([mock_model('Language', code: 'de'), mock_model('Language', code: 'en')])
123
+ expect(Language.all_codes_for_published).to eq(['de', 'en'])
124
+ end
125
+ end
126
+
127
+ context 'validations' do
128
+ let(:language) { Language.new(default: true, public: false) }
129
+
130
+ describe 'publicity_of_default_language' do
131
+ context 'if language is not published' do
132
+ it "should add an error to the object" do
133
+ expect(language.valid?).to eq(false)
134
+ expect(language.errors.messages).to have_key(:base)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe 'presence_of_default_language' do
140
+ context 'if no default language would exist anymore' do
141
+ before do
142
+ Language.stub!(:get_default).and_return(language)
143
+ language.stub!(:default_changed?).and_return(true)
144
+ end
145
+
146
+ it "should add an error to the object" do
147
+ expect(language.valid?).to eq(false)
148
+ expect(language.errors.messages).to have_key(:base)
149
+ end
150
+ end
151
+ end
152
+ end
153
+
120
154
  end
121
155
  end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ module Alchemy
4
+ describe Message do
5
+ let(:message) { Message.new }
6
+
7
+ describe '.config' do
8
+ it "should return the mailer config" do
9
+ Config.should_receive(:get).with(:mailer)
10
+ Message.config
11
+ end
12
+ end
13
+
14
+ describe '#persisted?' do
15
+ it "should return false" do
16
+ expect(message.persisted?).to eq(false)
17
+ end
18
+ end
19
+
20
+ describe '#attributes' do
21
+ it "should call .attributes" do
22
+ Message.should_receive(:attributes)
23
+ message.attributes
24
+ end
25
+ end
26
+
27
+ describe '#email_is_filled' do
28
+ context 'if email attribute is filled' do
29
+ it "should return true" do
30
+ message.stub!(:email).and_return('me@you.com')
31
+ expect(message.send(:email_is_filled)).to eq(true)
32
+ end
33
+ end
34
+
35
+ context 'if email attribute is not filled' do
36
+ it "should return false" do
37
+ message.stub!(:email).and_return('')
38
+ expect(message.send(:email_is_filled)).to eq(false)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -13,141 +13,91 @@ module Alchemy
13
13
  let(:public_page) { FactoryGirl.create(:public_page) }
14
14
  let(:news_page) { FactoryGirl.create(:public_page, :page_layout => 'news', :do_not_autogenerate => false) }
15
15
 
16
- describe ".layout_description" do
17
16
 
18
- context "for a language root page" do
19
-
20
- it "should return the page layout description as hash" do
21
- language_root.layout_description['name'].should == 'intro'
22
- end
23
-
24
- it "should return an empty hash for root page" do
25
- rootpage.layout_description.should == {}
26
- end
27
-
28
- end
29
-
30
- it "should raise Exception if the page_layout could not be found in the definition file" do
31
- expect { page.layout_description }.to raise_error
32
- end
33
-
34
- end
35
-
36
- it "should contain one rootpage" do
37
- Page.rootpage.should be_instance_of(Page)
38
- end
39
-
40
- it "should return all rss feed elements" do
41
- news_page.feed_elements.should_not be_empty
42
- news_page.feed_elements.should == Element.find_all_by_name('news')
43
- end
44
-
45
- context "finding elements" do
46
-
47
- before do
48
- FactoryGirl.create(:element, :public => false, :page => public_page)
49
- FactoryGirl.create(:element, :public => false, :page => public_page)
50
- end
51
-
52
- it "should return the collection of elements if passed an array into options[:collection]" do
53
- options = {:collection => public_page.elements}
54
- public_page.find_elements(options).all.should == public_page.elements.all
55
- end
56
-
57
- context "with show_non_public argument TRUE" do
58
-
59
- it "should return all elements from empty options" do
60
- public_page.find_elements({}, true).all.should == public_page.elements.all
61
- end
62
-
63
- it "should only return the elements passed as options[:only]" do
64
- public_page.find_elements({:only => ['article']}, true).all.should == public_page.elements.named('article').all
65
- end
17
+ # Validations
66
18
 
67
- it "should not return the elements passed as options[:except]" do
68
- public_page.find_elements({:except => ['article']}, true).all.should == public_page.elements - public_page.elements.named('article').all
69
- end
19
+ context 'validations' do
20
+ context "Creating a normal content page" do
21
+ let(:contentpage) { FactoryGirl.build(:page) }
70
22
 
71
- it "should return elements offsetted" do
72
- public_page.find_elements({:offset => 2}, true).all.should == public_page.elements.offset(2)
23
+ context "when its urlname exists as global page" do
24
+ it "it should be possible to save." do
25
+ contentpage.urlname = "existing_twice"
26
+ global_with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice", :layoutpage => true)
27
+ contentpage.should be_valid
28
+ end
73
29
  end
74
30
 
75
- it "should return elements limitted in count" do
76
- public_page.find_elements({:count => 1}, true).all.should == public_page.elements.limit(1)
31
+ it "should validate the page_layout" do
32
+ contentpage.page_layout = nil
33
+ contentpage.save
34
+ contentpage.should have(1).error_on(:page_layout)
77
35
  end
78
36
 
79
- end
80
-
81
- context "with show_non_public argument FALSE" do
82
-
83
- it "should return all elements from empty arguments" do
84
- public_page.find_elements().all.should == public_page.elements.published.all
37
+ it "should validate the parent_id" do
38
+ contentpage.parent_id = nil
39
+ contentpage.save
40
+ contentpage.should have(1).error_on(:parent_id)
85
41
  end
86
42
 
87
- it "should only return the public elements passed as options[:only]" do
88
- public_page.find_elements(:only => ['article']).all.should == public_page.elements.published.named('article').all
43
+ it "should validate the uniqueness of urlname" do
44
+ with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice")
45
+ contentpage.urlname = 'existing_twice'
46
+ contentpage.should_not be_valid
89
47
  end
90
48
 
91
- it "should return all public elements except the ones passed as options[:except]" do
92
- public_page.find_elements(:except => ['article']).all.should == public_page.elements.published.all - public_page.elements.published.named('article').all
93
- end
49
+ context "with url_nesting set to true" do
50
+ before { Config.stub!(:get).and_return(true) }
94
51
 
95
- it "should return elements offsetted" do
96
- public_page.find_elements({:offset => 2}).all.should == public_page.elements.published.offset(2)
97
- end
52
+ it "should only validate urlname dependent of parent" do
53
+ other_parent = FactoryGirl.create(:page, parent_id: Page.root.id)
54
+ with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice")
55
+ contentpage.urlname = 'existing_twice'
56
+ contentpage.parent_id = other_parent.id
57
+ contentpage.should be_valid
58
+ end
98
59
 
99
- it "should return elements limitted in count" do
100
- public_page.find_elements({:count => 1}).all.should == public_page.elements.published.limit(1)
60
+ it "should validate urlname dependent of parent" do
61
+ with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice")
62
+ contentpage.urlname = 'existing_twice'
63
+ contentpage.should_not be_valid
64
+ end
101
65
  end
102
-
103
66
  end
104
67
 
105
- end
106
-
107
- describe '#create' do
108
-
109
- context "before/after filter" do
110
-
111
- it "should automatically set the title from its name" do
112
- page = FactoryGirl.create(:page, :name => 'My Testpage', :language => language, :parent_id => language_root.id)
113
- page.title.should == 'My Testpage'
114
- end
115
-
116
- it "should get a webfriendly urlname" do
117
- page = FactoryGirl.create(:page, :name => 'klingon$&stößel ', :language => language, :parent_id => language_root.id)
118
- page.urlname.should == 'klingon-stoessel'
119
- end
120
-
121
- it "should generate a three letter urlname from two letter name" do
122
- page = FactoryGirl.create(:page, :name => 'Au', :language => language, :parent_id => language_root.id)
123
- page.urlname.should == '-au'
68
+ context "creating the rootpage without parent_id and page_layout" do
69
+ before do
70
+ Page.delete_all
71
+ @rootpage = FactoryGirl.build(:page, :parent_id => nil, :page_layout => nil, :name => 'Rootpage')
124
72
  end
125
73
 
126
- it "should generate a three letter urlname from two letter name with umlaut" do
127
- page = FactoryGirl.create(:page, :name => 'Aü', :language => language, :parent_id => language_root.id)
128
- page.urlname.should == 'aue'
74
+ it "should be valid" do
75
+ @rootpage.save
76
+ @rootpage.should be_valid
129
77
  end
78
+ end
130
79
 
131
- it "should generate a three letter urlname from one letter name" do
132
- page = FactoryGirl.create(:page, :name => 'A', :language => language, :parent_id => language_root.id)
133
- page.urlname.should == '--a'
80
+ context "saving a systempage" do
81
+ before do
82
+ @systempage = FactoryGirl.build(:systempage)
134
83
  end
135
84
 
136
- it "should add a user stamper" do
137
- page = FactoryGirl.create(:page, :name => 'A', :language => language, :parent_id => language_root.id)
138
- page.class.stamper_class.to_s.should == 'Alchemy::User'
85
+ it "should not validate the page_layout" do
86
+ @systempage.save
87
+ @systempage.should be_valid
139
88
  end
140
-
141
89
  end
142
-
143
90
  end
144
91
 
145
- describe '#update' do
146
92
 
147
- let(:page) { FactoryGirl.create(:page, :name => 'My Testpage', :language => language, :parent_id => language_root.id) }
93
+ # Callbacks
148
94
 
149
- context "before/after filter" do
95
+ context 'callbacks' do
96
+ let(:page) do
97
+ FactoryGirl.create(:page, :name => 'My Testpage', :language => language, :parent_id => language_root.id)
98
+ end
150
99
 
100
+ context 'before_save' do
151
101
  it "should not set the title automatically if the name changed but title is not blank" do
152
102
  page.name = "My Renaming Test"
153
103
  page.save; page.reload
@@ -159,9 +109,10 @@ module Alchemy
159
109
  page.save; page.reload
160
110
  page.title.should == "I like SEO"
161
111
  end
112
+ end
162
113
 
114
+ context 'after_update' do
163
115
  context "urlname has changed" do
164
-
165
116
  it "should store legacy url if page is not redirect to external page" do
166
117
  page.urlname = 'new-urlname'
167
118
  page.save!
@@ -185,178 +136,174 @@ module Alchemy
185
136
  page.save!
186
137
  page.legacy_urls.select { |u| u.urlname == 'my-testpage' }.size.should == 1
187
138
  end
188
-
189
139
  end
190
140
 
191
141
  context "urlname has not changed" do
192
-
193
142
  it "should not store a legacy url" do
194
143
  page.urlname = 'my-testpage'
195
144
  page.save!
196
145
  page.legacy_urls.should be_empty
197
146
  end
198
-
199
147
  end
200
-
201
148
  end
202
149
 
203
- end
204
-
205
- describe "#update_node!" do
206
-
207
- let(:original_url) { "sample-url" }
208
- let(:page) { FactoryGirl.create(:page, :language => language, :parent_id => language_root.id, :urlname => original_url, restricted: false) }
209
- let(:node) { TreeNode.new(10, 11, 12, 13, "another-url", true) }
150
+ context "a normal page" do
151
+ before do
152
+ @page = FactoryGirl.build(:page, :language_code => nil, :language => klingonian, :do_not_autogenerate => false)
153
+ end
210
154
 
211
- context "when nesting is enabled" do
212
- before { Alchemy::Config.stub(:get).with(:url_nesting) { true } }
155
+ it "should set the language code" do
156
+ @page.save
157
+ @page.language_code.should == "kl"
158
+ end
213
159
 
214
- context "when page is not external" do
160
+ it "should autogenerate the elements" do
161
+ @page.save
162
+ @page.elements.should_not be_empty
163
+ end
215
164
 
216
- before { page.stub(redirects_to_external?: false)}
165
+ it "should not autogenerate elements that are already on the page" do
166
+ @page.elements << FactoryGirl.create(:element, :name => 'header')
167
+ @page.save
168
+ @page.elements.select { |e| e.name == 'header' }.length.should == 1
169
+ end
217
170
 
218
- it "should update all attributes" do
219
- page.update_node!(node)
220
- page.reload
221
- expect(page.lft).to eq(node.left)
222
- expect(page.rgt).to eq(node.right)
223
- expect(page.parent_id).to eq(node.parent)
224
- expect(page.depth).to eq(node.depth)
225
- expect(page.urlname).to eq(node.url)
226
- expect(page.restricted).to eq(node.restricted)
171
+ context "with cells" do
172
+ before do
173
+ @page.stub!(:definition).and_return({'name' => 'with_cells', 'cells' => ['header', 'main']})
227
174
  end
228
175
 
229
- context "when url is the same" do
230
- let(:node) { TreeNode.new(10, 11, 12, 13, original_url, true) }
231
-
232
- it "should not create a legacy url" do
233
- page.update_node!(node)
234
- page.reload
235
- expect(page.legacy_urls.size).to eq(0)
236
- end
176
+ it "should have the generated elements in their cells" do
177
+ @page.stub!(:cell_definitions).and_return([{'name' => 'header', 'elements' => ['article']}])
178
+ @page.save
179
+ @page.cells.where(:name => 'header').first.elements.should_not be_empty
237
180
  end
238
181
 
239
- context "when url is not the same" do
240
- it "should create a legacy url" do
241
- page.update_node!(node)
242
- page.reload
243
- expect(page.legacy_urls.size).to eq(1)
182
+ context "and no elements in cell definitions" do
183
+ it "should have the elements in the nil cell" do
184
+ @page.stub!(:cell_definitions).and_return([{'name' => 'header', 'elements' => []}])
185
+ @page.save
186
+ @page.cells.collect(&:elements).flatten.should be_empty
244
187
  end
245
188
  end
246
189
  end
247
190
 
248
- context "when page is external" do
249
-
250
- before { page.stub(redirects_to_external?: true) }
251
-
252
- it "should update all attributes except url" do
253
- page.update_node!(node)
254
- page.reload
255
- expect(page.lft).to eq(node.left)
256
- expect(page.rgt).to eq(node.right)
257
- expect(page.parent_id).to eq(node.parent)
258
- expect(page.depth).to eq(node.depth)
259
- expect(page.urlname).to eq(original_url)
260
- expect(page.restricted).to eq(node.restricted)
191
+ context "with children getting restricted set to true" do
192
+ before do
193
+ @page.save
194
+ @child1 = FactoryGirl.create(:page, :name => 'Child 1', :parent_id => @page.id)
195
+ @page.reload
196
+ @page.restricted = true
197
+ @page.save
261
198
  end
262
199
 
263
- it "should not create a legacy url" do
264
- page.update_node!(node)
265
- page.reload
266
- expect(page.legacy_urls.size).to eq(0)
200
+ it "should restrict all its children" do
201
+ @child1.reload
202
+ @child1.restricted?.should be_true
267
203
  end
268
204
  end
269
- end
270
205
 
271
- context "when nesting is disabled" do
272
- before { Alchemy::Config.stub(:get).with(:url_nesting) { false } }
273
-
274
- context "when page is not external" do
275
-
276
- before { page.stub(redirects_to_external?: false)}
277
-
278
- it "should update all attributes except url" do
279
- page.update_node!(node)
280
- page.reload
281
- expect(page.lft).to eq(node.left)
282
- expect(page.rgt).to eq(node.right)
283
- expect(page.parent_id).to eq(node.parent)
284
- expect(page.depth).to eq(node.depth)
285
- expect(page.urlname).to eq(original_url)
286
- expect(page.restricted).to eq(node.restricted)
206
+ context "with restricted parent gets created" do
207
+ before do
208
+ @page.save
209
+ @page.parent.update_attributes(:restricted => true)
210
+ @new_page = FactoryGirl.create(:page, :name => 'New Page', :parent_id => @page.id)
287
211
  end
288
212
 
289
- it "should not create a legacy url" do
290
- page.update_node!(node)
291
- page.reload
292
- expect(page.legacy_urls.size).to eq(0)
213
+ it "should also be restricted" do
214
+ @new_page.restricted?.should be_true
293
215
  end
294
-
295
216
  end
296
217
 
297
- context "when page is external" do
298
-
299
- before { page.stub(redirects_to_external?: true) }
300
-
301
- before { Alchemy::Config.stub(get: true) }
302
-
303
- it "should update all attributes except url" do
304
- page.update_node!(node)
305
- page.reload
306
- expect(page.lft).to eq(node.left)
307
- expect(page.rgt).to eq(node.right)
308
- expect(page.parent_id).to eq(node.parent)
309
- expect(page.depth).to eq(node.depth)
310
- expect(page.urlname).to eq(original_url)
311
- expect(page.restricted).to eq(node.restricted)
218
+ context "with do_not_autogenerate set to true" do
219
+ before do
220
+ @page.do_not_autogenerate = true
312
221
  end
313
222
 
314
- it "should not create a legacy url" do
315
- page.update_node!(node)
316
- page.reload
317
- expect(page.legacy_urls.size).to eq(0)
223
+ it "should not autogenerate the elements" do
224
+ @page.save
225
+ @page.elements.should be_empty
318
226
  end
319
227
  end
320
228
  end
321
- end
322
-
323
-
324
- describe "#destroy" do
325
-
326
- context "with trashed but still assigned elements" do
327
229
 
230
+ context "a systempage" do
328
231
  before do
329
- news_page.elements.map(&:trash)
232
+ @page = FactoryGirl.create(:systempage)
330
233
  end
331
234
 
332
- it "should not delete the trashed elements" do
333
- news_page.destroy
334
- Element.trashed.should_not be_empty
235
+ it "should not get the language code for language" do
236
+ @page.language_code.should be_nil
335
237
  end
336
238
 
239
+ it "should not autogenerate the elements" do
240
+ @page.elements.should be_empty
241
+ end
337
242
  end
338
243
 
339
- end
340
-
341
- describe '#first_public_child' do
342
-
343
- before do
344
- FactoryGirl.create(:page, :name => "First child", :language => language, :public => false, :parent_id => language_root.id)
345
- end
244
+ context "after changing the page layout" do
245
+ let(:news_element) { FactoryGirl.create(:element, :name => 'news') }
346
246
 
347
- it "should return first_public_child" do
348
- first_public_child = FactoryGirl.create(:public_page, :name => "First public child", :language => language, :parent_id => language_root.id)
349
- language_root.first_public_child.should == first_public_child
350
- end
247
+ it "all elements not allowed on this page should be trashed" do
248
+ news_page.elements << news_element
249
+ news_page.update_attributes :page_layout => 'standard'
250
+ news_page.elements.trashed.should include(news_element)
251
+ end
351
252
 
352
- it "should return nil if no public child exists" do
353
- language_root.first_public_child.should == nil
253
+ it "should autogenerate elements" do
254
+ news_page.update_attributes :page_layout => 'standard'
255
+ news_page.elements.available.collect(&:name).should include('header')
256
+ end
354
257
  end
355
-
356
258
  end
357
259
 
358
- context ".contentpages" do
359
260
 
261
+ # ClassMethods (a-z)
262
+
263
+ describe '.all_from_clipboard_for_select' do
264
+ context "with clipboard holding pages having non unique page layout" do
265
+ it "should return the pages" do
266
+ page_1 = FactoryGirl.create(:page, :language => language)
267
+ page_2 = FactoryGirl.create(:page, :language => language, :name => 'Another page')
268
+ clipboard = [
269
+ {:id => page_1.id, :action => "copy"},
270
+ {:id => page_2.id, :action => "copy"}
271
+ ]
272
+ Page.all_from_clipboard_for_select(clipboard, language.id).should include(page_1, page_2)
273
+ end
274
+ end
275
+
276
+ context "with clipboard holding a page having unique page layout" do
277
+ it "should not return any pages" do
278
+ page_1 = FactoryGirl.create(:page, :language => language, :page_layout => 'contact')
279
+ clipboard = [
280
+ {:id => page_1.id, :action => "copy"}
281
+ ]
282
+ Page.all_from_clipboard_for_select(clipboard, language.id).should == []
283
+ end
284
+ end
285
+
286
+ context "with clipboard holding two pages. One having a unique page layout." do
287
+ it "should return one page" do
288
+ page_1 = FactoryGirl.create(:page, :language => language, :page_layout => 'standard')
289
+ page_2 = FactoryGirl.create(:page, :name => 'Another page', :language => language, :page_layout => 'contact')
290
+ clipboard = [
291
+ {:id => page_1.id, :action => "copy"},
292
+ {:id => page_2.id, :action => "copy"}
293
+ ]
294
+ Page.all_from_clipboard_for_select(clipboard, language.id).should == [page_1]
295
+ end
296
+ end
297
+ end
298
+
299
+ describe '.all_locked' do
300
+ it "should return 1 page that is blocked by a user at the moment" do
301
+ FactoryGirl.create(:public_page, :locked => true, :name => 'First Public Child', :parent_id => language_root.id, :language => language)
302
+ Page.all_locked.should have(1).pages
303
+ end
304
+ end
305
+
306
+ describe '.contentpages' do
360
307
  before do
361
308
  layoutroot = Page.find_or_create_layout_root_for(klingonian.id)
362
309
  @layoutpage = FactoryGirl.create(:public_page, :name => 'layoutpage', :layoutpage => true, :parent_id => layoutroot.id, :language => klingonian)
@@ -375,460 +322,693 @@ module Alchemy
375
322
  it "should contain pages with attribute :layoutpage set to nil" do
376
323
  Page.contentpages.to_a.select { |p| p.layoutpage == nil }.should include(@klingonian_lang_root)
377
324
  end
378
-
379
325
  end
380
326
 
381
- context ".public" do
327
+ describe '.copy' do
328
+ let(:page) { FactoryGirl.create(:page, :name => 'Source') }
329
+ subject { Page.copy(page) }
382
330
 
383
- it "should return pages that are public" do
384
- FactoryGirl.create(:public_page, :name => 'First Public Child', :parent_id => language_root.id, :language => language)
385
- FactoryGirl.create(:public_page, :name => 'Second Public Child', :parent_id => language_root.id, :language => language)
386
- Page.published.should have(3).pages
331
+ it "the copy should have added (copy) to name" do
332
+ subject.name.should == "#{page.name} (Copy)"
387
333
  end
388
334
 
389
- end
335
+ context "page with tags" do
336
+ before { page.tag_list = 'red, yellow'; page.save }
337
+
338
+ it "the copy should have source tag_list" do
339
+ # The order of tags varies between postgresql and sqlite/mysql
340
+ # This is related to acts-as-taggable-on v.2.4.1
341
+ # To fix the spec we sort the tags until the issue is solved (https://github.com/mbleigh/acts-as-taggable-on/issues/363)
342
+ subject.tag_list.should_not be_empty
343
+ subject.tag_list.sort.should == page.tag_list.sort
344
+ end
345
+ end
390
346
 
391
- context ".not_locked" do
347
+ context "page with elements" do
348
+ before { page.elements << FactoryGirl.create(:element) }
392
349
 
393
- it "should return pages that are not blocked by a user at the moment" do
394
- FactoryGirl.create(:public_page, :locked => true, :name => 'First Public Child', :parent_id => language_root.id, :language => language)
395
- FactoryGirl.create(:public_page, :name => 'Second Public Child', :parent_id => language_root.id, :language => language)
396
- Page.not_locked.should have(3).pages
350
+ it "the copy should have source elements" do
351
+ subject.elements.should_not be_empty
352
+ subject.elements.count.should == page.elements.count
353
+ end
354
+ end
355
+
356
+ context "page with trashed elements" do
357
+ before do
358
+ page.elements << FactoryGirl.create(:element)
359
+ page.elements.first.trash
360
+ end
361
+
362
+ it "the copy should not hold a copy of the trashed elements" do
363
+ subject.elements.should be_empty
364
+ end
365
+ end
366
+
367
+ context "page with cells" do
368
+ before { page.cells << FactoryGirl.create(:cell) }
369
+
370
+ it "the copy should have source cells" do
371
+ subject.cells.should_not be_empty
372
+ subject.cells.count.should == page.cells.length # It must be length, because!
373
+ end
374
+ end
375
+
376
+ context "page with autogenerate elements" do
377
+ before do
378
+ page = FactoryGirl.create(:page)
379
+ page.stub!(:definition).and_return({'name' => 'standard', 'elements' => ['headline'], 'autogenerate' => ['headline']})
380
+ end
381
+
382
+ it "the copy should not autogenerate elements" do
383
+ subject.elements.should be_empty
384
+ end
385
+ end
386
+
387
+ context "with different page name given" do
388
+ subject { Page.copy(page, {:name => 'Different name'}) }
389
+ it "should take this name" do
390
+ subject.name.should == 'Different name'
391
+ end
397
392
  end
398
393
  end
399
394
 
400
- context ".all_locked" do
401
- it "should return 1 page that is blocked by a user at the moment" do
402
- FactoryGirl.create(:public_page, :locked => true, :name => 'First Public Child', :parent_id => language_root.id, :language => language)
403
- Page.all_locked.should have(1).pages
395
+ describe '.create' do
396
+ context "before/after filter" do
397
+ it "should automatically set the title from its name" do
398
+ page = FactoryGirl.create(:page, :name => 'My Testpage', :language => language, :parent_id => language_root.id)
399
+ page.title.should == 'My Testpage'
400
+ end
401
+
402
+ it "should get a webfriendly urlname" do
403
+ page = FactoryGirl.create(:page, :name => 'klingon$&stößel ', :language => language, :parent_id => language_root.id)
404
+ page.urlname.should == 'klingon-stoessel'
405
+ end
406
+
407
+ it "should generate a three letter urlname from two letter name" do
408
+ page = FactoryGirl.create(:page, :name => 'Au', :language => language, :parent_id => language_root.id)
409
+ page.urlname.should == '-au'
410
+ end
411
+
412
+ it "should generate a three letter urlname from two letter name with umlaut" do
413
+ page = FactoryGirl.create(:page, :name => 'Aü', :language => language, :parent_id => language_root.id)
414
+ page.urlname.should == 'aue'
415
+ end
416
+
417
+ it "should generate a three letter urlname from one letter name" do
418
+ page = FactoryGirl.create(:page, :name => 'A', :language => language, :parent_id => language_root.id)
419
+ page.urlname.should == '--a'
420
+ end
421
+
422
+ it "should add a user stamper" do
423
+ page = FactoryGirl.create(:page, :name => 'A', :language => language, :parent_id => language_root.id)
424
+ page.class.stamper_class.to_s.should == 'Alchemy::User'
425
+ end
404
426
  end
405
427
  end
406
428
 
407
- context ".language_roots" do
429
+ describe '.language_roots' do
408
430
  it "should return 1 language_root" do
409
431
  FactoryGirl.create(:public_page, :name => 'First Public Child', :parent_id => language_root.id, :language => language)
410
432
  Page.language_roots.should have(1).pages
411
433
  end
412
434
  end
413
435
 
436
+ describe '.layout_description' do
437
+ it "should raise Exception if the page_layout could not be found in the definition file" do
438
+ expect { page.layout_description }.to raise_error
439
+ end
440
+
441
+ context "for a language root page" do
442
+ it "should return the page layout description as hash" do
443
+ language_root.layout_description['name'].should == 'intro'
444
+ end
445
+
446
+ it "should return an empty hash for root page" do
447
+ rootpage.layout_description.should == {}
448
+ end
449
+ end
450
+ end
414
451
 
415
- context ".layoutpages" do
452
+ describe '.layoutpages' do
416
453
  it "should return 1 layoutpage" do
417
454
  FactoryGirl.create(:public_page, :layoutpage => true, :name => 'Layoutpage', :parent_id => rootpage.id, :language => language)
418
455
  Page.layoutpages.should have(1).pages
419
456
  end
420
457
  end
421
458
 
422
- context ".visible" do
423
- it "should return 1 visible page" do
424
- FactoryGirl.create(:public_page, :name => 'First Public Child', :visible => true, :parent_id => language_root.id, :language => language)
425
- Page.visible.should have(1).pages
459
+ describe '.not_locked' do
460
+ it "should return pages that are not blocked by a user at the moment" do
461
+ FactoryGirl.create(:public_page, :locked => true, :name => 'First Public Child', :parent_id => language_root.id, :language => language)
462
+ FactoryGirl.create(:public_page, :name => 'Second Public Child', :parent_id => language_root.id, :language => language)
463
+ Page.not_locked.should have(3).pages
426
464
  end
427
465
  end
428
466
 
429
- context ".not_restricted" do
467
+ describe '.not_restricted' do
430
468
  it "should return 2 accessible pages" do
431
469
  FactoryGirl.create(:public_page, :name => 'First Public Child', :restricted => true, :parent_id => language_root.id, :language => language)
432
470
  Page.not_restricted.should have(2).pages
433
471
  end
434
472
  end
435
473
 
436
- context ".restricted" do
474
+ describe '.public' do
475
+ it "should return pages that are public" do
476
+ FactoryGirl.create(:public_page, :name => 'First Public Child', :parent_id => language_root.id, :language => language)
477
+ FactoryGirl.create(:public_page, :name => 'Second Public Child', :parent_id => language_root.id, :language => language)
478
+ Page.published.should have(3).pages
479
+ end
480
+ end
481
+
482
+ describe '.restricted' do
437
483
  it "should return 1 restricted page" do
438
484
  FactoryGirl.create(:public_page, :name => 'First Public Child', :restricted => true, :parent_id => language_root.id, :language => language)
439
485
  Page.restricted.should have(1).pages
440
486
  end
441
487
  end
442
488
 
443
- context "#unlock" do
444
- it "should set the locked status to false" do
445
- page = FactoryGirl.create(:public_page, :locked => true)
446
- page.unlock
447
- page.locked.should == false
489
+ describe '.rootpage' do
490
+ it "should contain one rootpage" do
491
+ Page.rootpage.should be_instance_of(Page)
448
492
  end
449
493
  end
450
494
 
451
- describe "#cell_definitions" do
452
-
453
- before do
454
- @page = FactoryGirl.build(:page, :page_layout => 'foo')
455
- @page.stub!(:layout_description).and_return({'name' => "foo", 'cells' => ["foo_cell"]})
456
- @cell_descriptions = [{'name' => "foo_cell", 'elements' => ["1", "2"]}]
457
- Cell.stub!(:definitions).and_return(@cell_descriptions)
458
- end
459
-
460
- it "should return all cell definitions for its page_layout" do
461
- @page.cell_definitions.should == @cell_descriptions
462
- end
463
-
464
- it "should return empty array if no cells defined in page layout" do
465
- @page.stub!(:layout_description).and_return({'name' => "foo"})
466
- @page.cell_definitions.should == []
495
+ describe '.visible' do
496
+ it "should return 1 visible page" do
497
+ FactoryGirl.create(:public_page, :name => 'First Public Child', :visible => true, :parent_id => language_root.id, :language => language)
498
+ Page.visible.should have(1).pages
467
499
  end
468
-
469
500
  end
470
501
 
471
- describe "#elements_grouped_by_cells" do
472
- let(:page) { FactoryGirl.create(:public_page, :do_not_autogenerate => false) }
473
502
 
474
- before do
475
- PageLayout.stub(:get).and_return({
476
- 'name' => 'standard',
477
- 'cells' => ['header'],
478
- 'elements' => ['header', 'text'],
479
- 'autogenerate' => ['header', 'text']
480
- })
481
- Cell.stub!(:definitions).and_return([{
482
- 'name' => "header",
483
- 'elements' => ["header"]
484
- }])
485
- end
503
+ # InstanceMethods (a-z)
486
504
 
487
- it "should return elements grouped by cell" do
488
- elements = page.elements_grouped_by_cells
489
- elements.keys.first.should be_instance_of(Cell)
490
- elements.values.first.first.should be_instance_of(Element)
491
- end
505
+ describe '#available_element_definitions' do
506
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
492
507
 
493
- it "should only include elements beeing in a cell " do
494
- page.elements_grouped_by_cells.keys.should_not include(nil)
508
+ it "returns all element definitions of available elements" do
509
+ page.available_element_definitions.should be_an(Array)
510
+ page.available_element_definitions.collect { |e| e['name'] }.should include('header')
495
511
  end
496
512
 
497
- end
498
-
499
- describe '.all_from_clipboard_for_select' do
500
-
501
- context "with clipboard holding pages having non unique page layout" do
513
+ context "with unique elements already on page" do
514
+ let(:element) { FactoryGirl.build_stubbed(:unique_element) }
515
+ before { page.stub_chain(:elements, :not_trashed, :pluck).and_return([element.name]) }
502
516
 
503
- it "should return the pages" do
504
- page_1 = FactoryGirl.create(:page, :language => language)
505
- page_2 = FactoryGirl.create(:page, :language => language, :name => 'Another page')
506
- clipboard = [
507
- {:id => page_1.id, :action => "copy"},
508
- {:id => page_2.id, :action => "copy"}
509
- ]
510
- Page.all_from_clipboard_for_select(clipboard, language.id).should include(page_1, page_2)
517
+ it "does not return unique element definitions" do
518
+ page.available_element_definitions.collect { |e| e['name'] }.should include('article')
519
+ page.available_element_definitions.collect { |e| e['name'] }.should_not include('header')
511
520
  end
512
-
513
521
  end
514
522
 
515
- context "with clipboard holding a page having unique page layout" do
523
+ context "for page_layout not existing" do
524
+ let(:page) { FactoryGirl.build_stubbed(:page, page_layout: 'not_existing_one') }
516
525
 
517
- it "should not return any pages" do
518
- page_1 = FactoryGirl.create(:page, :language => language, :page_layout => 'contact')
519
- clipboard = [
520
- {:id => page_1.id, :action => "copy"}
521
- ]
522
- Page.all_from_clipboard_for_select(clipboard, language.id).should == []
526
+ it "should raise error" do
527
+ expect {
528
+ page.available_element_definitions
529
+ }.to raise_error(Alchemy::PageLayoutDefinitionError)
523
530
  end
524
-
525
531
  end
526
532
 
527
- context "with clipboard holding two pages. One having a unique page layout." do
533
+ context 'limited amount' do
534
+ let(:page) { FactoryGirl.build_stubbed(:page, page_layout: 'columns') }
535
+ let(:unique_element) { FactoryGirl.build_stubbed(:unique_element, name: 'unique_headline') }
536
+ let(:element_1) { FactoryGirl.build_stubbed(:element, name: 'column_headline') }
537
+ let(:element_2) { FactoryGirl.build_stubbed(:element, name: 'column_headline') }
538
+ let(:element_3) { FactoryGirl.build_stubbed(:element, name: 'column_headline') }
528
539
 
529
- it "should return one page" do
530
- page_1 = FactoryGirl.create(:page, :language => language, :page_layout => 'standard')
531
- page_2 = FactoryGirl.create(:page, :name => 'Another page', :language => language, :page_layout => 'contact')
532
- clipboard = [
533
- {:id => page_1.id, :action => "copy"},
534
- {:id => page_2.id, :action => "copy"}
535
- ]
536
- Page.all_from_clipboard_for_select(clipboard, language.id).should == [page_1]
540
+ before {
541
+ Element.stub(:definitions).and_return([
542
+ {
543
+ 'name' => 'column_headline',
544
+ 'amount' => 3,
545
+ 'contents' => [{'name' => 'headline', 'type' => 'EssenceText'}]
546
+ },
547
+ {
548
+ 'name' => 'unique_headline',
549
+ 'unique' => true,
550
+ 'amount' => 3,
551
+ 'contents' => [{'name' => 'headline', 'type' => 'EssenceText'}]
552
+ }
553
+ ])
554
+ PageLayout.stub(:get).and_return({
555
+ 'name' => 'columns',
556
+ 'elements' => ['column_headline', 'unique_headline'],
557
+ 'autogenerate' => ['unique_headline', 'column_headline', 'column_headline', 'column_headline']
558
+ })
559
+ page.stub_chain(:elements, :not_trashed, :pluck).and_return([unique_element.name, element_1.name, element_2.name, element_3.name])
560
+ }
561
+
562
+ it "should be readable" do
563
+ element = page.element_definitions_by_name('column_headline').first
564
+ element['amount'].should be 3
565
+ end
566
+
567
+ it "should limit elements" do
568
+ page.available_element_definitions.collect { |e| e['name'] }.should_not include('column_headline')
537
569
  end
538
570
 
571
+ it "should be ignored if unique" do
572
+ page.available_element_definitions.collect { |e| e['name'] }.should_not include('unique_headline')
573
+ end
539
574
  end
575
+ end
576
+
577
+ describe '#available_element_names' do
578
+ let(:page) { FactoryGirl.build_stubbed(:page) }
540
579
 
580
+ it "returns all names of elements that could be placed on current page" do
581
+ page.available_element_names == %w(header article)
582
+ end
541
583
  end
542
584
 
543
- describe "Validations: " do
585
+ describe '#cache_key' do
586
+ let(:page) { stub_model(Page) }
587
+ subject { page }
588
+ its(:cache_key) { should match(page.id.to_s) }
589
+ end
544
590
 
545
- context "Creating a normal content page" do
591
+ describe '#cell_definitions' do
592
+ before do
593
+ @page = FactoryGirl.build(:page, :page_layout => 'foo')
594
+ @page.stub!(:layout_description).and_return({'name' => "foo", 'cells' => ["foo_cell"]})
595
+ @cell_descriptions = [{'name' => "foo_cell", 'elements' => ["1", "2"]}]
596
+ Cell.stub!(:definitions).and_return(@cell_descriptions)
597
+ end
546
598
 
547
- let(:contentpage) { FactoryGirl.build(:page) }
599
+ it "should return all cell definitions for its page_layout" do
600
+ @page.cell_definitions.should == @cell_descriptions
601
+ end
548
602
 
549
- context "when its urlname exists as global page" do
550
- it "it should be possible to save." do
551
- contentpage.urlname = "existing_twice"
552
- global_with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice", :layoutpage => true)
553
- contentpage.should be_valid
554
- end
555
- end
603
+ it "should return empty array if no cells defined in page layout" do
604
+ @page.stub!(:layout_description).and_return({'name' => "foo"})
605
+ @page.cell_definitions.should == []
606
+ end
607
+ end
556
608
 
557
- it "should validate the page_layout" do
558
- contentpage.page_layout = nil
559
- contentpage.save
560
- contentpage.should have(1).error_on(:page_layout)
561
- end
609
+ describe '#destroy' do
610
+ context "with trashed but still assigned elements" do
562
611
 
563
- it "should validate the parent_id" do
564
- contentpage.parent_id = nil
565
- contentpage.save
566
- contentpage.should have(1).error_on(:parent_id)
567
- end
612
+ before { news_page.elements.map(&:trash) }
568
613
 
569
- it "should validate the uniqueness of urlname" do
570
- with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice")
571
- contentpage.urlname = 'existing_twice'
572
- contentpage.should_not be_valid
614
+ it "should not delete the trashed elements" do
615
+ news_page.destroy
616
+ Element.trashed.should_not be_empty
573
617
  end
574
618
 
575
- context "with url_nesting set to true" do
576
- before { Config.stub!(:get).and_return(true) }
577
-
578
- it "should only validate urlname dependent of parent" do
579
- other_parent = FactoryGirl.create(:page, parent_id: Page.root.id)
580
- with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice")
581
- contentpage.urlname = 'existing_twice'
582
- contentpage.parent_id = other_parent.id
583
- contentpage.should be_valid
584
- end
619
+ end
620
+ end
585
621
 
586
- it "should validate urlname dependent of parent" do
587
- with_same_urlname = FactoryGirl.create(:page, :urlname => "existing_twice")
588
- contentpage.urlname = 'existing_twice'
589
- contentpage.should_not be_valid
590
- end
591
- end
622
+ describe '#element_definitions' do
623
+ let(:page) { FactoryGirl.build_stubbed(:page) }
624
+ subject { page.element_definitions }
625
+ before { Element.should_receive(:definitions).and_return([{'name' => 'article'}, {'name' => 'header'}]) }
592
626
 
627
+ it "returns all element definitions that could be placed on current page" do
628
+ should include({'name' => 'article'})
629
+ should include({'name' => 'header'})
593
630
  end
631
+ end
594
632
 
595
- context "creating the rootpage without parent_id and page_layout" do
633
+ describe '#element_definitions_by_name' do
634
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
596
635
 
597
- before do
598
- Page.delete_all
599
- @rootpage = FactoryGirl.build(:page, :parent_id => nil, :page_layout => nil, :name => 'Rootpage')
636
+ context "with no name given" do
637
+ it "returns empty array" do
638
+ page.element_definitions_by_name(nil).should == []
600
639
  end
640
+ end
601
641
 
602
- it "should be valid" do
603
- @rootpage.save
604
- @rootpage.should be_valid
642
+ context "with 'all' passed as name" do
643
+ it "returns all element definitions" do
644
+ Element.should_receive(:definitions)
645
+ page.element_definitions_by_name('all')
605
646
  end
606
-
607
647
  end
608
648
 
609
- context "saving a systempage" do
610
-
611
- before do
612
- @systempage = FactoryGirl.build(:systempage)
649
+ context "with :all passed as name" do
650
+ it "returns all element definitions" do
651
+ Element.should_receive(:definitions)
652
+ page.element_definitions_by_name(:all)
613
653
  end
654
+ end
655
+ end
614
656
 
615
- it "should not validate the page_layout" do
616
- @systempage.save
617
- @systempage.should be_valid
618
- end
657
+ describe '#element_definition_names' do
658
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
619
659
 
660
+ it "returns all element names defined in page layout" do
661
+ page.element_definition_names.should == %w(article header)
620
662
  end
621
663
 
664
+ it "returns always an array" do
665
+ page.stub(:definition).and_return({})
666
+ page.element_definition_names.should be_an(Array)
667
+ end
622
668
  end
623
669
 
624
- describe 'before and after filters' do
625
-
626
- context "a normal page" do
627
-
628
- before do
629
- @page = FactoryGirl.build(:page, :language_code => nil, :language => klingonian, :do_not_autogenerate => false)
630
- end
670
+ describe '#elements_grouped_by_cells' do
671
+ let(:page) { FactoryGirl.create(:public_page, :do_not_autogenerate => false) }
631
672
 
632
- it "should get the language code for language" do
633
- @page.save
634
- @page.language_code.should == "kl"
635
- end
673
+ before do
674
+ PageLayout.stub(:get).and_return({
675
+ 'name' => 'standard',
676
+ 'cells' => ['header'],
677
+ 'elements' => ['header', 'text'],
678
+ 'autogenerate' => ['header', 'text']
679
+ })
680
+ Cell.stub!(:definitions).and_return([{
681
+ 'name' => "header",
682
+ 'elements' => ["header"]
683
+ }])
684
+ end
636
685
 
637
- it "should autogenerate the elements" do
638
- @page.save
639
- @page.elements.should_not be_empty
640
- end
686
+ it "should return elements grouped by cell" do
687
+ elements = page.elements_grouped_by_cells
688
+ elements.keys.first.should be_instance_of(Cell)
689
+ elements.values.first.first.should be_instance_of(Element)
690
+ end
641
691
 
642
- it "should not autogenerate elements that are already on the page" do
643
- @page.elements << FactoryGirl.create(:element, :name => 'header')
644
- @page.save
645
- @page.elements.select { |e| e.name == 'header' }.length.should == 1
646
- end
692
+ it "should only include elements beeing in a cell " do
693
+ page.elements_grouped_by_cells.keys.should_not include(nil)
694
+ end
695
+ end
647
696
 
648
- context "with cells" do
697
+ describe '#feed_elements' do
698
+ it "should return all rss feed elements" do
699
+ news_page.feed_elements.should_not be_empty
700
+ news_page.feed_elements.should == Element.find_all_by_name('news')
701
+ end
702
+ end
649
703
 
650
- before do
651
- @page.stub!(:definition).and_return({'name' => 'with_cells', 'cells' => ['header', 'main']})
652
- end
704
+ describe '#find_elements' do
705
+ before do
706
+ FactoryGirl.create(:element, :public => false, :page => public_page)
707
+ FactoryGirl.create(:element, :public => false, :page => public_page)
708
+ end
653
709
 
654
- it "should have the generated elements in their cells" do
655
- @page.stub!(:cell_definitions).and_return([{'name' => 'header', 'elements' => ['article']}])
656
- @page.save
657
- @page.cells.where(:name => 'header').first.elements.should_not be_empty
658
- end
710
+ context "with show_non_public argument TRUE" do
711
+ it "should return all elements from empty options" do
712
+ public_page.find_elements({}, true).all.should == public_page.elements.all
713
+ end
659
714
 
660
- context "and no elements in cell definitions" do
715
+ it "should only return the elements passed as options[:only]" do
716
+ public_page.find_elements({:only => ['article']}, true).all.should == public_page.elements.named('article').all
717
+ end
661
718
 
662
- it "should have the elements in the nil cell" do
663
- @page.stub!(:cell_definitions).and_return([{'name' => 'header', 'elements' => []}])
664
- @page.save
665
- @page.cells.collect(&:elements).flatten.should be_empty
666
- end
719
+ it "should not return the elements passed as options[:except]" do
720
+ public_page.find_elements({:except => ['article']}, true).all.should == public_page.elements - public_page.elements.named('article').all
721
+ end
667
722
 
668
- end
723
+ it "should return elements offsetted" do
724
+ public_page.find_elements({:offset => 2}, true).all.should == public_page.elements.offset(2)
725
+ end
669
726
 
727
+ it "should return elements limitted in count" do
728
+ public_page.find_elements({:count => 1}, true).all.should == public_page.elements.limit(1)
670
729
  end
730
+ end
671
731
 
672
- context "with children" do
673
- before {
674
- @page.save
675
- @child1 = FactoryGirl.create(:page, :name => 'Child 1', :parent_id => @page.id)
676
- @page.reload
677
- }
732
+ context "with options[:from_cell]" do
733
+ let(:element) { FactoryGirl.build_stubbed(:element) }
678
734
 
679
- context "and restricted been set to true" do
735
+ context "given as String" do
736
+ context '' do
680
737
  before {
681
- @page.restricted = true
682
- @page.save
738
+ public_page.cells.stub_chain(:find_by_name, :elements, :offset, :limit, :published).and_return([element])
683
739
  }
684
740
 
685
- it "should restrict all children" do
686
- @child1.reload
687
- @child1.restricted?.should be_true
741
+ it "returns only the elements from given cell" do
742
+ public_page.find_elements(from_cell: 'A Cell').to_a.should == [element]
688
743
  end
689
744
  end
690
745
 
691
- context "and restricted been set to false" do
746
+ context "that can not be found" do
747
+ let(:elements) {[]}
692
748
  before {
693
- @page.restricted = false
694
- @page.save
749
+ elements.stub_chain(:offset, :limit, :published).and_return([])
695
750
  }
696
751
 
697
- it "should unrestrict all children" do
698
- @child1.reload
699
- @child1.restricted?.should be_false
752
+ it "returns empty set" do
753
+ public_page.elements.should_receive(:where).with('1 = 0').and_return(elements)
754
+ public_page.find_elements(from_cell: 'Lolo').to_a.should == []
755
+ end
756
+
757
+ it "loggs a warning" do
758
+ Rails.logger.should_receive(:debug)
759
+ public_page.find_elements(from_cell: 'Lolo')
700
760
  end
701
761
  end
702
762
  end
703
763
 
704
- context "with restricted parent gets created" do
705
-
706
- before do
707
- @page.save
708
- @page.parent.update_attributes(:restricted => true)
709
- @new_page = FactoryGirl.create(:page, :name => 'New Page', :parent_id => @page.id)
710
- end
764
+ context "given as cell object" do
765
+ let(:cell) { FactoryGirl.build_stubbed(:cell, page: public_page) }
711
766
 
712
- it "should also be restricted" do
713
- @new_page.restricted?.should be_true
767
+ it "returns only the elements from given cell" do
768
+ cell.stub_chain(:elements, :offset, :limit, :published).and_return([element])
769
+ public_page.find_elements(from_cell: cell).to_a.should == [element]
714
770
  end
771
+ end
772
+ end
715
773
 
774
+ context "with show_non_public argument FALSE" do
775
+ it "should return all elements from empty arguments" do
776
+ public_page.find_elements().all.should == public_page.elements.published.all
716
777
  end
717
778
 
718
- context "with do_not_autogenerate set to true" do
779
+ it "should only return the public elements passed as options[:only]" do
780
+ public_page.find_elements(:only => ['article']).all.should == public_page.elements.published.named('article').all
781
+ end
719
782
 
720
- before do
721
- @page.do_not_autogenerate = true
722
- end
783
+ it "should return all public elements except the ones passed as options[:except]" do
784
+ public_page.find_elements(:except => ['article']).all.should == public_page.elements.published.all - public_page.elements.published.named('article').all
785
+ end
723
786
 
724
- it "should not autogenerate the elements" do
725
- @page.save
726
- @page.elements.should be_empty
727
- end
787
+ it "should return elements offsetted" do
788
+ public_page.find_elements({:offset => 2}).all.should == public_page.elements.published.offset(2)
789
+ end
728
790
 
791
+ it "should return elements limitted in count" do
792
+ public_page.find_elements({:count => 1}).all.should == public_page.elements.published.limit(1)
729
793
  end
794
+ end
795
+ end
730
796
 
797
+ describe '#first_public_child' do
798
+ before do
799
+ FactoryGirl.create(:page, :name => "First child", :language => language, :public => false, :parent_id => language_root.id)
731
800
  end
732
801
 
733
- context "a systempage" do
802
+ it "should return first_public_child" do
803
+ first_public_child = FactoryGirl.create(:public_page, :name => "First public child", :language => language, :parent_id => language_root.id)
804
+ language_root.first_public_child.should == first_public_child
805
+ end
734
806
 
735
- before do
736
- @page = FactoryGirl.create(:systempage)
737
- end
807
+ it "should return nil if no public child exists" do
808
+ language_root.first_public_child.should == nil
809
+ end
810
+ end
738
811
 
739
- it "should not get the language code for language" do
740
- @page.language_code.should be_nil
741
- end
812
+ context 'folding' do
813
+ let(:user) { mock_model('User') }
742
814
 
743
- it "should not autogenerate the elements" do
744
- @page.elements.should be_empty
815
+ describe '#fold!' do
816
+ context "with folded status set to true" do
817
+ it "should create a folded page for user" do
818
+ public_page.fold!(user.id, true)
819
+ expect(public_page.folded_pages.first.user_id).to eq(user.id)
820
+ end
745
821
  end
746
-
747
822
  end
748
823
 
749
- context "after changing the page layout" do
824
+ describe '#folded?' do
825
+ let(:page) { Page.new }
750
826
 
751
- let(:news_element) { FactoryGirl.create(:element, :name => 'news') }
827
+ context 'if page is folded' do
828
+ before do
829
+ page.stub_chain(:folded_pages, :find_by_user_id).and_return(mock_model('FoldedPage', folded: true))
830
+ end
752
831
 
753
- it "all elements not allowed on this page should be trashed" do
754
- news_page.elements << news_element
755
- news_page.update_attributes :page_layout => 'standard'
756
- news_page.elements.trashed.should include(news_element)
832
+ it "should return true" do
833
+ expect(page.folded?(user.id)).to eq(true)
834
+ end
757
835
  end
758
836
 
759
- it "should autogenerate elements" do
760
- news_page.update_attributes :page_layout => 'standard'
761
- news_page.elements.available.collect(&:name).should include('header')
837
+ context 'if page is not folded' do
838
+ it "should return false" do
839
+ expect(page.folded?(101093)).to eq(false)
840
+ end
762
841
  end
842
+ end
843
+ end
844
+
845
+ describe '#lock!' do
846
+ let(:page) { FactoryGirl.create(:page) }
847
+ let(:user) { mock_model('User') }
763
848
 
849
+ it "should set locked to true" do
850
+ page.lock!(user)
851
+ page.reload
852
+ page.locked.should == true
764
853
  end
765
854
 
855
+ it "should not update the timestamps " do
856
+ expect { page.lock!(user) }.to_not change(page, :updated_at)
857
+ end
858
+
859
+ it "should set locked_by to the users id" do
860
+ page.lock!(user)
861
+ page.reload
862
+ page.locked_by.should == user.id
863
+ end
766
864
  end
767
865
 
768
- describe '#fold' do
866
+ describe '#paste_from_clipboard' do
867
+ let(:source) { FactoryGirl.build_stubbed(:page) }
868
+ let(:new_parent) { FactoryGirl.build_stubbed(:page) }
869
+ let(:page_name) { "Pagename (pasted)" }
870
+ let(:copied_page) { mock_model('Page') }
769
871
 
770
- before do
771
- @user = FactoryGirl.create(:admin_user, :email => 'faz@baz.com', :login => 'foo_baz')
872
+ subject { Page.paste_from_clipboard(source, new_parent, page_name) }
873
+
874
+ it "should copy the source page with the given name to the new parent" do
875
+ Page.should_receive(:copy).with(source, {
876
+ parent_id: new_parent.id,
877
+ language: new_parent.language,
878
+ name: page_name,
879
+ title: page_name
880
+ })
881
+ subject
772
882
  end
773
883
 
774
- context "with folded status set to true" do
884
+ it "should return the copied page" do
885
+ Page.stub!(:copy).and_return(copied_page)
886
+ expect(subject).to be_a(copied_page.class)
887
+ end
775
888
 
776
- it "should create a folded page for user" do
777
- public_page.fold(@user.id, true)
778
- FoldedPage.find_or_create_by_user_id_and_page_id(@user.id, public_page.id).should_not be_nil
889
+ context "if source page has children" do
890
+ it "should also copy and paste the children" do
891
+ Page.stub!(:copy).and_return(copied_page)
892
+ source.stub!(:children).and_return([mock_model('Page')])
893
+ source.should_receive(:copy_children_to).with(copied_page)
894
+ subject
779
895
  end
780
-
781
896
  end
782
-
783
897
  end
784
898
 
785
- describe 'previous and next. ' do
786
-
787
- let(:center_page) { FactoryGirl.create(:public_page, :name => 'Center Page') }
788
- let(:next_page) { FactoryGirl.create(:public_page, :name => 'Next Page') }
899
+ context 'previous and next.' do
900
+ let(:center_page) { FactoryGirl.create(:public_page, name: 'Center Page') }
901
+ let(:next_page) { FactoryGirl.create(:public_page, name: 'Next Page') }
902
+ let(:non_public_page) { FactoryGirl.create(:page, name: 'Not public Page') }
903
+ let(:restricted_page) { FactoryGirl.create(:restricted_page, public: true) }
789
904
 
790
905
  before do
791
906
  public_page
907
+ restricted_page
908
+ non_public_page
792
909
  center_page
793
910
  next_page
794
911
  end
795
912
 
796
913
  describe '#previous' do
797
-
798
914
  it "should return the previous page on the same level" do
799
915
  center_page.previous.should == public_page
800
916
  end
801
917
 
802
918
  context "no previous page on same level present" do
803
-
804
919
  it "should return nil" do
805
920
  public_page.previous.should be_nil
806
921
  end
922
+ end
923
+
924
+ context "with options restricted" do
925
+ context "set to true" do
926
+ it "returns previous restricted page" do
927
+ center_page.previous(restricted: true).should == restricted_page
928
+ end
929
+ end
807
930
 
931
+ context "set to false" do
932
+ it "skips restricted page" do
933
+ center_page.previous(restricted: false).should == public_page
934
+ end
935
+ end
808
936
  end
809
937
 
938
+ context "with options public" do
939
+ context "set to true" do
940
+ it "returns previous public page" do
941
+ center_page.previous(public: true).should == public_page
942
+ end
943
+ end
944
+
945
+ context "set to false" do
946
+ it "skips public page" do
947
+ center_page.previous(public: false).should == non_public_page
948
+ end
949
+ end
950
+ end
810
951
  end
811
952
 
812
953
  describe '#next' do
813
-
814
954
  it "should return the next page on the same level" do
815
955
  center_page.next.should == next_page
816
956
  end
817
957
 
818
958
  context "no next page on same level present" do
819
-
820
959
  it "should return nil" do
821
960
  next_page.next.should be_nil
822
961
  end
962
+ end
963
+ end
964
+ end
965
+
966
+ describe '#publish!' do
967
+ let(:page) { FactoryGirl.build_stubbed(:page, public: false) }
968
+
969
+ before do
970
+ page.stub!(:save).and_return(true)
971
+ page.publish!
972
+ end
823
973
 
974
+ it "sets public attribute to true" do
975
+ page.public.should == true
976
+ end
977
+ end
978
+
979
+ describe '#set_language_from_parent_or_default_language' do
980
+ let(:default_language) { mock_model('Language', code: 'es') }
981
+ let(:page) { Page.new }
982
+
983
+ before { page.stub!(:parent).and_return(parent) }
984
+
985
+ subject { page }
986
+
987
+ context "parent has a language" do
988
+ let(:parent) { mock_model('Page', language: default_language, language_id: default_language.id, language_code: default_language.code) }
989
+
990
+ before do
991
+ page.set_language_from_parent_or_default_language
824
992
  end
825
993
 
994
+ its(:language_id) { should eq(parent.language_id) }
995
+ its(:language_code) { should eq(parent.language_code) }
826
996
  end
827
997
 
998
+ context "parent has no language" do
999
+ let(:parent) { mock_model('Page', language: nil, language_id: nil, language_code: nil) }
1000
+
1001
+ before do
1002
+ Language.stub!(:get_default).and_return(default_language)
1003
+ page.set_language_from_parent_or_default_language
1004
+ end
1005
+
1006
+ its(:language_id) { should eq(default_language.id) }
1007
+ its(:language_code) { should eq(default_language.code) }
1008
+ end
828
1009
  end
829
1010
 
830
1011
  describe '#taggable?' do
831
-
832
1012
  context "definition has 'taggable' key with true value" do
833
1013
  it "should return true" do
834
1014
  page = FactoryGirl.build(:page)
@@ -852,93 +1032,29 @@ module Alchemy
852
1032
  page.taggable?.should be_false
853
1033
  end
854
1034
  end
855
-
856
1035
  end
857
1036
 
858
- describe '.copy' do
859
- let(:page) { FactoryGirl.create(:page, :name => 'Source') }
860
- subject { Page.copy(page) }
861
-
862
- it "the copy should have added (copy) to name" do
863
- subject.name.should == "#{page.name} (Copy)"
864
- end
865
-
866
- context "page with tags" do
867
- before { page.tag_list = 'red, yellow'; page.save }
868
-
869
- it "the copy should have source tag_list" do
870
- # The order of tags varies between postgresql and sqlite/mysql
871
- # This is related to acts-as-taggable-on v.2.4.1
872
- # To fix the spec we sort the tags until the issue is solved (https://github.com/mbleigh/acts-as-taggable-on/issues/363)
873
- subject.tag_list.should_not be_empty
874
- subject.tag_list.sort.should == page.tag_list.sort
875
- end
876
- end
877
-
878
- context "page with elements" do
879
- before { page.elements << FactoryGirl.create(:element) }
880
-
881
- it "the copy should have source elements" do
882
- subject.elements.should_not be_empty
883
- subject.elements.count.should == page.elements.count
884
- end
885
- end
886
-
887
- context "page with trashed elements" do
888
- before do
889
- page.elements << FactoryGirl.create(:element)
890
- page.elements.first.trash
891
- end
892
-
893
- it "the copy should not hold a copy of the trashed elements" do
894
- subject.elements.should be_empty
895
- end
896
- end
897
-
898
- context "page with cells" do
899
- before { page.cells << FactoryGirl.create(:cell) }
900
-
901
- it "the copy should have source cells" do
902
- subject.cells.should_not be_empty
903
- subject.cells.count.should == page.cells.length # It must be length, because!
904
- end
905
- end
906
-
907
- context "page with autogenerate elements" do
908
- before do
909
- page = FactoryGirl.create(:page)
910
- page.stub!(:definition).and_return({'name' => 'standard', 'elements' => ['headline'], 'autogenerate' => ['headline']})
911
- end
1037
+ describe '#unlock!' do
1038
+ let(:page) { FactoryGirl.create(:page, locked: true, locked_by: 1) }
912
1039
 
913
- it "the copy should not autogenerate elements" do
914
- subject.elements.should be_empty
915
- end
1040
+ its "should set the locked status to false" do
1041
+ page.unlock!
1042
+ page.reload
1043
+ page.locked.should == false
916
1044
  end
917
1045
 
918
- context "with different page name given" do
919
- subject { Page.copy(page, {:name => 'Different name'}) }
920
- it "should take this name" do
921
- subject.name.should == 'Different name'
922
- end
1046
+ it "should not update the timestamps " do
1047
+ expect { page.unlock! }.to_not change(page, :updated_at)
923
1048
  end
924
- end
925
-
926
- describe "#cache_key" do
927
- let(:page) { stub_model(Page) }
928
- subject { page }
929
- its(:cache_key) { should match(page.id.to_s) }
930
- end
931
-
932
- describe "#publish!" do
933
- let(:page) { FactoryGirl.create(:page) }
934
- before { page.publish! }
935
1049
 
936
- it "sets public attribute to true" do
937
- page.public.should == true
1050
+ it "should set locked_by to nil" do
1051
+ page.unlock!
1052
+ page.reload
1053
+ page.locked_by.should == nil
938
1054
  end
939
1055
  end
940
1056
 
941
- describe 'urlname updating' do
1057
+ context 'urlname updating' do
942
1058
  let(:parentparent) { FactoryGirl.create(:page, name: 'parentparent', visible: true) }
943
1059
  let(:parent) { FactoryGirl.create(:page, parent_id: parentparent.id, name: 'parent', visible: true) }
944
1060
  let(:page) { FactoryGirl.create(:page, parent_id: parent.id, name: 'page', visible: true) }
@@ -1027,7 +1143,7 @@ module Alchemy
1027
1143
  end
1028
1144
  end
1029
1145
 
1030
- describe "page status methods" do
1146
+ context 'page status methods' do
1031
1147
  let(:page) { FactoryGirl.build(:page, public: true, visible: true, restricted: false, locked: false)}
1032
1148
 
1033
1149
  describe '#status' do
@@ -1053,7 +1169,50 @@ module Alchemy
1053
1169
  page.status_title(:restricted).should == 'Page is not restricted.'
1054
1170
  end
1055
1171
  end
1172
+ end
1173
+
1174
+ context 'indicate page editors' do
1175
+ let(:page) { Page.new }
1176
+ let(:user) { User.new(firstname: 'Paul', lastname: 'Page') }
1177
+
1178
+ describe '#creator_name' do
1179
+ before { page.stub(:creator).and_return(user) }
1180
+
1181
+ it "should return the name of the creator" do
1182
+ expect(page.creator_name).to eq('Paul Page')
1183
+ end
1184
+ end
1185
+
1186
+ describe '#updater_name' do
1187
+ before { page.stub(:updater).and_return(user) }
1188
+
1189
+ it "should return the name of the updater" do
1190
+ expect(page.updater_name).to eq('Paul Page')
1191
+ end
1192
+ end
1193
+
1194
+ describe '#locker_name' do
1195
+ before { page.stub(:locker).and_return(user) }
1196
+
1197
+ it "should return the name of the current page editor" do
1198
+ expect(page.locker_name).to eq('Paul Page')
1199
+ end
1200
+ end
1201
+
1202
+ end
1203
+
1204
+ describe '#controller_and_action' do
1205
+ let(:page) { Page.new }
1056
1206
 
1207
+ context 'if the page has a custom controller defined in its description' do
1208
+ before do
1209
+ page.stub!(:has_controller?).and_return(true)
1210
+ page.stub!(:layout_description).and_return({'controller' => 'comments', 'action' => 'index'})
1211
+ end
1212
+ it "should return a Hash with controller and action key-value pairs" do
1213
+ expect(page.controller_and_action).to eq({controller: '/comments', action: 'index'})
1214
+ end
1215
+ end
1057
1216
  end
1058
1217
 
1059
1218
  end