lc_alchemy_cms 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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