alchemy_cms 4.6.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +33 -1
  3. data/.github/workflows/stale.yml +1 -1
  4. data/.gitignore +20 -2
  5. data/.hound.yml +2 -1
  6. data/.prettierrc +6 -0
  7. data/.rubocop.yml +30 -9
  8. data/CHANGELOG.md +102 -1
  9. data/Gemfile +24 -22
  10. data/README.md +32 -20
  11. data/Rakefile +11 -8
  12. data/alchemy_cms.gemspec +6 -5
  13. data/app/assets/javascripts/alchemy/admin.js +1 -5
  14. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +2 -1
  15. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +0 -2
  16. data/app/assets/javascripts/alchemy/alchemy.page_sorter.js +17 -17
  17. data/app/assets/javascripts/alchemy/node_select.js +39 -0
  18. data/app/assets/javascripts/alchemy/templates/index.js +1 -0
  19. data/app/assets/javascripts/alchemy/templates/node.hbs +16 -0
  20. data/app/assets/stylesheets/alchemy/admin.scss +3 -2
  21. data/app/assets/stylesheets/alchemy/base.scss +0 -1
  22. data/app/assets/stylesheets/alchemy/elements.scss +1 -1
  23. data/app/assets/stylesheets/alchemy/forms.scss +5 -0
  24. data/app/assets/stylesheets/alchemy/navigation.scss +1 -0
  25. data/app/assets/stylesheets/alchemy/node-select.scss +43 -0
  26. data/app/assets/stylesheets/alchemy/nodes.scss +1 -1
  27. data/app/assets/stylesheets/alchemy/sitemap.scss +5 -1
  28. data/app/assets/stylesheets/alchemy/tables.scss +1 -24
  29. data/app/assets/stylesheets/alchemy/tags.scss +2 -2
  30. data/app/controllers/alchemy/admin/attachments_controller.rb +8 -7
  31. data/app/controllers/alchemy/admin/base_controller.rb +13 -33
  32. data/app/controllers/alchemy/admin/clipboard_controller.rb +5 -4
  33. data/app/controllers/alchemy/admin/contents_controller.rb +1 -2
  34. data/app/controllers/alchemy/admin/dashboard_controller.rb +10 -9
  35. data/app/controllers/alchemy/admin/elements_controller.rb +20 -20
  36. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +12 -14
  37. data/app/controllers/alchemy/admin/languages_controller.rb +35 -2
  38. data/app/controllers/alchemy/admin/layoutpages_controller.rb +5 -2
  39. data/app/controllers/alchemy/admin/nodes_controller.rb +5 -4
  40. data/app/controllers/alchemy/admin/pages_controller.rb +51 -63
  41. data/app/controllers/alchemy/admin/pictures_controller.rb +16 -16
  42. data/app/controllers/alchemy/admin/resources_controller.rb +21 -13
  43. data/app/controllers/alchemy/admin/sites_controller.rb +18 -0
  44. data/app/controllers/alchemy/admin/styleguide_controller.rb +1 -0
  45. data/app/controllers/alchemy/admin/tags_controller.rb +5 -3
  46. data/app/controllers/alchemy/admin/trash_controller.rb +6 -6
  47. data/app/controllers/alchemy/api/base_controller.rb +2 -2
  48. data/app/controllers/alchemy/api/contents_controller.rb +4 -4
  49. data/app/controllers/alchemy/api/elements_controller.rb +8 -8
  50. data/app/controllers/alchemy/api/nodes_controller.rb +37 -1
  51. data/app/controllers/alchemy/api/pages_controller.rb +14 -23
  52. data/app/controllers/alchemy/attachments_controller.rb +5 -5
  53. data/app/controllers/alchemy/base_controller.rb +10 -9
  54. data/app/controllers/alchemy/messages_controller.rb +16 -23
  55. data/app/controllers/alchemy/pages_controller.rb +13 -11
  56. data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +3 -2
  57. data/app/controllers/concerns/alchemy/admin/current_language.rb +23 -0
  58. data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +5 -5
  59. data/app/controllers/concerns/alchemy/legacy_page_redirects.rb +4 -4
  60. data/app/controllers/concerns/alchemy/page_redirects.rb +2 -9
  61. data/app/controllers/concerns/alchemy/site_redirects.rb +2 -2
  62. data/app/decorators/alchemy/element_editor.rb +39 -0
  63. data/app/helpers/alchemy/admin/attachments_helper.rb +6 -6
  64. data/app/helpers/alchemy/admin/base_helper.rb +38 -35
  65. data/app/helpers/alchemy/admin/contents_helper.rb +3 -3
  66. data/app/helpers/alchemy/admin/elements_helper.rb +3 -88
  67. data/app/helpers/alchemy/admin/essences_helper.rb +8 -117
  68. data/app/helpers/alchemy/admin/form_helper.rb +1 -1
  69. data/app/helpers/alchemy/admin/navigation_helper.rb +24 -23
  70. data/app/helpers/alchemy/admin/pages_helper.rb +4 -4
  71. data/app/helpers/alchemy/admin/pictures_helper.rb +4 -6
  72. data/app/helpers/alchemy/admin/tags_helper.rb +8 -7
  73. data/app/helpers/alchemy/base_helper.rb +13 -8
  74. data/app/helpers/alchemy/elements_block_helper.rb +2 -31
  75. data/app/helpers/alchemy/elements_helper.rb +12 -58
  76. data/app/helpers/alchemy/pages_helper.rb +24 -174
  77. data/app/helpers/alchemy/url_helper.rb +4 -3
  78. data/app/mailers/alchemy/base_mailer.rb +1 -1
  79. data/app/mailers/alchemy/messages_mailer.rb +1 -1
  80. data/app/models/alchemy/attachment.rb +24 -19
  81. data/app/models/alchemy/base_record.rb +2 -5
  82. data/app/models/alchemy/content.rb +33 -38
  83. data/app/models/alchemy/content/factory.rb +24 -31
  84. data/app/models/alchemy/element.rb +45 -53
  85. data/app/models/alchemy/element/definitions.rb +4 -4
  86. data/app/models/alchemy/element/element_contents.rb +9 -6
  87. data/app/models/alchemy/element/element_essences.rb +4 -3
  88. data/app/models/alchemy/element/presenters.rb +3 -2
  89. data/app/models/alchemy/element_to_page.rb +1 -1
  90. data/app/models/alchemy/essence_boolean.rb +1 -3
  91. data/app/models/alchemy/essence_date.rb +2 -3
  92. data/app/models/alchemy/essence_file.rb +5 -5
  93. data/app/models/alchemy/essence_html.rb +1 -3
  94. data/app/models/alchemy/essence_link.rb +1 -3
  95. data/app/models/alchemy/essence_node.rb +18 -0
  96. data/app/models/alchemy/essence_page.rb +3 -16
  97. data/app/models/alchemy/essence_picture.rb +18 -17
  98. data/app/models/alchemy/essence_picture_view.rb +7 -6
  99. data/app/models/alchemy/essence_richtext.rb +1 -3
  100. data/app/models/alchemy/essence_select.rb +1 -3
  101. data/app/models/alchemy/essence_text.rb +0 -2
  102. data/app/models/alchemy/folded_page.rb +1 -0
  103. data/app/models/alchemy/language.rb +21 -35
  104. data/app/models/alchemy/language/code.rb +4 -4
  105. data/app/models/alchemy/legacy_page_url.rb +1 -1
  106. data/app/models/alchemy/message.rb +3 -3
  107. data/app/models/alchemy/node.rb +27 -4
  108. data/app/models/alchemy/page.rb +46 -127
  109. data/app/models/alchemy/page/fixed_attributes.rb +3 -2
  110. data/app/models/alchemy/page/page_elements.rb +35 -44
  111. data/app/models/alchemy/page/page_naming.rb +20 -70
  112. data/app/models/alchemy/page/page_natures.rb +7 -34
  113. data/app/models/alchemy/page/page_scopes.rb +23 -29
  114. data/app/models/alchemy/page/url_path.rb +0 -2
  115. data/app/models/alchemy/picture.rb +40 -30
  116. data/app/models/alchemy/picture/preprocessor.rb +26 -0
  117. data/app/models/alchemy/picture/transformations.rb +9 -7
  118. data/app/models/alchemy/picture/url.rb +9 -7
  119. data/app/models/alchemy/site.rb +6 -36
  120. data/app/models/alchemy/site/layout.rb +2 -2
  121. data/app/models/concerns/alchemy/touch_elements.rb +24 -0
  122. data/app/serializers/alchemy/content_serializer.rb +0 -3
  123. data/app/serializers/alchemy/essence_boolean_serializer.rb +3 -3
  124. data/app/serializers/alchemy/essence_date_serializer.rb +3 -3
  125. data/app/serializers/alchemy/essence_file_serializer.rb +4 -2
  126. data/app/serializers/alchemy/essence_html_serializer.rb +3 -3
  127. data/app/serializers/alchemy/essence_link_serializer.rb +3 -3
  128. data/app/serializers/alchemy/essence_picture_serializer.rb +5 -4
  129. data/app/serializers/alchemy/essence_richtext_serializer.rb +3 -3
  130. data/app/serializers/alchemy/essence_select_serializer.rb +3 -3
  131. data/app/serializers/alchemy/essence_text_serializer.rb +5 -4
  132. data/app/serializers/alchemy/node_serializer.rb +2 -0
  133. data/app/serializers/alchemy/page_tree_serializer.rb +9 -13
  134. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +1 -2
  135. data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +1 -2
  136. data/app/views/alchemy/admin/attachments/_files_list.html.erb +4 -4
  137. data/app/views/alchemy/admin/contents/create.js.erb +1 -3
  138. data/app/views/alchemy/admin/elements/_element.html.erb +9 -18
  139. data/app/views/alchemy/admin/elements/create.js.erb +1 -1
  140. data/app/views/alchemy/admin/elements/fold.js.erb +1 -1
  141. data/app/views/alchemy/admin/elements/index.html.erb +3 -3
  142. data/app/views/alchemy/admin/essence_files/assign.js.erb +1 -6
  143. data/app/views/alchemy/admin/essence_files/edit.html.erb +1 -1
  144. data/app/views/alchemy/admin/essence_pictures/assign.js.erb +1 -7
  145. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +1 -1
  146. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +7 -7
  147. data/app/views/alchemy/admin/essence_pictures/update.js.erb +1 -1
  148. data/app/views/alchemy/admin/languages/_form.html.erb +5 -5
  149. data/app/views/alchemy/admin/languages/_table.html.erb +3 -3
  150. data/app/views/alchemy/admin/languages/edit.html.erb +1 -0
  151. data/app/views/alchemy/admin/languages/index.html.erb +23 -4
  152. data/app/views/alchemy/admin/languages/new.html.erb +1 -0
  153. data/app/views/alchemy/admin/layoutpages/index.html.erb +3 -3
  154. data/app/views/alchemy/admin/nodes/_form.html.erb +18 -15
  155. data/app/views/alchemy/admin/nodes/_node.html.erb +1 -5
  156. data/app/views/alchemy/admin/nodes/index.html.erb +3 -4
  157. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +0 -8
  158. data/app/views/alchemy/admin/pages/_form.html.erb +0 -1
  159. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +1 -0
  160. data/app/views/alchemy/admin/pages/_page.html.erb +11 -19
  161. data/app/views/alchemy/admin/pages/_page_infos.html.erb +0 -4
  162. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  163. data/app/views/alchemy/admin/pages/info.html.erb +0 -9
  164. data/app/views/alchemy/admin/pages/unlock.js.erb +13 -6
  165. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +0 -2
  166. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -5
  167. data/app/views/alchemy/admin/pictures/_overlay_picture_list.html.erb +1 -1
  168. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +1 -2
  169. data/app/views/alchemy/admin/pictures/index.html.erb +18 -3
  170. data/app/views/alchemy/admin/pictures/show.html.erb +1 -1
  171. data/app/views/alchemy/admin/resources/_resource.html.erb +1 -1
  172. data/app/views/alchemy/admin/resources/_table.html.erb +2 -2
  173. data/app/views/alchemy/admin/resources/index.html.erb +21 -22
  174. data/app/views/alchemy/admin/sites/_form.html.erb +8 -0
  175. data/app/views/alchemy/admin/sites/edit.html.erb +1 -0
  176. data/app/views/alchemy/admin/sites/index.html.erb +21 -9
  177. data/app/views/alchemy/admin/sites/new.html.erb +1 -0
  178. data/app/views/alchemy/admin/tags/index.html.erb +2 -2
  179. data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +10 -12
  180. data/app/views/alchemy/essences/_essence_date_editor.html.erb +11 -8
  181. data/app/views/alchemy/essences/_essence_file_editor.html.erb +16 -17
  182. data/app/views/alchemy/essences/_essence_file_view.html.erb +1 -1
  183. data/app/views/alchemy/essences/_essence_html_editor.html.erb +8 -5
  184. data/app/views/alchemy/essences/_essence_link_editor.html.erb +18 -15
  185. data/app/views/alchemy/essences/_essence_node_editor.html.erb +27 -0
  186. data/app/views/alchemy/essences/_essence_node_view.html.erb +1 -0
  187. data/app/views/alchemy/essences/_essence_page_editor.html.erb +14 -11
  188. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +22 -20
  189. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +10 -7
  190. data/app/views/alchemy/essences/_essence_select_editor.html.erb +12 -16
  191. data/app/views/alchemy/essences/_essence_text_editor.html.erb +18 -17
  192. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +6 -11
  193. data/app/views/alchemy/pages/show.rss.builder +3 -2
  194. data/app/views/layouts/alchemy/admin.html.erb +1 -0
  195. data/babel.config.js +12 -0
  196. data/bin/rails +5 -4
  197. data/config/alchemy/config.yml +23 -16
  198. data/config/brakeman.ignore +1 -1
  199. data/config/initializers/assets.rb +2 -1
  200. data/config/initializers/dragonfly.rb +2 -1
  201. data/config/initializers/mime_types.rb +1 -0
  202. data/config/initializers/mini_profiler.rb +3 -2
  203. data/config/initializers/simple_form.rb +6 -6
  204. data/config/locales/alchemy.en.yml +23 -8
  205. data/config/routes.rb +25 -24
  206. data/config/spring.rb +3 -2
  207. data/db/migrate/20200226213334_alchemy_four_point_four.rb +313 -0
  208. data/db/migrate/20200423073425_create_alchemy_essence_nodes.rb +11 -0
  209. data/db/migrate/20200504210159_remove_site_id_from_nodes.rb +28 -0
  210. data/db/migrate/20200505215518_add_language_id_foreign_key_to_alchemy_pages.rb +8 -0
  211. data/db/migrate/20200511113603_add_menu_type_to_alchemy_nodes.rb +27 -0
  212. data/db/migrate/20200514091507_make_page_layoutpage_null_false.rb +6 -0
  213. data/db/migrate/20200519073500_remove_visible_from_alchemy_pages.rb +24 -0
  214. data/lib/alchemy/admin/locale.rb +3 -1
  215. data/lib/alchemy/admin/preview_url.rb +85 -0
  216. data/lib/alchemy/auth_accessors.rb +8 -7
  217. data/lib/alchemy/cache_digests/template_tracker.rb +5 -4
  218. data/lib/alchemy/config.rb +3 -5
  219. data/lib/alchemy/configuration_methods.rb +3 -1
  220. data/lib/alchemy/controller_actions.rb +6 -5
  221. data/lib/alchemy/deprecation.rb +2 -1
  222. data/lib/alchemy/elements_finder.rb +5 -5
  223. data/lib/alchemy/engine.rb +23 -8
  224. data/lib/alchemy/errors.rb +0 -7
  225. data/lib/alchemy/essence.rb +17 -16
  226. data/lib/alchemy/filetypes.rb +5 -5
  227. data/lib/alchemy/forms/builder.rb +4 -4
  228. data/lib/alchemy/hints.rb +1 -1
  229. data/lib/alchemy/i18n.rb +2 -1
  230. data/lib/alchemy/install/tasks.rb +41 -0
  231. data/lib/alchemy/modules.rb +12 -12
  232. data/lib/alchemy/name_conversions.rb +5 -5
  233. data/lib/alchemy/on_page_layout/callbacks_runner.rb +1 -0
  234. data/lib/alchemy/page_layout.rb +15 -12
  235. data/lib/alchemy/paths.rb +1 -1
  236. data/lib/alchemy/permissions.rb +7 -6
  237. data/lib/alchemy/resource.rb +25 -15
  238. data/lib/alchemy/resources_helper.rb +12 -18
  239. data/lib/alchemy/routing_constraints.rb +1 -1
  240. data/lib/alchemy/seeder.rb +42 -14
  241. data/lib/alchemy/shell.rb +13 -10
  242. data/lib/alchemy/taggable.rb +1 -0
  243. data/lib/alchemy/tasks/tidy.rb +4 -3
  244. data/lib/alchemy/test_support/config_stubbing.rb +1 -0
  245. data/lib/alchemy/test_support/essence_shared_examples.rb +72 -72
  246. data/lib/alchemy/test_support/factories.rb +1 -1
  247. data/lib/alchemy/test_support/factories/attachment_factory.rb +5 -5
  248. data/lib/alchemy/test_support/factories/content_factory.rb +6 -6
  249. data/lib/alchemy/test_support/factories/dummy_user_factory.rb +7 -7
  250. data/lib/alchemy/test_support/factories/element_factory.rb +9 -9
  251. data/lib/alchemy/test_support/factories/essence_file_factory.rb +3 -3
  252. data/lib/alchemy/test_support/factories/essence_page_factory.rb +3 -3
  253. data/lib/alchemy/test_support/factories/essence_picture_factory.rb +4 -4
  254. data/lib/alchemy/test_support/factories/essence_text_factory.rb +3 -3
  255. data/lib/alchemy/test_support/factories/language_factory.rb +21 -14
  256. data/lib/alchemy/test_support/factories/node_factory.rb +8 -8
  257. data/lib/alchemy/test_support/factories/page_factory.rb +15 -27
  258. data/lib/alchemy/test_support/factories/picture_factory.rb +5 -5
  259. data/lib/alchemy/test_support/factories/site_factory.rb +7 -6
  260. data/lib/alchemy/test_support/integration_helpers.rb +1 -0
  261. data/lib/alchemy/test_support/shared_contexts.rb +5 -4
  262. data/lib/alchemy/test_support/shared_uploader_examples.rb +4 -3
  263. data/lib/alchemy/tinymce.rb +15 -13
  264. data/lib/alchemy/upgrader.rb +8 -7
  265. data/lib/alchemy/upgrader/five_point_zero.rb +41 -0
  266. data/lib/alchemy/upgrader/tasks/element_views_updater.rb +4 -4
  267. data/lib/alchemy/upgrader/tasks/harden_gutentag_migrations.rb +29 -0
  268. data/lib/alchemy/version.rb +1 -1
  269. data/lib/alchemy_cms.rb +52 -50
  270. data/lib/{rails/generators → generators}/alchemy/base.rb +5 -4
  271. data/lib/{rails/generators → generators}/alchemy/elements/elements_generator.rb +13 -9
  272. data/lib/{rails/generators → generators}/alchemy/elements/templates/view.html.erb +0 -0
  273. data/lib/{rails/generators → generators}/alchemy/elements/templates/view.html.haml +0 -0
  274. data/lib/{rails/generators → generators}/alchemy/elements/templates/view.html.slim +0 -0
  275. data/lib/{rails/generators → generators}/alchemy/essence/essence_generator.rb +15 -13
  276. data/lib/generators/alchemy/essence/templates/editor.html.erb +17 -0
  277. data/lib/{rails/generators → generators}/alchemy/essence/templates/view.html.erb +0 -0
  278. data/lib/{rails/generators → generators}/alchemy/install/files/_article.html.erb +0 -0
  279. data/lib/{rails/generators → generators}/alchemy/install/files/_standard.html.erb +0 -0
  280. data/lib/{rails/generators → generators}/alchemy/install/files/alchemy.en.yml +0 -0
  281. data/lib/generators/alchemy/install/files/alchemy_admin.js +1 -0
  282. data/lib/{rails/generators → generators}/alchemy/install/files/all.css +0 -0
  283. data/lib/{rails/generators → generators}/alchemy/install/files/all.js +0 -0
  284. data/lib/{rails/generators → generators}/alchemy/install/files/application.html.erb +0 -0
  285. data/lib/{rails/generators → generators}/alchemy/install/files/article.scss +0 -0
  286. data/lib/generators/alchemy/install/install_generator.rb +169 -0
  287. data/lib/{rails/generators → generators}/alchemy/install/templates/dragonfly.rb.tt +0 -0
  288. data/lib/{rails/generators → generators}/alchemy/install/templates/elements.yml.tt +0 -0
  289. data/lib/{rails/generators → generators}/alchemy/install/templates/menus.yml.tt +0 -0
  290. data/lib/{rails/generators → generators}/alchemy/install/templates/page_layouts.yml.tt +0 -0
  291. data/lib/{rails/generators → generators}/alchemy/menus/menus_generator.rb +2 -2
  292. data/lib/{rails/generators → generators}/alchemy/menus/templates/node.html.erb +1 -4
  293. data/lib/{rails/generators → generators}/alchemy/menus/templates/node.html.haml +1 -4
  294. data/lib/{rails/generators → generators}/alchemy/menus/templates/node.html.slim +1 -4
  295. data/lib/{rails/generators → generators}/alchemy/menus/templates/wrapper.html.erb +1 -1
  296. data/lib/{rails/generators → generators}/alchemy/menus/templates/wrapper.html.haml +1 -1
  297. data/lib/{rails/generators → generators}/alchemy/menus/templates/wrapper.html.slim +1 -1
  298. data/lib/{rails/generators → generators}/alchemy/module/module_generator.rb +3 -2
  299. data/lib/{rails/generators → generators}/alchemy/module/templates/ability.rb.tt +0 -0
  300. data/lib/{rails/generators → generators}/alchemy/module/templates/controller.rb.tt +0 -0
  301. data/lib/{rails/generators → generators}/alchemy/module/templates/module_config.rb.tt +0 -0
  302. data/lib/{rails/generators → generators}/alchemy/page_layouts/page_layouts_generator.rb +5 -4
  303. data/lib/{rails/generators → generators}/alchemy/page_layouts/templates/layout.html.erb +0 -0
  304. data/lib/{rails/generators → generators}/alchemy/page_layouts/templates/layout.html.haml +0 -0
  305. data/lib/{rails/generators → generators}/alchemy/page_layouts/templates/layout.html.slim +0 -0
  306. data/lib/{rails/generators → generators}/alchemy/site_layouts/site_layouts_generator.rb +4 -2
  307. data/lib/{rails/generators → generators}/alchemy/site_layouts/templates/layout.html.erb +0 -0
  308. data/lib/{rails/generators → generators}/alchemy/site_layouts/templates/layout.html.haml +0 -0
  309. data/lib/{rails/generators → generators}/alchemy/site_layouts/templates/layout.html.slim +0 -0
  310. data/lib/{rails/generators → generators}/alchemy/views/views_generator.rb +7 -6
  311. data/lib/kaminari/scoped_pagination_url_helper.rb +1 -0
  312. data/lib/tasks/alchemy/db.rake +3 -19
  313. data/lib/tasks/alchemy/install.rake +5 -48
  314. data/lib/tasks/alchemy/tidy.rake +9 -8
  315. data/lib/tasks/alchemy/upgrade.rake +18 -116
  316. data/package.json +45 -0
  317. data/package/admin.js +14 -0
  318. data/package/src/__tests__/i18n.spec.js +70 -0
  319. data/package/src/i18n.js +48 -0
  320. data/package/src/node_tree.js +72 -0
  321. data/package/src/translations.js +32 -0
  322. data/package/src/utils/__tests__/ajax.spec.js +124 -0
  323. data/package/src/utils/__tests__/events.spec.js +38 -0
  324. data/package/src/utils/ajax.js +48 -0
  325. data/package/src/utils/events.js +16 -0
  326. data/vendor/assets/fonts/fa-regular-400.eot +0 -0
  327. data/vendor/assets/fonts/fa-regular-400.svg +798 -358
  328. data/vendor/assets/fonts/fa-regular-400.ttf +0 -0
  329. data/vendor/assets/fonts/fa-regular-400.woff +0 -0
  330. data/vendor/assets/fonts/fa-regular-400.woff2 +0 -0
  331. data/vendor/assets/fonts/fa-solid-900.eot +0 -0
  332. data/vendor/assets/fonts/fa-solid-900.svg +4933 -1408
  333. data/vendor/assets/fonts/fa-solid-900.ttf +0 -0
  334. data/vendor/assets/fonts/fa-solid-900.woff +0 -0
  335. data/vendor/assets/fonts/fa-solid-900.woff2 +0 -0
  336. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +1 -2
  337. data/vendor/assets/stylesheets/fontawesome/_core.scss +5 -0
  338. data/vendor/assets/stylesheets/fontawesome/_fixed-width.scss +1 -1
  339. data/vendor/assets/stylesheets/fontawesome/_icons.scss +651 -2
  340. data/vendor/assets/stylesheets/fontawesome/_mixins.scss +0 -1
  341. data/vendor/assets/stylesheets/fontawesome/_rotated-flipped.scss +3 -2
  342. data/vendor/assets/stylesheets/fontawesome/_stacked.scss +1 -1
  343. data/vendor/assets/stylesheets/fontawesome/_variables.scss +662 -9
  344. data/vendor/assets/stylesheets/fontawesome/fontawesome.scss +2 -2
  345. data/vendor/assets/stylesheets/fontawesome/regular.scss +23 -0
  346. data/vendor/assets/stylesheets/fontawesome/solid.scss +24 -0
  347. metadata +112 -88
  348. data/app/assets/javascripts/alchemy/alchemy.i18n.js.coffee +0 -32
  349. data/app/assets/javascripts/alchemy/alchemy.node_tree.js +0 -66
  350. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +0 -29
  351. data/app/assets/javascripts/alchemy/alchemy.utils.js +0 -45
  352. data/app/helpers/alchemy/essences_helper.rb +0 -119
  353. data/app/models/concerns/alchemy/content_touching.rb +0 -23
  354. data/app/serializers/alchemy/legacy_element_serializer.rb +0 -15
  355. data/app/views/alchemy/admin/contents/_missing.html.erb +0 -17
  356. data/app/views/alchemy/admin/pages/_menu_fields.html.erb +0 -37
  357. data/app/views/alchemy/admin/pages/configure_external.html.erb +0 -32
  358. data/app/views/alchemy/elements/_editor_not_found.html.erb +0 -4
  359. data/app/views/alchemy/navigation/_image_link.html.erb +0 -14
  360. data/app/views/alchemy/navigation/_link.html.erb +0 -19
  361. data/app/views/alchemy/navigation/_renderer.html.erb +0 -29
  362. data/db/migrate/20180226123013_alchemy_four_point_zero.rb +0 -363
  363. data/db/migrate/20180227224537_migrate_tags_to_gutentag.rb +0 -41
  364. data/db/migrate/20180519204655_add_fixed_to_alchemy_elements.rb +0 -6
  365. data/db/migrate/20191016073858_create_alchemy_essence_pages.rb +0 -8
  366. data/db/migrate/20191029212236_create_alchemy_nodes.rb +0 -24
  367. data/db/migrate/20200226081535_add_site_id_to_alchemy_nodes.rb +0 -15
  368. data/lib/alchemy/ssl_protection.rb +0 -34
  369. data/lib/alchemy/tasks/helpers.rb +0 -81
  370. data/lib/alchemy/test_support/controller_requests.rb +0 -93
  371. data/lib/alchemy/upgrader/four_point_four.rb +0 -52
  372. data/lib/alchemy/upgrader/four_point_one.rb +0 -42
  373. data/lib/alchemy/upgrader/four_point_six.rb +0 -50
  374. data/lib/alchemy/upgrader/four_point_two.rb +0 -85
  375. data/lib/alchemy/upgrader/tasks/cells_migration.rb +0 -43
  376. data/lib/alchemy/upgrader/tasks/cells_upgrader.rb +0 -148
  377. data/lib/alchemy/upgrader/tasks/element_partial_name_variable_updater.rb +0 -28
  378. data/lib/alchemy/upgrader/tasks/harden_acts_as_taggable_on_migrations.rb +0 -27
  379. data/lib/alchemy/upgrader/tasks/picture_gallery_migration.rb +0 -65
  380. data/lib/alchemy/upgrader/tasks/picture_gallery_upgrader.rb +0 -210
  381. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +0 -15
  382. data/lib/rails/generators/alchemy/install/install_generator.rb +0 -60
  383. data/lib/tasks/alchemy/convert.rake +0 -97
  384. data/vendor/assets/javascripts/sortable/Sortable.min.js +0 -2
  385. data/vendor/assets/stylesheets/fontawesome/fa-regular.scss +0 -22
  386. data/vendor/assets/stylesheets/fontawesome/fa-solid.scss +0 -23
@@ -18,19 +18,20 @@ module Alchemy
18
18
 
19
19
  # Returns the correct params-hash for passing to show_page_path
20
20
  def show_page_path_params(page, optional_params = {})
21
- raise ArgumentError, 'Page is nil' if page.nil?
21
+ raise ArgumentError, "Page is nil" if page.nil?
22
+
22
23
  url_params = {urlname: page.urlname}.update(optional_params)
23
24
  prefix_locale? ? url_params.update(locale: page.language_code) : url_params
24
25
  end
25
26
 
26
27
  # Returns the path for downloading an alchemy attachment
27
28
  def download_alchemy_attachment_path(attachment)
28
- alchemy.download_attachment_path(attachment, attachment.urlname)
29
+ alchemy.download_attachment_path(attachment, attachment.slug)
29
30
  end
30
31
 
31
32
  # Returns the url for downloading an alchemy attachment
32
33
  def download_alchemy_attachment_url(attachment)
33
- alchemy.download_attachment_url(attachment, attachment.urlname)
34
+ alchemy.download_attachment_url(attachment, attachment.slug)
34
35
  end
35
36
 
36
37
  # Returns the full url containing host, page and anchor for the given element
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Alchemy
4
4
  begin
5
- base_class = Object.const_get('::ApplicationMailer')
5
+ base_class = Object.const_get("::ApplicationMailer")
6
6
  rescue NameError
7
7
  base_class = ActionMailer::Base
8
8
  end
@@ -8,7 +8,7 @@ module Alchemy
8
8
  from: mail_from,
9
9
  to: mail_to,
10
10
  reply_to: message.try(:email),
11
- subject: subject
11
+ subject: subject,
12
12
  )
13
13
  end
14
14
  end
@@ -22,7 +22,7 @@ module Alchemy
22
22
  include Alchemy::Filetypes
23
23
  include Alchemy::NameConversions
24
24
  include Alchemy::Taggable
25
- include Alchemy::ContentTouching
25
+ include Alchemy::TouchElements
26
26
 
27
27
  dragonfly_accessor :file, app: :alchemy_attachments do
28
28
  after_assign { |f| write_attribute(:file_mime_type, f.mime_type) }
@@ -30,7 +30,7 @@ module Alchemy
30
30
 
31
31
  stampable stamper_class_name: Alchemy.user_class_name
32
32
 
33
- has_many :essence_files, class_name: 'Alchemy::EssenceFile', foreign_key: 'attachment_id'
33
+ has_many :essence_files, class_name: "Alchemy::EssenceFile", foreign_key: "attachment_id"
34
34
  has_many :contents, through: :essence_files
35
35
  has_many :elements, through: :contents
36
36
  has_many :pages, through: :elements
@@ -42,24 +42,25 @@ module Alchemy
42
42
  end
43
43
 
44
44
  def allowed_filetypes
45
- Config.get(:uploader).fetch('allowed_filetypes', {}).fetch('alchemy/attachments', [])
45
+ Config.get(:uploader).fetch("allowed_filetypes", {}).fetch("alchemy/attachments", [])
46
46
  end
47
47
 
48
48
  def file_types_for_select
49
49
  file_types = Alchemy::Attachment.pluck(:file_mime_type).uniq.map do |type|
50
- [Alchemy.t(type, scope: 'mime_types'), type]
50
+ [Alchemy.t(type, scope: "mime_types"), type]
51
51
  end
52
52
  file_types.sort_by(&:first)
53
53
  end
54
54
  end
55
55
 
56
56
  validates_presence_of :file
57
- validates_size_of :file, maximum: Config.get(:uploader)['file_size_limit'].megabytes
58
- validates_property :ext, of: :file,
57
+ validates_size_of :file, maximum: Config.get(:uploader)["file_size_limit"].megabytes
58
+ validates_property :ext,
59
+ of: :file,
59
60
  in: allowed_filetypes,
60
61
  case_sensitive: false,
61
62
  message: Alchemy.t("not a valid file"),
62
- unless: -> { self.class.allowed_filetypes.include?('*') }
63
+ unless: -> { self.class.allowed_filetypes.include?("*") }
63
64
 
64
65
  before_save :set_name, if: :file_name_changed?
65
66
 
@@ -71,15 +72,18 @@ module Alchemy
71
72
  {
72
73
  "name" => read_attribute(:file_name),
73
74
  "size" => read_attribute(:file_size),
74
- 'error' => errors[:file].join
75
+ "error" => errors[:file].join,
75
76
  }
76
77
  end
77
78
 
78
79
  # An url save filename without format suffix
79
- def urlname
80
- CGI.escape(file_name.gsub(/\.#{extension}$/, '').tr('.', ' '))
80
+ def slug
81
+ CGI.escape(file_name.gsub(/\.#{extension}$/, "").tr(".", " "))
81
82
  end
82
83
 
84
+ alias_method :urlname, :slug
85
+ deprecate urlname: :slug, deprecator: Alchemy::Deprecation
86
+
83
87
  # Checks if the attachment is restricted, because it is attached on restricted pages only
84
88
  def restricted?
85
89
  pages.any? && pages.not_restricted.blank?
@@ -89,6 +93,7 @@ module Alchemy
89
93
  def extension
90
94
  file_name.split(".").last
91
95
  end
96
+
92
97
  alias_method :suffix, :extension
93
98
 
94
99
  # Returns a css class name for kind of file
@@ -96,23 +101,23 @@ module Alchemy
96
101
  def icon_css_class
97
102
  case file_mime_type
98
103
  when "application/pdf"
99
- then "file-pdf"
104
+ "file-pdf"
100
105
  when "application/msword"
101
- then "file-word"
106
+ "file-word"
102
107
  when *TEXT_FILE_TYPES
103
- then "file-alt"
108
+ "file-alt"
104
109
  when *EXCEL_FILE_TYPES
105
- then "file-excel"
110
+ "file-excel"
106
111
  when *VCARD_FILE_TYPES
107
- then "address-card"
112
+ "address-card"
108
113
  when *ARCHIVE_FILE_TYPES
109
- then "file-archive"
114
+ "file-archive"
110
115
  when *AUDIO_FILE_TYPES
111
- then "file-audio"
116
+ "file-audio"
112
117
  when *IMAGE_FILE_TYPES
113
- then "file-image"
118
+ "file-image"
114
119
  when *VIDEO_FILE_TYPES
115
- then "file-video"
120
+ "file-video"
116
121
  else
117
122
  "file"
118
123
  end
@@ -1,13 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  module Alchemy
2
3
  def self.table_name_prefix
3
- 'alchemy_'
4
+ "alchemy_"
4
5
  end
5
6
 
6
7
  class BaseRecord < ActiveRecord::Base
7
8
  self.abstract_class = true
8
-
9
- def active_record_5_1?
10
- ActiveRecord.gem_version >= Gem::Version.new('5.1.0')
11
- end
12
9
  end
13
10
  end
@@ -28,29 +28,25 @@ module Alchemy
28
28
  belongs_to :element, touch: true, inverse_of: :contents
29
29
  has_one :page, through: :element
30
30
 
31
- stampable stamper_class_name: Alchemy.user_class_name
32
-
33
- acts_as_list scope: [:element_id]
34
-
35
31
  # Essence scopes
36
- scope :essence_booleans, -> { where(essence_type: "Alchemy::EssenceBoolean") }
37
- scope :essence_dates, -> { where(essence_type: "Alchemy::EssenceDate") }
38
- scope :essence_files, -> { where(essence_type: "Alchemy::EssenceFile") }
39
- scope :essence_htmls, -> { where(essence_type: "Alchemy::EssenceHtml") }
40
- scope :essence_links, -> { where(essence_type: "Alchemy::EssenceLink") }
41
- scope :essence_pictures, -> { where(essence_type: "Alchemy::EssencePicture") }
32
+ scope :essence_booleans, -> { where(essence_type: "Alchemy::EssenceBoolean") }
33
+ scope :essence_dates, -> { where(essence_type: "Alchemy::EssenceDate") }
34
+ scope :essence_files, -> { where(essence_type: "Alchemy::EssenceFile") }
35
+ scope :essence_htmls, -> { where(essence_type: "Alchemy::EssenceHtml") }
36
+ scope :essence_links, -> { where(essence_type: "Alchemy::EssenceLink") }
37
+ scope :essence_pictures, -> { where(essence_type: "Alchemy::EssencePicture") }
42
38
  scope :essence_richtexts, -> { where(essence_type: "Alchemy::EssenceRichtext") }
43
- scope :essence_selects, -> { where(essence_type: "Alchemy::EssenceSelect") }
44
- scope :essence_texts, -> { where(essence_type: "Alchemy::EssenceText") }
45
- scope :named, ->(name) { where(name: name) }
46
- scope :available, -> { published.not_trashed }
47
- scope :published, -> { joins(:element).merge(Element.published) }
48
- scope :not_trashed, -> { joins(:element).merge(Element.not_trashed) }
49
- scope :not_restricted, -> { joins(:element).merge(Element.not_restricted) }
50
-
51
- delegate :restricted?, to: :page, allow_nil: true
52
- delegate :trashed?, to: :element, allow_nil: true
53
- delegate :public?, to: :element, allow_nil: true
39
+ scope :essence_selects, -> { where(essence_type: "Alchemy::EssenceSelect") }
40
+ scope :essence_texts, -> { where(essence_type: "Alchemy::EssenceText") }
41
+ scope :named, ->(name) { where(name: name) }
42
+ scope :available, -> { published.not_trashed }
43
+ scope :published, -> { joins(:element).merge(Element.published) }
44
+ scope :not_trashed, -> { joins(:element).merge(Element.not_trashed) }
45
+ scope :not_restricted, -> { joins(:element).merge(Element.not_restricted) }
46
+
47
+ delegate :restricted?, to: :page, allow_nil: true
48
+ delegate :trashed?, to: :element, allow_nil: true
49
+ delegate :public?, to: :element, allow_nil: true
54
50
 
55
51
  class << self
56
52
  # Returns the translated label for a content name.
@@ -72,7 +68,7 @@ module Alchemy
72
68
  Alchemy.t(
73
69
  content_name,
74
70
  scope: "content_names.#{element_name}",
75
- default: Alchemy.t("content_names.#{content_name}", default: content_name.humanize)
71
+ default: Alchemy.t("content_names.#{content_name}", default: content_name.humanize),
76
72
  )
77
73
  end
78
74
  end
@@ -99,6 +95,7 @@ module Alchemy
99
95
  # Settings from the elements.yml definition
100
96
  def settings
101
97
  return {} if definition.blank?
98
+
102
99
  @settings ||= definition.fetch(:settings, {})
103
100
  end
104
101
 
@@ -114,12 +111,14 @@ module Alchemy
114
111
 
115
112
  def siblings
116
113
  return [] if !element
114
+
117
115
  element.contents
118
116
  end
119
117
 
120
118
  # Gets the ingredient from essence
121
119
  def ingredient
122
120
  return nil if essence.nil?
121
+
123
122
  essence.ingredient
124
123
  end
125
124
 
@@ -129,7 +128,7 @@ module Alchemy
129
128
  {
130
129
  name: name,
131
130
  value: serialized_ingredient,
132
- link: essence.try(:link)
131
+ link: essence.try(:link),
133
132
  }.delete_if { |_k, v| v.blank? }
134
133
  end
135
134
 
@@ -145,6 +144,7 @@ module Alchemy
145
144
  # Sets the ingredient from essence
146
145
  def ingredient=(value)
147
146
  raise EssenceMissingError if essence.nil?
147
+
148
148
  essence.ingredient = value
149
149
  end
150
150
 
@@ -156,11 +156,12 @@ module Alchemy
156
156
  #
157
157
  def update_essence(params = {})
158
158
  raise EssenceMissingError if essence.nil?
159
+
159
160
  if essence.update(params)
160
- return true
161
+ true
161
162
  else
162
163
  errors.add(:essence, :validation_failed)
163
- return false
164
+ false
164
165
  end
165
166
  end
166
167
 
@@ -169,12 +170,13 @@ module Alchemy
169
170
  end
170
171
 
171
172
  def has_validations?
172
- definition['validate'].present?
173
+ definition["validate"].present?
173
174
  end
174
175
 
175
176
  # Returns a string used as dom id on html elements.
176
177
  def dom_id
177
- return '' if essence.nil?
178
+ return "" if essence.nil?
179
+
178
180
  "#{essence_partial_name}_#{id}"
179
181
  end
180
182
 
@@ -189,7 +191,7 @@ module Alchemy
189
191
 
190
192
  # Returns true if this content should be taken for element preview.
191
193
  def preview_content?
192
- !!definition['as_element_title']
194
+ !!definition["as_element_title"]
193
195
  end
194
196
 
195
197
  # Proxy method that returns the preview text from essence.
@@ -199,7 +201,8 @@ module Alchemy
199
201
  end
200
202
 
201
203
  def essence_partial_name
202
- return '' if essence.nil?
204
+ return "" if essence.nil?
205
+
203
206
  essence.partial_name
204
207
  end
205
208
 
@@ -223,19 +226,11 @@ module Alchemy
223
226
  "has_tinymce" + (has_custom_tinymce_config? ? " #{element.name}_#{name}" : "")
224
227
  end
225
228
 
226
- def editor
227
- @_editor ||= ContentEditor.new(self)
228
- end
229
- delegate :form_field_name, to: :editor
230
- deprecate form_field_name: "use Alchemy::ContentEditor#form_field_name instead", deprecator: Alchemy::Deprecation
231
- delegate :form_field_id, to: :editor
232
- deprecate form_field_id: "use Alchemy::ContentEditor#form_field_id instead", deprecator: Alchemy::Deprecation
233
-
234
229
  # Returns the default value from content definition
235
230
  #
236
231
  # If the value is a symbol it gets passed through i18n
237
232
  # inside the +alchemy.default_content_texts+ scope
238
- def default_text(default)
233
+ def default_value(default = definition[:default])
239
234
  case default
240
235
  when Symbol
241
236
  Alchemy.t(default, scope: :default_content_texts)
@@ -22,10 +22,13 @@ module Alchemy
22
22
  if definition.blank?
23
23
  raise ContentDefinitionError, "No definition found in elements.yml for #{attributes.inspect} and #{element.inspect}"
24
24
  end
25
- super(name: definition['name'], element_id: element.id)
25
+
26
+ super(
27
+ name: definition[:name],
28
+ essence_type: normalize_essence_type(definition[:type]),
29
+ element_id: element.id
30
+ ).tap(&:build_essence)
26
31
  end
27
- alias_method :build, :new
28
- deprecate build: :new, deprecator: Alchemy::Deprecation
29
32
 
30
33
  # Creates a new content from elements definition in the +elements.yml+ file.
31
34
  #
@@ -34,20 +37,11 @@ module Alchemy
34
37
  #
35
38
  # @return [Alchemy::Content]
36
39
  #
37
- def create(*args)
38
- attributes = args.last || {}
39
- if args.length > 1
40
- Alchemy::Deprecation.warn 'Passing an element as first argument to Alchemy::Content.create is deprecated! Pass an attribute hash with element inside instead.'
41
- element = args.first
42
- else
43
- element = attributes[:element]
44
- end
45
- new(attributes.merge(element: element)).tap do |content|
46
- content.create_essence!(attributes[:essence_type])
40
+ def create(attributes = {})
41
+ new(attributes).tap do |content|
42
+ content.essence.save && content.save
47
43
  end
48
44
  end
49
- alias_method :create_from_scratch, :create
50
- deprecate create_from_scratch: :create, deprecator: Alchemy::Deprecation
51
45
 
52
46
  # Creates a copy of source and also copies the associated essence.
53
47
  #
@@ -62,11 +56,11 @@ module Alchemy
62
56
  new_content = Content.new(
63
57
  source.attributes.
64
58
  except(*SKIPPED_ATTRIBUTES_ON_COPY).
65
- merge(differences.with_indifferent_access)
59
+ merge(differences.with_indifferent_access),
66
60
  )
67
61
  new_essence = source.essence.class.create!(
68
62
  source.essence.attributes.
69
- except(*SKIPPED_ATTRIBUTES_ON_COPY)
63
+ except(*SKIPPED_ATTRIBUTES_ON_COPY),
70
64
  )
71
65
  new_content.tap do |content|
72
66
  content.essence = new_essence
@@ -77,7 +71,7 @@ module Alchemy
77
71
  # Returns all content definitions from elements.yml
78
72
  #
79
73
  def definitions
80
- definitions = Element.definitions.flat_map { |e| e['contents'] }
74
+ definitions = Element.definitions.flat_map { |e| e["contents"] }
81
75
  definitions.compact!
82
76
  definitions
83
77
  end
@@ -119,12 +113,22 @@ module Alchemy
119
113
  element.content_definition_for(name) || {}
120
114
  end
121
115
 
116
+ # Build essence from definition.
117
+ #
118
+ # If an optional type is passed, this type of essence gets created.
119
+ #
120
+ def build_essence(type = essence_type)
121
+ self.essence = essence_class(type).new({
122
+ ingredient: default_value,
123
+ })
124
+ end
125
+
122
126
  # Creates essence from definition.
123
127
  #
124
128
  # If an optional type is passed, this type of essence gets created.
125
129
  #
126
130
  def create_essence!(type = nil)
127
- self.essence = essence_class(type).create!(prepared_attributes_for_essence)
131
+ build_essence(type).save!
128
132
  save!
129
133
  end
130
134
 
@@ -135,18 +139,7 @@ module Alchemy
135
139
  # If an optional type is passed, this type of essence gets constantized.
136
140
  #
137
141
  def essence_class(type = nil)
138
- Content.normalize_essence_type(type || definition['type']).constantize
139
- end
140
-
141
- # Prepares the attributes for creating the essence.
142
- #
143
- # 1. It sets a default text if given in +elements.yml+
144
- #
145
- def prepared_attributes_for_essence
146
- attributes = {
147
- ingredient: default_text(definition['default'])
148
- }
149
- attributes
142
+ Content.normalize_essence_type(type || definition["type"]).constantize
150
143
  end
151
144
  end
152
145
  end
@@ -22,6 +22,8 @@
22
22
 
23
23
  module Alchemy
24
24
  class Element < BaseRecord
25
+ NAME_REGEXP = /\A[a-z0-9_-]+\z/
26
+
25
27
  include Alchemy::Logger
26
28
  include Alchemy::Taggable
27
29
  include Alchemy::Hints
@@ -34,7 +36,7 @@ module Alchemy
34
36
  "hint",
35
37
  "taggable",
36
38
  "compact",
37
- "message"
39
+ "message",
38
40
  ].freeze
39
41
 
40
42
  SKIPPED_ATTRIBUTES_ON_COPY = [
@@ -45,7 +47,7 @@ module Alchemy
45
47
  "folded",
46
48
  "position",
47
49
  "updated_at",
48
- "updater_id"
50
+ "updater_id",
49
51
  ].freeze
50
52
 
51
53
  # All Elements that share the same page id and parent element id and are fixed or not are considered a list.
@@ -60,17 +62,17 @@ module Alchemy
60
62
 
61
63
  stampable stamper_class_name: Alchemy.user_class_name
62
64
 
63
- has_many :contents, -> { order(:position) }, dependent: :destroy, inverse_of: :element
65
+ has_many :contents, dependent: :destroy, inverse_of: :element
64
66
 
65
67
  has_many :all_nested_elements,
66
68
  -> { order(:position).not_trashed },
67
- class_name: 'Alchemy::Element',
69
+ class_name: "Alchemy::Element",
68
70
  foreign_key: :parent_element_id,
69
71
  dependent: :destroy
70
72
 
71
73
  has_many :nested_elements,
72
74
  -> { order(:position).available },
73
- class_name: 'Alchemy::Element',
75
+ class_name: "Alchemy::Element",
74
76
  foreign_key: :parent_element_id,
75
77
  dependent: :destroy,
76
78
  inverse_of: :parent_element
@@ -79,17 +81,17 @@ module Alchemy
79
81
 
80
82
  # A nested element belongs to a parent element.
81
83
  belongs_to :parent_element,
82
- class_name: 'Alchemy::Element',
84
+ class_name: "Alchemy::Element",
83
85
  optional: true,
84
86
  touch: true,
85
87
  inverse_of: :nested_elements
86
88
 
87
89
  has_and_belongs_to_many :touchable_pages, -> { distinct },
88
- class_name: 'Alchemy::Page',
90
+ class_name: "Alchemy::Page",
89
91
  join_table: ElementToPage.table_name
90
92
 
91
93
  validates_presence_of :name, on: :create
92
- validates_format_of :name, on: :create, with: /\A[a-z0-9_-]+\z/
94
+ validates_format_of :name, on: :create, with: NAME_REGEXP
93
95
 
94
96
  attr_accessor :autogenerate_contents
95
97
  attr_accessor :autogenerate_nested_elements
@@ -98,19 +100,19 @@ module Alchemy
98
100
 
99
101
  after_update :touch_touchable_pages
100
102
 
101
- scope :trashed, -> { where(position: nil).order('updated_at DESC') }
102
- scope :not_trashed, -> { where.not(position: nil) }
103
- scope :published, -> { where(public: true) }
104
- scope :not_restricted, -> { joins(:page).merge(Page.not_restricted) }
105
- scope :available, -> { published.not_trashed }
106
- scope :named, ->(names) { where(name: names) }
107
- scope :excluded, ->(names) { where.not(name: names) }
108
- scope :fixed, -> { where(fixed: true) }
109
- scope :unfixed, -> { where(fixed: false) }
110
- scope :from_current_site, -> { where(Language.table_name => {site_id: Site.current || Site.default}).joins(page: 'language') }
111
- scope :folded, -> { where(folded: true) }
112
- scope :expanded, -> { where(folded: false) }
113
- scope :not_nested, -> { where(parent_element_id: nil) }
103
+ scope :trashed, -> { where(position: nil).order("updated_at DESC") }
104
+ scope :not_trashed, -> { where.not(position: nil) }
105
+ scope :published, -> { where(public: true) }
106
+ scope :not_restricted, -> { joins(:page).merge(Page.not_restricted) }
107
+ scope :available, -> { published.not_trashed }
108
+ scope :named, ->(names) { where(name: names) }
109
+ scope :excluded, ->(names) { where.not(name: names) }
110
+ scope :fixed, -> { where(fixed: true) }
111
+ scope :unfixed, -> { where(fixed: false) }
112
+ scope :from_current_site, -> { where(Language.table_name => { site_id: Site.current || Site.default }).joins(page: "language") }
113
+ scope :folded, -> { where(folded: true) }
114
+ scope :expanded, -> { where(folded: false) }
115
+ scope :not_nested, -> { where(parent_element_id: nil) }
114
116
 
115
117
  delegate :restricted?, to: :page, allow_nil: true
116
118
 
@@ -131,7 +133,8 @@ module Alchemy
131
133
  #
132
134
  def new(attributes = {})
133
135
  return super if attributes[:name].blank?
134
- element_attributes = attributes.to_h.merge(name: attributes[:name].split('#').first)
136
+
137
+ element_attributes = attributes.to_h.merge(name: attributes[:name].split("#").first)
135
138
  element_definition = Element.definition_by_name(element_attributes[:name])
136
139
  if element_definition.nil?
137
140
  raise(ElementDefinitionError, attributes)
@@ -139,11 +142,6 @@ module Alchemy
139
142
 
140
143
  super(element_definition.merge(element_attributes).except(*FORBIDDEN_DEFINITION_ATTRIBUTES))
141
144
  end
142
- alias_method :new_from_scratch, :new
143
- deprecate new_from_scratch: :new, deprecator: Alchemy::Deprecation
144
-
145
- alias_method :create_from_scratch, :create
146
- deprecate create_from_scratch: :create, deprecator: Alchemy::Deprecation
147
145
 
148
146
  # This methods does a copy of source and all depending contents and all of their depending essences.
149
147
  #
@@ -158,13 +156,13 @@ module Alchemy
158
156
  #
159
157
  def copy(source_element, differences = {})
160
158
  attributes = source_element.attributes.with_indifferent_access
161
- .except(*SKIPPED_ATTRIBUTES_ON_COPY)
162
- .merge(differences)
163
- .merge({
164
- autogenerate_contents: false,
165
- autogenerate_nested_elements: false,
166
- tag_list: source_element.tag_list
167
- })
159
+ .except(*SKIPPED_ATTRIBUTES_ON_COPY)
160
+ .merge(differences)
161
+ .merge({
162
+ autogenerate_contents: false,
163
+ autogenerate_nested_elements: false,
164
+ tag_list: source_element.tag_list,
165
+ })
168
166
 
169
167
  new_element = create!(attributes)
170
168
 
@@ -181,13 +179,15 @@ module Alchemy
181
179
 
182
180
  def all_from_clipboard(clipboard)
183
181
  return [] if clipboard.nil?
184
- where(id: clipboard.collect { |e| e['id'] })
182
+
183
+ where(id: clipboard.collect { |e| e["id"] })
185
184
  end
186
185
 
187
186
  # All elements in clipboard that could be placed on page
188
187
  #
189
188
  def all_from_clipboard_for_page(clipboard, page)
190
189
  return [] if clipboard.nil? || page.nil?
190
+
191
191
  all_from_clipboard(clipboard).select { |ce|
192
192
  page.available_element_names.include?(ce.name)
193
193
  }
@@ -199,7 +199,7 @@ module Alchemy
199
199
  # Pass an element name to get next of this kind.
200
200
  #
201
201
  def next(name = nil)
202
- elements = page.elements.published.where('position > ?', position)
202
+ elements = page.elements.published.where("position > ?", position)
203
203
  select_element(elements, name, :asc)
204
204
  end
205
205
 
@@ -208,13 +208,14 @@ module Alchemy
208
208
  # Pass an element name to get previous of this kind.
209
209
  #
210
210
  def prev(name = nil)
211
- elements = page.elements.published.where('position < ?', position)
211
+ elements = page.elements.published.where("position < ?", position)
212
212
  select_element(elements, name, :desc)
213
213
  end
214
214
 
215
215
  # Stores the page into +touchable_pages+ (Pages that have to be touched after updating the element).
216
216
  def store_page(page)
217
217
  return true if page.nil?
218
+
218
219
  unless touchable_pages.include? page
219
220
  touchable_pages << page
220
221
  end
@@ -233,7 +234,7 @@ module Alchemy
233
234
 
234
235
  # Returns true if the definition of this element has a taggable true value.
235
236
  def taggable?
236
- definition['taggable'] == true
237
+ definition["taggable"] == true
237
238
  end
238
239
 
239
240
  # The opposite of folded?
@@ -243,7 +244,7 @@ module Alchemy
243
244
 
244
245
  # Defined as compact element?
245
246
  def compact?
246
- definition['compact'] == true
247
+ definition["compact"] == true
247
248
  end
248
249
 
249
250
  # The element's view partial is dependent from its name
@@ -261,17 +262,7 @@ module Alchemy
261
262
  # Element partials live in +app/views/alchemy/elements+
262
263
  #
263
264
  def to_partial_path
264
- if Alchemy::LOOKUP_CONTEXT.exists?("#{name}_view", ["elements"], true)
265
- Alchemy::Deprecation.warn <<~WARN
266
- Having the `_view` suffix on your element view partials is deprecated
267
- and will not be supported in Alchemy 5.0 anymore. You can safely remove the suffix now.
268
-
269
- Please also rename the local `element` or `#{name}_view` variable into `#{name}`.
270
- WARN
271
- "alchemy/elements/#{name}_view"
272
- else
273
- "alchemy/elements/#{name}"
274
- end
265
+ "alchemy/elements/#{name}"
275
266
  end
276
267
 
277
268
  # Returns the key that's taken for cache path.
@@ -290,7 +281,7 @@ module Alchemy
290
281
 
291
282
  # A collection of element names that can be nested inside this element.
292
283
  def nestable_elements
293
- definition.fetch('nestable_elements', [])
284
+ definition.fetch("nestable_elements", [])
294
285
  end
295
286
 
296
287
  # Copy all nested elements from current element to given target element.
@@ -298,7 +289,7 @@ module Alchemy
298
289
  nested_elements.map do |nested_element|
299
290
  Element.copy(nested_element, {
300
291
  parent_element_id: target_element.id,
301
- page_id: target_element.page_id
292
+ page_id: target_element.page_id,
302
293
  })
303
294
  end
304
295
  end
@@ -306,7 +297,7 @@ module Alchemy
306
297
  private
307
298
 
308
299
  def generate_nested_elements
309
- definition.fetch('autogenerate', []).each do |nestable_element|
300
+ definition.fetch("autogenerate", []).each do |nestable_element|
310
301
  if nestable_elements.include?(nestable_element)
311
302
  Element.create(page: page, parent_element_id: id, name: nestable_element)
312
303
  else
@@ -326,6 +317,7 @@ module Alchemy
326
317
  #
327
318
  def touch_touchable_pages
328
319
  return unless respond_to?(:touchable_pages)
320
+
329
321
  touchable_pages.each(&:touch)
330
322
  end
331
323
  end