alchemy_cms 2.1.12 → 2.2.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. data/.travis.yml +1 -1
  2. data/Gemfile +20 -16
  3. data/README.md +11 -7
  4. data/alchemy_cms.gemspec +31 -29
  5. data/app/assets/javascripts/alchemy/alchemy.base.js +234 -233
  6. data/app/assets/javascripts/alchemy/alchemy.buttons.js +26 -26
  7. data/app/assets/javascripts/alchemy/alchemy.datepicker.js +32 -32
  8. data/app/assets/javascripts/alchemy/alchemy.dirty.js +78 -78
  9. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +103 -103
  10. data/app/assets/javascripts/alchemy/alchemy.element_editor_selector.js +84 -84
  11. data/app/assets/javascripts/alchemy/alchemy.elements_window.js +88 -88
  12. data/app/assets/javascripts/alchemy/alchemy.file_progress.js +92 -0
  13. data/app/assets/javascripts/alchemy/alchemy.growler.js +23 -23
  14. data/app/assets/javascripts/alchemy/alchemy.html5.js +23 -0
  15. data/app/assets/javascripts/alchemy/alchemy.image_cropper.js +53 -52
  16. data/app/assets/javascripts/alchemy/alchemy.jquery_loader.js +33 -30
  17. data/app/assets/javascripts/alchemy/alchemy.js +2 -0
  18. data/app/assets/javascripts/alchemy/alchemy.js_extensions.js +14 -14
  19. data/app/assets/javascripts/alchemy/alchemy.link_overlay.js.coffee +181 -0
  20. data/app/assets/javascripts/alchemy/alchemy.menubar.js +48 -48
  21. data/app/assets/javascripts/alchemy/alchemy.page_sorter.js +39 -39
  22. data/app/assets/javascripts/alchemy/alchemy.preview.js +81 -83
  23. data/app/assets/javascripts/alchemy/alchemy.preview_window.js +86 -86
  24. data/app/assets/javascripts/alchemy/alchemy.routes.js.erb +23 -23
  25. data/app/assets/javascripts/alchemy/alchemy.swf_upload.js +208 -353
  26. data/app/assets/javascripts/alchemy/alchemy.uploader.js +279 -0
  27. data/app/assets/javascripts/alchemy/alchemy.windows.js +268 -268
  28. data/app/assets/javascripts/tiny_mce/plugins/alchemy_link/editor_plugin.js +42 -42
  29. data/app/assets/stylesheets/alchemy/_defaults.scss +89 -89
  30. data/app/assets/stylesheets/alchemy/base.css.scss +790 -804
  31. data/app/assets/stylesheets/alchemy/buttons.css.scss +263 -264
  32. data/app/assets/stylesheets/alchemy/dashboard.css.scss +66 -66
  33. data/app/assets/stylesheets/alchemy/elements.css.scss +575 -564
  34. data/app/assets/stylesheets/alchemy/flash.css.scss +45 -45
  35. data/app/assets/stylesheets/alchemy/frame.css.scss +262 -262
  36. data/app/assets/stylesheets/alchemy/icons.css.scss +86 -86
  37. data/app/assets/stylesheets/alchemy/jquery-ui.alchemy.css.scss +706 -707
  38. data/app/assets/stylesheets/alchemy/jquery.Jcrop.css.scss +46 -27
  39. data/app/assets/stylesheets/alchemy/jquery.sb.css.scss +107 -107
  40. data/app/assets/stylesheets/alchemy/login.css.scss +40 -38
  41. data/app/assets/stylesheets/alchemy/menubar.css.scss +84 -82
  42. data/app/assets/stylesheets/alchemy/modules.css.scss +9 -9
  43. data/app/assets/stylesheets/alchemy/pagination.css.scss +49 -49
  44. data/app/assets/stylesheets/alchemy/print.css +7 -7
  45. data/app/assets/stylesheets/alchemy/sitemap.css.scss +185 -154
  46. data/app/assets/stylesheets/alchemy/standard_set.css +205 -206
  47. data/app/assets/stylesheets/alchemy/tables.css.scss +94 -60
  48. data/app/assets/stylesheets/alchemy/tinymce_content.css.scss +34 -34
  49. data/app/assets/stylesheets/alchemy/tinymce_dialog.css.scss +177 -177
  50. data/app/assets/stylesheets/alchemy/upload.css.scss +68 -45
  51. data/app/assets/stylesheets/tiny_mce/plugins/inlinepopups/skins/alchemy/window.css.scss +222 -221
  52. data/app/controllers/alchemy/admin/attachments_controller.rb +100 -100
  53. data/app/controllers/alchemy/admin/base_controller.rb +116 -116
  54. data/app/controllers/alchemy/admin/clipboard_controller.rb +44 -44
  55. data/app/controllers/alchemy/admin/contents_controller.rb +55 -55
  56. data/app/controllers/alchemy/admin/dashboard_controller.rb +12 -11
  57. data/app/controllers/alchemy/admin/elements_controller.rb +109 -109
  58. data/app/controllers/alchemy/admin/essence_audios_controller.rb +8 -8
  59. data/app/controllers/alchemy/admin/essence_files_controller.rb +20 -20
  60. data/app/controllers/alchemy/admin/essence_flashes_controller.rb +8 -8
  61. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +81 -81
  62. data/app/controllers/alchemy/admin/essence_videos_controller.rb +8 -8
  63. data/app/controllers/alchemy/admin/languages_controller.rb +16 -4
  64. data/app/controllers/alchemy/admin/layoutpages_controller.rb +9 -9
  65. data/app/controllers/alchemy/admin/pages_controller.rb +249 -248
  66. data/app/controllers/alchemy/admin/pictures_controller.rb +105 -105
  67. data/app/controllers/alchemy/admin/resources_controller.rb +111 -166
  68. data/app/controllers/alchemy/admin/trash_controller.rb +18 -18
  69. data/app/controllers/alchemy/admin/users_controller.rb +69 -69
  70. data/app/controllers/alchemy/attachments_controller.rb +27 -27
  71. data/app/controllers/alchemy/base_controller.rb +159 -155
  72. data/app/controllers/alchemy/elements_controller.rb +16 -16
  73. data/app/controllers/alchemy/messages_controller.rb +59 -59
  74. data/app/controllers/alchemy/pages_controller.rb +194 -198
  75. data/app/controllers/alchemy/pictures_controller.rb +57 -57
  76. data/app/controllers/alchemy/user_sessions_controller.rb +70 -70
  77. data/app/helpers/alchemy/admin/attachments_helper.rb +7 -7
  78. data/app/helpers/alchemy/admin/base_helper.rb +425 -425
  79. data/app/helpers/alchemy/admin/contents_helper.rb +72 -72
  80. data/app/helpers/alchemy/admin/elements_helper.rb +108 -107
  81. data/app/helpers/alchemy/admin/essences_helper.rb +108 -108
  82. data/app/helpers/alchemy/admin/pages_helper.rb +42 -42
  83. data/app/helpers/alchemy/admin/pictures_helper.rb +26 -26
  84. data/app/helpers/alchemy/base_helper.rb +90 -90
  85. data/app/helpers/alchemy/elements_helper.rb +167 -168
  86. data/app/helpers/alchemy/essences_helper.rb +133 -133
  87. data/app/helpers/alchemy/pages_helper.rb +515 -526
  88. data/app/helpers/alchemy/pictures_helper.rb +19 -0
  89. data/app/mailers/alchemy/messages.rb +13 -13
  90. data/app/mailers/alchemy/notifications.rb +19 -19
  91. data/app/models/alchemy.rb +3 -3
  92. data/app/models/alchemy/attachment.rb +52 -78
  93. data/app/models/alchemy/cell.rb +48 -48
  94. data/app/models/alchemy/content.rb +196 -196
  95. data/app/models/alchemy/element.rb +500 -490
  96. data/app/models/alchemy/essence_audio.rb +7 -7
  97. data/app/models/alchemy/essence_date.rb +10 -10
  98. data/app/models/alchemy/essence_file.rb +7 -7
  99. data/app/models/alchemy/essence_flash.rb +6 -6
  100. data/app/models/alchemy/essence_html.rb +9 -9
  101. data/app/models/alchemy/essence_picture.rb +17 -17
  102. data/app/models/alchemy/essence_richtext.rb +53 -53
  103. data/app/models/alchemy/essence_text.rb +21 -21
  104. data/app/models/alchemy/essence_video.rb +7 -7
  105. data/app/models/alchemy/folded_page.rb +3 -3
  106. data/app/models/alchemy/language.rb +91 -91
  107. data/app/models/alchemy/language/code.rb +14 -14
  108. data/app/models/alchemy/message.rb +48 -45
  109. data/app/models/alchemy/page.rb +555 -558
  110. data/app/models/alchemy/picture.rb +100 -99
  111. data/app/models/alchemy/user.rb +76 -74
  112. data/app/models/alchemy/user_session.rb +8 -8
  113. data/app/sweepers/alchemy/content_sweeper.rb +15 -15
  114. data/app/sweepers/alchemy/pages_sweeper.rb +50 -50
  115. data/app/sweepers/alchemy/pictures_sweeper.rb +14 -14
  116. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +23 -23
  117. data/app/views/alchemy/admin/attachments/_attachment.html.erb +56 -56
  118. data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +15 -15
  119. data/app/views/alchemy/admin/attachments/_files_list.html.erb +16 -16
  120. data/app/views/alchemy/admin/attachments/create.js.erb +2 -2
  121. data/app/views/alchemy/admin/attachments/destroy.js.erb +3 -3
  122. data/app/views/alchemy/admin/attachments/edit.html.erb +10 -10
  123. data/app/views/alchemy/admin/attachments/index.html.erb +14 -14
  124. data/app/views/alchemy/admin/attachments/new.html.erb +9 -9
  125. data/app/views/alchemy/admin/clipboard/index.html.erb +21 -21
  126. data/app/views/alchemy/admin/clipboard/insert.js.erb +5 -5
  127. data/app/views/alchemy/admin/contents/_missing.html.erb +1 -1
  128. data/app/views/alchemy/admin/contents/create.js.erb +33 -33
  129. data/app/views/alchemy/admin/contents/destroy.js.erb +5 -5
  130. data/app/views/alchemy/admin/contents/new.html.erb +13 -13
  131. data/app/views/alchemy/admin/contents/order.js.erb +2 -2
  132. data/app/views/alchemy/admin/dashboard/index.html.erb +96 -92
  133. data/app/views/alchemy/admin/elements/_add_content.html.erb +12 -12
  134. data/app/views/alchemy/admin/elements/_element.html.erb +3 -3
  135. data/app/views/alchemy/admin/elements/_new_element_form.html.erb +28 -28
  136. data/app/views/alchemy/admin/elements/_picture_editor.html.erb +21 -21
  137. data/app/views/alchemy/admin/elements/create.js.erb +32 -32
  138. data/app/views/alchemy/admin/elements/index.html.erb +2 -2
  139. data/app/views/alchemy/admin/elements/new.html.erb +31 -31
  140. data/app/views/alchemy/admin/elements/order.js.erb +3 -3
  141. data/app/views/alchemy/admin/elements/trash.js.erb +13 -13
  142. data/app/views/alchemy/admin/elements/update.js.erb +6 -6
  143. data/app/views/alchemy/admin/essence_files/assign.js.erb +6 -6
  144. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +32 -32
  145. data/app/views/alchemy/admin/essence_pictures/destroy.js.erb +1 -1
  146. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +1 -1
  147. data/app/views/alchemy/admin/essence_pictures/update.js.erb +11 -11
  148. data/app/views/alchemy/admin/languages/_form.html.erb +51 -45
  149. data/app/views/alchemy/admin/languages/_language.html.erb +45 -45
  150. data/app/views/alchemy/admin/languages/_table.html.erb +25 -25
  151. data/app/views/alchemy/admin/layoutpages/index.html.erb +35 -35
  152. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +42 -42
  153. data/app/views/alchemy/admin/pages/_external_link.html.erb +1 -1
  154. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +27 -27
  155. data/app/views/alchemy/admin/pages/_page.html.erb +112 -112
  156. data/app/views/alchemy/admin/pages/_sitemap.html.erb +1 -1
  157. data/app/views/alchemy/admin/pages/configure.html.erb +71 -69
  158. data/app/views/alchemy/admin/pages/configure_external.html.erb +38 -38
  159. data/app/views/alchemy/admin/pages/destroy.js.erb +10 -10
  160. data/app/views/alchemy/admin/pages/edit.html.erb +165 -165
  161. data/app/views/alchemy/admin/pages/fold.js.erb +2 -2
  162. data/app/views/alchemy/admin/pages/index.html.erb +66 -66
  163. data/app/views/alchemy/admin/pages/locked.html.erb +1 -1
  164. data/app/views/alchemy/admin/pages/new.html.erb +40 -40
  165. data/app/views/alchemy/admin/pages/sort.js.erb +4 -4
  166. data/app/views/alchemy/admin/pages/unlock.js.erb +6 -6
  167. data/app/views/alchemy/admin/pages/update.js.erb +19 -19
  168. data/app/views/alchemy/admin/partials/_flash.html.erb +2 -2
  169. data/app/views/alchemy/admin/partials/_flash_notices.html.erb +1 -1
  170. data/app/views/alchemy/admin/partials/_flash_upload.html.erb +38 -0
  171. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +15 -15
  172. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +8 -8
  173. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +35 -35
  174. data/app/views/alchemy/admin/partials/_search_form.html.erb +14 -14
  175. data/app/views/alchemy/admin/partials/_sub_navigation_tab.html.erb +6 -6
  176. data/app/views/alchemy/admin/partials/_upload_form.html.erb +56 -118
  177. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +11 -11
  178. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +71 -71
  179. data/app/views/alchemy/admin/pictures/_picture.html.erb +32 -32
  180. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +26 -26
  181. data/app/views/alchemy/admin/pictures/_pictures_list.html.erb +6 -6
  182. data/app/views/alchemy/admin/pictures/create.js.erb +7 -7
  183. data/app/views/alchemy/admin/pictures/index.html.erb +67 -67
  184. data/app/views/alchemy/admin/pictures/show_in_window.html.erb +5 -4
  185. data/app/views/alchemy/admin/resources/_form.html.erb +25 -19
  186. data/app/views/alchemy/admin/resources/_resource.html.erb +37 -36
  187. data/app/views/alchemy/admin/resources/_table.html.erb +17 -17
  188. data/app/views/alchemy/admin/resources/destroy.js.erb +1 -1
  189. data/app/views/alchemy/admin/resources/index.html.erb +14 -14
  190. data/app/views/alchemy/admin/trash/index.html.erb +20 -20
  191. data/app/views/alchemy/admin/users/_table.html.erb +52 -52
  192. data/app/views/alchemy/admin/users/_user.html.erb +35 -36
  193. data/app/views/alchemy/admin/users/index.html.erb +26 -27
  194. data/app/views/alchemy/base/error_notice.html.erb +2 -2
  195. data/app/views/alchemy/base/update.js.erb +1 -1
  196. data/app/views/alchemy/elements/_contactform_view.html.erb +76 -76
  197. data/app/views/alchemy/elements/_download_view.html.erb +1 -1
  198. data/app/views/alchemy/elements/_editor_not_found.html.erb +3 -3
  199. data/app/views/alchemy/elements/_searchresult_view.html.erb +2 -2
  200. data/app/views/alchemy/elements/_sitemap_editor.html.erb +1 -1
  201. data/app/views/alchemy/essences/_essence_audio_view.html.erb +13 -13
  202. data/app/views/alchemy/essences/_essence_date_editor.html.erb +10 -10
  203. data/app/views/alchemy/essences/_essence_file_editor.html.erb +62 -62
  204. data/app/views/alchemy/essences/_essence_file_view.html.erb +8 -8
  205. data/app/views/alchemy/essences/_essence_flash_view.html.erb +19 -19
  206. data/app/views/alchemy/essences/_essence_html_editor.html.erb +6 -6
  207. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +73 -73
  208. data/app/views/alchemy/essences/_essence_picture_view.html.erb +34 -34
  209. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +7 -7
  210. data/app/views/alchemy/essences/_essence_text_editor.html.erb +8 -8
  211. data/app/views/alchemy/navigation/_image_link.html.erb +12 -12
  212. data/app/views/alchemy/navigation/_link.html.erb +13 -13
  213. data/app/views/alchemy/navigation/_renderer.html.erb +17 -17
  214. data/app/views/alchemy/page_layouts/_layout_footer.html.erb +1 -1
  215. data/app/views/alchemy/page_layouts/_layout_header.html.erb +1 -1
  216. data/app/views/alchemy/pages/show.rss.builder +14 -14
  217. data/app/views/alchemy/search/_result.html.erb +7 -7
  218. data/app/views/alchemy/user_sessions/leave.html.erb +9 -9
  219. data/app/views/alchemy/user_sessions/login.html.erb +39 -39
  220. data/app/views/alchemy/user_sessions/logout.html.erb +1 -1
  221. data/app/views/layouts/alchemy/admin.html.erb +81 -81
  222. data/app/views/layouts/alchemy/login.html.erb +36 -36
  223. data/app/views/layouts/alchemy/pages.html.erb +48 -27
  224. data/bin/alchemy +136 -134
  225. data/config/alchemy/config.yml +13 -9
  226. data/config/locales/alchemy.de.yml +24 -30
  227. data/config/locales/alchemy.en.yml +11 -28
  228. data/config/routes.rb +146 -146
  229. data/db/migrate/20100607193638_create_essence_pictures.rb +3 -3
  230. data/db/migrate/20101109150312_alter_pages_visible_column_default.rb +1 -1
  231. data/db/migrate/20101109151812_create_languages.rb +3 -3
  232. data/db/migrate/20110115123343_remove_css_class_default_from_essence_pictures.rb +3 -3
  233. data/db/migrate/20110224105120_change_pages_visible_default.rb +3 -3
  234. data/db/migrate/20110228182659_remove_default_page_layout_from_pages.rb +2 -2
  235. data/db/migrate/20110414163140_remove_display_name_from_elements.rb +3 -3
  236. data/db/migrate/20110511100516_rename_essence_texts_title_to_link_title.rb +2 -2
  237. data/db/migrate/20110529130500_add_cell_id_to_elements.rb +3 -3
  238. data/db/migrate/20110530102804_change_pages_page_layout_column.rb +3 -3
  239. data/db/migrate/20110711142057_change_open_link_in_new_window_to_link_target.rb +1 -1
  240. data/db/migrate/20111116125112_namespace_alchemy_models.rb +21 -21
  241. data/lib/alchemy/auth_engine.rb +5 -5
  242. data/lib/alchemy/capistrano.rb +64 -64
  243. data/lib/alchemy/config.rb +47 -44
  244. data/lib/alchemy/engine.rb +41 -41
  245. data/lib/alchemy/essence.rb +50 -51
  246. data/lib/alchemy/i18n.rb +48 -48
  247. data/lib/alchemy/modules.rb +30 -30
  248. data/lib/alchemy/mount_point.rb +6 -6
  249. data/lib/alchemy/page_layout.rb +94 -93
  250. data/lib/alchemy/resource.rb +82 -0
  251. data/lib/alchemy/resources_helper.rb +56 -0
  252. data/lib/alchemy/scoped_pagination_url_helper.rb +8 -8
  253. data/lib/alchemy/seeder.rb +88 -88
  254. data/lib/alchemy/tinymce.rb +49 -49
  255. data/lib/alchemy/upgrader.rb +103 -103
  256. data/lib/alchemy/version.rb +1 -1
  257. data/lib/alchemy_cms.rb +26 -27
  258. data/lib/extensions/action_view.rb +1 -1
  259. data/lib/extensions/array.rb +3 -3
  260. data/lib/extensions/hash.rb +3 -3
  261. data/lib/middleware/flash_session_cookie.rb +1 -1
  262. data/lib/rails/generators/alchemy/deploy_script/deploy_script_generator.rb +24 -24
  263. data/lib/rails/generators/alchemy/elements/elements_generator.rb +7 -7
  264. data/lib/rails/generators/alchemy/elements/templates/view.html.erb +17 -17
  265. data/lib/rails/generators/alchemy/page_layouts/page_layouts_generator.rb +6 -6
  266. data/lib/rails/generators/alchemy/plugin/templates/authorization_rules.rb +8 -8
  267. data/lib/rails/generators/alchemy/scaffold/files/pages.html.erb +7 -7
  268. data/lib/rails/generators/alchemy/scaffold/scaffold_generator.rb +36 -36
  269. data/lib/rails/templates/alchemy.rb +1 -1
  270. data/lib/tasks/ferret.rake +2 -2
  271. data/lib/tasks/fleximage.rake +32 -32
  272. data/lib/tasks/install.rake +12 -12
  273. data/lib/tasks/upgrade.rake +4 -4
  274. data/spec/controllers/admin/clipboard_controller_spec.rb +43 -43
  275. data/spec/controllers/admin/contents_controller_spec.rb +22 -22
  276. data/spec/controllers/admin/elements_controller_spec.rb +13 -13
  277. data/spec/controllers/admin/languages_controller_spec.rb +31 -0
  278. data/spec/controllers/admin/pages_controller_spec.rb +10 -41
  279. data/spec/controllers/admin/resources_controller_spec.rb +19 -0
  280. data/spec/controllers/admin/trash_controller_spec.rb +11 -11
  281. data/spec/controllers/base_controller_spec.rb +46 -46
  282. data/spec/controllers/pages_controller_spec.rb +102 -92
  283. data/spec/dummy/app/controllers/admin/events_controller.rb +3 -0
  284. data/spec/dummy/app/controllers/errors_controller.rb +5 -0
  285. data/spec/dummy/app/models/event.rb +3 -0
  286. data/spec/dummy/config.ru +1 -1
  287. data/spec/dummy/config/application.rb +3 -0
  288. data/spec/dummy/config/authorization_rules.rb +7 -0
  289. data/spec/dummy/config/environments/development.rb +8 -1
  290. data/spec/dummy/config/environments/production.rb +1 -1
  291. data/spec/dummy/config/environments/test.rb +5 -2
  292. data/spec/dummy/config/routes.rb +9 -1
  293. data/spec/dummy/db/schema.rb +47 -35
  294. data/spec/dummy/public/404.html +19 -7
  295. data/spec/dummy/public/404.html.bak +26 -0
  296. data/spec/dummy/public/422.html +19 -7
  297. data/spec/dummy/public/500.html +19 -7
  298. data/spec/dummy/script/rails +2 -2
  299. data/spec/factories.rb +76 -76
  300. data/spec/helpers/admin/base_helper_spec.rb +21 -21
  301. data/spec/helpers/admin/contents_helper_spec.rb +14 -14
  302. data/spec/helpers/admin/elements_helper_spec.rb +40 -40
  303. data/spec/helpers/admin/essences_helper_spec.rb +7 -7
  304. data/spec/helpers/elements_helper_spec.rb +154 -154
  305. data/spec/helpers/essences_helper_spec.rb +10 -10
  306. data/spec/helpers/pages_helper_spec.rb +369 -372
  307. data/spec/helpers/pictures_helper_spec.rb +14 -0
  308. data/spec/helpers/url_helpers_spec.rb +29 -34
  309. data/spec/integration/admin/pages_controller_spec.rb +50 -50
  310. data/spec/integration/admin/resources_integration_spec.rb +100 -0
  311. data/spec/integration/navigation_spec.rb +1 -1
  312. data/spec/integration/pages_controller_spec.rb +205 -207
  313. data/spec/integration/security_spec.rb +47 -47
  314. data/spec/javascripts/alchemy/link_overlay_spec.js.coffee +42 -0
  315. data/spec/javascripts/alchemy_integration_spec.coffee +12 -0
  316. data/spec/javascripts/fixtures/select.html +3 -0
  317. data/spec/javascripts/helpers/.gitkeep +0 -0
  318. data/spec/javascripts/helpers/jasmine-jquery-1.3.1.js +289 -0
  319. data/spec/javascripts/helpers/mock-ajax.js +207 -0
  320. data/spec/javascripts/helpers/test_responses/test_responses.js +1 -0
  321. data/spec/javascripts/spec.css +3 -0
  322. data/spec/javascripts/spec.js +2 -0
  323. data/spec/libraries/resource_spec.rb +122 -0
  324. data/spec/libraries/resources_helper_spec.rb +105 -0
  325. data/spec/models/content_spec.rb +67 -67
  326. data/spec/models/element_spec.rb +133 -133
  327. data/spec/models/essence_picture_spec.rb +5 -5
  328. data/spec/models/essence_richtext_spec.rb +4 -4
  329. data/spec/models/language_spec.rb +89 -89
  330. data/spec/models/page_spec.rb +436 -506
  331. data/spec/models/picture_spec.rb +34 -34
  332. data/spec/models/user_spec.rb +2 -2
  333. data/spec/page_layout_spec.rb +24 -20
  334. data/spec/routing_spec.rb +253 -253
  335. data/spec/spec_helper.rb +51 -50
  336. data/spec/support/alchemy/controller_hacks.rb +25 -25
  337. data/spec/support/alchemy/specs_helpers.rb +25 -25
  338. data/spec/support/integration_spec_helper.rb +24 -0
  339. data/vendor/assets/javascripts/jquery_plugins/jquery.html5uploader.js +271 -0
  340. data/vendor/assets/javascripts/jquery_plugins/jquery.in-place-edit.js +86 -85
  341. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +380 -377
  342. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js +285 -279
  343. data/vendor/assets/javascripts/swfupload/SWFObject License.txt +3 -3
  344. data/vendor/assets/javascripts/swfupload/swfupload.cookies.js +47 -47
  345. data/vendor/assets/javascripts/swfupload/swfupload.js +612 -612
  346. data/vendor/assets/javascripts/swfupload/swfupload.queue.js +93 -93
  347. data/vendor/assets/javascripts/swfupload/swfupload.speed.js +337 -337
  348. data/vendor/assets/javascripts/swfupload/swfupload.swfobject.js +706 -102
  349. data/vendor/assets/javascripts/tiny_mce/license.txt +6 -6
  350. data/vendor/assets/javascripts/tiny_mce/plugins/fullscreen/fullscreen.htm +97 -97
  351. data/vendor/assets/javascripts/tiny_mce/plugins/inlinepopups/template.htm +386 -376
  352. data/vendor/assets/javascripts/tiny_mce/plugins/paste/js/pastetext.js +30 -30
  353. data/vendor/assets/javascripts/tiny_mce/plugins/paste/js/pasteword.js +45 -45
  354. data/vendor/assets/javascripts/tiny_mce/plugins/paste/pastetext.htm +20 -17
  355. data/vendor/assets/javascripts/tiny_mce/plugins/paste/pasteword.htm +12 -12
  356. data/vendor/assets/javascripts/tiny_mce/plugins/table/cell.htm +183 -173
  357. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/cell.css +4 -4
  358. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/row.css +7 -7
  359. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/table.css +3 -3
  360. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/cell.js +282 -280
  361. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/merge_cells.js +15 -15
  362. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/row.js +204 -204
  363. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/table.js +435 -435
  364. data/vendor/assets/javascripts/tiny_mce/plugins/table/merge_cells.htm +24 -22
  365. data/vendor/assets/javascripts/tiny_mce/plugins/table/row.htm +144 -136
  366. data/vendor/assets/javascripts/tiny_mce/plugins/table/table.htm +184 -168
  367. data/vendor/assets/javascripts/tiny_mce/themes/advanced/about.htm +62 -46
  368. data/vendor/assets/javascripts/tiny_mce/themes/advanced/anchor.htm +17 -16
  369. data/vendor/assets/javascripts/tiny_mce/themes/advanced/charmap.htm +56 -47
  370. data/vendor/assets/javascripts/tiny_mce/themes/advanced/color_picker.htm +67 -54
  371. data/vendor/assets/javascripts/tiny_mce/themes/advanced/editor_template.js +852 -1
  372. data/vendor/assets/javascripts/tiny_mce/themes/advanced/image.htm +79 -69
  373. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/about.js +48 -48
  374. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/anchor.js +28 -28
  375. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/charmap.js +317 -317
  376. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/color_picker.js +248 -246
  377. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/image.js +246 -246
  378. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/link.js +146 -146
  379. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/source_editor.js +31 -31
  380. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/de.js +1 -1
  381. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/de_dlg.js +1 -1
  382. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/en.js +1 -1
  383. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/en_dlg.js +1 -1
  384. data/vendor/assets/javascripts/tiny_mce/themes/advanced/link.htm +53 -46
  385. data/vendor/assets/javascripts/tiny_mce/themes/advanced/shortcuts.htm +57 -45
  386. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/content.css +182 -47
  387. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/dialog.css +399 -92
  388. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/ui.css +891 -187
  389. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/content.css +102 -24
  390. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/dialog.css +377 -78
  391. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/ui.css +452 -77
  392. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/content.css +167 -45
  393. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css +399 -92
  394. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui.css +890 -190
  395. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui_black.css +33 -7
  396. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui_silver.css +15 -4
  397. data/vendor/assets/javascripts/tiny_mce/themes/advanced/source_editor.htm +18 -15
  398. data/vendor/assets/javascripts/tiny_mce/utils/editable_selects.js +46 -46
  399. data/vendor/assets/javascripts/tiny_mce/utils/form_utils.js +124 -124
  400. data/vendor/assets/javascripts/tiny_mce/utils/mctabs.js +112 -112
  401. data/vendor/assets/javascripts/tiny_mce/utils/validate.js +213 -213
  402. metadata +102 -115
  403. data/app/assets/javascripts/alchemy/alchemy.link_overlay.js +0 -216
  404. data/lib/tasks/routes.rake +0 -29
  405. data/spec/integration/admin/resources_spec.rb +0 -19
@@ -1,19 +1,19 @@
1
1
  module Alchemy::Language::Code
2
- extend ActiveSupport::Concern
2
+ extend ActiveSupport::Concern
3
3
 
4
- def code
5
- [language_code, country_code].select(&:present?).join('-')
6
- end
4
+ def code
5
+ [language_code, country_code].select(&:present?).join('-')
6
+ end
7
7
 
8
- def code=(code)
9
- self.language_code = code
10
- end
8
+ def code=(code)
9
+ self.language_code = code
10
+ end
11
11
 
12
- module ClassMethods
13
- def find_by_code(code)
14
- codes = code.split('-')
15
- codes << nil if codes.length == 1
16
- find_by_language_code_and_country_code *codes
17
- end
18
- end
12
+ module ClassMethods
13
+ def find_by_code(code)
14
+ codes = code.split('-')
15
+ codes << nil if codes.length == 1
16
+ find_by_language_code_and_country_code *codes
17
+ end
18
+ end
19
19
  end
@@ -1,54 +1,57 @@
1
- # This is a tableless model only used for validating contactform fields.
2
- #
3
- # You can specify the fields for your contactform in the +config/alchemy/config.yml+ file in the +:mailer+ options.
1
+ # This is a tableless model only used for validating Contactform Fields.
2
+ # You can specify the fields for your contactform in the +config/alchemy/config.yml+ file in the +:mailer+ options
4
3
  #
5
4
  # === Example Contactform Configuration:
6
5
  #
7
6
  # :mailer:
8
7
  # :form_layout_name: contact
9
8
  # :fields: [subject, name, email, message, info]
10
- # :validate_fields: [name, email]
9
+ # :validate_fields:
10
+ # :name:
11
+ # :message: blank_name
12
+ # :email:
13
+ # :message: blank_email
11
14
 
12
15
  module Alchemy
13
- class Message
14
-
15
- @@config = Config.get(:mailer)
16
-
17
- extend ::ActiveModel::Naming
18
- include ::ActiveModel::Validations
19
- include ::ActiveModel::Conversion
20
- include ::ActiveModel::MassAssignmentSecurity
21
-
22
- attr_accessor :contact_form_id, :ip
23
- attr_accessible :contact_form_id
24
-
25
- @@config[:fields].each do |field|
26
- attr_accessor field.to_sym
27
- attr_accessible field.to_sym
28
- end
29
-
30
- @@config[:validate_fields].each do |field|
31
- validates_presence_of field
32
- if field.to_s == "email"
33
- validates_format_of field, :with => ::Authlogic::Regex.email, :if => :email_is_filled
34
- end
35
- end
36
-
37
- def initialize(attributes = {})
38
- attributes.keys.each do |a|
39
- send("#{a}=", attributes[a])
40
- end
41
- end
42
-
43
- def persisted? #:nodoc:
44
- false
45
- end
46
-
47
- private
48
-
49
- def email_is_filled #:nodoc:
50
- !email.blank?
51
- end
52
-
53
- end
16
+ class Message
17
+
18
+ @@config = Config.get(:mailer)
19
+
20
+ extend ::ActiveModel::Naming
21
+ include ::ActiveModel::Validations
22
+ include ::ActiveModel::Conversion
23
+ include ::ActiveModel::MassAssignmentSecurity
24
+
25
+ attr_accessor :contact_form_id, :ip
26
+ attr_accessible :contact_form_id
27
+
28
+ @@config[:fields].each do |field|
29
+ attr_accessor field.to_sym
30
+ attr_accessible field.to_sym
31
+ end
32
+
33
+ @@config[:validate_fields].each do |field|
34
+ validates_presence_of field[0], :message => '^' + I18n.t(field[1][:message].to_s, :scope => [:contactform, :validations])
35
+ if field[0].to_s.include?('email')
36
+ validates_format_of field[0], :with => ::Authlogic::Regex.email, :message => '^' + I18n.t(:wrong_email_format, :scope => [:contactform, :validations]), :if => :email_is_filled
37
+ end
38
+ end
39
+
40
+ def initialize(attributes = {})
41
+ attributes.keys.each do |a|
42
+ send("#{a}=", attributes[a])
43
+ end
44
+ end
45
+
46
+ def persisted? #:nodoc:
47
+ false
48
+ end
49
+
50
+ private
51
+
52
+ def email_is_filled #:nodoc:
53
+ !email.blank?
54
+ end
55
+
56
+ end
54
57
  end
@@ -1,561 +1,558 @@
1
1
  # encoding: UTF-8
2
2
  module Alchemy
3
- class Page < ActiveRecord::Base
4
-
5
- RESERVED_URLNAMES = %w(admin messages)
6
-
7
- acts_as_nested_set(:dependent => :destroy)
8
- stampable
9
-
10
- has_many :folded_pages
11
- has_many :cells, :dependent => :destroy
12
- has_many :elements, :dependent => :destroy, :order => :position
13
- has_and_belongs_to_many :to_be_sweeped_elements, :class_name => 'Alchemy::Element', :uniq => true, :join_table => 'alchemy_elements_alchemy_pages'
14
- belongs_to :language
15
-
16
- validates_presence_of :name
17
- validates_presence_of :page_layout, :unless => :systempage?
18
- validates_presence_of :parent_id, :if => proc { Page.count > 1 }
19
- validates_length_of :urlname, :minimum => 3, :if => :urlname_entered?
20
- validates_uniqueness_of :urlname, :scope => [:language_id, :layoutpage], :if => :urlname_entered?
21
- validates :urlname, :exclusion => { :in => RESERVED_URLNAMES }
22
-
23
- attr_accessor :do_not_autogenerate
24
- attr_accessor :do_not_sweep
25
- attr_accessor :do_not_validate_language
26
-
27
- before_validation :set_url_name, :if => :contentpage?
28
- before_save :set_title, :if => :contentpage?, :unless => proc { |page| !page.title.blank? }
29
- before_save :set_language_code, :unless => :systempage?
30
- before_save :set_restrictions_to_child_pages, :if => proc { |page| page.contentpage? && page.restricted_changed? }
31
- before_save :inherit_restricted_status, :if => proc { |page| page.contentpage? && page.parent && page.parent.restricted? }
32
- after_create :autogenerate_elements, :unless => proc { |page| page.systempage? || page.do_not_autogenerate }
33
- after_create :create_cells, :unless => :systempage?
34
-
35
- scope :language_roots, where(:language_root => true)
36
- scope :layoutpages, where(:layoutpage => true)
37
- scope :all_locked, where(:locked => true)
38
- scope :all_locked_by, lambda { |user| where(:locked => true, :locked_by => user.id) }
39
- scope :not_locked, where(:locked => false)
40
- scope :visible, where(:visible => true)
41
- scope :published, where(:public => true)
42
- scope :accessable, where(:restricted => false)
43
- scope :restricted, where(:restricted => true)
44
- scope :not_restricted, where(:restricted => false)
45
- scope :public_language_roots, lambda {
46
- where(:language_root => true).where("`alchemy_pages`.`language_code` IN ('#{Language.all_codes_for_published.join('\',\'')}')").where(:public => true)
47
- }
48
- scope :all_last_edited_from, lambda { |user| where(:updater_id => user.id).order('`alchemy_pages`.`updated_at` DESC').limit(5) }
49
- # Returns all pages that have the given language_id
50
- scope :with_language, lambda { |language_id| where(:language_id => language_id) }
51
- scope :contentpages, where(:layoutpage => [false, nil]).where("`alchemy_pages`.`parent_id` IS NOT NULL")
52
- # Returns all pages that are not locked and public.
53
- # Used for flushing all page caches at once.
54
- scope :flushables, not_locked.published.contentpages
55
- scope :searchables, not_restricted.published.contentpages
56
-
57
- # Finds selected elements from page.
58
- #
59
- # Options are:
60
- #
61
- # :only => Array of element names # Returns only elements with given names
62
- # :except => Array of element names # Returns all elements except the ones with given names
63
- # :count => Integer # Limit the count of returned elements
64
- # :offset => Integer # Starts with an offset while returning elements
65
- # :random => Boolean # Returning elements randomly shuffled
66
- # :from_cell => Cell or String # Returning elements from given cell
67
- #
68
- # Returns only public elements by default.
69
- # Pass true as second argument to get all elements.
70
- #
71
- def find_selected_elements(options = {}, show_non_public = false)
72
- if options[:from_cell].class.name == 'Alchemy::Cell'
73
- elements = options[:from_cell].elements
74
- elsif !options[:from_cell].blank? && options[:from_cell].class.name == 'String'
75
- cell = cells.find_by_name(options[:from_cell])
76
- elements = cell ? cell.elements : nil
77
- else
78
- elements = self.elements.not_in_cell
79
- end
80
- if !options[:only].blank?
81
- elements = elements.named(options[:only])
82
- elsif !options[:except].blank?
83
- elements = elements.excluded(options[:except])
84
- end
85
- elements = elements.offset(options[:offset]).limit(options[:count])
86
- elements = elements.order("RAND()") if options[:random]
87
- if show_non_public
88
- elements
89
- else
90
- elements.published
91
- end
92
- end
93
-
94
- def find_elements(options = {}, show_non_public = false) #:nodoc:
95
- # TODO: What is this? A Kind of proxy method? Why not rendering the elements directly if you already have them????
96
- if !options[:collection].blank? && options[:collection].is_a?(Array)
97
- return options[:collection]
98
- else
99
- find_selected_elements(options, show_non_public)
100
- end
101
- end
102
-
103
- # Returns all elements that should be feeded via rss.
104
- #
105
- # Define feedable elements in your +page_layouts.yml+:
106
- #
107
- # - name: news
108
- # feed: true
109
- # feed_elements: [element_name, element_2_name]
110
- #
111
- def feed_elements
112
- elements.find_all_by_name(definition['feed_elements'])
113
- end
114
-
115
- def elements_grouped_by_cells
116
- group = ::ActiveSupport::OrderedHash.new
117
- self.cells.each { |cell| group[cell] = cell.elements.not_trashed }
118
- if element_names_not_in_cell.any?
119
- group[Cell.new({:name => 'for_other_elements'})] = elements.not_trashed.not_in_cell
120
- end
121
- return group
122
- end
123
-
124
- def element_names_from_cells
125
- cell_definitions.collect { |c| c['elements'] }.flatten.uniq
126
- end
127
-
128
- def element_names_not_in_cell
129
- layout_description['elements'].uniq - element_names_from_cells
130
- end
131
-
132
- # Finds the previous page on the same structure level. Otherwise it returns nil.
133
- # Options:
134
- # => :restricted => boolean (standard: nil) - next restricted page (true), skip restricted pages (false), ignore restriction (nil)
135
- # => :public => boolean (standard: true) - next public page (true), skip public pages (false)
136
- def previous_page(options = {})
137
- default_options = {
138
- :restricted => nil,
139
- :public => true
140
- }
141
- options = default_options.merge(options)
142
- find_next_or_previous_page("previous", options)
143
- end
144
-
145
- # Finds the next page on the same structure level. Otherwise it returns nil.
146
- # Options:
147
- # => :restricted => boolean (standard: nil) - next restricted page (true), skip restricted pages (false), ignore restriction (nil)
148
- # => :public => boolean (standard: true) - next public page (true), skip public pages (false)
149
- def next_page(options = {})
150
- default_options = {
151
- :restricted => nil,
152
- :public => true
153
- }
154
- options = default_options.merge(options)
155
- find_next_or_previous_page("next", options)
156
- end
157
-
158
- def find_first_public(page)
159
- if(page.public == true)
160
- return page
161
- end
162
- page.children.each do |child|
163
- result = find_first_public(child)
164
- if(result!=nil)
165
- return result
166
- end
167
- end
168
- return nil
169
- end
170
-
171
- def name_entered?
172
- !self.name.blank?
173
- end
174
-
175
- def urlname_entered?
176
- !self.urlname.blank?
177
- end
178
-
179
- def set_url_name
180
- self.urlname = convert_url_name((self.urlname.blank? ? self.name : self.urlname))
181
- end
182
-
183
- def set_title
184
- self.title = self.name
185
- end
186
-
187
- def show_in_navigation?
188
- if visible?
189
- return true
190
- end
191
- return false
192
- end
193
-
194
- def lock(user)
195
- self.locked = true
196
- self.locked_by = user.id
197
- self.save(:validate => false)
198
- end
199
-
200
- def unlock
201
- self.locked = false
202
- self.locked_by = nil
203
- self.do_not_sweep = true
204
- self.save
205
- end
206
-
207
- def public_elements
208
- self.elements.select{ |m| m.public? }
209
- end
210
-
211
- # Returns the name of the creator of this page.
212
- def creator
213
- @page_creator ||= User.find_by_id(creator_id)
214
- return I18n.t('unknown') if @page_creator.nil?
215
- @page_creator.name
216
- end
217
-
218
- # Returns the name of the last updater of this page.
219
- def updater
220
- @page_updater = User.find_by_id(updater_id)
221
- return I18n.t('unknown') if @page_updater.nil?
222
- @page_updater.name
223
- end
224
-
225
- # Returns the name of the user currently editing this page.
226
- def current_editor
227
- @current_editor = User.find_by_id(locked_by)
228
- return I18n.t('unknown') if @current_editor.nil?
229
- @current_editor.name
230
- end
231
-
232
- def locker
233
- User.find_by_id(self.locked_by)
234
- end
235
-
236
- def fold(user_id, status)
237
- folded_page = FoldedPage.find_or_create_by_user_id_and_page_id(user_id, self.id)
238
- folded_page.update_attributes(:folded => status)
239
- folded_page.save
240
- end
241
-
242
- def folded?(user_id)
243
- folded_page = FoldedPage.find_by_user_id_and_page_id(user_id, self.id)
244
- return false if folded_page.nil?
245
- folded_page.folded
246
- end
247
-
248
- def elements_by_type type
249
- elements.select{|m| type.include? m.name}
250
- end
251
-
252
- # Returns the translated explanation of seven the page stati.
253
- # TODO: Let I18n do this!
254
- def humanized_status
255
- case self.status
256
- when 0
257
- return I18n.t('page_status_visible_public_locked')
258
- when 1
259
- return I18n.t('page_status_visible_unpublic_locked')
260
- when 2
261
- return I18n.t('page_status_invisible_public_locked')
262
- when 3
263
- return I18n.t('page_status_invisible_unpublic_locked')
264
- when 4
265
- return I18n.t('page_status_visible_public')
266
- when 5
267
- return I18n.t('page_status_visible_unpublic')
268
- when 6
269
- return I18n.t('page_status_invisible_public')
270
- when 7
271
- return I18n.t('page_status_invisible_unpublic')
272
- end
273
- end
274
-
275
- # Returns the status code. Used by humanized_status and the page status icon inside the sitemap rendered by Pages.index.
276
- def status
277
- if self.locked
278
- if self.public? && self.visible?
279
- return 0
280
- elsif !self.public? && self.visible?
281
- return 1
282
- elsif self.public? && !self.visible?
283
- return 2
284
- elsif !self.public? && !self.visible?
285
- return 3
286
- end
287
- else
288
- if self.public? && self.visible?
289
- return 4
290
- elsif !self.public? && self.visible?
291
- return 5
292
- elsif self.public? && !self.visible?
293
- return 6
294
- elsif !self.public? && !self.visible?
295
- return 7
296
- end
297
- end
298
- end
299
-
300
- def has_controller?
301
- !PageLayout.get(self.page_layout).nil? && !PageLayout.get(self.page_layout)["controller"].blank?
302
- end
303
-
304
- def controller_and_action
305
- if self.has_controller?
306
- {:controller => self.layout_description["controller"], :action => self.layout_description["action"]}
307
- end
308
- end
309
-
310
- # Returns the self#page_layout description from config/alchemy/page_layouts.yml file.
311
- def layout_description
312
- return {} if self.systempage?
313
- description = PageLayout.get(self.page_layout)
314
- if description.nil?
315
- raise "Description could not be found for page layout named #{self.page_layout}. Please check page_layouts.yml file."
316
- else
317
- description
318
- end
319
- end
320
- alias_method :definition, :layout_description
321
-
322
- def cell_definitions
323
- cell_names = self.layout_description['cells']
324
- return [] if cell_names.blank?
325
- Cell.all_definitions_for(cell_names)
326
- end
327
-
328
- # Returns translated name of the pages page_layout value.
329
- # Page layout names are defined inside the config/alchemy/page_layouts.yml file.
330
- # Translate the name in your config/locales language yml file.
331
- def layout_display_name
332
- I18n.t(self.page_layout, :scope => :page_layout_names)
333
- end
334
-
335
- def renamed?
336
- self.name_was != self.name || self.urlname_was != self.urlname
337
- end
338
-
339
- def changed_publicity?
340
- self.public_was != self.public
341
- end
342
-
343
- def set_restrictions_to_child_pages
344
- descendants.each do |child|
345
- child.update_attribute(:restricted, self.restricted?)
346
- end
347
- end
348
-
349
- def inherit_restricted_status
350
- self.restricted = parent.restricted?
351
- end
352
-
353
- def contains_feed?
354
- definition["feed"]
355
- end
356
-
357
- # Returns true or false if the pages layout_description for config/alchemy/page_layouts.yml contains redirects_to_external: true
358
- def redirects_to_external?
359
- definition["redirects_to_external"]
360
- end
361
-
362
- def first_public_child
363
- self.children.where(:public => true).limit(1).first
364
- end
365
-
366
- def self.language_root_for(language_id)
367
- self.language_roots.find_by_language_id(language_id)
368
- end
369
-
370
- # Creates a copy of source (a Page object) and does a copy of all elements depending to source.
371
- # You can pass any kind of Page#attributes as a difference to source.
372
- # Notice: It prevents the element auto_generator from running.
373
- def self.copy(source, differences = {})
374
- attributes = source.attributes.symbolize_keys.merge(differences)
375
- attributes.merge!(
376
- :do_not_autogenerate => true,
377
- :do_not_sweep => true,
378
- :visible => false,
379
- :public => false,
380
- :locked => false,
381
- :locked_by => nil
382
- )
383
- page = self.new(attributes.except(:id, :updated_at, :created_at, :created_id, :updater_id, :lft, :rgt, :depth))
384
- if page.save
385
- # copy the page´s cells
386
- source.cells.each do |cell|
387
- new_cell = Cell.create(:name => cell.name, :page_id => page.id)
388
- end
389
- # copy the page´s elements
390
- source.elements.each do |element|
391
- # detect cell for element
392
- # if cell is nil also pass nil to element.cell_id
393
- cell = nil
394
- cell = page.cells.detect{ |c| c.name == element.cell.name } if element.cell
395
- new_element = Element.copy(element, :page_id => page.id, :cell_id => (cell.blank? ? nil : cell.id))
396
- new_element.move_to_bottom
397
- end
398
- return page
399
- else
400
- raise page.errors.full_messages
401
- end
402
- end
403
-
404
- # Gets the language_root page for page
405
- def get_language_root
406
- return self if self.language_root
407
- page = self
408
- while page.parent do
409
- page = page.parent
410
- break if page.language_root?
411
- end
412
- return page
413
- end
414
-
415
- def self.layout_root_for(language_id)
416
- where({:parent_id => Page.root.id, :layoutpage => true, :language_id => language_id}).limit(1).first
417
- end
418
-
419
- def self.find_or_create_layout_root_for(language_id)
420
- layoutroot = layout_root_for(language_id)
421
- return layoutroot if layoutroot
422
- language = Language.find(language_id)
423
- layoutroot = Page.new({
424
- :name => "Layoutroot for #{language.name}",
425
- :layoutpage => true,
426
- :language => language,
427
- :do_not_autogenerate => true
428
- })
429
- if layoutroot.save(:validate => false)
430
- layoutroot.move_to_child_of(Page.root)
431
- return layoutroot
432
- else
433
- raise "Layout root for #{language.name} could not be created"
434
- end
435
- end
436
-
437
- def self.all_from_clipboard(clipboard)
438
- return [] if clipboard.blank?
439
- self.find_all_by_id(clipboard.collect { |i| i[:id] })
440
- end
441
-
442
- def self.all_from_clipboard_for_select(clipboard, language_id, layoutpage = false)
443
- return [] if clipboard.blank?
444
- clipboard_pages = self.all_from_clipboard(clipboard)
445
- allowed_page_layouts = Alchemy::PageLayout.selectable_layouts(language_id, layoutpage)
446
- allowed_page_layout_names = allowed_page_layouts.collect{ |p| p['name'] }
447
- clipboard_pages.select { |cp| allowed_page_layout_names.include?(cp.page_layout) }
448
- end
449
-
450
- def copy_children_to(new_parent)
451
- self.children.each do |child|
452
- next if child == new_parent
453
- new_child = Page.copy(child, {
454
- :language_id => new_parent.language_id,
455
- :language_code => new_parent.language_code,
456
- :name => child.name + ' (' + I18n.t('Copy') + ')',
457
- :urlname => child.redirects_to_external? ? child.urlname : '',
458
- :title => ''
459
- })
460
- new_child.move_to_child_of(new_parent)
461
- child.copy_children_to(new_child) unless child.children.blank?
462
- end
463
- end
464
-
465
- # Returns true or false if the page has a page_layout that has cells.
466
- def can_have_cells?
467
- !definition['cells'].blank?
468
- end
469
-
470
- def has_cells?
471
- cells.any?
472
- end
473
-
474
- def self.link_target_options
475
- options = [
476
- [I18n.t('default', :scope => :link_target_options), '']
477
- ]
478
- link_target_options = Config.get(:link_target_options)
479
- link_target_options.each do |option|
480
- options << [I18n.t(option, :scope => :link_target_options), option]
481
- end
482
- options
483
- end
484
-
485
- def locker_name
486
- return I18n.t('unknown') if self.locker.nil?
487
- self.locker.name
488
- end
489
-
490
- def rootpage?
491
- !self.new_record? && self.parent_id.blank?
492
- end
493
-
494
- def systempage?
495
- return true if Page.root.nil?
496
- rootpage? || (self.parent_id == Page.root.id && !self.language_root?)
497
- end
498
-
499
- def contentpage?
500
- !systempage? && !redirects_to_external? && !layoutpage?
501
- end
502
-
503
- def self.rootpage
504
- self.root
505
- end
506
-
507
- private
508
-
509
- def find_next_or_previous_page(direction = "next", options = {})
510
- if direction == "previous"
511
- step_direction = ["pages.lft < ?", self.lft]
512
- order_direction = "lft DESC"
513
- else
514
- step_direction = ["pages.lft > ?", self.lft]
515
- order_direction = "lft"
516
- end
517
- conditions = Page.merge_conditions(
518
- {:parent_id => self.parent_id},
519
- {:public => options[:public]},
520
- step_direction
521
- )
522
- if !options[:restricted].nil?
523
- conditions = Page.merge_conditions(conditions, {:restricted => options[:restricted]})
524
- end
525
- return Page.where(conditions).order(order_direction).limit(1)
526
- end
527
-
528
- # Converts the given nbame into an url friendly string
529
- # Names shorter than 3 will be filled with dashes, so it does not collidate with the language code.
530
- def convert_url_name(name)
531
- url_name = name.gsub(/[äÄ]/, 'ae').gsub(/[üÜ]/, 'ue').gsub(/[öÖ]/, 'oe').parameterize
532
- url_name = ('-' * (3 - url_name.length)) + url_name if url_name.length < 3
533
- return url_name
534
- end
535
-
536
- # Looks in the layout_descripion, if there are elements to autogenerate.
537
- # If so, it generates them.
538
- def autogenerate_elements
539
- elements = self.layout_description["autogenerate"]
540
- unless (elements.blank?)
541
- elements.each do |element|
542
- element = Element.create_from_scratch({'page_id' => self.id, 'name' => element})
543
- element.move_to_bottom if element
544
- end
545
- end
546
- end
547
-
548
- def set_language_code
549
- return false if self.language.blank?
550
- self.language_code = self.language.code
551
- end
552
-
553
- def create_cells
554
- return false if !can_have_cells?
555
- definition['cells'].each do |cellname|
556
- cells.create({:name => cellname})
557
- end
558
- end
559
-
560
- end
3
+ class Page < ActiveRecord::Base
4
+
5
+ RESERVED_URLNAMES = %w(admin messages)
6
+
7
+ acts_as_nested_set(:dependent => :destroy)
8
+ stampable
9
+
10
+ has_many :folded_pages
11
+ has_many :cells, :dependent => :destroy
12
+ has_many :elements, :dependent => :destroy, :order => :position
13
+ has_and_belongs_to_many :to_be_sweeped_elements, :class_name => 'Alchemy::Element', :uniq => true, :join_table => 'alchemy_elements_alchemy_pages'
14
+ belongs_to :language
15
+
16
+ validates_presence_of :name, :message => '^' + I18n.t("please enter a name")
17
+ validates_presence_of :page_layout, :message => '^' + I18n.t("Please choose a page layout."), :unless => :systempage?
18
+ validates_presence_of :parent_id, :message => '^' + I18n.t("No parent page was given."), :if => proc { Page.count > 1 }
19
+ validates_length_of :urlname, :minimum => 3, :too_short => I18n.t("urlname_to_short"), :if => :urlname_entered?
20
+ validates_uniqueness_of :urlname, :message => '^' + I18n.t("URL-Name already token"), :scope => [:language_id, :layoutpage], :if => :urlname_entered?
21
+ validates :urlname, :exclusion => {:in => RESERVED_URLNAMES, :message => '^' + I18n.t("This urlname is reserved.")}
22
+
23
+ attr_accessor :do_not_autogenerate
24
+ attr_accessor :do_not_sweep
25
+ attr_accessor :do_not_validate_language
26
+
27
+ before_save :set_url_name, :unless => proc { |page| page.systempage? || page.redirects_to_external? }
28
+ before_save :set_title, :unless => proc { |page| page.systempage? || page.redirects_to_external? || !page.title.blank? }
29
+ before_save :set_language_code, :unless => :systempage?
30
+ before_save :set_restrictions_to_child_pages, :if => proc { |page| !page.systempage? && page.restricted_changed? }
31
+ before_save :inherit_restricted_status, :if => proc { |page| !page.systempage? && page.parent && page.parent.restricted? }
32
+ after_create :autogenerate_elements, :unless => proc { |page| page.systempage? || page.do_not_autogenerate }
33
+ after_create :create_cells, :unless => :systempage?
34
+
35
+ scope :language_roots, where(:language_root => true)
36
+ scope :layoutpages, where(:layoutpage => true)
37
+ scope :all_locked, where(:locked => true)
38
+ scope :all_locked_by, lambda { |user| where(:locked => true, :locked_by => user.id) }
39
+ scope :not_locked, where(:locked => false)
40
+ scope :visible, where(:visible => true)
41
+ scope :published, where(:public => true)
42
+ scope :accessable, where(:restricted => false)
43
+ scope :restricted, where(:restricted => true)
44
+ scope :not_restricted, where(:restricted => false)
45
+ scope :public_language_roots, lambda {
46
+ where(:language_root => true).where("`alchemy_pages`.`language_code` IN ('#{Language.all_codes_for_published.join('\',\'')}')").where(:public => true)
47
+ }
48
+ scope :all_last_edited_from, lambda { |user| where(:updater_id => user.id).order('`alchemy_pages`.`updated_at` DESC').limit(5) }
49
+ # Returns all pages that have the given language_id
50
+ scope :with_language, lambda { |language_id| where(:language_id => language_id) }
51
+ # Returns all pages that are not locked and public.
52
+ # Used for flushing all page caches at once.
53
+ scope :contentpages, where("`alchemy_pages`.`layoutpage` = 0 AND `alchemy_pages`.`parent_id` IS NOT NULL")
54
+ scope :flushables, not_locked.published.contentpages
55
+ scope :searchables, not_restricted.published.contentpages
56
+
57
+ # Finds selected elements from page.
58
+ #
59
+ # Options are:
60
+ #
61
+ # :only => Array of element names # Returns only elements with given names
62
+ # :except => Array of element names # Returns all elements except the ones with given names
63
+ # :count => Integer # Limit the count of returned elements
64
+ # :offset => Integer # Starts with an offset while returning elements
65
+ # :random => Boolean # Returning elements randomly shuffled
66
+ # :from_cell => Cell or String # Returning elements from given cell
67
+ #
68
+ # Returns only public elements by default.
69
+ # Pass true as second argument to get all elements.
70
+ #
71
+ def find_selected_elements(options = {}, show_non_public = false)
72
+ if options[:from_cell].class.name == 'Alchemy::Cell'
73
+ elements = options[:from_cell].elements
74
+ elsif !options[:from_cell].blank? && options[:from_cell].class.name == 'String'
75
+ cell = cells.find_by_name(options[:from_cell])
76
+ elements = cell ? cell.elements : nil
77
+ else
78
+ elements = self.elements.not_in_cell
79
+ end
80
+ if !options[:only].blank?
81
+ elements = elements.named(options[:only])
82
+ elsif !options[:except].blank?
83
+ elements = elements.excluded(options[:except])
84
+ end
85
+ elements = elements.offset(options[:offset]).limit(options[:count])
86
+ elements = elements.order("RAND()") if options[:random]
87
+ if show_non_public
88
+ elements
89
+ else
90
+ elements.published
91
+ end
92
+ end
93
+
94
+ def find_elements(options = {}, show_non_public = false) #:nodoc:
95
+ # TODO: What is this? A Kind of proxy method? Why not rendering the elements directly if you already have them????
96
+ if !options[:collection].blank? && options[:collection].is_a?(Array)
97
+ return options[:collection]
98
+ else
99
+ find_selected_elements(options, show_non_public)
100
+ end
101
+ end
102
+
103
+ # Returns all elements that should be feeded via rss.
104
+ #
105
+ # Define feedable elements in your +page_layouts.yml+:
106
+ #
107
+ # - name: news
108
+ # feed: true
109
+ # feed_elements: [element_name, element_2_name]
110
+ #
111
+ def feed_elements
112
+ elements.find_all_by_name(definition['feed_elements'])
113
+ end
114
+
115
+ def elements_grouped_by_cells
116
+ group = ::ActiveSupport::OrderedHash.new
117
+ self.cells.each { |cell| group[cell] = cell.elements.not_trashed }
118
+ if element_names_not_in_cell.any?
119
+ group[Cell.new({:name => 'for_other_elements'})] = elements.not_trashed.not_in_cell
120
+ end
121
+ return group
122
+ end
123
+
124
+ def element_names_from_cells
125
+ cell_definitions.collect { |c| c['elements'] }.flatten.uniq
126
+ end
127
+
128
+ def element_names_not_in_cell
129
+ layout_description['elements'].uniq - element_names_from_cells
130
+ end
131
+
132
+ # Finds the previous page on the same structure level. Otherwise it returns nil.
133
+ # Options:
134
+ # => :restricted => boolean (standard: nil) - next restricted page (true), skip restricted pages (false), ignore restriction (nil)
135
+ # => :public => boolean (standard: true) - next public page (true), skip public pages (false)
136
+ def previous_page(options = {})
137
+ default_options = {
138
+ :restricted => nil,
139
+ :public => true
140
+ }
141
+ options = default_options.merge(options)
142
+ find_next_or_previous_page("previous", options)
143
+ end
144
+
145
+ # Finds the next page on the same structure level. Otherwise it returns nil.
146
+ # Options:
147
+ # => :restricted => boolean (standard: nil) - next restricted page (true), skip restricted pages (false), ignore restriction (nil)
148
+ # => :public => boolean (standard: true) - next public page (true), skip public pages (false)
149
+ def next_page(options = {})
150
+ default_options = {
151
+ :restricted => nil,
152
+ :public => true
153
+ }
154
+ options = default_options.merge(options)
155
+ find_next_or_previous_page("next", options)
156
+ end
157
+
158
+ def find_first_public(page)
159
+ if (page.public == true)
160
+ return page
161
+ end
162
+ page.children.each do |child|
163
+ result = find_first_public(child)
164
+ if (result!=nil)
165
+ return result
166
+ end
167
+ end
168
+ return nil
169
+ end
170
+
171
+ def name_entered?
172
+ !self.name.blank?
173
+ end
174
+
175
+ def urlname_entered?
176
+ !self.urlname.blank?
177
+ end
178
+
179
+ def set_url_name
180
+ self.urlname = convert_url_name((self.urlname.blank? ? self.name : self.urlname))
181
+ end
182
+
183
+ def set_title
184
+ self.title = self.name
185
+ end
186
+
187
+ def show_in_navigation?
188
+ if visible?
189
+ return true
190
+ end
191
+ return false
192
+ end
193
+
194
+ def lock(user)
195
+ self.locked = true
196
+ self.locked_by = user.id
197
+ self.save(:validate => false)
198
+ end
199
+
200
+ def unlock
201
+ self.locked = false
202
+ self.locked_by = nil
203
+ self.do_not_sweep = true
204
+ self.save
205
+ end
206
+
207
+ def public_elements
208
+ self.elements.select { |m| m.public? }
209
+ end
210
+
211
+ # Returns the name of the creator of this page.
212
+ def creator
213
+ @page_creator ||= User.find_by_id(creator_id)
214
+ return I18n.t('unknown') if @page_creator.nil?
215
+ @page_creator.name
216
+ end
217
+
218
+ # Returns the name of the last updater of this page.
219
+ def updater
220
+ @page_updater = User.find_by_id(updater_id)
221
+ return I18n.t('unknown') if @page_updater.nil?
222
+ @page_updater.name
223
+ end
224
+
225
+ # Returns the name of the user currently editing this page.
226
+ def current_editor
227
+ @current_editor = User.find_by_id(locked_by)
228
+ return I18n.t('unknown') if @current_editor.nil?
229
+ @current_editor.name
230
+ end
231
+
232
+ def locker
233
+ User.find_by_id(self.locked_by)
234
+ end
235
+
236
+ def fold(user_id, status)
237
+ folded_page = FoldedPage.find_or_create_by_user_id_and_page_id(user_id, self.id)
238
+ folded_page.update_attributes(:folded => status)
239
+ folded_page.save
240
+ end
241
+
242
+ def folded?(user_id)
243
+ folded_page = FoldedPage.find_by_user_id_and_page_id(user_id, self.id)
244
+ return false if folded_page.nil?
245
+ folded_page.folded
246
+ end
247
+
248
+ def elements_by_type type
249
+ elements.select { |m| type.include? m.name }
250
+ end
251
+
252
+ # Returns the translated explanation of seven the page stati.
253
+ # TODO: Let I18n do this!
254
+ def humanized_status
255
+ case self.status
256
+ when 0
257
+ return I18n.t('page_status_visible_public_locked')
258
+ when 1
259
+ return I18n.t('page_status_visible_unpublic_locked')
260
+ when 2
261
+ return I18n.t('page_status_invisible_public_locked')
262
+ when 3
263
+ return I18n.t('page_status_invisible_unpublic_locked')
264
+ when 4
265
+ return I18n.t('page_status_visible_public')
266
+ when 5
267
+ return I18n.t('page_status_visible_unpublic')
268
+ when 6
269
+ return I18n.t('page_status_invisible_public')
270
+ when 7
271
+ return I18n.t('page_status_invisible_unpublic')
272
+ end
273
+ end
274
+
275
+ # Returns the status code. Used by humanized_status and the page status icon inside the sitemap rendered by Pages.index.
276
+ def status
277
+ if self.locked
278
+ if self.public? && self.visible?
279
+ return 0
280
+ elsif !self.public? && self.visible?
281
+ return 1
282
+ elsif self.public? && !self.visible?
283
+ return 2
284
+ elsif !self.public? && !self.visible?
285
+ return 3
286
+ end
287
+ else
288
+ if self.public? && self.visible?
289
+ return 4
290
+ elsif !self.public? && self.visible?
291
+ return 5
292
+ elsif self.public? && !self.visible?
293
+ return 6
294
+ elsif !self.public? && !self.visible?
295
+ return 7
296
+ end
297
+ end
298
+ end
299
+
300
+ def has_controller?
301
+ !PageLayout.get(self.page_layout).nil? && !PageLayout.get(self.page_layout)["controller"].blank?
302
+ end
303
+
304
+ def controller_and_action
305
+ if self.has_controller?
306
+ {:controller => self.layout_description["controller"], :action => self.layout_description["action"]}
307
+ end
308
+ end
309
+
310
+ # Returns the self#page_layout description from config/alchemy/page_layouts.yml file.
311
+ def layout_description
312
+ return {} if self.systempage?
313
+ description = PageLayout.get(self.page_layout)
314
+ if description.nil?
315
+ raise "Description could not be found for page layout named #{self.page_layout}. Please check page_layouts.yml file."
316
+ else
317
+ description
318
+ end
319
+ end
320
+
321
+ alias_method :definition, :layout_description
322
+
323
+ def cell_definitions
324
+ cell_names = self.layout_description['cells']
325
+ return [] if cell_names.blank?
326
+ Cell.all_definitions_for(cell_names)
327
+ end
328
+
329
+ # Returns translated name of the pages page_layout value.
330
+ # Page layout names are defined inside the config/alchemy/page_layouts.yml file.
331
+ # Translate the name in your config/locales language yml file.
332
+ def layout_display_name
333
+ I18n.t(self.page_layout, :scope => :page_layout_names)
334
+ end
335
+
336
+ def renamed?
337
+ self.name_was != self.name || self.urlname_was != self.urlname
338
+ end
339
+
340
+ def changed_publicity?
341
+ self.public_was != self.public
342
+ end
343
+
344
+ def set_restrictions_to_child_pages
345
+ descendants.each do |child|
346
+ child.update_attribute(:restricted, self.restricted?)
347
+ end
348
+ end
349
+
350
+ def inherit_restricted_status
351
+ self.restricted = parent.restricted?
352
+ end
353
+
354
+ def contains_feed?
355
+ definition["feed"]
356
+ end
357
+
358
+ # Returns true or false if the pages layout_description for config/alchemy/page_layouts.yml contains redirects_to_external: true
359
+ def redirects_to_external?
360
+ definition["redirects_to_external"]
361
+ end
362
+
363
+ def first_public_child
364
+ self.children.where(:public => true).limit(1).first
365
+ end
366
+
367
+ def self.language_root_for(language_id)
368
+ self.language_roots.find_by_language_id(language_id)
369
+ end
370
+
371
+ # Creates a copy of source (a Page object) and does a copy of all elements depending to source.
372
+ # You can pass any kind of Page#attributes as a difference to source.
373
+ # Notice: It prevents the element auto_generator from running.
374
+ def self.copy(source, differences = {})
375
+ attributes = source.attributes.symbolize_keys.merge(differences)
376
+ attributes.merge!(
377
+ :do_not_autogenerate => true,
378
+ :do_not_sweep => true,
379
+ :visible => false,
380
+ :public => false,
381
+ :locked => false,
382
+ :locked_by => nil
383
+ )
384
+ page = self.new(attributes.except(:id, :updated_at, :created_at, :created_id, :updater_id, :lft, :rgt, :depth))
385
+ if page.save
386
+ # copy the page´s cells
387
+ source.cells.each do |cell|
388
+ new_cell = Cell.create(:name => cell.name, :page_id => page.id)
389
+ end
390
+ # copy the page´s elements
391
+ source.elements.each do |element|
392
+ # detect cell for element
393
+ # if cell is nil also pass nil to element.cell_id
394
+ cell = nil
395
+ cell = page.cells.detect { |c| c.name == element.cell.name } if element.cell
396
+ new_element = Element.copy(element, :page_id => page.id, :cell_id => (cell.blank? ? nil : cell.id))
397
+ new_element.move_to_bottom
398
+ end
399
+ return page
400
+ else
401
+ raise page.errors.full_messages
402
+ end
403
+ end
404
+
405
+ # Gets the language_root page for page
406
+ def get_language_root
407
+ return self if self.language_root
408
+ page = self
409
+ while page.parent do
410
+ page = page.parent
411
+ break if page.language_root?
412
+ end
413
+ return page
414
+ end
415
+
416
+ def self.layout_root_for(language_id)
417
+ where({:parent_id => Page.root.id, :layoutpage => true, :language_id => language_id}).limit(1).first
418
+ end
419
+
420
+ def self.find_or_create_layout_root_for(language_id)
421
+ layoutroot = layout_root_for(language_id)
422
+ return layoutroot if layoutroot
423
+ language = Language.find(language_id)
424
+ layoutroot = Page.new({
425
+ :name => "Layoutroot for #{language.name}",
426
+ :layoutpage => true,
427
+ :language => language,
428
+ :do_not_autogenerate => true
429
+ })
430
+ if layoutroot.save(:validate => false)
431
+ layoutroot.move_to_child_of(Page.root)
432
+ return layoutroot
433
+ else
434
+ raise "Layout root for #{language.name} could not be created"
435
+ end
436
+ end
437
+
438
+ def self.all_from_clipboard(clipboard)
439
+ return [] if clipboard.blank?
440
+ self.find_all_by_id(clipboard.collect { |i| i[:id] })
441
+ end
442
+
443
+ def self.all_from_clipboard_for_select(clipboard, language_id, layoutpage = false)
444
+ return [] if clipboard.blank?
445
+ clipboard_pages = self.all_from_clipboard(clipboard)
446
+ allowed_page_layouts = Alchemy::PageLayout.selectable_layouts(language_id, layoutpage)
447
+ allowed_page_layout_names = allowed_page_layouts.collect { |p| p['name'] }
448
+ clipboard_pages.select { |cp| allowed_page_layout_names.include?(cp.page_layout) }
449
+ end
450
+
451
+ def copy_children_to(new_parent)
452
+ self.children.each do |child|
453
+ next if child == new_parent
454
+ new_child = Page.copy(child, {
455
+ :language_id => new_parent.language_id,
456
+ :language_code => new_parent.language_code,
457
+ :name => child.name + ' (' + I18n.t('Copy') + ')',
458
+ :urlname => child.redirects_to_external? ? child.urlname : '',
459
+ :title => ''
460
+ })
461
+ new_child.move_to_child_of(new_parent)
462
+ child.copy_children_to(new_child) unless child.children.blank?
463
+ end
464
+ end
465
+
466
+ # Returns true or false if the page has a page_layout that has cells.
467
+ def can_have_cells?
468
+ !definition['cells'].blank?
469
+ end
470
+
471
+ def has_cells?
472
+ cells.any?
473
+ end
474
+
475
+ def self.link_target_options
476
+ options = [
477
+ [I18n.t('default', :scope => :link_target_options), '']
478
+ ]
479
+ link_target_options = Config.get(:link_target_options)
480
+ link_target_options.each do |option|
481
+ options << [I18n.t(option, :scope => :link_target_options), option]
482
+ end
483
+ options
484
+ end
485
+
486
+ def locker_name
487
+ return I18n.t('unknown') if self.locker.nil?
488
+ self.locker.name
489
+ end
490
+
491
+ def rootpage?
492
+ !self.new_record? && self.parent_id.blank?
493
+ end
494
+
495
+ def systempage?
496
+ return true if Page.root.nil?
497
+ rootpage? || (self.parent_id == Page.root.id && !self.language_root?)
498
+ end
499
+
500
+ def self.rootpage
501
+ self.root
502
+ end
503
+
504
+ private
505
+
506
+ def find_next_or_previous_page(direction = "next", options = {})
507
+ if direction == "previous"
508
+ step_direction = ["pages.lft < ?", self.lft]
509
+ order_direction = "lft DESC"
510
+ else
511
+ step_direction = ["pages.lft > ?", self.lft]
512
+ order_direction = "lft"
513
+ end
514
+ conditions = Page.merge_conditions(
515
+ {:parent_id => self.parent_id},
516
+ {:public => options[:public]},
517
+ step_direction
518
+ )
519
+ if !options[:restricted].nil?
520
+ conditions = Page.merge_conditions(conditions, {:restricted => options[:restricted]})
521
+ end
522
+ return Page.where(conditions).order(order_direction).limit(1)
523
+ end
524
+
525
+ # Converts the given nbame into an url friendly string
526
+ # Names shorter than 3 will be filled with dashes, so it does not collidate with the language code.
527
+ def convert_url_name(name)
528
+ url_name = name.gsub(/[äÄ]/, 'ae').gsub(/[üÜ]/, 'ue').gsub(/[öÖ]/, 'oe').parameterize
529
+ url_name = ('-' * (3 - url_name.length)) + url_name if url_name.length < 3
530
+ return url_name
531
+ end
532
+
533
+ # Looks in the layout_descripion, if there are elements to autogenerate.
534
+ # If so, it generates them.
535
+ def autogenerate_elements
536
+ elements = self.layout_description["autogenerate"]
537
+ unless (elements.blank?)
538
+ elements.each do |element|
539
+ element = Element.create_from_scratch({'page_id' => self.id, 'name' => element})
540
+ element.move_to_bottom if element
541
+ end
542
+ end
543
+ end
544
+
545
+ def set_language_code
546
+ return false if self.language.blank?
547
+ self.language_code = self.language.code
548
+ end
549
+
550
+ def create_cells
551
+ return false if !can_have_cells?
552
+ definition['cells'].each do |cellname|
553
+ cells.create({:name => cellname})
554
+ end
555
+ end
556
+
557
+ end
561
558
  end