alchemy_cms 2.6.3 → 2.7.0

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.
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