lc_alchemy_cms 3.2.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 (744) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +23 -0
  3. data/.gitignore +21 -0
  4. data/.hound.yml +2 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +861 -0
  7. data/.teatro.yml +7 -0
  8. data/.travis.yml +27 -0
  9. data/.yardopts +5 -0
  10. data/CODE_OF_CONDUCT.md +13 -0
  11. data/CONTRIBUTING.md +73 -0
  12. data/Gemfile +34 -0
  13. data/LICENSE +27 -0
  14. data/README.md +297 -0
  15. data/Rakefile +48 -0
  16. data/alchemy_cms.gemspec +81 -0
  17. data/app/assets/fonts/alchemy/icons.eot +0 -0
  18. data/app/assets/fonts/alchemy/icons.svg +71 -0
  19. data/app/assets/fonts/alchemy/icons.ttf +0 -0
  20. data/app/assets/fonts/alchemy/icons.woff +0 -0
  21. data/app/assets/images/alchemy/alchemy-logo.svg +70 -0
  22. data/app/assets/images/alchemy/favicon.ico +0 -0
  23. data/app/assets/images/alchemy/icon.svg +14 -0
  24. data/app/assets/images/alchemy/icons.png +0 -0
  25. data/app/assets/images/alchemy/lupe.cur +0 -0
  26. data/app/assets/images/alchemy/ui-icons_666666_256x240.png +0 -0
  27. data/app/assets/images/sassy-ie-overlay.png +0 -0
  28. data/app/assets/javascripts/alchemy/alchemy.autocomplete.js.coffee +30 -0
  29. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +142 -0
  30. data/app/assets/javascripts/alchemy/alchemy.browser.js.coffee +22 -0
  31. data/app/assets/javascripts/alchemy/alchemy.buttons.js.coffee +32 -0
  32. data/app/assets/javascripts/alchemy/alchemy.char_counter.js.coffee +19 -0
  33. data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +85 -0
  34. data/app/assets/javascripts/alchemy/alchemy.custom.js +1 -0
  35. data/app/assets/javascripts/alchemy/alchemy.datepicker.js.coffee +31 -0
  36. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +266 -0
  37. data/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +70 -0
  38. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +84 -0
  39. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +138 -0
  40. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +89 -0
  41. data/app/assets/javascripts/alchemy/alchemy.file_progress.js.coffee +63 -0
  42. data/app/assets/javascripts/alchemy/alchemy.growler.js.coffee +27 -0
  43. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +22 -0
  44. data/app/assets/javascripts/alchemy/alchemy.hotkeys.js.coffee +49 -0
  45. data/app/assets/javascripts/alchemy/alchemy.html5.js +22 -0
  46. data/app/assets/javascripts/alchemy/alchemy.i18n.js.coffee +26 -0
  47. data/app/assets/javascripts/alchemy/alchemy.image_cropper.js.coffee +44 -0
  48. data/app/assets/javascripts/alchemy/alchemy.initializer.js.coffee +63 -0
  49. data/app/assets/javascripts/alchemy/alchemy.jquery_loader.js +42 -0
  50. data/app/assets/javascripts/alchemy/alchemy.js +52 -0
  51. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +250 -0
  52. data/app/assets/javascripts/alchemy/alchemy.list_filter.js.coffee +49 -0
  53. data/app/assets/javascripts/alchemy/alchemy.page_sorter.js +48 -0
  54. data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +106 -0
  55. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +58 -0
  56. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +50 -0
  57. data/app/assets/javascripts/alchemy/alchemy.spinner.js.coffee +49 -0
  58. data/app/assets/javascripts/alchemy/alchemy.string_extension.js.coffee +11 -0
  59. data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee +66 -0
  60. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +132 -0
  61. data/app/assets/javascripts/alchemy/alchemy.uploader.js.coffee +102 -0
  62. data/app/assets/javascripts/alchemy/alchemy.windows.js.coffee +42 -0
  63. data/app/assets/javascripts/alchemy/menubar.js.coffee +8 -0
  64. data/app/assets/javascripts/alchemy/preview.js +1 -0
  65. data/app/assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js +19 -0
  66. data/app/assets/stylesheets/alchemy/_defaults.scss +8 -0
  67. data/app/assets/stylesheets/alchemy/_extends.scss +93 -0
  68. data/app/assets/stylesheets/alchemy/_mixins.scss +112 -0
  69. data/app/assets/stylesheets/alchemy/_variables.scss +61 -0
  70. data/app/assets/stylesheets/alchemy/admin.scss +31 -0
  71. data/app/assets/stylesheets/alchemy/archive.scss +436 -0
  72. data/app/assets/stylesheets/alchemy/base.scss +609 -0
  73. data/app/assets/stylesheets/alchemy/buttons.scss +259 -0
  74. data/app/assets/stylesheets/alchemy/custom.scss +1 -0
  75. data/app/assets/stylesheets/alchemy/dashboard.scss +104 -0
  76. data/app/assets/stylesheets/alchemy/dialogs.scss +214 -0
  77. data/app/assets/stylesheets/alchemy/elements.scss +772 -0
  78. data/app/assets/stylesheets/alchemy/errors.scss +39 -0
  79. data/app/assets/stylesheets/alchemy/flash.scss +65 -0
  80. data/app/assets/stylesheets/alchemy/form_fields.scss +89 -0
  81. data/app/assets/stylesheets/alchemy/forms.scss +193 -0
  82. data/app/assets/stylesheets/alchemy/frame.scss +301 -0
  83. data/app/assets/stylesheets/alchemy/icon-font.scss +67 -0
  84. data/app/assets/stylesheets/alchemy/icons.scss +324 -0
  85. data/app/assets/stylesheets/alchemy/jquery-ui.scss +1511 -0
  86. data/app/assets/stylesheets/alchemy/menubar.scss +81 -0
  87. data/app/assets/stylesheets/alchemy/modules.scss +33 -0
  88. data/app/assets/stylesheets/alchemy/notices.scss +64 -0
  89. data/app/assets/stylesheets/alchemy/pagination.scss +65 -0
  90. data/app/assets/stylesheets/alchemy/print.scss +34 -0
  91. data/app/assets/stylesheets/alchemy/search.scss +73 -0
  92. data/app/assets/stylesheets/alchemy/selects.scss +235 -0
  93. data/app/assets/stylesheets/alchemy/sitemap.scss +385 -0
  94. data/app/assets/stylesheets/alchemy/tables.scss +218 -0
  95. data/app/assets/stylesheets/alchemy/toolbar.scss +146 -0
  96. data/app/assets/stylesheets/alchemy/upload.scss +121 -0
  97. data/app/assets/stylesheets/tinymce/skins/alchemy/content.min.css.scss +90 -0
  98. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/readme.md +1 -0
  99. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.eot +0 -0
  100. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.svg +175 -0
  101. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.ttf +0 -0
  102. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce-small.woff +0 -0
  103. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.eot +0 -0
  104. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.svg +153 -0
  105. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.ttf +0 -0
  106. data/app/assets/stylesheets/tinymce/skins/alchemy/fonts/tinymce.woff +0 -0
  107. data/app/assets/stylesheets/tinymce/skins/alchemy/img/anchor.gif +0 -0
  108. data/app/assets/stylesheets/tinymce/skins/alchemy/img/loader.gif +0 -0
  109. data/app/assets/stylesheets/tinymce/skins/alchemy/img/object.gif +0 -0
  110. data/app/assets/stylesheets/tinymce/skins/alchemy/img/trans.gif +0 -0
  111. data/app/assets/stylesheets/tinymce/skins/alchemy/img/wline.gif +0 -0
  112. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.ie7.min.css +1 -0
  113. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +1810 -0
  114. data/app/controllers/alchemy/admin/attachments_controller.rb +106 -0
  115. data/app/controllers/alchemy/admin/base_controller.rb +168 -0
  116. data/app/controllers/alchemy/admin/clipboard_controller.rb +51 -0
  117. data/app/controllers/alchemy/admin/contents_controller.rb +76 -0
  118. data/app/controllers/alchemy/admin/dashboard_controller.rb +89 -0
  119. data/app/controllers/alchemy/admin/elements_controller.rb +190 -0
  120. data/app/controllers/alchemy/admin/essence_files_controller.rb +47 -0
  121. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +120 -0
  122. data/app/controllers/alchemy/admin/languages_controller.rb +17 -0
  123. data/app/controllers/alchemy/admin/layoutpages_controller.rb +19 -0
  124. data/app/controllers/alchemy/admin/legacy_page_urls_controller.rb +39 -0
  125. data/app/controllers/alchemy/admin/pages_controller.rb +359 -0
  126. data/app/controllers/alchemy/admin/pictures_controller.rb +168 -0
  127. data/app/controllers/alchemy/admin/resources_controller.rb +185 -0
  128. data/app/controllers/alchemy/admin/sites_controller.rb +6 -0
  129. data/app/controllers/alchemy/admin/tags_controller.rb +76 -0
  130. data/app/controllers/alchemy/admin/trash_controller.rb +27 -0
  131. data/app/controllers/alchemy/api/base_controller.rb +19 -0
  132. data/app/controllers/alchemy/api/contents_controller.rb +35 -0
  133. data/app/controllers/alchemy/api/elements_controller.rb +29 -0
  134. data/app/controllers/alchemy/api/pages_controller.rb +32 -0
  135. data/app/controllers/alchemy/attachments_controller.rb +35 -0
  136. data/app/controllers/alchemy/base_controller.rb +106 -0
  137. data/app/controllers/alchemy/elements_controller.rb +27 -0
  138. data/app/controllers/alchemy/messages_controller.rb +114 -0
  139. data/app/controllers/alchemy/pages_controller.rb +211 -0
  140. data/app/controllers/alchemy/pictures_controller.rb +82 -0
  141. data/app/helpers/alchemy/admin/attachments_helper.rb +12 -0
  142. data/app/helpers/alchemy/admin/base_helper.rb +471 -0
  143. data/app/helpers/alchemy/admin/contents_helper.rb +113 -0
  144. data/app/helpers/alchemy/admin/elements_helper.rb +134 -0
  145. data/app/helpers/alchemy/admin/essences_helper.rb +120 -0
  146. data/app/helpers/alchemy/admin/form_helper.rb +30 -0
  147. data/app/helpers/alchemy/admin/navigation_helper.rb +238 -0
  148. data/app/helpers/alchemy/admin/pages_helper.rb +70 -0
  149. data/app/helpers/alchemy/admin/pictures_helper.rb +30 -0
  150. data/app/helpers/alchemy/admin/tags_helper.rb +102 -0
  151. data/app/helpers/alchemy/base_helper.rb +81 -0
  152. data/app/helpers/alchemy/elements_block_helper.rb +162 -0
  153. data/app/helpers/alchemy/elements_helper.rb +291 -0
  154. data/app/helpers/alchemy/essences_helper.rb +138 -0
  155. data/app/helpers/alchemy/pages_helper.rb +417 -0
  156. data/app/helpers/alchemy/url_helper.rb +76 -0
  157. data/app/mailers/alchemy/messages.rb +15 -0
  158. data/app/models/alchemy.rb +7 -0
  159. data/app/models/alchemy/attachment.rb +129 -0
  160. data/app/models/alchemy/cell.rb +94 -0
  161. data/app/models/alchemy/content.rb +255 -0
  162. data/app/models/alchemy/content/factory.rb +190 -0
  163. data/app/models/alchemy/element.rb +513 -0
  164. data/app/models/alchemy/element/definitions.rb +52 -0
  165. data/app/models/alchemy/element/presenters.rb +87 -0
  166. data/app/models/alchemy/element_to_page.rb +7 -0
  167. data/app/models/alchemy/essence_boolean.rb +19 -0
  168. data/app/models/alchemy/essence_date.rb +24 -0
  169. data/app/models/alchemy/essence_file.rb +42 -0
  170. data/app/models/alchemy/essence_html.rb +23 -0
  171. data/app/models/alchemy/essence_link.rb +20 -0
  172. data/app/models/alchemy/essence_picture.rb +152 -0
  173. data/app/models/alchemy/essence_richtext.rb +47 -0
  174. data/app/models/alchemy/essence_select.rb +18 -0
  175. data/app/models/alchemy/essence_text.rb +22 -0
  176. data/app/models/alchemy/folded_page.rb +16 -0
  177. data/app/models/alchemy/language.rb +144 -0
  178. data/app/models/alchemy/language/code.rb +22 -0
  179. data/app/models/alchemy/legacy_page_url.rb +20 -0
  180. data/app/models/alchemy/message.rb +40 -0
  181. data/app/models/alchemy/page.rb +409 -0
  182. data/app/models/alchemy/page/page_cells.rb +71 -0
  183. data/app/models/alchemy/page/page_elements.rb +272 -0
  184. data/app/models/alchemy/page/page_naming.rb +122 -0
  185. data/app/models/alchemy/page/page_natures.rb +114 -0
  186. data/app/models/alchemy/page/page_scopes.rb +95 -0
  187. data/app/models/alchemy/page/page_users.rb +58 -0
  188. data/app/models/alchemy/picture.rb +220 -0
  189. data/app/models/alchemy/picture/sweeping.rb +17 -0
  190. data/app/models/alchemy/picture/transformations.rb +256 -0
  191. data/app/models/alchemy/site.rb +91 -0
  192. data/app/models/alchemy/site/layout.rb +38 -0
  193. data/app/models/alchemy/tag.rb +24 -0
  194. data/app/models/alchemy/translations.rb +7 -0
  195. data/app/models/alchemy/translations/essence_body_updater.rb +64 -0
  196. data/app/models/alchemy/translations/translation_pusher.rb +18 -0
  197. data/app/models/alchemy/tree_node.rb +4 -0
  198. data/app/serializers/alchemy/attachment_serializer.rb +15 -0
  199. data/app/serializers/alchemy/cell_serializer.rb +18 -0
  200. data/app/serializers/alchemy/content_serializer.rb +20 -0
  201. data/app/serializers/alchemy/element_serializer.rb +20 -0
  202. data/app/serializers/alchemy/essence_boolean_serializer.rb +11 -0
  203. data/app/serializers/alchemy/essence_date_serializer.rb +11 -0
  204. data/app/serializers/alchemy/essence_file_serializer.rb +12 -0
  205. data/app/serializers/alchemy/essence_html_serializer.rb +11 -0
  206. data/app/serializers/alchemy/essence_link_serializer.rb +14 -0
  207. data/app/serializers/alchemy/essence_picture_serializer.rb +28 -0
  208. data/app/serializers/alchemy/essence_richtext_serializer.rb +12 -0
  209. data/app/serializers/alchemy/essence_select_serializer.rb +11 -0
  210. data/app/serializers/alchemy/essence_text_serializer.rb +22 -0
  211. data/app/serializers/alchemy/legacy_element_serializer.rb +17 -0
  212. data/app/serializers/alchemy/page_serializer.rb +29 -0
  213. data/app/serializers/alchemy/picture_serializer.rb +16 -0
  214. data/app/views/alchemy/_menubar.html.erb +16 -0
  215. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +32 -0
  216. data/app/views/alchemy/admin/attachments/_attachment.html.erb +63 -0
  217. data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +15 -0
  218. data/app/views/alchemy/admin/attachments/_files_list.html.erb +39 -0
  219. data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +11 -0
  220. data/app/views/alchemy/admin/attachments/_tag_list.html.erb +14 -0
  221. data/app/views/alchemy/admin/attachments/archive_overlay.js.erb +7 -0
  222. data/app/views/alchemy/admin/attachments/create.js.erb +11 -0
  223. data/app/views/alchemy/admin/attachments/destroy.js.erb +1 -0
  224. data/app/views/alchemy/admin/attachments/edit.html.erb +10 -0
  225. data/app/views/alchemy/admin/attachments/index.html.erb +29 -0
  226. data/app/views/alchemy/admin/attachments/new.html.erb +14 -0
  227. data/app/views/alchemy/admin/attachments/show.html.erb +3 -0
  228. data/app/views/alchemy/admin/clipboard/clear.js.erb +3 -0
  229. data/app/views/alchemy/admin/clipboard/index.html.erb +22 -0
  230. data/app/views/alchemy/admin/clipboard/insert.js.erb +19 -0
  231. data/app/views/alchemy/admin/clipboard/remove.js.erb +10 -0
  232. data/app/views/alchemy/admin/contents/_missing.html.erb +19 -0
  233. data/app/views/alchemy/admin/contents/create.js.erb +56 -0
  234. data/app/views/alchemy/admin/contents/destroy.js.erb +4 -0
  235. data/app/views/alchemy/admin/contents/new.html.erb +11 -0
  236. data/app/views/alchemy/admin/contents/order.js.erb +3 -0
  237. data/app/views/alchemy/admin/dashboard/_locked_pages.html.erb +38 -0
  238. data/app/views/alchemy/admin/dashboard/_recent_pages.html.erb +22 -0
  239. data/app/views/alchemy/admin/dashboard/_sites.html.erb +23 -0
  240. data/app/views/alchemy/admin/dashboard/_users.html.erb +19 -0
  241. data/app/views/alchemy/admin/dashboard/help.html.erb +21 -0
  242. data/app/views/alchemy/admin/dashboard/index.html.erb +44 -0
  243. data/app/views/alchemy/admin/dashboard/info.html.erb +64 -0
  244. data/app/views/alchemy/admin/elements/_add_picture.html.erb +14 -0
  245. data/app/views/alchemy/admin/elements/_element.html.erb +23 -0
  246. data/app/views/alchemy/admin/elements/_element_foot.html.erb +36 -0
  247. data/app/views/alchemy/admin/elements/_element_head.html.erb +23 -0
  248. data/app/views/alchemy/admin/elements/_new_element_form.html.erb +26 -0
  249. data/app/views/alchemy/admin/elements/_picture_gallery_editor.html.erb +24 -0
  250. data/app/views/alchemy/admin/elements/_refresh_editor.js.erb +8 -0
  251. data/app/views/alchemy/admin/elements/create.js.erb +46 -0
  252. data/app/views/alchemy/admin/elements/fold.js.erb +28 -0
  253. data/app/views/alchemy/admin/elements/index.html.erb +26 -0
  254. data/app/views/alchemy/admin/elements/list.html.erb +16 -0
  255. data/app/views/alchemy/admin/elements/new.html.erb +25 -0
  256. data/app/views/alchemy/admin/elements/order.js.erb +14 -0
  257. data/app/views/alchemy/admin/elements/trash.js.erb +12 -0
  258. data/app/views/alchemy/admin/elements/update.js.erb +28 -0
  259. data/app/views/alchemy/admin/essence_files/assign.js.erb +9 -0
  260. data/app/views/alchemy/admin/essence_files/edit.html.erb +18 -0
  261. data/app/views/alchemy/admin/essence_pictures/assign.js.erb +18 -0
  262. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +50 -0
  263. data/app/views/alchemy/admin/essence_pictures/destroy.js.erb +24 -0
  264. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +28 -0
  265. data/app/views/alchemy/admin/essence_pictures/save_link.js.erb +3 -0
  266. data/app/views/alchemy/admin/essence_pictures/update.js.erb +9 -0
  267. data/app/views/alchemy/admin/languages/_form.html.erb +14 -0
  268. data/app/views/alchemy/admin/languages/_language.html.erb +42 -0
  269. data/app/views/alchemy/admin/languages/_table.html.erb +37 -0
  270. data/app/views/alchemy/admin/languages/index.html.erb +23 -0
  271. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +63 -0
  272. data/app/views/alchemy/admin/layoutpages/edit.html.erb +15 -0
  273. data/app/views/alchemy/admin/layoutpages/index.html.erb +41 -0
  274. data/app/views/alchemy/admin/leave.html.erb +11 -0
  275. data/app/views/alchemy/admin/legacy_page_urls/_form.html.erb +5 -0
  276. data/app/views/alchemy/admin/legacy_page_urls/_label.html.erb +1 -0
  277. data/app/views/alchemy/admin/legacy_page_urls/_legacy_page_url.html.erb +13 -0
  278. data/app/views/alchemy/admin/legacy_page_urls/_new.html.erb +20 -0
  279. data/app/views/alchemy/admin/legacy_page_urls/create.js.erb +10 -0
  280. data/app/views/alchemy/admin/legacy_page_urls/destroy.js.erb +6 -0
  281. data/app/views/alchemy/admin/legacy_page_urls/update.js.erb +2 -0
  282. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +54 -0
  283. data/app/views/alchemy/admin/pages/_external_link.html.erb +31 -0
  284. data/app/views/alchemy/admin/pages/_file_link.html.erb +31 -0
  285. data/app/views/alchemy/admin/pages/_form.html.erb +62 -0
  286. data/app/views/alchemy/admin/pages/_internal_link.html.erb +41 -0
  287. data/app/views/alchemy/admin/pages/_legacy_urls.html.erb +23 -0
  288. data/app/views/alchemy/admin/pages/_locked_page.html.erb +24 -0
  289. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +11 -0
  290. data/app/views/alchemy/admin/pages/_page.html.erb +112 -0
  291. data/app/views/alchemy/admin/pages/_page_for_links.html.erb +45 -0
  292. data/app/views/alchemy/admin/pages/_page_status.html.erb +14 -0
  293. data/app/views/alchemy/admin/pages/_sitemap.html.erb +8 -0
  294. data/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb +13 -0
  295. data/app/views/alchemy/admin/pages/configure.html.erb +16 -0
  296. data/app/views/alchemy/admin/pages/configure_external.html.erb +33 -0
  297. data/app/views/alchemy/admin/pages/destroy.js.erb +19 -0
  298. data/app/views/alchemy/admin/pages/edit.html.erb +206 -0
  299. data/app/views/alchemy/admin/pages/flush.js.erb +2 -0
  300. data/app/views/alchemy/admin/pages/fold.js.erb +3 -0
  301. data/app/views/alchemy/admin/pages/index.html.erb +101 -0
  302. data/app/views/alchemy/admin/pages/info.html.erb +43 -0
  303. data/app/views/alchemy/admin/pages/link.html.erb +16 -0
  304. data/app/views/alchemy/admin/pages/locked.html.erb +3 -0
  305. data/app/views/alchemy/admin/pages/new.html.erb +26 -0
  306. data/app/views/alchemy/admin/pages/show.html.erb +1 -0
  307. data/app/views/alchemy/admin/pages/sort.js.erb +5 -0
  308. data/app/views/alchemy/admin/pages/unlock.js.erb +8 -0
  309. data/app/views/alchemy/admin/pages/update.js.erb +37 -0
  310. data/app/views/alchemy/admin/partials/_autocomplete_tag_list.html.erb +1 -0
  311. data/app/views/alchemy/admin/partials/_flash.html.erb +4 -0
  312. data/app/views/alchemy/admin/partials/_flash_notices.html.erb +5 -0
  313. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +11 -0
  314. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +12 -0
  315. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +33 -0
  316. data/app/views/alchemy/admin/partials/_routes.html.erb +25 -0
  317. data/app/views/alchemy/admin/partials/_search_form.html.erb +20 -0
  318. data/app/views/alchemy/admin/partials/_sub_navigation.html.erb +8 -0
  319. data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +25 -0
  320. data/app/views/alchemy/admin/partials/_upload_form.html.erb +67 -0
  321. data/app/views/alchemy/admin/pictures/_archive.html.erb +53 -0
  322. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +14 -0
  323. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +78 -0
  324. data/app/views/alchemy/admin/pictures/_filter_bar.html.erb +33 -0
  325. data/app/views/alchemy/admin/pictures/_overlay_picture_list.html.erb +11 -0
  326. data/app/views/alchemy/admin/pictures/_picture.html.erb +87 -0
  327. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +28 -0
  328. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +16 -0
  329. data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +8 -0
  330. data/app/views/alchemy/admin/pictures/edit.html.erb +13 -0
  331. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +30 -0
  332. data/app/views/alchemy/admin/pictures/flush.js.erb +2 -0
  333. data/app/views/alchemy/admin/pictures/index.html.erb +109 -0
  334. data/app/views/alchemy/admin/pictures/index.js.erb +3 -0
  335. data/app/views/alchemy/admin/pictures/info.html.erb +38 -0
  336. data/app/views/alchemy/admin/pictures/new.html.erb +16 -0
  337. data/app/views/alchemy/admin/pictures/show.html.erb +11 -0
  338. data/app/views/alchemy/admin/resources/_form.html.erb +13 -0
  339. data/app/views/alchemy/admin/resources/_resource.html.erb +30 -0
  340. data/app/views/alchemy/admin/resources/_table.html.erb +25 -0
  341. data/app/views/alchemy/admin/resources/edit.html.erb +1 -0
  342. data/app/views/alchemy/admin/resources/index.csv.erb +13 -0
  343. data/app/views/alchemy/admin/resources/index.html.erb +32 -0
  344. data/app/views/alchemy/admin/resources/new.html.erb +1 -0
  345. data/app/views/alchemy/admin/sites/index.html.erb +23 -0
  346. data/app/views/alchemy/admin/tags/_radio_tag.html.erb +6 -0
  347. data/app/views/alchemy/admin/tags/_tag.html.erb +30 -0
  348. data/app/views/alchemy/admin/tags/edit.html.erb +24 -0
  349. data/app/views/alchemy/admin/tags/index.html.erb +52 -0
  350. data/app/views/alchemy/admin/tags/new.html.erb +7 -0
  351. data/app/views/alchemy/admin/trash/clear.js.erb +4 -0
  352. data/app/views/alchemy/admin/trash/index.html.erb +29 -0
  353. data/app/views/alchemy/attachments/show.html.erb +1 -0
  354. data/app/views/alchemy/base/500.html.erb +26 -0
  355. data/app/views/alchemy/base/error_notice.html.erb +1 -0
  356. data/app/views/alchemy/base/error_notice.js.erb +2 -0
  357. data/app/views/alchemy/base/permission_denied.js.erb +3 -0
  358. data/app/views/alchemy/base/redirect.js.erb +12 -0
  359. data/app/views/alchemy/base/update.js.erb +5 -0
  360. data/app/views/alchemy/breadcrumb/_page.html.erb +16 -0
  361. data/app/views/alchemy/breadcrumb/_separator.html.erb +1 -0
  362. data/app/views/alchemy/breadcrumb/_wrapper.html.erb +13 -0
  363. data/app/views/alchemy/elements/_editor_not_found.html.erb +5 -0
  364. data/app/views/alchemy/elements/_view_not_found.html.erb +1 -0
  365. data/app/views/alchemy/elements/show.html.erb +1 -0
  366. data/app/views/alchemy/elements/show.js.erb +1 -0
  367. data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +16 -0
  368. data/app/views/alchemy/essences/_essence_boolean_view.html.erb +3 -0
  369. data/app/views/alchemy/essences/_essence_date_editor.html.erb +19 -0
  370. data/app/views/alchemy/essences/_essence_date_view.html.erb +11 -0
  371. data/app/views/alchemy/essences/_essence_file_editor.html.erb +57 -0
  372. data/app/views/alchemy/essences/_essence_file_view.html.erb +10 -0
  373. data/app/views/alchemy/essences/_essence_html_editor.html.erb +9 -0
  374. data/app/views/alchemy/essences/_essence_html_view.html.erb +3 -0
  375. data/app/views/alchemy/essences/_essence_link_editor.html.erb +25 -0
  376. data/app/views/alchemy/essences/_essence_link_view.html.erb +11 -0
  377. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +59 -0
  378. data/app/views/alchemy/essences/_essence_picture_view.html.erb +5 -0
  379. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +13 -0
  380. data/app/views/alchemy/essences/_essence_richtext_view.html.erb +5 -0
  381. data/app/views/alchemy/essences/_essence_select_editor.html.erb +25 -0
  382. data/app/views/alchemy/essences/_essence_select_view.html.erb +3 -0
  383. data/app/views/alchemy/essences/_essence_text_editor.html.erb +18 -0
  384. data/app/views/alchemy/essences/_essence_text_view.html.erb +18 -0
  385. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +61 -0
  386. data/app/views/alchemy/essences/shared/_linkable_essence_tools.html.erb +19 -0
  387. data/app/views/alchemy/language_links/_language.html.erb +12 -0
  388. data/app/views/alchemy/language_links/_spacer.html.erb +1 -0
  389. data/app/views/alchemy/messages/contact_form_mail.de.text.erb +12 -0
  390. data/app/views/alchemy/messages/contact_form_mail.en.text.erb +12 -0
  391. data/app/views/alchemy/messages/contact_form_mail.es.text.erb +12 -0
  392. data/app/views/alchemy/messages/new.html.erb +1 -0
  393. data/app/views/alchemy/navigation/_image_link.html.erb +14 -0
  394. data/app/views/alchemy/navigation/_link.html.erb +20 -0
  395. data/app/views/alchemy/navigation/_renderer.html.erb +30 -0
  396. data/app/views/alchemy/pages/show.html.erb +1 -0
  397. data/app/views/alchemy/pages/show.rss.builder +20 -0
  398. data/app/views/alchemy/pages/sitemap.xml.erb +6 -0
  399. data/app/views/kaminari/alchemy/_first_page.html.erb +11 -0
  400. data/app/views/kaminari/alchemy/_gap.html.erb +8 -0
  401. data/app/views/kaminari/alchemy/_last_page.html.erb +11 -0
  402. data/app/views/kaminari/alchemy/_next_page.html.erb +13 -0
  403. data/app/views/kaminari/alchemy/_page.html.erb +14 -0
  404. data/app/views/kaminari/alchemy/_paginator.html.erb +23 -0
  405. data/app/views/kaminari/alchemy/_prev_page.html.erb +13 -0
  406. data/app/views/layouts/alchemy/admin.html.erb +109 -0
  407. data/app/views/layouts/alchemy/sitemap.xml.erb +4 -0
  408. data/bin/alchemy +267 -0
  409. data/bin/rails +8 -0
  410. data/config/alchemy/config.yml +189 -0
  411. data/config/alchemy/modules.yml +88 -0
  412. data/config/initializers/dragonfly.rb +25 -0
  413. data/config/initializers/simple_form.rb +144 -0
  414. data/config/locales/alchemy.de.yml +918 -0
  415. data/config/locales/alchemy.en.yml +920 -0
  416. data/config/locales/alchemy.es.yml +958 -0
  417. data/config/locales/alchemy.fr.yml +942 -0
  418. data/config/locales/alchemy.nl.yml +921 -0
  419. data/config/locales/alchemy.ru.yml +837 -0
  420. data/config/locales/simple_form.de.yml +26 -0
  421. data/config/locales/simple_form.en.yml +25 -0
  422. data/config/locales/simple_form.es.yml +6 -0
  423. data/config/locales/simple_form.fr.yml +26 -0
  424. data/config/locales/simple_form.nl.yml +25 -0
  425. data/config/locales/simple_form.ru.yml +25 -0
  426. data/config/routes.rb +164 -0
  427. data/db/migrate/20130827094554_alchemy_two_point_six.rb +380 -0
  428. data/db/migrate/20130828121054_remove_do_not_index_from_alchemy_essence_texts.rb +5 -0
  429. data/db/migrate/20130828121120_remove_do_not_index_from_alchemy_essence_richtexts.rb +5 -0
  430. data/db/migrate/20130918201742_add_published_at_to_alchemy_pages.rb +5 -0
  431. data/lib/alchemy/auth_accessors.rb +104 -0
  432. data/lib/alchemy/cache_digests/template_tracker.rb +65 -0
  433. data/lib/alchemy/config.rb +65 -0
  434. data/lib/alchemy/configuration_methods.rb +29 -0
  435. data/lib/alchemy/controller_actions.rb +138 -0
  436. data/lib/alchemy/engine.rb +104 -0
  437. data/lib/alchemy/errors.rb +82 -0
  438. data/lib/alchemy/essence.rb +232 -0
  439. data/lib/alchemy/filetypes.rb +33 -0
  440. data/lib/alchemy/forms/builder.rb +23 -0
  441. data/lib/alchemy/hints.rb +57 -0
  442. data/lib/alchemy/i18n.rb +70 -0
  443. data/lib/alchemy/kaminari/scoped_pagination_url_helper.rb +13 -0
  444. data/lib/alchemy/locale.rb +65 -0
  445. data/lib/alchemy/logger.rb +15 -0
  446. data/lib/alchemy/middleware/rescue_old_cookies.rb +27 -0
  447. data/lib/alchemy/modules.rb +75 -0
  448. data/lib/alchemy/mount_point.rb +47 -0
  449. data/lib/alchemy/name_conversions.rb +28 -0
  450. data/lib/alchemy/on_page_layout.rb +58 -0
  451. data/lib/alchemy/page_layout.rb +183 -0
  452. data/lib/alchemy/permissions.rb +221 -0
  453. data/lib/alchemy/picture_attributes.rb +29 -0
  454. data/lib/alchemy/resource.rb +238 -0
  455. data/lib/alchemy/resources_helper.rb +135 -0
  456. data/lib/alchemy/routing_constraints.rb +49 -0
  457. data/lib/alchemy/seeder.rb +53 -0
  458. data/lib/alchemy/shell.rb +102 -0
  459. data/lib/alchemy/ssl_protection.rb +30 -0
  460. data/lib/alchemy/tasks/helpers.rb +83 -0
  461. data/lib/alchemy/test_support/controller_requests.rb +73 -0
  462. data/lib/alchemy/test_support/essence_shared_examples.rb +286 -0
  463. data/lib/alchemy/test_support/factories.rb +147 -0
  464. data/lib/alchemy/test_support/integration_helpers.rb +33 -0
  465. data/lib/alchemy/tinymce.rb +68 -0
  466. data/lib/alchemy/touching.rb +41 -0
  467. data/lib/alchemy/upgrader.rb +80 -0
  468. data/lib/alchemy/upgrader/three_point_one.rb +54 -0
  469. data/lib/alchemy/upgrader/three_point_two.rb +69 -0
  470. data/lib/alchemy/upgrader/three_point_zero.rb +93 -0
  471. data/lib/alchemy/userstamp.rb +10 -0
  472. data/lib/alchemy/version.rb +7 -0
  473. data/lib/alchemy_cms.rb +5 -0
  474. data/lib/rails/generators/alchemy/base.rb +41 -0
  475. data/lib/rails/generators/alchemy/elements/elements_generator.rb +37 -0
  476. data/lib/rails/generators/alchemy/elements/templates/editor.html.erb +14 -0
  477. data/lib/rails/generators/alchemy/elements/templates/editor.html.haml +13 -0
  478. data/lib/rails/generators/alchemy/elements/templates/editor.html.slim +12 -0
  479. data/lib/rails/generators/alchemy/elements/templates/view.html.erb +27 -0
  480. data/lib/rails/generators/alchemy/elements/templates/view.html.haml +20 -0
  481. data/lib/rails/generators/alchemy/elements/templates/view.html.slim +20 -0
  482. data/lib/rails/generators/alchemy/essence/essence_generator.rb +52 -0
  483. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +17 -0
  484. data/lib/rails/generators/alchemy/essence/templates/view.html.erb +2 -0
  485. data/lib/rails/generators/alchemy/install/files/_article_editor.html.erb +5 -0
  486. data/lib/rails/generators/alchemy/install/files/_article_view.html.erb +7 -0
  487. data/lib/rails/generators/alchemy/install/files/_standard.html.erb +1 -0
  488. data/lib/rails/generators/alchemy/install/files/alchemy.de.yml +31 -0
  489. data/lib/rails/generators/alchemy/install/files/alchemy.elements.css.scss +30 -0
  490. data/lib/rails/generators/alchemy/install/files/alchemy.en.yml +31 -0
  491. data/lib/rails/generators/alchemy/install/files/alchemy.es.yml +31 -0
  492. data/lib/rails/generators/alchemy/install/files/application.html.erb +14 -0
  493. data/lib/rails/generators/alchemy/install/install_generator.rb +69 -0
  494. data/lib/rails/generators/alchemy/install/templates/elements.yml.tt +21 -0
  495. data/lib/rails/generators/alchemy/install/templates/page_layouts.yml.tt +10 -0
  496. data/lib/rails/generators/alchemy/module/module_generator.rb +30 -0
  497. data/lib/rails/generators/alchemy/module/templates/ability.rb.tt +11 -0
  498. data/lib/rails/generators/alchemy/module/templates/controller.rb.tt +2 -0
  499. data/lib/rails/generators/alchemy/module/templates/module_config.rb.tt +17 -0
  500. data/lib/rails/generators/alchemy/page_layouts/page_layouts_generator.rb +24 -0
  501. data/lib/rails/generators/alchemy/page_layouts/templates/layout.html.erb +1 -0
  502. data/lib/rails/generators/alchemy/page_layouts/templates/layout.html.haml +1 -0
  503. data/lib/rails/generators/alchemy/page_layouts/templates/layout.html.slim +1 -0
  504. data/lib/rails/generators/alchemy/site_layouts/site_layouts_generator.rb +23 -0
  505. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.erb +1 -0
  506. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.haml +1 -0
  507. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.slim +1 -0
  508. data/lib/rails/generators/alchemy/views/views_generator.rb +41 -0
  509. data/lib/rails/templates/alchemy.rb +7 -0
  510. data/lib/tasks/alchemy/convert.rake +39 -0
  511. data/lib/tasks/alchemy/db.rake +22 -0
  512. data/lib/tasks/alchemy/install.rake +61 -0
  513. data/lib/tasks/alchemy/tidy.rake +102 -0
  514. data/lib/tasks/alchemy/upgrade.rake +28 -0
  515. data/spec/controllers/admin/attachments_controller_spec.rb +211 -0
  516. data/spec/controllers/admin/base_controller_spec.rb +74 -0
  517. data/spec/controllers/admin/clipboard_controller_spec.rb +60 -0
  518. data/spec/controllers/admin/contents_controller_spec.rb +72 -0
  519. data/spec/controllers/admin/dashboard_controller_spec.rb +139 -0
  520. data/spec/controllers/admin/elements_controller_spec.rb +473 -0
  521. data/spec/controllers/admin/essence_files_controller_spec.rb +71 -0
  522. data/spec/controllers/admin/essence_pictures_controller_spec.rb +202 -0
  523. data/spec/controllers/admin/languages_controller_spec.rb +61 -0
  524. data/spec/controllers/admin/layoutpages_controller_spec.rb +27 -0
  525. data/spec/controllers/admin/pages_controller_spec.rb +553 -0
  526. data/spec/controllers/admin/pictures_controller_spec.rb +358 -0
  527. data/spec/controllers/admin/resources_controller_spec.rb +53 -0
  528. data/spec/controllers/admin/trash_controller_spec.rb +68 -0
  529. data/spec/controllers/alchemy/admin/tags_controller_spec.rb +63 -0
  530. data/spec/controllers/alchemy/api/contents_controller_spec.rb +126 -0
  531. data/spec/controllers/alchemy/api/elements_controller_spec.rb +125 -0
  532. data/spec/controllers/alchemy/api/pages_controller_spec.rb +134 -0
  533. data/spec/controllers/attachments_controller_spec.rb +64 -0
  534. data/spec/controllers/base_controller_spec.rb +51 -0
  535. data/spec/controllers/elements_controller_spec.rb +48 -0
  536. data/spec/controllers/messages_controller_spec.rb +199 -0
  537. data/spec/controllers/pages_controller_spec.rb +265 -0
  538. data/spec/controllers/pictures_controller_spec.rb +393 -0
  539. data/spec/dummy/Rakefile +6 -0
  540. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  541. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  542. data/spec/dummy/app/controllers/admin/events_controller.rb +3 -0
  543. data/spec/dummy/app/controllers/application_controller.rb +15 -0
  544. data/spec/dummy/app/controllers/login_controller.rb +5 -0
  545. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  546. data/spec/dummy/app/mailers/.gitkeep +0 -0
  547. data/spec/dummy/app/models/ability.rb +9 -0
  548. data/spec/dummy/app/models/dummy_model.rb +3 -0
  549. data/spec/dummy/app/models/dummy_user.rb +11 -0
  550. data/spec/dummy/app/models/event.rb +10 -0
  551. data/spec/dummy/app/models/location.rb +3 -0
  552. data/spec/dummy/app/views/alchemy/elements/_all_you_can_eat_editor.html.erb +11 -0
  553. data/spec/dummy/app/views/alchemy/elements/_article_editor.html.erb +10 -0
  554. data/spec/dummy/app/views/alchemy/elements/_article_view.html.erb +25 -0
  555. data/spec/dummy/app/views/alchemy/elements/_headline_editor.html.erb +1 -0
  556. data/spec/dummy/app/views/alchemy/elements/_headline_view.html.erb +1 -0
  557. data/spec/dummy/app/views/alchemy/elements/_news_view.html.erb +11 -0
  558. data/spec/dummy/app/views/alchemy/page_layouts/_standard.html.erb +14 -0
  559. data/spec/dummy/app/views/layouts/application.html.erb +17 -0
  560. data/spec/dummy/bin/bundle +3 -0
  561. data/spec/dummy/bin/rails +4 -0
  562. data/spec/dummy/bin/rake +4 -0
  563. data/spec/dummy/config.ru +4 -0
  564. data/spec/dummy/config/alchemy/cells.yml +5 -0
  565. data/spec/dummy/config/alchemy/elements.yml +112 -0
  566. data/spec/dummy/config/alchemy/page_layouts.yml +32 -0
  567. data/spec/dummy/config/application.rb +32 -0
  568. data/spec/dummy/config/boot.rb +5 -0
  569. data/spec/dummy/config/database.yml +32 -0
  570. data/spec/dummy/config/environment.rb +5 -0
  571. data/spec/dummy/config/environments/development.rb +41 -0
  572. data/spec/dummy/config/environments/production.rb +77 -0
  573. data/spec/dummy/config/environments/test.rb +43 -0
  574. data/spec/dummy/config/initializers/alchemy.rb +2 -0
  575. data/spec/dummy/config/initializers/assets.rb +11 -0
  576. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  577. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  578. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  579. data/spec/dummy/config/initializers/inflections.rb +16 -0
  580. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  581. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  582. data/spec/dummy/config/initializers/session_store.rb +3 -0
  583. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  584. data/spec/dummy/config/routes.rb +12 -0
  585. data/spec/dummy/config/secrets.yml +22 -0
  586. data/spec/dummy/db/migrate/20121026104128_create_events.rb +20 -0
  587. data/spec/dummy/db/migrate/20130328101418_create_locations.rb +9 -0
  588. data/spec/dummy/db/migrate/20130827094554_alchemy_two_point_six.rb +380 -0
  589. data/spec/dummy/db/migrate/20130828121054_remove_do_not_index_from_alchemy_essence_texts.rb +5 -0
  590. data/spec/dummy/db/migrate/20130828121120_remove_do_not_index_from_alchemy_essence_richtexts.rb +5 -0
  591. data/spec/dummy/db/migrate/20130918201742_add_published_at_to_alchemy_pages.rb +5 -0
  592. data/spec/dummy/db/migrate/20131209225243_create_dummy_users.rb +9 -0
  593. data/spec/dummy/db/migrate/20150122213511_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb +31 -0
  594. data/spec/dummy/db/migrate/20150122213512_add_missing_unique_indices.acts_as_taggable_on_engine.rb +20 -0
  595. data/spec/dummy/db/migrate/20150122213513_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb +15 -0
  596. data/spec/dummy/db/migrate/20150122213514_add_missing_taggable_index.acts_as_taggable_on_engine.rb +10 -0
  597. data/spec/dummy/db/migrate/20150412103152_create_dummy_model.rb +7 -0
  598. data/spec/dummy/db/schema.rb +322 -0
  599. data/spec/dummy/db/seeds.rb +1 -0
  600. data/spec/dummy/lib/assets/.gitkeep +0 -0
  601. data/spec/dummy/public/404.html +67 -0
  602. data/spec/dummy/public/422.html +67 -0
  603. data/spec/dummy/public/500.html +66 -0
  604. data/spec/dummy/public/favicon.ico +0 -0
  605. data/spec/features/admin/dashboard_spec.rb +82 -0
  606. data/spec/features/admin/language_tree_feature_spec.rb +33 -0
  607. data/spec/features/admin/legacy_page_url_management_spec.rb +62 -0
  608. data/spec/features/admin/link_overlay_spec.rb +54 -0
  609. data/spec/features/admin/locale_select_feature_spec.rb +25 -0
  610. data/spec/features/admin/modules_integration_spec.rb +24 -0
  611. data/spec/features/admin/navigation_feature_spec.rb +13 -0
  612. data/spec/features/admin/page_creation_feature_spec.rb +51 -0
  613. data/spec/features/admin/page_editing_feature_spec.rb +135 -0
  614. data/spec/features/admin/picture_library_integration_spec.rb +62 -0
  615. data/spec/features/admin/resources_integration_spec.rb +133 -0
  616. data/spec/features/admin/site_select_feature_spec.rb +32 -0
  617. data/spec/features/admin/tinymce_feature_spec.rb +34 -0
  618. data/spec/features/navigation_spec.rb +9 -0
  619. data/spec/features/page_feature_spec.rb +287 -0
  620. data/spec/features/picture_security_spec.rb +35 -0
  621. data/spec/features/security_spec.rb +24 -0
  622. data/spec/features/translation_integration_spec.rb +51 -0
  623. data/spec/fixtures/500x500.png +0 -0
  624. data/spec/fixtures/80x60.png +0 -0
  625. data/spec/fixtures/image with spaces.png +0 -0
  626. data/spec/fixtures/image.png +0 -0
  627. data/spec/fixtures/image2.PNG +0 -0
  628. data/spec/fixtures/image3.jpeg +0 -0
  629. data/spec/helpers/admin/attachments_helper_spec.rb +15 -0
  630. data/spec/helpers/admin/base_helper_spec.rb +270 -0
  631. data/spec/helpers/admin/contents_helper_spec.rb +65 -0
  632. data/spec/helpers/admin/elements_helper_spec.rb +178 -0
  633. data/spec/helpers/admin/essences_helper_spec.rb +140 -0
  634. data/spec/helpers/admin/navigation_helper_spec.rb +288 -0
  635. data/spec/helpers/admin/pages_helper_spec.rb +95 -0
  636. data/spec/helpers/admin/pictures_helper_spec.rb +29 -0
  637. data/spec/helpers/admin/tags_helper_spec.rb +113 -0
  638. data/spec/helpers/base_helper_spec.rb +45 -0
  639. data/spec/helpers/elements_block_helper_spec.rb +135 -0
  640. data/spec/helpers/elements_helper_spec.rb +311 -0
  641. data/spec/helpers/essences_helper_spec.rb +156 -0
  642. data/spec/helpers/pages_helper_spec.rb +413 -0
  643. data/spec/helpers/picture_url_helpers_spec.rb +35 -0
  644. data/spec/helpers/url_helper_spec.rb +174 -0
  645. data/spec/javascripts/alchemy/dialog_spec.coffee +82 -0
  646. data/spec/javascripts/alchemy/link_overlay_spec.coffee +25 -0
  647. data/spec/javascripts/alchemy_integration_spec.coffee +3 -0
  648. data/spec/javascripts/fixtures/select.html +3 -0
  649. data/spec/javascripts/helpers/mock-ajax.js +199 -0
  650. data/spec/javascripts/helpers/test_responses/test_responses.js +1 -0
  651. data/spec/javascripts/support/jasmine.yml +29 -0
  652. data/spec/libraries/auth_accessors_spec.rb +40 -0
  653. data/spec/libraries/config_spec.rb +112 -0
  654. data/spec/libraries/controller_actions_spec.rb +133 -0
  655. data/spec/libraries/i18n_spec.rb +30 -0
  656. data/spec/libraries/kaminari/scoped_pagination_url_helper_spec.rb +30 -0
  657. data/spec/libraries/modules_spec.rb +67 -0
  658. data/spec/libraries/mount_point_spec.rb +77 -0
  659. data/spec/libraries/on_page_layout_spec.rb +112 -0
  660. data/spec/libraries/page_layout_spec.rb +144 -0
  661. data/spec/libraries/permissions_spec.rb +261 -0
  662. data/spec/libraries/resource_spec.rb +371 -0
  663. data/spec/libraries/resources_helper_spec.rb +144 -0
  664. data/spec/libraries/shell_spec.rb +125 -0
  665. data/spec/libraries/template_tracker_spec.rb +94 -0
  666. data/spec/libraries/tinymce_spec.rb +67 -0
  667. data/spec/libraries/userstamp_spec.rb +10 -0
  668. data/spec/mailers/messages_spec.rb +23 -0
  669. data/spec/models/attachment_spec.rb +227 -0
  670. data/spec/models/cell_spec.rb +102 -0
  671. data/spec/models/content_spec.rb +404 -0
  672. data/spec/models/dummy_model_spec.rb +11 -0
  673. data/spec/models/element_spec.rb +617 -0
  674. data/spec/models/element_to_page_spec.rb +14 -0
  675. data/spec/models/essence_boolean_spec.rb +10 -0
  676. data/spec/models/essence_date_spec.rb +29 -0
  677. data/spec/models/essence_file_spec.rb +43 -0
  678. data/spec/models/essence_html_spec.rb +18 -0
  679. data/spec/models/essence_link_spec.rb +10 -0
  680. data/spec/models/essence_picture_spec.rb +183 -0
  681. data/spec/models/essence_richtext_spec.rb +18 -0
  682. data/spec/models/essence_select_spec.rb +10 -0
  683. data/spec/models/essence_text_spec.rb +114 -0
  684. data/spec/models/language_spec.rb +123 -0
  685. data/spec/models/legacy_page_url_spec.rb +21 -0
  686. data/spec/models/message_spec.rb +55 -0
  687. data/spec/models/page_spec.rb +1896 -0
  688. data/spec/models/picture_spec.rb +330 -0
  689. data/spec/models/site_spec.rb +233 -0
  690. data/spec/models/tag_spec.rb +31 -0
  691. data/spec/routing/api_routing_spec.rb +150 -0
  692. data/spec/routing/routing_spec.rb +234 -0
  693. data/spec/spec_helper.rb +89 -0
  694. data/spec/support/ci/install_phantomjs +7 -0
  695. data/spec/support/hint_examples.rb +30 -0
  696. data/spec/support/rspec-activemodel-mocks_patch.rb +8 -0
  697. data/spec/support/test_tweaks.rb +31 -0
  698. data/spec/support/transformation_examples.rb +180 -0
  699. data/spec/tasks/helpers_spec.rb +219 -0
  700. data/spec/views/essences/essence_boolean_editor_spec.rb +33 -0
  701. data/spec/views/essences/essence_boolean_view_spec.rb +25 -0
  702. data/spec/views/essences/essence_date_view_spec.rb +39 -0
  703. data/spec/views/essences/essence_file_editor_spec.rb +61 -0
  704. data/spec/views/essences/essence_file_view_spec.rb +24 -0
  705. data/spec/views/essences/essence_html_view_spec.rb +22 -0
  706. data/spec/views/essences/essence_link_view_spec.rb +41 -0
  707. data/spec/views/essences/essence_picture_view_spec.rb +129 -0
  708. data/spec/views/essences/essence_richtext_view_spec.rb +32 -0
  709. data/spec/views/essences/essence_select_view_spec.rb +12 -0
  710. data/spec/views/essences/essence_text_view_spec.rb +52 -0
  711. data/vendor/assets/images/Jcrop.gif +0 -0
  712. data/vendor/assets/javascripts/fileupload/jquery.fileupload-process.js +175 -0
  713. data/vendor/assets/javascripts/fileupload/jquery.fileupload-validate.js +122 -0
  714. data/vendor/assets/javascripts/fileupload/jquery.fileupload.js +1467 -0
  715. data/vendor/assets/javascripts/fileupload/jquery.iframe-transport.js +217 -0
  716. data/vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js +22 -0
  717. data/vendor/assets/javascripts/jquery_plugins/jquery.floatThead.min.js +3 -0
  718. data/vendor/assets/javascripts/jquery_plugins/jquery.scrollTo.min.js +7 -0
  719. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +435 -0
  720. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js +296 -0
  721. data/vendor/assets/javascripts/keymaster.js +296 -0
  722. data/vendor/assets/javascripts/requestAnimationFrame.js +31 -0
  723. data/vendor/assets/javascripts/spin.min.js +1 -0
  724. data/vendor/assets/javascripts/tinymce/langs/de.js +200 -0
  725. data/vendor/assets/javascripts/tinymce/langs/es.js +200 -0
  726. data/vendor/assets/javascripts/tinymce/langs/fr.js +200 -0
  727. data/vendor/assets/javascripts/tinymce/langs/nl.js +200 -0
  728. data/vendor/assets/javascripts/tinymce/langs/ru.js +200 -0
  729. data/vendor/assets/javascripts/tinymce/license.txt +504 -0
  730. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.min.js +1 -0
  731. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.min.js +1 -0
  732. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.min.js +1 -0
  733. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.min.js +1 -0
  734. data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.min.js +1 -0
  735. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.min.js +1 -0
  736. data/vendor/assets/javascripts/tinymce/plugins/hr/plugin.min.js +1 -0
  737. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.min.js +1 -0
  738. data/vendor/assets/javascripts/tinymce/plugins/paste/plugin.min.js +1 -0
  739. data/vendor/assets/javascripts/tinymce/plugins/tabfocus/plugin.min.js +1 -0
  740. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.min.js +1 -0
  741. data/vendor/assets/javascripts/tinymce/themes/modern/theme.min.js +1 -0
  742. data/vendor/assets/javascripts/tinymce/tinymce.min.js +13 -0
  743. data/vendor/assets/stylesheets/jquery.Jcrop.min.css +28 -0
  744. metadata +1453 -0
@@ -0,0 +1,123 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ module Alchemy
5
+ describe Language do
6
+ let(:default_language) { Alchemy::Language.default }
7
+ let(:language) { FactoryGirl.create(:klingonian) }
8
+ let(:page) { FactoryGirl.create(:page, language: language) }
9
+
10
+ it "should return a label for code" do
11
+ expect(language.label(:code)).to eq('kl')
12
+ end
13
+
14
+ it "should return a label for name" do
15
+ expect(language.label(:name)).to eq('Klingonian')
16
+ end
17
+
18
+ context "with language_code and empty country_code" do
19
+ it "#code should return language locale only" do
20
+ language.country_code = ''
21
+ expect(language.code).to eq('kl')
22
+ end
23
+
24
+ context "adding a value for country code" do
25
+ it "#code should return a joined locale" do
26
+ language.country_code = 'cr'
27
+ expect(language.code).to eq('kl-cr')
28
+ end
29
+
30
+ it "should update all associated Pages with self.code as value for Page#language_code" do
31
+ page = FactoryGirl.create(:page, language: language)
32
+ language.country_code = 'cr'
33
+ language.save
34
+ page.reload; expect(page.language_code).to eq('kl-cr')
35
+ end
36
+ end
37
+ end
38
+
39
+ context "with country_code and_language_code" do
40
+ context "removing the country_code" do
41
+ it "should update all associated Pages´s language_code with Language#code" do
42
+ language = FactoryGirl.create(:language, country_code: 'kl')
43
+ language.country_code = ''
44
+ language.save
45
+ page.reload; expect(page.language_code).to eq("kl")
46
+ end
47
+ end
48
+ end
49
+
50
+ it "should not be deletable if it is the default language" do
51
+ expect {
52
+ default_language.destroy
53
+ }.to raise_error(DefaultLanguageNotDeletable)
54
+ end
55
+
56
+ describe "before save" do
57
+ describe "#remove_old_default if default attribute has changed to true" do
58
+ it "should unset the default status of the old default language" do
59
+ default_language
60
+ language.update_attributes(default: true)
61
+ default_language.reload
62
+ expect(default_language.default).to be_falsey
63
+ end
64
+ end
65
+ end
66
+
67
+ context "after_update" do
68
+ describe "#set_pages_language if language´s code has changed" do
69
+ it "should update all its pages with the new code" do
70
+ @other_page = FactoryGirl.create(:page, language: language)
71
+ language.update_attributes(code: "fo")
72
+ language.reload; page.reload; @other_page.reload
73
+ expect([page.language_code, @other_page.language_code]).to eq([language.code, language.code])
74
+ end
75
+ end
76
+
77
+ describe "#unpublish_pages" do
78
+ it "should set all pages to unpublic if it gets set to unpublic" do
79
+ page = FactoryGirl.create(:page, language: language)
80
+ @other_page = FactoryGirl.create(:page, language: language)
81
+ language.update_attributes(public: false)
82
+ language.reload; page.reload; @other_page.reload
83
+ expect([page.public?, @other_page.public?]).to eq([false, false])
84
+ end
85
+ end
86
+ end
87
+
88
+ describe '.find_by_code' do
89
+ context "with only the language code given" do
90
+ it "should find the language" do
91
+ expect(Language.find_by_code(language.code)).to eq(language)
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'validations' do
97
+ let(:language) { Language.new(default: true, public: false) }
98
+
99
+ describe 'publicity_of_default_language' do
100
+ context 'if language is not published' do
101
+ it "should add an error to the object" do
102
+ expect(language.valid?).to eq(false)
103
+ expect(language.errors.messages).to have_key(:public)
104
+ end
105
+ end
106
+ end
107
+
108
+ describe 'presence_of_default_language' do
109
+ context 'if no default language would exist anymore' do
110
+ before do
111
+ allow(Language).to receive(:default).and_return(language)
112
+ allow(language).to receive(:default_changed?).and_return(true)
113
+ end
114
+
115
+ it "should add an error to the object" do
116
+ expect(language.valid?).to eq(false)
117
+ expect(language.errors.messages).to have_key(:default)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Alchemy::LegacyPageUrl do
4
+ let(:page) { build_stubbed(:page) }
5
+
6
+ let(:page_url_with_parameters) do
7
+ Alchemy::LegacyPageUrl.new(urlname: 'index.php?id=2', page: page)
8
+ end
9
+
10
+ let(:valid_page_url) do
11
+ Alchemy::LegacyPageUrl.new(urlname: 'my/0-work+is-nice_stuff', page: page)
12
+ end
13
+
14
+ it 'is only valid with correct urlname format' do
15
+ expect(valid_page_url).to be_valid
16
+ end
17
+
18
+ it 'is also valid with get parameters in urlname' do
19
+ expect(page_url_with_parameters).to be_valid
20
+ end
21
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ module Alchemy
4
+ Config.get(:mailer)['fields'].push('email_of_my_boss')
5
+ Config.get(:mailer)['validate_fields'].push('email_of_my_boss')
6
+
7
+ describe Message do
8
+ let(:message) { Message.new }
9
+
10
+ describe '.config' do
11
+ it "should return the mailer config" do
12
+ expect(Config).to receive(:get).with(:mailer)
13
+ Message.config
14
+ end
15
+ end
16
+
17
+ it "has attributes writers and getters for all fields defined in mailer config" do
18
+ Config.get(:mailer)['fields'].each do |field|
19
+ expect(message).to respond_to(field)
20
+ expect(message).to respond_to("#{field}=")
21
+ end
22
+ end
23
+
24
+ context "validation of" do
25
+ context "all fields defined in mailer config" do
26
+ it "adds errors on that fields" do
27
+ Config.get(:mailer)['validate_fields'].each do |field|
28
+ expect(message).to_not be_valid
29
+ expect(message.errors[field].size).to eq(1)
30
+ end
31
+ end
32
+ end
33
+
34
+ context 'field containing email in its name' do
35
+ context "when field has a value" do
36
+ before { message.email_of_my_boss = 'wrong email format' }
37
+
38
+ it "adds error notice (is invalid) to the field" do
39
+ expect(message).to_not be_valid
40
+ expect(message.errors[:email_of_my_boss]).to include("is invalid")
41
+ end
42
+ end
43
+
44
+ context "when field is blank" do
45
+ before { message.email_of_my_boss = '' }
46
+
47
+ it "adds error notice (can't be blank) to the field" do
48
+ expect(message).to_not be_valid
49
+ expect(message.errors[:email_of_my_boss]).to include("can't be blank")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,1896 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Alchemy
6
+ describe Page do
7
+
8
+ let(:rootpage) { Page.root }
9
+ let(:language) { Language.default }
10
+ let(:klingonian) { FactoryGirl.create(:klingonian) }
11
+ let(:language_root) { FactoryGirl.create(:language_root_page) }
12
+ let(:page) { mock_model(Page, page_layout: 'foo') }
13
+ let(:public_page) { FactoryGirl.create(:public_page) }
14
+ let(:news_page) { FactoryGirl.create(:public_page, page_layout: 'news', do_not_autogenerate: false) }
15
+
16
+
17
+ # Validations
18
+
19
+ context 'validations' do
20
+ context "Creating a normal content page" do
21
+ let(:contentpage) { FactoryGirl.build(:page) }
22
+ let(:with_same_urlname) { FactoryGirl.create(:page, urlname: "existing_twice") }
23
+ let(:global_with_same_urlname) { FactoryGirl.create(:page, urlname: "existing_twice", layoutpage: true) }
24
+
25
+ context "when its urlname exists as global page" do
26
+ before { global_with_same_urlname }
27
+
28
+ it "it should be possible to save." do
29
+ contentpage.urlname = "existing_twice"
30
+ expect(contentpage).to be_valid
31
+ end
32
+ end
33
+
34
+ it "should validate the page_layout" do
35
+ contentpage.page_layout = nil
36
+ expect(contentpage).not_to be_valid
37
+ contentpage.valid?
38
+ expect(contentpage.errors[:page_layout].size).to eq(1)
39
+ end
40
+
41
+ it "should validate the parent_id" do
42
+ contentpage.parent_id = nil
43
+ expect(contentpage).not_to be_valid
44
+ contentpage.valid?
45
+ expect(contentpage.errors[:parent_id].size).to eq(1)
46
+ end
47
+
48
+ context 'with page having same urlname' do
49
+ before { with_same_urlname }
50
+
51
+ it "should not be valid" do
52
+ contentpage.urlname = 'existing_twice'
53
+ expect(contentpage).not_to be_valid
54
+ end
55
+ end
56
+
57
+ context "with url_nesting set to true" do
58
+ let(:other_parent) { create(:page, parent_id: Page.root.id, visible: true) }
59
+
60
+ before do
61
+ allow(Config).to receive(:get).and_return(true)
62
+ with_same_urlname
63
+ end
64
+
65
+ it "should only validate urlname dependent of parent" do
66
+ contentpage.urlname = 'existing_twice'
67
+ contentpage.parent_id = other_parent.id
68
+ expect(contentpage).to be_valid
69
+ end
70
+
71
+ it "should validate urlname dependent of parent" do
72
+ contentpage.urlname = 'existing_twice'
73
+ expect(contentpage).not_to be_valid
74
+ end
75
+ end
76
+ end
77
+
78
+ context "creating the rootpage without parent_id and page_layout" do
79
+ let(:rootpage) { build(:page, parent_id: nil, page_layout: nil, name: 'Rootpage') }
80
+
81
+ before do
82
+ Page.delete_all
83
+ end
84
+
85
+ it "should be valid" do
86
+ expect(rootpage).to be_valid
87
+ end
88
+ end
89
+
90
+ context "saving a systempage" do
91
+ let(:systempage) { build(:systempage) }
92
+
93
+ it "should not validate the page_layout" do
94
+ expect(systempage).to be_valid
95
+ end
96
+ end
97
+
98
+ context 'saving an external page' do
99
+ let(:external_page) { build(:page, page_layout: 'external') }
100
+
101
+ it "does not pass with invalid url given" do
102
+ external_page.urlname = 'not, a valid page url'
103
+ expect(external_page).to_not be_valid
104
+ end
105
+
106
+ it "only be valid with correct url given" do
107
+ external_page.urlname = 'www.google.com&utf_src=alchemy;page_id=%20'
108
+ expect(external_page).to be_valid
109
+ end
110
+
111
+ context 'on create' do
112
+ it "is valid without urlname given" do
113
+ external_page.urlname = ''
114
+ expect(external_page).to be_valid
115
+ end
116
+ end
117
+
118
+ context 'on update' do
119
+ before { external_page.save! }
120
+
121
+ it "is not valid without urlname given" do
122
+ external_page.urlname = ''
123
+ expect(external_page).to_not be_valid
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+
130
+ # Callbacks
131
+
132
+ context 'callbacks' do
133
+ let(:page) do
134
+ FactoryGirl.create(:page, name: 'My Testpage', language: language, parent_id: language_root.id)
135
+ end
136
+
137
+ context 'before_save' do
138
+ it "should not set the title automatically if the name changed but title is not blank" do
139
+ page.name = "My Renaming Test"
140
+ page.save; page.reload
141
+ expect(page.title).to eq("My Testpage")
142
+ end
143
+
144
+ it "should not automatically set the title if it changed its value" do
145
+ page.title = "I like SEO"
146
+ page.save; page.reload
147
+ expect(page.title).to eq("I like SEO")
148
+ end
149
+ end
150
+
151
+ context 'after_update' do
152
+ context "urlname has changed" do
153
+ it "should store legacy url if page is not redirect to external page" do
154
+ page.urlname = 'new-urlname'
155
+ page.save!
156
+ expect(page.legacy_urls).not_to be_empty
157
+ expect(page.legacy_urls.first.urlname).to eq('my-testpage')
158
+ end
159
+
160
+ it "should not store legacy url if page is redirect to external page" do
161
+ page.urlname = 'new-urlname'
162
+ page.page_layout = "external"
163
+ page.save!
164
+ expect(page.legacy_urls).to be_empty
165
+ end
166
+
167
+ it "should not store legacy url twice for same urlname" do
168
+ page.urlname = 'new-urlname'
169
+ page.save!
170
+ page.urlname = 'my-testpage'
171
+ page.save!
172
+ page.urlname = 'another-urlname'
173
+ page.save!
174
+ expect(page.legacy_urls.select { |u| u.urlname == 'my-testpage' }.size).to eq(1)
175
+ end
176
+ end
177
+
178
+ context "urlname has not changed" do
179
+ it "should not store a legacy url" do
180
+ page.urlname = 'my-testpage'
181
+ page.save!
182
+ expect(page.legacy_urls).to be_empty
183
+ end
184
+ end
185
+
186
+ context "public has changed" do
187
+ it "should update published_at" do
188
+ expect {
189
+ page.update_attributes!(public: true)
190
+ }.to change {page.read_attribute(:published_at) }
191
+ end
192
+
193
+ it "should not update already set published_at" do
194
+ page.update_attributes!(published_at: 2.weeks.ago)
195
+ expect {
196
+ page.update_attributes!(public: true)
197
+ }.to_not change { page.read_attribute(:published_at) }
198
+ end
199
+ end
200
+
201
+ context "public has not changed" do
202
+ it "should not update published_at" do
203
+ page.update_attributes!(name: 'New Name')
204
+ expect(page.read_attribute(:published_at)).to be_nil
205
+ end
206
+ end
207
+ end
208
+
209
+ context 'after_move' do
210
+ let(:parent_1) { FactoryGirl.create(:page, name: 'Parent 1', visible: true) }
211
+ let(:parent_2) { FactoryGirl.create(:page, name: 'Parent 2', visible: true) }
212
+ let(:page) { FactoryGirl.create(:page, parent_id: parent_1.id, name: 'Page', visible: true) }
213
+
214
+ it "updates the urlname" do
215
+ expect(page.urlname).to eq('parent-1/page')
216
+ page.move_to_child_of parent_2
217
+ expect(page.urlname).to eq('parent-2/page')
218
+ end
219
+
220
+ context 'of an external page' do
221
+ let(:external) { FactoryGirl.create(:page, parent_id: parent_1.id, name: 'external', page_layout: 'external', urlname: 'http://google.com') }
222
+
223
+ it "the urlname does not get updated" do
224
+ external.move_to_child_of parent_2
225
+ expect(external.urlname).to eq('http://google.com')
226
+ end
227
+ end
228
+ end
229
+
230
+ context "a normal page" do
231
+ before do
232
+ @page = FactoryGirl.build(:page, language_code: nil, language: klingonian, do_not_autogenerate: false)
233
+ end
234
+
235
+ it "should set the language code" do
236
+ @page.save
237
+ expect(@page.language_code).to eq("kl")
238
+ end
239
+
240
+ it "should autogenerate the elements" do
241
+ @page.save
242
+ expect(@page.elements).not_to be_empty
243
+ end
244
+
245
+ it "should not autogenerate elements that are already on the page" do
246
+ @page.elements << FactoryGirl.create(:element, name: 'header')
247
+ @page.save
248
+ expect(@page.elements.select { |e| e.name == 'header' }.length).to eq(1)
249
+ end
250
+
251
+ context "with cells" do
252
+ before do
253
+ allow(@page).to receive(:definition).and_return({'name' => 'with_cells', 'cells' => ['header', 'main']})
254
+ end
255
+
256
+ it "should have the generated elements in their cells" do
257
+ allow(@page).to receive(:cell_definitions).and_return([{'name' => 'header', 'elements' => ['article']}])
258
+ @page.save
259
+ expect(@page.cells.where(name: 'header').first.elements).not_to be_empty
260
+ end
261
+
262
+ context "and no elements in cell definitions" do
263
+ it "should have the elements in the nil cell" do
264
+ allow(@page).to receive(:cell_definitions).and_return([{'name' => 'header', 'elements' => []}])
265
+ @page.save
266
+ expect(@page.cells.collect(&:elements).flatten).to be_empty
267
+ end
268
+ end
269
+ end
270
+
271
+ context "with children getting restricted set to true" do
272
+ before do
273
+ @page.save
274
+ @child1 = FactoryGirl.create(:page, name: 'Child 1', parent_id: @page.id)
275
+ @page.reload
276
+ @page.restricted = true
277
+ @page.save
278
+ end
279
+
280
+ it "should restrict all its children" do
281
+ @child1.reload
282
+ expect(@child1.restricted?).to be_truthy
283
+ end
284
+ end
285
+
286
+ context "with restricted parent gets created" do
287
+ before do
288
+ @page.save
289
+ @page.parent.update_attributes(restricted: true)
290
+ @new_page = FactoryGirl.create(:page, name: 'New Page', parent_id: @page.id)
291
+ end
292
+
293
+ it "should also be restricted" do
294
+ expect(@new_page.restricted?).to be_truthy
295
+ end
296
+ end
297
+
298
+ context "with do_not_autogenerate set to true" do
299
+ before do
300
+ @page.do_not_autogenerate = true
301
+ end
302
+
303
+ it "should not autogenerate the elements" do
304
+ @page.save
305
+ expect(@page.elements).to be_empty
306
+ end
307
+ end
308
+ end
309
+
310
+ context "a systempage" do
311
+ before do
312
+ @page = FactoryGirl.create(:systempage)
313
+ end
314
+
315
+ it "should not get the language code for language" do
316
+ expect(@page.language_code).to be_nil
317
+ end
318
+
319
+ it "should not autogenerate the elements" do
320
+ expect(@page.elements).to be_empty
321
+ end
322
+ end
323
+
324
+ context "after changing the page layout" do
325
+ let(:news_element) { news_page.elements.find_by(name: 'news') }
326
+
327
+ it "all elements not allowed on this page should be trashed" do
328
+ expect(news_page.elements.trashed).to be_empty
329
+ news_page.update_attributes(page_layout: 'standard')
330
+ trashed = news_page.elements.trashed.pluck(:name)
331
+ expect(trashed).to eq(['news'])
332
+ expect(trashed).to_not include('article', 'header')
333
+ end
334
+
335
+ it "should autogenerate elements" do
336
+ news_page.update_attributes(page_layout: 'contact')
337
+ expect(news_page.elements.pluck(:name)).to include('contactform')
338
+ end
339
+ end
340
+ end
341
+
342
+
343
+ # ClassMethods (a-z)
344
+
345
+ describe '.all_from_clipboard_for_select' do
346
+ context "with clipboard holding pages having non unique page layout" do
347
+ it "should return the pages" do
348
+ page_1 = FactoryGirl.create(:page, language: language)
349
+ page_2 = FactoryGirl.create(:page, language: language, name: 'Another page')
350
+ clipboard = [
351
+ {'id' => page_1.id.to_s, 'action' => 'copy'},
352
+ {'id' => page_2.id.to_s, 'action' => 'copy'}
353
+ ]
354
+ expect(Page.all_from_clipboard_for_select(clipboard, language.id)).to include(page_1, page_2)
355
+ end
356
+ end
357
+
358
+ context "with clipboard holding a page having unique page layout" do
359
+ it "should not return any pages" do
360
+ page_1 = FactoryGirl.create(:page, language: language, page_layout: 'contact')
361
+ clipboard = [
362
+ {'id' => page_1.id.to_s, 'action' => 'copy'}
363
+ ]
364
+ expect(Page.all_from_clipboard_for_select(clipboard, language.id)).to eq([])
365
+ end
366
+ end
367
+
368
+ context "with clipboard holding two pages. One having a unique page layout." do
369
+ it "should return one page" do
370
+ page_1 = FactoryGirl.create(:page, language: language, page_layout: 'standard')
371
+ page_2 = FactoryGirl.create(:page, name: 'Another page', language: language, page_layout: 'contact')
372
+ clipboard = [
373
+ {'id' => page_1.id.to_s, 'action' => 'copy'},
374
+ {'id' => page_2.id.to_s, 'action' => 'copy'}
375
+ ]
376
+ expect(Page.all_from_clipboard_for_select(clipboard, language.id)).to eq([page_1])
377
+ end
378
+ end
379
+ end
380
+
381
+ describe '.all_locked' do
382
+ it "should return 1 page that is blocked by a user at the moment" do
383
+ FactoryGirl.create(:public_page, locked: true, name: 'First Public Child', parent_id: language_root.id, language: language)
384
+ expect(Page.all_locked.size).to eq(1)
385
+ end
386
+ end
387
+
388
+ describe '.all_locked_by' do
389
+ let(:user) { double(:user, id: 1, class: DummyUser) }
390
+
391
+ before do
392
+ FactoryGirl.create(:public_page, locked: true, locked_by: 53) # This page must not be part of the collection
393
+ allow(user.class)
394
+ .to receive(:primary_key)
395
+ .and_return('id')
396
+ end
397
+
398
+ it "should return the correct page collection blocked by a certain user" do
399
+ page = FactoryGirl.create(:public_page, locked: true, locked_by: 1)
400
+ expect(Page.all_locked_by(user).pluck(:id)).to eq([page.id])
401
+ end
402
+
403
+ context 'with user class having a different primary key' do
404
+ let(:user) { double(:user, user_id: 123, class: DummyUser) }
405
+
406
+ before do
407
+ allow(user.class)
408
+ .to receive(:primary_key)
409
+ .and_return('user_id')
410
+ end
411
+
412
+ it "should return the correct page collection blocked by a certain user" do
413
+ page = FactoryGirl.create(:public_page, locked: true, locked_by: 123)
414
+ expect(Page.all_locked_by(user).pluck(:id)).to eq([page.id])
415
+ end
416
+ end
417
+ end
418
+
419
+ describe '.ancestors_for' do
420
+ let(:lang_root) { Page.language_root_for(Language.default.id) }
421
+ let(:parent) { create(:public_page) }
422
+ let(:page) { create(:public_page, parent_id: parent.id) }
423
+
424
+ it "returns an array of all parents including self" do
425
+ expect(Page.ancestors_for(page)).to eq([lang_root, parent, page])
426
+ end
427
+
428
+ it "does not include the root page" do
429
+ expect(Page.ancestors_for(page)).not_to include(Page.root)
430
+ end
431
+
432
+ context "with current page nil" do
433
+ it "should return an empty array" do
434
+ expect(Page.ancestors_for(nil)).to eq([])
435
+ end
436
+ end
437
+ end
438
+
439
+ describe '.contentpages' do
440
+ before do
441
+ layoutroot = Page.find_or_create_layout_root_for(klingonian.id)
442
+ @layoutpage = FactoryGirl.create(:public_page, name: 'layoutpage', layoutpage: true, parent_id: layoutroot.id, language: klingonian)
443
+ @klingonian_lang_root = FactoryGirl.create(:language_root_page, name: 'klingonian_lang_root', layoutpage: nil, language: klingonian)
444
+ @contentpage = FactoryGirl.create(:public_page, name: 'contentpage', parent_id: language_root.id, language: language)
445
+ end
446
+
447
+ it "should return a collection of contentpages" do
448
+ expect(Page.contentpages.to_a).to include(language_root, @klingonian_lang_root, @contentpage)
449
+ end
450
+
451
+ it "should not contain pages with attribute :layoutpage set to true" do
452
+ expect(Page.contentpages.to_a.select { |p| p.layoutpage == true }).to be_empty
453
+ end
454
+
455
+ it "should contain pages with attribute :layoutpage set to nil" do
456
+ expect(Page.contentpages.to_a.select { |p| p.layoutpage == nil }).to include(@klingonian_lang_root)
457
+ end
458
+ end
459
+
460
+ describe '.copy' do
461
+ let(:page) { FactoryGirl.create(:page, name: 'Source') }
462
+ subject { Page.copy(page) }
463
+
464
+ it "the copy should have added (copy) to name" do
465
+ expect(subject.name).to eq("#{page.name} (Copy)")
466
+ end
467
+
468
+ context "page with tags" do
469
+ before { page.tag_list = 'red, yellow'; page.save }
470
+
471
+ it "the copy should have source tag_list" do
472
+ # The order of tags varies between postgresql and sqlite/mysql
473
+ # This is related to acts-as-taggable-on v.2.4.1
474
+ # To fix the spec we sort the tags until the issue is solved (https://github.com/mbleigh/acts-as-taggable-on/issues/363)
475
+ expect(subject.tag_list).not_to be_empty
476
+ expect(subject.tag_list.sort).to eq(page.tag_list.sort)
477
+ end
478
+ end
479
+
480
+ context "page with elements" do
481
+ before { page.elements << FactoryGirl.create(:element) }
482
+
483
+ it "the copy should have source elements" do
484
+ expect(subject.elements).not_to be_empty
485
+ expect(subject.elements.count).to eq(page.elements.count)
486
+ end
487
+ end
488
+
489
+ context "page with trashed elements" do
490
+ before do
491
+ page.elements << FactoryGirl.create(:element)
492
+ page.elements.first.trash!
493
+ end
494
+
495
+ it "the copy should not hold a copy of the trashed elements" do
496
+ expect(subject.elements).to be_empty
497
+ end
498
+ end
499
+
500
+ context "page with cells" do
501
+ before { page.cells << FactoryGirl.create(:cell) }
502
+
503
+ it "the copy should have source cells" do
504
+ expect(subject.cells).not_to be_empty
505
+ expect(subject.cells.count).to eq(page.cells.length) # It must be length, because!
506
+ end
507
+ end
508
+
509
+ context "page with autogenerate elements" do
510
+ before do
511
+ page = FactoryGirl.create(:page)
512
+ allow(page).to receive(:definition).and_return({'name' => 'standard', 'elements' => ['headline'], 'autogenerate' => ['headline']})
513
+ end
514
+
515
+ it "the copy should not autogenerate elements" do
516
+ expect(subject.elements).to be_empty
517
+ end
518
+ end
519
+
520
+ context "with different page name given" do
521
+ subject { Page.copy(page, {name: 'Different name'}) }
522
+ it "should take this name" do
523
+ expect(subject.name).to eq('Different name')
524
+ end
525
+ end
526
+ end
527
+
528
+ describe '.create' do
529
+ context "before/after filter" do
530
+ it "should automatically set the title from its name" do
531
+ page = FactoryGirl.create(:page, name: 'My Testpage', language: language, parent_id: language_root.id)
532
+ expect(page.title).to eq('My Testpage')
533
+ end
534
+
535
+ it "should get a webfriendly urlname" do
536
+ page = FactoryGirl.create(:page, name: 'klingon$&stößel ', language: language, parent_id: language_root.id)
537
+ expect(page.urlname).to eq('klingon-stoessel')
538
+ end
539
+
540
+ context "with no name set" do
541
+ it "should not set a urlname" do
542
+ page = Page.create(name: '', language: language, parent_id: language_root.id)
543
+ expect(page.urlname).to be_blank
544
+ end
545
+ end
546
+
547
+ it "should generate a three letter urlname from two letter name" do
548
+ page = FactoryGirl.create(:page, name: 'Au', language: language, parent_id: language_root.id)
549
+ expect(page.urlname).to eq('-au')
550
+ end
551
+
552
+ it "should generate a three letter urlname from two letter name with umlaut" do
553
+ page = FactoryGirl.create(:page, name: 'Aü', language: language, parent_id: language_root.id)
554
+ expect(page.urlname).to eq('aue')
555
+ end
556
+
557
+ it "should generate a three letter urlname from one letter name" do
558
+ page = FactoryGirl.create(:page, name: 'A', language: language, parent_id: language_root.id)
559
+ expect(page.urlname).to eq('--a')
560
+ end
561
+
562
+ it "should add a user stamper" do
563
+ page = FactoryGirl.create(:page, name: 'A', language: language, parent_id: language_root.id)
564
+ expect(page.class.stamper_class.to_s).to eq('DummyUser')
565
+ end
566
+
567
+ context "with language given" do
568
+ it "does not set the language from parent" do
569
+ expect_any_instance_of(Page).not_to receive(:set_language_from_parent_or_default)
570
+ Page.create!(name: 'A', parent_id: language_root.id, page_layout: 'standard', language: language)
571
+ end
572
+ end
573
+
574
+ context "with no language given" do
575
+ it "sets the language from parent" do
576
+ expect_any_instance_of(Page).to receive(:set_language_from_parent_or_default)
577
+ Page.create!(name: 'A', parent_id: language_root.id, page_layout: 'standard')
578
+ end
579
+ end
580
+ end
581
+ end
582
+
583
+ describe '.find_or_create_layout_root_for' do
584
+ subject { Page.find_or_create_layout_root_for(language_id) }
585
+
586
+ let(:language) { mock_model('Language', name: 'English') }
587
+ let(:language_id) { language.id }
588
+
589
+ before { allow(Language).to receive(:find).and_return(language) }
590
+
591
+ context 'if no layout root page for given language id could be found' do
592
+ before do
593
+ expect(Page).to receive(:create!).and_return(page)
594
+ end
595
+
596
+ it "creates one" do
597
+ is_expected.to eq(page)
598
+ end
599
+ end
600
+
601
+ context 'if layout root page for given language id could be found' do
602
+ let(:page) { mock_model('Page') }
603
+
604
+ before do
605
+ expect(Page).to receive(:layout_root_for).and_return(page)
606
+ end
607
+
608
+ it "returns layout root page" do
609
+ is_expected.to eq(page)
610
+ end
611
+ end
612
+ end
613
+
614
+ describe '.language_roots' do
615
+ it "should return 1 language_root" do
616
+ FactoryGirl.create(:public_page, name: 'First Public Child', parent_id: language_root.id, language: language)
617
+ expect(Page.language_roots.size).to eq(1)
618
+ end
619
+ end
620
+
621
+ describe '.layoutpages' do
622
+ it "should return 1 layoutpage" do
623
+ FactoryGirl.create(:public_page, layoutpage: true, name: 'Layoutpage', parent_id: rootpage.id, language: language)
624
+ expect(Page.layoutpages.size).to eq(1)
625
+ end
626
+ end
627
+
628
+ describe '.not_locked' do
629
+ it "should return pages that are not blocked by a user at the moment" do
630
+ FactoryGirl.create(:public_page, locked: true, name: 'First Public Child', parent_id: language_root.id, language: language)
631
+ FactoryGirl.create(:public_page, name: 'Second Public Child', parent_id: language_root.id, language: language)
632
+ expect(Page.not_locked.size).to eq(3)
633
+ end
634
+ end
635
+
636
+ describe '.not_restricted' do
637
+ it "should return 2 accessible pages" do
638
+ FactoryGirl.create(:public_page, name: 'First Public Child', restricted: true, parent_id: language_root.id, language: language)
639
+ expect(Page.not_restricted.size).to eq(2)
640
+ end
641
+ end
642
+
643
+ describe '.public' do
644
+ it "should return pages that are public" do
645
+ FactoryGirl.create(:public_page, name: 'First Public Child', parent_id: language_root.id, language: language)
646
+ FactoryGirl.create(:public_page, name: 'Second Public Child', parent_id: language_root.id, language: language)
647
+ expect(Page.published.size).to eq(3)
648
+ end
649
+ end
650
+
651
+ describe '.restricted' do
652
+ it "should return 1 restricted page" do
653
+ FactoryGirl.create(:public_page, name: 'First Public Child', restricted: true, parent_id: language_root.id, language: language)
654
+ expect(Page.restricted.size).to eq(1)
655
+ end
656
+ end
657
+
658
+ describe '.rootpage' do
659
+ it "should contain one rootpage" do
660
+ expect(Page.rootpage).to be_instance_of(Page)
661
+ end
662
+ end
663
+
664
+ describe '.visible' do
665
+ it "should return 1 visible page" do
666
+ FactoryGirl.create(:public_page, name: 'First Public Child', visible: true, parent_id: language_root.id, language: language)
667
+ expect(Page.visible.size).to eq(1)
668
+ end
669
+ end
670
+
671
+
672
+ # InstanceMethods (a-z)
673
+
674
+ describe '#available_element_definitions' do
675
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
676
+
677
+ it "returns all element definitions of available elements" do
678
+ expect(page.available_element_definitions).to be_an(Array)
679
+ expect(page.available_element_definitions.collect { |e| e['name'] }).to include('header')
680
+ end
681
+
682
+ context "with unique elements already on page" do
683
+ let(:element) { FactoryGirl.build_stubbed(:unique_element) }
684
+
685
+ before do
686
+ allow(page)
687
+ .to receive(:elements)
688
+ .and_return double(not_trashed: double(pluck: [element.name]))
689
+ end
690
+
691
+ it "does not return unique element definitions" do
692
+ expect(page.available_element_definitions.collect { |e| e['name'] }).to include('article')
693
+ expect(page.available_element_definitions.collect { |e| e['name'] }).not_to include('header')
694
+ end
695
+ end
696
+
697
+ context 'limited amount' do
698
+ let(:page) { FactoryGirl.build_stubbed(:page, page_layout: 'columns') }
699
+ let(:unique_element) { FactoryGirl.build_stubbed(:unique_element, name: 'unique_headline') }
700
+ let(:element_1) { FactoryGirl.build_stubbed(:element, name: 'column_headline') }
701
+ let(:element_2) { FactoryGirl.build_stubbed(:element, name: 'column_headline') }
702
+ let(:element_3) { FactoryGirl.build_stubbed(:element, name: 'column_headline') }
703
+
704
+ before {
705
+ allow(Element).to receive(:definitions).and_return([
706
+ {
707
+ 'name' => 'column_headline',
708
+ 'amount' => 3,
709
+ 'contents' => [{'name' => 'headline', 'type' => 'EssenceText'}]
710
+ },
711
+ {
712
+ 'name' => 'unique_headline',
713
+ 'unique' => true,
714
+ 'amount' => 3,
715
+ 'contents' => [{'name' => 'headline', 'type' => 'EssenceText'}]
716
+ }
717
+ ])
718
+ allow(PageLayout).to receive(:get).and_return({
719
+ 'name' => 'columns',
720
+ 'elements' => ['column_headline', 'unique_headline'],
721
+ 'autogenerate' => ['unique_headline', 'column_headline', 'column_headline', 'column_headline']
722
+ })
723
+ allow(page).to receive(:elements).and_return double(
724
+ not_trashed: double(pluck: [
725
+ unique_element.name,
726
+ element_1.name,
727
+ element_2.name,
728
+ element_3.name
729
+ ])
730
+ )
731
+ }
732
+
733
+ it "should be readable" do
734
+ element = page.element_definitions_by_name('column_headline').first
735
+ expect(element['amount']).to be 3
736
+ end
737
+
738
+ it "should limit elements" do
739
+ expect(page.available_element_definitions.collect { |e| e['name'] }).not_to include('column_headline')
740
+ end
741
+
742
+ it "should be ignored if unique" do
743
+ expect(page.available_element_definitions.collect { |e| e['name'] }).not_to include('unique_headline')
744
+ end
745
+ end
746
+ end
747
+
748
+ describe '#available_element_names' do
749
+ let(:page) { FactoryGirl.build_stubbed(:page) }
750
+
751
+ it "returns all names of elements that could be placed on current page" do
752
+ page.available_element_names == %w(header article)
753
+ end
754
+ end
755
+
756
+ describe '#cache_key' do
757
+ let(:page) do
758
+ stub_model(Page, updated_at: Time.now, published_at: Time.now - 1.week)
759
+ end
760
+
761
+ subject { page.cache_key }
762
+
763
+ before do
764
+ expect(Page).to receive(:current_preview).and_return(preview)
765
+ end
766
+
767
+ context "when current page rendered in preview mode" do
768
+ let(:preview) { page }
769
+
770
+ it { is_expected.to eq("alchemy/pages/#{page.id}-#{page.updated_at}") }
771
+ end
772
+
773
+ context "when current page not in preview mode" do
774
+ let(:preview) { nil }
775
+
776
+ it { is_expected.to eq("alchemy/pages/#{page.id}-#{page.published_at}") }
777
+ end
778
+ end
779
+
780
+ describe '#cell_definitions' do
781
+ before do
782
+ @page = FactoryGirl.build(:page, page_layout: 'foo')
783
+ allow(@page).to receive(:definition).and_return({'name' => "foo", 'cells' => ["foo_cell"]})
784
+ @cell_descriptions = [{'name' => "foo_cell", 'elements' => ["1", "2"]}]
785
+ allow(Cell).to receive(:definitions).and_return(@cell_descriptions)
786
+ end
787
+
788
+ it "should return all cell definitions for its page_layout" do
789
+ expect(@page.cell_definitions).to eq(@cell_descriptions)
790
+ end
791
+
792
+ it "should return empty array if no cells defined in page layout" do
793
+ allow(@page).to receive(:definition).and_return({'name' => "foo"})
794
+ expect(@page.cell_definitions).to eq([])
795
+ end
796
+ end
797
+
798
+ describe '#destroy' do
799
+ context "with trashed but still assigned elements" do
800
+ before { news_page.elements.map(&:trash!) }
801
+
802
+ it "should not delete the trashed elements" do
803
+ news_page.destroy
804
+ expect(Element.trashed).not_to be_empty
805
+ end
806
+ end
807
+ end
808
+
809
+ describe '#element_definitions' do
810
+ let(:page) { FactoryGirl.build_stubbed(:page) }
811
+ subject { page.element_definitions }
812
+ before { expect(Element).to receive(:definitions).and_return([{'name' => 'article'}, {'name' => 'header'}]) }
813
+
814
+ it "returns all element definitions that could be placed on current page" do
815
+ is_expected.to include({'name' => 'article'})
816
+ is_expected.to include({'name' => 'header'})
817
+ end
818
+ end
819
+
820
+ describe '#element_definitions_by_name' do
821
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
822
+
823
+ context "with no name given" do
824
+ it "returns empty array" do
825
+ expect(page.element_definitions_by_name(nil)).to eq([])
826
+ end
827
+ end
828
+
829
+ context "with 'all' passed as name" do
830
+ it "returns all element definitions" do
831
+ expect(Element).to receive(:definitions)
832
+ page.element_definitions_by_name('all')
833
+ end
834
+ end
835
+
836
+ context "with :all passed as name" do
837
+ it "returns all element definitions" do
838
+ expect(Element).to receive(:definitions)
839
+ page.element_definitions_by_name(:all)
840
+ end
841
+ end
842
+ end
843
+
844
+ describe '#element_definition_names' do
845
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
846
+
847
+ context "with assigned elements from page and cell definition" do
848
+ before do
849
+ allow(page).to receive(:element_names_from_page_definition).and_return(['header', 'article'])
850
+ allow(page).to receive(:element_names_from_cell_definition).and_return(['search'])
851
+ end
852
+
853
+ it "returns the combined element names" do
854
+ expect(page.element_definition_names).to eq(%w(header article search))
855
+ end
856
+
857
+ context "when cell definition contains same element name as page definition" do
858
+ before do
859
+ allow(page).to receive(:element_names_from_page_definition).and_return(['header', 'article'])
860
+ allow(page).to receive(:element_names_from_cell_definition).and_return(['header', 'search'])
861
+ end
862
+
863
+ it "returns no duplicates" do
864
+ expect(page.element_definition_names).to eq(%w(header article search))
865
+ end
866
+ end
867
+ end
868
+
869
+ context "without assigned elements from page definition" do
870
+ before do
871
+ allow(page).to receive(:element_names_from_page_definition).and_return([])
872
+ allow(page).to receive(:element_names_from_cell_definition).and_return(['article'])
873
+ end
874
+
875
+ it "returns an array" do
876
+ expect(page.element_definition_names).to be_an(Array)
877
+ end
878
+ end
879
+
880
+ context "without assigned elements from cell definition" do
881
+ before do
882
+ allow(page).to receive(:element_names_from_page_definition).and_return(['article'])
883
+ allow(page).to receive(:element_names_from_cell_definition).and_return([])
884
+ end
885
+
886
+ it "returns an array" do
887
+ expect(page.element_definition_names).to be_an(Array)
888
+ end
889
+ end
890
+ end
891
+
892
+ describe "#element_names_from_page_definition" do
893
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
894
+
895
+ context "with assigned elements from page definition" do
896
+ before do
897
+ allow(page).to receive(:definition).and_return(
898
+ {'name' => 'test_page', 'elements' => ['article']}
899
+ )
900
+ end
901
+
902
+ it "returns an array of the page's element names" do
903
+ expect(page.element_names_from_page_definition).to eq(['article'])
904
+ end
905
+ end
906
+
907
+ context "without assigned elements from page definition" do
908
+ before do
909
+ allow(page).to receive(:definition).and_return({})
910
+ end
911
+
912
+ it "returns an array" do
913
+ expect(page.element_names_from_page_definition).to be_an(Array)
914
+ end
915
+ end
916
+ end
917
+
918
+ describe "#element_names_from_cell_definition" do
919
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
920
+
921
+ context "with assigned elements from cell definition" do
922
+ before do
923
+ allow(page).to receive(:cell_definitions).and_return([
924
+ {'name' => 'header', 'elements' => ['article']}
925
+ ])
926
+ end
927
+
928
+ it "returns an array of the cell's element names" do
929
+ expect(page.element_names_from_cell_definition).to eq(['article'])
930
+ end
931
+ end
932
+
933
+ context "without assigned elements from cell definition" do
934
+ it "returns an array" do
935
+ expect(page.element_names_from_cell_definition).to be_an(Array)
936
+ end
937
+ end
938
+ end
939
+
940
+ describe '#elements_grouped_by_cells' do
941
+ let(:page) { FactoryGirl.create(:public_page, do_not_autogenerate: false) }
942
+
943
+ before do
944
+ allow(PageLayout).to receive(:get).and_return({
945
+ 'name' => 'standard',
946
+ 'cells' => ['header'],
947
+ 'elements' => ['header', 'text'],
948
+ 'autogenerate' => ['header', 'text']
949
+ })
950
+ allow(Cell).to receive(:definitions).and_return([{
951
+ 'name' => "header",
952
+ 'elements' => ["header"]
953
+ }])
954
+ end
955
+
956
+ it "should return elements grouped by cell" do
957
+ elements = page.elements_grouped_by_cells
958
+ expect(elements.keys.first).to be_instance_of(Cell)
959
+ expect(elements.values.first.first).to be_instance_of(Element)
960
+ end
961
+
962
+ it "should only include elements beeing in a cell " do
963
+ expect(page.elements_grouped_by_cells.keys).not_to include(nil)
964
+ end
965
+ end
966
+
967
+ describe '#feed_elements' do
968
+ it "should return all rss feed elements" do
969
+ expect(news_page.feed_elements).not_to be_empty
970
+ expect(news_page.feed_elements).to eq(Element.where(name: 'news').to_a)
971
+ end
972
+ end
973
+
974
+ describe '#find_elements' do
975
+ before do
976
+ FactoryGirl.create(:element, public: false, page: public_page)
977
+ FactoryGirl.create(:element, public: false, page: public_page)
978
+ end
979
+
980
+ context "with show_non_public argument TRUE" do
981
+ it "should return all elements from empty options" do
982
+ expect(public_page.find_elements({}, true).to_a).to eq(public_page.elements.to_a)
983
+ end
984
+
985
+ it "should only return the elements passed as options[:only]" do
986
+ expect(public_page.find_elements({only: ['article']}, true).to_a).to eq(public_page.elements.named('article').to_a)
987
+ end
988
+
989
+ it "should not return the elements passed as options[:except]" do
990
+ expect(public_page.find_elements({except: ['article']}, true).to_a).to eq(public_page.elements - public_page.elements.named('article').to_a)
991
+ end
992
+
993
+ it "should return elements offsetted" do
994
+ expect(public_page.find_elements({offset: 2}, true).to_a).to eq(public_page.elements.offset(2))
995
+ end
996
+
997
+ it "should return elements limitted in count" do
998
+ expect(public_page.find_elements({count: 1}, true).to_a).to eq(public_page.elements.limit(1))
999
+ end
1000
+ end
1001
+
1002
+ context "with options[:from_cell]" do
1003
+ let(:element) { FactoryGirl.build_stubbed(:element) }
1004
+
1005
+ context "given as String" do
1006
+ context 'with elements present' do
1007
+ before do
1008
+ expect(public_page.cells)
1009
+ .to receive(:find_by_name)
1010
+ .and_return double(elements: double(offset: double(limit: double(published: [element]))))
1011
+ end
1012
+
1013
+ it "returns only the elements from given cell" do
1014
+ expect(public_page.find_elements(from_cell: 'A Cell').to_a).to eq([element])
1015
+ end
1016
+ end
1017
+
1018
+ context "that can not be found" do
1019
+ let(:elements) {[]}
1020
+
1021
+ before do
1022
+ allow(elements)
1023
+ .to receive(:offset)
1024
+ .and_return double(limit: double(published: elements))
1025
+ end
1026
+
1027
+ it "returns empty set" do
1028
+ expect(Element).to receive(:none).and_return(elements)
1029
+ expect(public_page.find_elements(from_cell: 'Lolo').to_a).to eq([])
1030
+ end
1031
+
1032
+ it "loggs a warning" do
1033
+ expect(Rails.logger).to receive(:debug)
1034
+ public_page.find_elements(from_cell: 'Lolo')
1035
+ end
1036
+ end
1037
+ end
1038
+
1039
+ context "given as cell object" do
1040
+ let(:cell) { FactoryGirl.build_stubbed(:cell, page: public_page) }
1041
+
1042
+ it "returns only the elements from given cell" do
1043
+ expect(cell)
1044
+ .to receive(:elements)
1045
+ .and_return double(offset: double(limit: double(published: [element])))
1046
+
1047
+ expect(public_page.find_elements(from_cell: cell).to_a).to eq([element])
1048
+ end
1049
+ end
1050
+ end
1051
+
1052
+ context "with show_non_public argument FALSE" do
1053
+ it "should return all elements from empty arguments" do
1054
+ expect(public_page.find_elements().to_a).to eq(public_page.elements.published.to_a)
1055
+ end
1056
+
1057
+ it "should only return the public elements passed as options[:only]" do
1058
+ expect(public_page.find_elements(only: ['article']).to_a).to eq(public_page.elements.published.named('article').to_a)
1059
+ end
1060
+
1061
+ it "should return all public elements except the ones passed as options[:except]" do
1062
+ expect(public_page.find_elements(except: ['article']).to_a).to eq(public_page.elements.published.to_a - public_page.elements.published.named('article').to_a)
1063
+ end
1064
+
1065
+ it "should return elements offsetted" do
1066
+ expect(public_page.find_elements({offset: 2}).to_a).to eq(public_page.elements.published.offset(2))
1067
+ end
1068
+
1069
+ it "should return elements limitted in count" do
1070
+ expect(public_page.find_elements({count: 1}).to_a).to eq(public_page.elements.published.limit(1))
1071
+ end
1072
+ end
1073
+ end
1074
+
1075
+ describe '#first_public_child' do
1076
+ before do
1077
+ FactoryGirl.create(:page, name: "First child", language: language, public: false, parent_id: language_root.id)
1078
+ end
1079
+
1080
+ it "should return first_public_child" do
1081
+ first_public_child = FactoryGirl.create(:public_page, name: "First public child", language: language, parent_id: language_root.id)
1082
+ expect(language_root.first_public_child).to eq(first_public_child)
1083
+ end
1084
+
1085
+ it "should return nil if no public child exists" do
1086
+ expect(language_root.first_public_child).to eq(nil)
1087
+ end
1088
+ end
1089
+
1090
+ context 'folding' do
1091
+ let(:user) { mock_model('DummyUser') }
1092
+
1093
+ describe '#fold!' do
1094
+ context "with folded status set to true" do
1095
+ it "should create a folded page for user" do
1096
+ public_page.fold!(user.id, true)
1097
+ expect(public_page.folded_pages.first.user_id).to eq(user.id)
1098
+ end
1099
+ end
1100
+ end
1101
+
1102
+ describe '#folded?' do
1103
+ let(:page) { Page.new }
1104
+
1105
+ context 'with user is a active record model' do
1106
+ before do
1107
+ expect(Alchemy.user_class).to receive(:'<').and_return(true)
1108
+ end
1109
+
1110
+ context 'if page is folded' do
1111
+ before do
1112
+ expect(page)
1113
+ .to receive(:folded_pages)
1114
+ .and_return double(where: double(any?: true))
1115
+ end
1116
+
1117
+ it "should return true" do
1118
+ expect(page.folded?(user.id)).to eq(true)
1119
+ end
1120
+ end
1121
+
1122
+ context 'if page is not folded' do
1123
+ it "should return false" do
1124
+ expect(page.folded?(101093)).to eq(false)
1125
+ end
1126
+ end
1127
+ end
1128
+ end
1129
+ end
1130
+
1131
+ describe '#get_language_root' do
1132
+ before { language_root }
1133
+ subject { public_page.get_language_root }
1134
+
1135
+ it "returns the language root page" do
1136
+ is_expected.to eq language_root
1137
+ end
1138
+ end
1139
+
1140
+ describe '#layout_description' do
1141
+ context 'if the page layout could not be found in the definition file' do
1142
+ let(:page) { build_stubbed(:page, page_layout: 'notexisting') }
1143
+
1144
+ it "it loggs a warning." do
1145
+ expect(Alchemy::Logger).to receive(:warn)
1146
+ page.layout_description
1147
+ end
1148
+
1149
+ it "it returns empty hash." do
1150
+ expect(page.layout_description).to eq({})
1151
+ end
1152
+ end
1153
+
1154
+ context "for a language root page" do
1155
+ it "it returns the page layout description as hash." do
1156
+ expect(language_root.layout_description['name']).to eq('index')
1157
+ end
1158
+
1159
+ it "it returns an empty hash for root page." do
1160
+ expect(rootpage.layout_description).to eq({})
1161
+ end
1162
+ end
1163
+ end
1164
+
1165
+ describe '#lock_to!' do
1166
+ let(:page) { create(:page) }
1167
+ let(:user) { mock_model('DummyUser') }
1168
+
1169
+ it "should set locked to true" do
1170
+ page.lock_to!(user)
1171
+ page.reload
1172
+ expect(page.locked).to eq(true)
1173
+ end
1174
+
1175
+ it "should not update the timestamps " do
1176
+ expect { page.lock!(user) }.to_not change(page, :updated_at)
1177
+ end
1178
+
1179
+ it "should set locked_by to the users id" do
1180
+ page.lock_to!(user)
1181
+ page.reload
1182
+ expect(page.locked_by).to eq(user.id)
1183
+ end
1184
+ end
1185
+
1186
+ describe '#copy_and_paste' do
1187
+ let(:source) { FactoryGirl.build_stubbed(:page) }
1188
+ let(:new_parent) { FactoryGirl.build_stubbed(:page) }
1189
+ let(:page_name) { "Pagename (pasted)" }
1190
+ let(:copied_page) { mock_model('Page') }
1191
+
1192
+ subject { Page.copy_and_paste(source, new_parent, page_name) }
1193
+
1194
+ it "should copy the source page with the given name to the new parent" do
1195
+ expect(Page).to receive(:copy).with(source, {
1196
+ parent_id: new_parent.id,
1197
+ language: new_parent.language,
1198
+ name: page_name,
1199
+ title: page_name
1200
+ })
1201
+ subject
1202
+ end
1203
+
1204
+ it "should return the copied page" do
1205
+ allow(Page).to receive(:copy).and_return(copied_page)
1206
+ expect(subject).to be_a(copied_page.class)
1207
+ end
1208
+
1209
+ context "if source page has children" do
1210
+ it "should also copy and paste the children" do
1211
+ allow(Page).to receive(:copy).and_return(copied_page)
1212
+ allow(source).to receive(:children).and_return([mock_model('Page')])
1213
+ expect(source).to receive(:copy_children_to).with(copied_page)
1214
+ subject
1215
+ end
1216
+ end
1217
+ end
1218
+
1219
+ context 'previous and next.' do
1220
+ let(:center_page) { FactoryGirl.create(:public_page, name: 'Center Page') }
1221
+ let(:next_page) { FactoryGirl.create(:public_page, name: 'Next Page') }
1222
+ let(:non_public_page) { FactoryGirl.create(:page, name: 'Not public Page') }
1223
+ let(:restricted_page) { FactoryGirl.create(:restricted_page, public: true) }
1224
+
1225
+ before do
1226
+ public_page
1227
+ restricted_page
1228
+ non_public_page
1229
+ center_page
1230
+ next_page
1231
+ end
1232
+
1233
+ describe '#previous' do
1234
+ it "should return the previous page on the same level" do
1235
+ expect(center_page.previous).to eq(public_page)
1236
+ expect(next_page.previous).to eq(center_page)
1237
+ end
1238
+
1239
+ context "no previous page on same level present" do
1240
+ it "should return nil" do
1241
+ expect(public_page.previous).to be_nil
1242
+ end
1243
+ end
1244
+
1245
+ context "with options restricted" do
1246
+ context "set to true" do
1247
+ it "returns previous restricted page" do
1248
+ expect(center_page.previous(restricted: true)).to eq(restricted_page)
1249
+ end
1250
+ end
1251
+
1252
+ context "set to false" do
1253
+ it "skips restricted page" do
1254
+ expect(center_page.previous(restricted: false)).to eq(public_page)
1255
+ end
1256
+ end
1257
+ end
1258
+
1259
+ context "with options public" do
1260
+ context "set to true" do
1261
+ it "returns previous public page" do
1262
+ expect(center_page.previous(public: true)).to eq(public_page)
1263
+ end
1264
+ end
1265
+
1266
+ context "set to false" do
1267
+ it "skips public page" do
1268
+ expect(center_page.previous(public: false)).to eq(non_public_page)
1269
+ end
1270
+ end
1271
+ end
1272
+ end
1273
+
1274
+ describe '#next' do
1275
+ it "should return the next page on the same level" do
1276
+ expect(center_page.next).to eq(next_page)
1277
+ end
1278
+
1279
+ context "no next page on same level present" do
1280
+ it "should return nil" do
1281
+ expect(next_page.next).to be_nil
1282
+ end
1283
+ end
1284
+ end
1285
+ end
1286
+
1287
+ describe '#publish!' do
1288
+ let(:page) { FactoryGirl.build_stubbed(:page, public: false) }
1289
+ let(:current_time) { Time.now }
1290
+
1291
+ before do
1292
+ current_time
1293
+ allow(Time).to receive(:now).and_return(current_time)
1294
+ page.publish!
1295
+ end
1296
+
1297
+ it "sets public attribute to true" do
1298
+ expect(page.public).to eq(true)
1299
+ end
1300
+
1301
+ it "sets published_at attribute to current time" do
1302
+ expect(page.published_at).to eq(current_time)
1303
+ end
1304
+ end
1305
+
1306
+ describe '#set_language_from_parent_or_default' do
1307
+ let(:default_language) { mock_model('Language', code: 'es') }
1308
+ let(:page) { Page.new }
1309
+
1310
+ before { allow(page).to receive(:parent).and_return(parent) }
1311
+
1312
+ subject { page }
1313
+
1314
+ context "parent has a language" do
1315
+ let(:parent) { mock_model('Page', language: default_language, language_id: default_language.id, language_code: default_language.code) }
1316
+
1317
+ before do
1318
+ page.send(:set_language_from_parent_or_default)
1319
+ end
1320
+
1321
+ describe '#language_id' do
1322
+ subject { super().language_id }
1323
+ it { is_expected.to eq(parent.language_id) }
1324
+ end
1325
+ end
1326
+
1327
+ context "parent has no language" do
1328
+ let(:parent) { mock_model('Page', language: nil, language_id: nil, language_code: nil) }
1329
+
1330
+ before do
1331
+ allow(Language).to receive(:default).and_return(default_language)
1332
+ page.send(:set_language_from_parent_or_default)
1333
+ end
1334
+
1335
+ describe '#language_id' do
1336
+ subject { super().language_id }
1337
+ it { is_expected.to eq(default_language.id) }
1338
+ end
1339
+ end
1340
+ end
1341
+
1342
+ describe '#taggable?' do
1343
+ context "definition has 'taggable' key with true value" do
1344
+ it "should return true" do
1345
+ page = FactoryGirl.build(:page)
1346
+ allow(page).to receive(:definition).and_return({'name' => 'standard', 'taggable' => true})
1347
+ expect(page.taggable?).to be_truthy
1348
+ end
1349
+ end
1350
+
1351
+ context "definition has 'taggable' key with foo value" do
1352
+ it "should return false" do
1353
+ page = FactoryGirl.build(:page)
1354
+ allow(page).to receive(:definition).and_return({'name' => 'standard', 'taggable' => 'foo'})
1355
+ expect(page.taggable?).to be_falsey
1356
+ end
1357
+ end
1358
+
1359
+ context "definition has no 'taggable' key" do
1360
+ it "should return false" do
1361
+ page = FactoryGirl.build(:page)
1362
+ allow(page).to receive(:definition).and_return({'name' => 'standard'})
1363
+ expect(page.taggable?).to be_falsey
1364
+ end
1365
+ end
1366
+ end
1367
+
1368
+ describe '#unlock!' do
1369
+ let(:page) { FactoryGirl.create(:page, locked: true, locked_by: 1) }
1370
+
1371
+ before do
1372
+ allow(page).to receive(:save).and_return(true)
1373
+ end
1374
+
1375
+ it "should set the locked status to false" do
1376
+ page.unlock!
1377
+ page.reload
1378
+ expect(page.locked).to eq(false)
1379
+ end
1380
+
1381
+ it "should not update the timestamps " do
1382
+ expect { page.unlock! }.to_not change(page, :updated_at)
1383
+ end
1384
+
1385
+ it "should set locked_by to nil" do
1386
+ page.unlock!
1387
+ page.reload
1388
+ expect(page.locked_by).to eq(nil)
1389
+ end
1390
+
1391
+ it "sets current preview to nil" do
1392
+ Page.current_preview = page
1393
+ page.unlock!
1394
+ expect(Page.current_preview).to be_nil
1395
+ end
1396
+ end
1397
+
1398
+ context 'urlname updating' do
1399
+ let(:parentparent) { FactoryGirl.create(:page, name: 'parentparent', visible: true) }
1400
+ let(:parent) { FactoryGirl.create(:page, parent_id: parentparent.id, name: 'parent', visible: true) }
1401
+ let(:page) { FactoryGirl.create(:page, parent_id: parent.id, name: 'page', visible: true) }
1402
+ let(:invisible) { FactoryGirl.create(:page, parent_id: page.id, name: 'invisible', visible: false) }
1403
+ let(:contact) { FactoryGirl.create(:page, parent_id: invisible.id, name: 'contact', visible: true) }
1404
+ let(:external) { FactoryGirl.create(:page, parent_id: parent.id, name: 'external', page_layout: 'external', urlname: 'http://google.com') }
1405
+ let(:language_root) { parentparent.parent }
1406
+
1407
+ context "with activated url_nesting" do
1408
+ before { allow(Config).to receive(:get).and_return(true) }
1409
+
1410
+ it "should store all parents urlnames delimited by slash" do
1411
+ expect(page.urlname).to eq('parentparent/parent/page')
1412
+ end
1413
+
1414
+ it "should not include the root page" do
1415
+ Page.root.update_column(:urlname, 'root')
1416
+ language_root.update(urlname: 'new-urlname')
1417
+ expect(language_root.urlname).not_to match(/root/)
1418
+ end
1419
+
1420
+ it "should not include the language root page" do
1421
+ expect(page.urlname).not_to match(/startseite/)
1422
+ end
1423
+
1424
+ it "should not include invisible pages" do
1425
+ expect(contact.urlname).not_to match(/invisible/)
1426
+ end
1427
+
1428
+ context "with an invisible parent" do
1429
+ before { parent.update_attribute(:visible, false) }
1430
+
1431
+ it "does not change if set_urlname is called" do
1432
+ expect { page.send(:set_urlname) }.not_to change { page.urlname }
1433
+ end
1434
+
1435
+ it "does not change if update_urlname! is called" do
1436
+ expect { page.update_urlname! }.not_to change { page.urlname }
1437
+ end
1438
+ end
1439
+
1440
+ context "after changing page's urlname" do
1441
+ it "updates urlnames of descendants" do
1442
+ page
1443
+ parentparent.urlname = 'new-urlname'
1444
+ parentparent.save!
1445
+ page.reload
1446
+ expect(page.urlname).to eq('new-urlname/parent/page')
1447
+ end
1448
+
1449
+ context 'with descendants that are redirecting to external' do
1450
+ it "it skips this page" do
1451
+ external
1452
+ parent.urlname = 'new-urlname'
1453
+ parent.save!
1454
+ external.reload
1455
+ expect(external.urlname).to eq('http://google.com')
1456
+ end
1457
+ end
1458
+
1459
+ it "should create a legacy url" do
1460
+ allow(page).to receive(:slug).and_return('foo')
1461
+ page.update_urlname!
1462
+ expect(page.legacy_urls).not_to be_empty
1463
+ expect(page.legacy_urls.pluck(:urlname)).to include('parentparent/parent/page')
1464
+ end
1465
+ end
1466
+
1467
+ context "after updating my visibility" do
1468
+ it "should update urlnames of descendants" do
1469
+ page
1470
+ parentparent.visible = false
1471
+ parentparent.save!
1472
+ page.reload
1473
+ expect(page.urlname).to eq('parent/page')
1474
+ end
1475
+ end
1476
+ end
1477
+
1478
+ context "with disabled url_nesting" do
1479
+ before { allow(Config).to receive(:get).and_return(false) }
1480
+
1481
+ it "should only store my urlname" do
1482
+ expect(page.urlname).to eq('page')
1483
+ end
1484
+ end
1485
+ end
1486
+
1487
+ describe "#update_node!" do
1488
+
1489
+ let(:original_url) { "sample-url" }
1490
+ let(:page) { FactoryGirl.create(:page, language: language, parent_id: language_root.id, urlname: original_url, restricted: false) }
1491
+ let(:node) { TreeNode.new(10, 11, 12, 13, "another-url", true) }
1492
+
1493
+ context "when nesting is enabled" do
1494
+ before { allow(Alchemy::Config).to receive(:get).with(:url_nesting) { true } }
1495
+
1496
+ context "when page is not external" do
1497
+
1498
+ before do
1499
+ expect(page).to receive(:redirects_to_external?).and_return(false)
1500
+ end
1501
+
1502
+ it "should update all attributes" do
1503
+ page.update_node!(node)
1504
+ page.reload
1505
+ expect(page.lft).to eq(node.left)
1506
+ expect(page.rgt).to eq(node.right)
1507
+ expect(page.parent_id).to eq(node.parent)
1508
+ expect(page.depth).to eq(node.depth)
1509
+ expect(page.urlname).to eq(node.url)
1510
+ expect(page.restricted).to eq(node.restricted)
1511
+ end
1512
+
1513
+ context "when url is the same" do
1514
+ let(:node) { TreeNode.new(10, 11, 12, 13, original_url, true) }
1515
+
1516
+ it "should not create a legacy url" do
1517
+ page.update_node!(node)
1518
+ page.reload
1519
+ expect(page.legacy_urls.size).to eq(0)
1520
+ end
1521
+ end
1522
+
1523
+ context "when url is not the same" do
1524
+ it "should create a legacy url" do
1525
+ page.update_node!(node)
1526
+ page.reload
1527
+ expect(page.legacy_urls.size).to eq(1)
1528
+ end
1529
+ end
1530
+ end
1531
+
1532
+ context "when page is external" do
1533
+ before do
1534
+ expect(page)
1535
+ .to receive(:redirects_to_external?)
1536
+ .and_return(true)
1537
+ end
1538
+
1539
+ it "should update all attributes except url" do
1540
+ page.update_node!(node)
1541
+ page.reload
1542
+ expect(page.lft).to eq(node.left)
1543
+ expect(page.rgt).to eq(node.right)
1544
+ expect(page.parent_id).to eq(node.parent)
1545
+ expect(page.depth).to eq(node.depth)
1546
+ expect(page.urlname).to eq(original_url)
1547
+ expect(page.restricted).to eq(node.restricted)
1548
+ end
1549
+
1550
+ it "should not create a legacy url" do
1551
+ page.update_node!(node)
1552
+ page.reload
1553
+ expect(page.legacy_urls.size).to eq(0)
1554
+ end
1555
+ end
1556
+ end
1557
+
1558
+ context "when nesting is disabled" do
1559
+ before do
1560
+ allow(Alchemy::Config).to receive(:get).with(:url_nesting) { false }
1561
+ end
1562
+
1563
+ context "when page is not external" do
1564
+ before do
1565
+ allow(page).to receive(:redirects_to_external?).and_return(false)
1566
+ end
1567
+
1568
+ it "should update all attributes except url" do
1569
+ page.update_node!(node)
1570
+ page.reload
1571
+ expect(page.lft).to eq(node.left)
1572
+ expect(page.rgt).to eq(node.right)
1573
+ expect(page.parent_id).to eq(node.parent)
1574
+ expect(page.depth).to eq(node.depth)
1575
+ expect(page.urlname).to eq(original_url)
1576
+ expect(page.restricted).to eq(node.restricted)
1577
+ end
1578
+
1579
+ it "should not create a legacy url" do
1580
+ page.update_node!(node)
1581
+ page.reload
1582
+ expect(page.legacy_urls.size).to eq(0)
1583
+ end
1584
+ end
1585
+
1586
+ context "when page is external" do
1587
+ before do
1588
+ expect(Alchemy::Config).to receive(:get).and_return(true)
1589
+ allow(page).to receive(:redirects_to_external?).and_return(true)
1590
+ end
1591
+
1592
+ it "should update all attributes except url" do
1593
+ page.update_node!(node)
1594
+ page.reload
1595
+ expect(page.lft).to eq(node.left)
1596
+ expect(page.rgt).to eq(node.right)
1597
+ expect(page.parent_id).to eq(node.parent)
1598
+ expect(page.depth).to eq(node.depth)
1599
+ expect(page.urlname).to eq(original_url)
1600
+ expect(page.restricted).to eq(node.restricted)
1601
+ end
1602
+
1603
+ it "should not create a legacy url" do
1604
+ page.update_node!(node)
1605
+ page.reload
1606
+ expect(page.legacy_urls.size).to eq(0)
1607
+ end
1608
+ end
1609
+ end
1610
+ end
1611
+
1612
+ describe '#slug' do
1613
+ context "with parents path saved in urlname" do
1614
+ let(:page) { FactoryGirl.build(:page, urlname: 'root/parent/my-name')}
1615
+
1616
+ it "should return the last part of the urlname" do
1617
+ expect(page.slug).to eq('my-name')
1618
+ end
1619
+ end
1620
+
1621
+ context "with single urlname" do
1622
+ let(:page) { FactoryGirl.build(:page, urlname: 'my-name')}
1623
+
1624
+ it "should return the last part of the urlname" do
1625
+ expect(page.slug).to eq('my-name')
1626
+ end
1627
+ end
1628
+
1629
+ context "with nil as urlname" do
1630
+ let(:page) { FactoryGirl.build(:page, urlname: nil)}
1631
+
1632
+ it "should return nil" do
1633
+ expect(page.slug).to be_nil
1634
+ end
1635
+ end
1636
+ end
1637
+
1638
+ describe '#external_urlname' do
1639
+ let(:external_page) { build(:page, page_layout: 'external') }
1640
+
1641
+ context 'with missing protocol' do
1642
+ before { external_page.urlname = 'google.com'}
1643
+
1644
+ it "returns an urlname prefixed with http://" do
1645
+ expect(external_page.external_urlname).to eq 'http://google.com'
1646
+ end
1647
+ end
1648
+
1649
+ context 'with protocol present' do
1650
+ before { external_page.urlname = 'ftp://google.com'}
1651
+
1652
+ it "returns the urlname" do
1653
+ expect(external_page.external_urlname).to eq 'ftp://google.com'
1654
+ end
1655
+ end
1656
+
1657
+ context 'beginngin with a slash' do
1658
+ before { external_page.urlname = '/internal-url'}
1659
+
1660
+ it "returns the urlname" do
1661
+ expect(external_page.external_urlname).to eq '/internal-url'
1662
+ end
1663
+ end
1664
+ end
1665
+
1666
+ context 'page status methods' do
1667
+ let(:page) { FactoryGirl.build(:page, public: true, visible: true, restricted: false, locked: false)}
1668
+
1669
+ describe '#status' do
1670
+ it "returns a combined status hash" do
1671
+ expect(page.status).to eq({public: true, visible: true, restricted: false, locked: false})
1672
+ end
1673
+ end
1674
+
1675
+ describe '#status_title' do
1676
+ it "returns a translated status string for public status" do
1677
+ expect(page.status_title(:public)).to eq('Page is published.')
1678
+ end
1679
+
1680
+ it "returns a translated status string for visible status" do
1681
+ expect(page.status_title(:visible)).to eq('Page is visible in navigation.')
1682
+ end
1683
+
1684
+ it "returns a translated status string for locked status" do
1685
+ expect(page.status_title(:locked)).to eq('')
1686
+ end
1687
+
1688
+ it "returns a translated status string for restricted status" do
1689
+ expect(page.status_title(:restricted)).to eq('Page is not restricted.')
1690
+ end
1691
+ end
1692
+ end
1693
+
1694
+ context 'indicate page editors' do
1695
+ let(:page) { Page.new }
1696
+ let(:user) { create(:alchemy_dummy_user, :as_editor) }
1697
+
1698
+ describe '#creator' do
1699
+ before { page.update(creator_id: user.id) }
1700
+
1701
+ it "returns the user that created the page" do
1702
+ expect(page.creator).to eq(user)
1703
+ end
1704
+
1705
+ context 'with user class having a different primary key' do
1706
+ before do
1707
+ allow(Alchemy.user_class)
1708
+ .to receive(:primary_key)
1709
+ .and_return('user_id')
1710
+
1711
+ allow(page)
1712
+ .to receive(:creator_id)
1713
+ .and_return(1)
1714
+ end
1715
+
1716
+ it "returns the user that created the page" do
1717
+ expect(Alchemy.user_class)
1718
+ .to receive(:find_by)
1719
+ .with({'user_id' => 1})
1720
+
1721
+ page.creator
1722
+ end
1723
+ end
1724
+ end
1725
+
1726
+ describe '#updater' do
1727
+ before { page.update(updater_id: user.id) }
1728
+
1729
+ it "returns the user that updated the page" do
1730
+ expect(page.updater).to eq(user)
1731
+ end
1732
+
1733
+ context 'with user class having a different primary key' do
1734
+ before do
1735
+ allow(Alchemy.user_class)
1736
+ .to receive(:primary_key)
1737
+ .and_return('user_id')
1738
+
1739
+ allow(page)
1740
+ .to receive(:updater_id)
1741
+ .and_return(1)
1742
+ end
1743
+
1744
+ it "returns the user that updated the page" do
1745
+ expect(Alchemy.user_class)
1746
+ .to receive(:find_by)
1747
+ .with({'user_id' => 1})
1748
+
1749
+ page.updater
1750
+ end
1751
+ end
1752
+ end
1753
+
1754
+ describe '#locker' do
1755
+ before { page.update(locked_by: user.id) }
1756
+
1757
+ it "returns the user that locked the page" do
1758
+ expect(page.locker).to eq(user)
1759
+ end
1760
+
1761
+ context 'with user class having a different primary key' do
1762
+ before do
1763
+ allow(Alchemy.user_class)
1764
+ .to receive(:primary_key)
1765
+ .and_return('user_id')
1766
+
1767
+ allow(page)
1768
+ .to receive(:locked_by)
1769
+ .and_return(1)
1770
+ end
1771
+
1772
+ it "returns the user that locked the page" do
1773
+ expect(Alchemy.user_class)
1774
+ .to receive(:find_by)
1775
+ .with({'user_id' => 1})
1776
+
1777
+ page.locker
1778
+ end
1779
+ end
1780
+ end
1781
+
1782
+ context 'with user that can not be found' do
1783
+ it 'does not raise not found error' do
1784
+ %w(creator updater locker).each do |user_type|
1785
+ expect {
1786
+ page.send(user_type)
1787
+ }.to_not raise_error
1788
+ end
1789
+ end
1790
+ end
1791
+
1792
+ context 'with user class having a name accessor' do
1793
+ let(:user) { double(name: 'Paul Page') }
1794
+
1795
+ describe '#creator_name' do
1796
+ before { allow(page).to receive(:creator).and_return(user) }
1797
+
1798
+ it "returns the name of the creator" do
1799
+ expect(page.creator_name).to eq('Paul Page')
1800
+ end
1801
+ end
1802
+
1803
+ describe '#updater_name' do
1804
+ before { allow(page).to receive(:updater).and_return(user) }
1805
+
1806
+ it "returns the name of the updater" do
1807
+ expect(page.updater_name).to eq('Paul Page')
1808
+ end
1809
+ end
1810
+
1811
+ describe '#locker_name' do
1812
+ before { allow(page).to receive(:locker).and_return(user) }
1813
+
1814
+ it "returns the name of the current page editor" do
1815
+ expect(page.locker_name).to eq('Paul Page')
1816
+ end
1817
+ end
1818
+ end
1819
+
1820
+ context 'with user class not having a name accessor' do
1821
+ let(:user) { Alchemy.user_class.new }
1822
+
1823
+ describe '#creator_name' do
1824
+ before { allow(page).to receive(:creator).and_return(user) }
1825
+
1826
+ it "returns unknown" do
1827
+ expect(page.creator_name).to eq('unknown')
1828
+ end
1829
+ end
1830
+
1831
+ describe '#updater_name' do
1832
+ before { allow(page).to receive(:updater).and_return(user) }
1833
+
1834
+ it "returns unknown" do
1835
+ expect(page.updater_name).to eq('unknown')
1836
+ end
1837
+ end
1838
+
1839
+ describe '#locker_name' do
1840
+ before { allow(page).to receive(:locker).and_return(user) }
1841
+
1842
+ it "returns unknown" do
1843
+ expect(page.locker_name).to eq('unknown')
1844
+ end
1845
+ end
1846
+ end
1847
+ end
1848
+
1849
+ describe '#controller_and_action' do
1850
+ let(:page) { Page.new }
1851
+
1852
+ context 'if the page has a custom controller defined in its description' do
1853
+ before do
1854
+ allow(page).to receive(:has_controller?).and_return(true)
1855
+ allow(page).to receive(:layout_description).and_return({'controller' => 'comments', 'action' => 'index'})
1856
+ end
1857
+ it "should return a Hash with controller and action key-value pairs" do
1858
+ expect(page.controller_and_action).to eq({controller: '/comments', action: 'index'})
1859
+ end
1860
+ end
1861
+ end
1862
+
1863
+ it_behaves_like "having a hint" do
1864
+ let(:subject) { Page.new }
1865
+ end
1866
+
1867
+ describe '#layout_partial_name' do
1868
+ let(:page) { Page.new(page_layout: 'Standard Page') }
1869
+
1870
+ it "returns a partial renderer compatible name" do
1871
+ expect(page.layout_partial_name).to eq('standard_page')
1872
+ end
1873
+ end
1874
+
1875
+ describe '#published_at' do
1876
+ context 'with published_at date set' do
1877
+ let(:published_at) { Time.now }
1878
+ let(:page) { build_stubbed(:page, published_at: published_at) }
1879
+
1880
+ it "returns the published_at value from database" do
1881
+ expect(page.published_at).to eq(published_at)
1882
+ end
1883
+ end
1884
+
1885
+ context 'with published_at is nil' do
1886
+ let(:updated_at) { Time.now }
1887
+ let(:page) { build_stubbed(:page, published_at: nil, updated_at: updated_at) }
1888
+
1889
+ it "returns the updated_at value" do
1890
+ expect(page.published_at).to eq(updated_at)
1891
+ end
1892
+ end
1893
+ end
1894
+
1895
+ end
1896
+ end