alchemy_cms 4.6.7 → 5.0.0.beta1

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 (384) 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 +31 -10
  8. data/CHANGELOG.md +89 -32
  9. data/Gemfile +24 -22
  10. data/README.md +31 -19
  11. data/Rakefile +10 -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/node-select.scss +43 -0
  25. data/app/assets/stylesheets/alchemy/nodes.scss +1 -1
  26. data/app/assets/stylesheets/alchemy/sitemap.scss +5 -1
  27. data/app/assets/stylesheets/alchemy/tables.scss +1 -24
  28. data/app/assets/stylesheets/tinymce/skins/alchemy/content.min.css.scss +3 -3
  29. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +7 -7
  30. data/app/controllers/alchemy/admin/attachments_controller.rb +8 -7
  31. data/app/controllers/alchemy/admin/base_controller.rb +16 -42
  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 +50 -62
  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 +36 -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 +3 -3
  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 +2 -1
  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 +21 -19
  81. data/app/models/alchemy/base_record.rb +2 -5
  82. data/app/models/alchemy/content/factory.rb +24 -31
  83. data/app/models/alchemy/content.rb +33 -38
  84. data/app/models/alchemy/element/definitions.rb +4 -4
  85. data/app/models/alchemy/element/element_contents.rb +9 -6
  86. data/app/models/alchemy/element/element_essences.rb +4 -3
  87. data/app/models/alchemy/element/presenters.rb +3 -2
  88. data/app/models/alchemy/element.rb +46 -54
  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 +4 -4
  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 +17 -16
  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/code.rb +4 -4
  104. data/app/models/alchemy/language.rb +21 -35
  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 +28 -5
  108. data/app/models/alchemy/page/fixed_attributes.rb +3 -2
  109. data/app/models/alchemy/page/page_elements.rb +35 -44
  110. data/app/models/alchemy/page/page_naming.rb +20 -70
  111. data/app/models/alchemy/page/page_natures.rb +7 -34
  112. data/app/models/alchemy/page/page_scopes.rb +23 -29
  113. data/app/models/alchemy/page/url_path.rb +0 -2
  114. data/app/models/alchemy/page.rb +47 -128
  115. data/app/models/alchemy/picture/transformations.rb +9 -7
  116. data/app/models/alchemy/picture/url.rb +5 -5
  117. data/app/models/alchemy/picture.rb +19 -28
  118. data/app/models/alchemy/site/layout.rb +2 -2
  119. data/app/models/alchemy/site.rb +6 -36
  120. data/app/models/concerns/alchemy/touch_elements.rb +24 -0
  121. data/app/serializers/alchemy/content_serializer.rb +0 -3
  122. data/app/serializers/alchemy/essence_boolean_serializer.rb +3 -3
  123. data/app/serializers/alchemy/essence_date_serializer.rb +3 -3
  124. data/app/serializers/alchemy/essence_file_serializer.rb +4 -2
  125. data/app/serializers/alchemy/essence_html_serializer.rb +3 -3
  126. data/app/serializers/alchemy/essence_link_serializer.rb +3 -3
  127. data/app/serializers/alchemy/essence_picture_serializer.rb +5 -4
  128. data/app/serializers/alchemy/essence_richtext_serializer.rb +3 -3
  129. data/app/serializers/alchemy/essence_select_serializer.rb +3 -3
  130. data/app/serializers/alchemy/essence_text_serializer.rb +5 -4
  131. data/app/serializers/alchemy/node_serializer.rb +2 -0
  132. data/app/serializers/alchemy/page_tree_serializer.rb +9 -13
  133. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +1 -2
  134. data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +1 -2
  135. data/app/views/alchemy/admin/attachments/_files_list.html.erb +4 -4
  136. data/app/views/alchemy/admin/contents/create.js.erb +1 -3
  137. data/app/views/alchemy/admin/elements/_element.html.erb +9 -18
  138. data/app/views/alchemy/admin/elements/create.js.erb +1 -1
  139. data/app/views/alchemy/admin/elements/fold.js.erb +1 -1
  140. data/app/views/alchemy/admin/elements/index.html.erb +3 -3
  141. data/app/views/alchemy/admin/essence_files/assign.js.erb +1 -6
  142. data/app/views/alchemy/admin/essence_files/edit.html.erb +1 -1
  143. data/app/views/alchemy/admin/essence_pictures/assign.js.erb +1 -7
  144. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +1 -1
  145. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +7 -7
  146. data/app/views/alchemy/admin/essence_pictures/update.js.erb +1 -1
  147. data/app/views/alchemy/admin/languages/_form.html.erb +5 -5
  148. data/app/views/alchemy/admin/languages/_table.html.erb +3 -3
  149. data/app/views/alchemy/admin/languages/edit.html.erb +1 -0
  150. data/app/views/alchemy/admin/languages/index.html.erb +23 -4
  151. data/app/views/alchemy/admin/languages/new.html.erb +1 -0
  152. data/app/views/alchemy/admin/layoutpages/index.html.erb +3 -3
  153. data/app/views/alchemy/admin/nodes/_form.html.erb +18 -15
  154. data/app/views/alchemy/admin/nodes/_node.html.erb +1 -5
  155. data/app/views/alchemy/admin/nodes/index.html.erb +3 -4
  156. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +0 -8
  157. data/app/views/alchemy/admin/pages/_form.html.erb +0 -1
  158. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +1 -0
  159. data/app/views/alchemy/admin/pages/_page.html.erb +11 -19
  160. data/app/views/alchemy/admin/pages/_page_infos.html.erb +0 -4
  161. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  162. data/app/views/alchemy/admin/pages/info.html.erb +0 -9
  163. data/app/views/alchemy/admin/pages/unlock.js.erb +13 -6
  164. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +0 -2
  165. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -5
  166. data/app/views/alchemy/admin/pictures/_overlay_picture_list.html.erb +1 -1
  167. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +1 -2
  168. data/app/views/alchemy/admin/resources/_resource.html.erb +1 -1
  169. data/app/views/alchemy/admin/resources/_table.html.erb +2 -2
  170. data/app/views/alchemy/admin/sites/_form.html.erb +8 -0
  171. data/app/views/alchemy/admin/sites/edit.html.erb +1 -0
  172. data/app/views/alchemy/admin/sites/index.html.erb +21 -9
  173. data/app/views/alchemy/admin/sites/new.html.erb +1 -0
  174. data/app/views/alchemy/admin/tags/index.html.erb +2 -2
  175. data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +10 -12
  176. data/app/views/alchemy/essences/_essence_date_editor.html.erb +11 -8
  177. data/app/views/alchemy/essences/_essence_file_editor.html.erb +16 -17
  178. data/app/views/alchemy/essences/_essence_html_editor.html.erb +8 -5
  179. data/app/views/alchemy/essences/_essence_link_editor.html.erb +18 -15
  180. data/app/views/alchemy/essences/_essence_node_editor.html.erb +27 -0
  181. data/app/views/alchemy/essences/_essence_node_view.html.erb +1 -0
  182. data/app/views/alchemy/essences/_essence_page_editor.html.erb +14 -11
  183. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +22 -20
  184. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +10 -7
  185. data/app/views/alchemy/essences/_essence_select_editor.html.erb +12 -16
  186. data/app/views/alchemy/essences/_essence_text_editor.html.erb +18 -17
  187. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +6 -11
  188. data/app/views/alchemy/pages/show.rss.builder +3 -2
  189. data/app/views/layouts/alchemy/admin.html.erb +1 -0
  190. data/babel.config.js +12 -0
  191. data/bin/rails +5 -4
  192. data/config/alchemy/config.yml +15 -18
  193. data/config/brakeman.ignore +1 -1
  194. data/config/initializers/assets.rb +2 -1
  195. data/config/initializers/dragonfly.rb +2 -1
  196. data/config/initializers/mime_types.rb +1 -0
  197. data/config/initializers/mini_profiler.rb +3 -2
  198. data/config/initializers/simple_form.rb +6 -6
  199. data/config/locales/alchemy.en.yml +23 -8
  200. data/config/routes.rb +25 -24
  201. data/config/spring.rb +3 -2
  202. data/db/migrate/20200226213334_alchemy_four_point_four.rb +313 -0
  203. data/db/migrate/20200423073425_create_alchemy_essence_nodes.rb +11 -0
  204. data/db/migrate/20200504210159_remove_site_id_from_nodes.rb +28 -0
  205. data/db/migrate/20200505215518_add_language_id_foreign_key_to_alchemy_pages.rb +8 -0
  206. data/db/migrate/20200511113603_add_menu_type_to_alchemy_nodes.rb +27 -0
  207. data/db/migrate/20200514091507_make_page_layoutpage_null_false.rb +6 -0
  208. data/db/migrate/20200519073500_remove_visible_from_alchemy_pages.rb +24 -0
  209. data/lib/alchemy/admin/locale.rb +3 -1
  210. data/lib/alchemy/admin/preview_url.rb +64 -0
  211. data/lib/alchemy/auth_accessors.rb +8 -7
  212. data/lib/alchemy/cache_digests/template_tracker.rb +5 -4
  213. data/lib/alchemy/config.rb +1 -5
  214. data/lib/alchemy/configuration_methods.rb +3 -1
  215. data/lib/alchemy/controller_actions.rb +6 -5
  216. data/lib/alchemy/deprecation.rb +2 -1
  217. data/lib/alchemy/elements_finder.rb +5 -5
  218. data/lib/alchemy/engine.rb +22 -14
  219. data/lib/alchemy/errors.rb +0 -7
  220. data/lib/alchemy/essence.rb +17 -16
  221. data/lib/alchemy/filetypes.rb +5 -5
  222. data/lib/alchemy/forms/builder.rb +4 -4
  223. data/lib/alchemy/hints.rb +1 -1
  224. data/lib/alchemy/i18n.rb +2 -1
  225. data/lib/alchemy/modules.rb +12 -12
  226. data/lib/alchemy/name_conversions.rb +5 -5
  227. data/lib/alchemy/on_page_layout/callbacks_runner.rb +1 -0
  228. data/lib/alchemy/page_layout.rb +15 -12
  229. data/lib/alchemy/paths.rb +1 -1
  230. data/lib/alchemy/permissions.rb +7 -6
  231. data/lib/alchemy/resource.rb +25 -17
  232. data/lib/alchemy/resources_helper.rb +12 -18
  233. data/lib/alchemy/routing_constraints.rb +1 -1
  234. data/lib/alchemy/seeder.rb +42 -14
  235. data/lib/alchemy/shell.rb +13 -10
  236. data/lib/alchemy/taggable.rb +1 -0
  237. data/lib/alchemy/tasks/tidy.rb +4 -3
  238. data/lib/alchemy/test_support/config_stubbing.rb +1 -0
  239. data/lib/alchemy/test_support/essence_shared_examples.rb +72 -72
  240. data/lib/alchemy/test_support/factories/attachment_factory.rb +5 -5
  241. data/lib/alchemy/test_support/factories/content_factory.rb +6 -6
  242. data/lib/alchemy/test_support/factories/dummy_user_factory.rb +7 -7
  243. data/lib/alchemy/test_support/factories/element_factory.rb +9 -9
  244. data/lib/alchemy/test_support/factories/essence_file_factory.rb +3 -3
  245. data/lib/alchemy/test_support/factories/essence_page_factory.rb +3 -3
  246. data/lib/alchemy/test_support/factories/essence_picture_factory.rb +4 -4
  247. data/lib/alchemy/test_support/factories/essence_text_factory.rb +3 -3
  248. data/lib/alchemy/test_support/factories/language_factory.rb +16 -14
  249. data/lib/alchemy/test_support/factories/node_factory.rb +8 -8
  250. data/lib/alchemy/test_support/factories/page_factory.rb +15 -27
  251. data/lib/alchemy/test_support/factories/picture_factory.rb +5 -5
  252. data/lib/alchemy/test_support/factories/site_factory.rb +7 -6
  253. data/lib/alchemy/test_support/factories.rb +1 -1
  254. data/lib/alchemy/test_support/integration_helpers.rb +1 -0
  255. data/lib/alchemy/test_support/shared_contexts.rb +5 -4
  256. data/lib/alchemy/test_support/shared_uploader_examples.rb +4 -3
  257. data/lib/alchemy/tinymce.rb +15 -13
  258. data/lib/alchemy/upgrader/five_point_zero.rb +41 -0
  259. data/lib/alchemy/upgrader/tasks/element_views_updater.rb +4 -4
  260. data/lib/alchemy/upgrader/tasks/harden_gutentag_migrations.rb +29 -0
  261. data/lib/alchemy/upgrader.rb +8 -7
  262. data/lib/alchemy/userstamp.rb +1 -1
  263. data/lib/alchemy/version.rb +1 -1
  264. data/lib/alchemy_cms.rb +52 -51
  265. data/lib/{rails/generators → generators}/alchemy/base.rb +5 -4
  266. data/lib/{rails/generators → generators}/alchemy/elements/elements_generator.rb +13 -9
  267. data/lib/{rails/generators → generators}/alchemy/elements/templates/view.html.erb +0 -0
  268. data/lib/{rails/generators → generators}/alchemy/elements/templates/view.html.haml +0 -0
  269. data/lib/{rails/generators → generators}/alchemy/elements/templates/view.html.slim +0 -0
  270. data/lib/{rails/generators → generators}/alchemy/essence/essence_generator.rb +15 -13
  271. data/lib/generators/alchemy/essence/templates/editor.html.erb +17 -0
  272. data/lib/{rails/generators → generators}/alchemy/essence/templates/view.html.erb +0 -0
  273. data/lib/{rails/generators → generators}/alchemy/install/files/_article.html.erb +0 -0
  274. data/lib/{rails/generators → generators}/alchemy/install/files/_standard.html.erb +0 -0
  275. data/lib/{rails/generators → generators}/alchemy/install/files/alchemy.en.yml +0 -0
  276. data/lib/generators/alchemy/install/files/alchemy_admin.js +1 -0
  277. data/lib/{rails/generators → generators}/alchemy/install/files/all.css +0 -0
  278. data/lib/{rails/generators → generators}/alchemy/install/files/all.js +0 -0
  279. data/lib/{rails/generators → generators}/alchemy/install/files/application.html.erb +0 -0
  280. data/lib/{rails/generators → generators}/alchemy/install/files/article.scss +0 -0
  281. data/lib/generators/alchemy/install/install_generator.rb +110 -0
  282. data/lib/{rails/generators → generators}/alchemy/install/templates/dragonfly.rb.tt +0 -0
  283. data/lib/{rails/generators → generators}/alchemy/install/templates/elements.yml.tt +0 -0
  284. data/lib/{rails/generators → generators}/alchemy/install/templates/menus.yml.tt +0 -0
  285. data/lib/{rails/generators → generators}/alchemy/install/templates/page_layouts.yml.tt +0 -0
  286. data/lib/{rails/generators → generators}/alchemy/menus/menus_generator.rb +2 -2
  287. data/lib/{rails/generators → generators}/alchemy/menus/templates/node.html.erb +1 -4
  288. data/lib/{rails/generators → generators}/alchemy/menus/templates/node.html.haml +1 -4
  289. data/lib/{rails/generators → generators}/alchemy/menus/templates/node.html.slim +1 -4
  290. data/lib/{rails/generators → generators}/alchemy/menus/templates/wrapper.html.erb +1 -1
  291. data/lib/{rails/generators → generators}/alchemy/menus/templates/wrapper.html.haml +1 -1
  292. data/lib/{rails/generators → generators}/alchemy/menus/templates/wrapper.html.slim +1 -1
  293. data/lib/{rails/generators → generators}/alchemy/module/module_generator.rb +3 -2
  294. data/lib/{rails/generators → generators}/alchemy/module/templates/ability.rb.tt +0 -0
  295. data/lib/{rails/generators → generators}/alchemy/module/templates/controller.rb.tt +0 -0
  296. data/lib/{rails/generators → generators}/alchemy/module/templates/module_config.rb.tt +0 -0
  297. data/lib/{rails/generators → generators}/alchemy/page_layouts/page_layouts_generator.rb +5 -4
  298. data/lib/{rails/generators → generators}/alchemy/page_layouts/templates/layout.html.erb +0 -0
  299. data/lib/{rails/generators → generators}/alchemy/page_layouts/templates/layout.html.haml +0 -0
  300. data/lib/{rails/generators → generators}/alchemy/page_layouts/templates/layout.html.slim +0 -0
  301. data/lib/{rails/generators → generators}/alchemy/site_layouts/site_layouts_generator.rb +4 -2
  302. data/lib/{rails/generators → generators}/alchemy/site_layouts/templates/layout.html.erb +0 -0
  303. data/lib/{rails/generators → generators}/alchemy/site_layouts/templates/layout.html.haml +0 -0
  304. data/lib/{rails/generators → generators}/alchemy/site_layouts/templates/layout.html.slim +0 -0
  305. data/lib/{rails/generators → generators}/alchemy/views/views_generator.rb +7 -6
  306. data/lib/kaminari/scoped_pagination_url_helper.rb +1 -0
  307. data/lib/tasks/alchemy/db.rake +3 -19
  308. data/lib/tasks/alchemy/install.rake +3 -2
  309. data/lib/tasks/alchemy/tidy.rake +9 -8
  310. data/lib/tasks/alchemy/upgrade.rake +18 -120
  311. data/package/admin.js +14 -0
  312. data/package/src/__tests__/i18n.spec.js +70 -0
  313. data/package/src/i18n.js +48 -0
  314. data/package/src/node_tree.js +72 -0
  315. data/package/src/translations.js +32 -0
  316. data/package/src/utils/__tests__/ajax.spec.js +124 -0
  317. data/package/src/utils/__tests__/events.spec.js +38 -0
  318. data/package/src/utils/ajax.js +48 -0
  319. data/package/src/utils/events.js +16 -0
  320. data/package.json +45 -0
  321. data/vendor/assets/fonts/fa-regular-400.eot +0 -0
  322. data/vendor/assets/fonts/fa-regular-400.svg +798 -358
  323. data/vendor/assets/fonts/fa-regular-400.ttf +0 -0
  324. data/vendor/assets/fonts/fa-regular-400.woff +0 -0
  325. data/vendor/assets/fonts/fa-regular-400.woff2 +0 -0
  326. data/vendor/assets/fonts/fa-solid-900.eot +0 -0
  327. data/vendor/assets/fonts/fa-solid-900.svg +4933 -1408
  328. data/vendor/assets/fonts/fa-solid-900.ttf +0 -0
  329. data/vendor/assets/fonts/fa-solid-900.woff +0 -0
  330. data/vendor/assets/fonts/fa-solid-900.woff2 +0 -0
  331. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +1 -2
  332. data/vendor/assets/stylesheets/fontawesome/_core.scss +5 -0
  333. data/vendor/assets/stylesheets/fontawesome/_fixed-width.scss +1 -1
  334. data/vendor/assets/stylesheets/fontawesome/_icons.scss +651 -2
  335. data/vendor/assets/stylesheets/fontawesome/_mixins.scss +0 -1
  336. data/vendor/assets/stylesheets/fontawesome/_rotated-flipped.scss +3 -2
  337. data/vendor/assets/stylesheets/fontawesome/_stacked.scss +1 -1
  338. data/vendor/assets/stylesheets/fontawesome/_variables.scss +662 -9
  339. data/vendor/assets/stylesheets/fontawesome/fontawesome.scss +2 -2
  340. data/vendor/assets/stylesheets/fontawesome/regular.scss +23 -0
  341. data/vendor/assets/stylesheets/fontawesome/solid.scss +24 -0
  342. metadata +117 -98
  343. data/app/assets/javascripts/alchemy/alchemy.i18n.js.coffee +0 -32
  344. data/app/assets/javascripts/alchemy/alchemy.node_tree.js +0 -66
  345. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +0 -29
  346. data/app/assets/javascripts/alchemy/alchemy.utils.js +0 -45
  347. data/app/helpers/alchemy/essences_helper.rb +0 -119
  348. data/app/models/concerns/alchemy/content_touching.rb +0 -23
  349. data/app/serializers/alchemy/legacy_element_serializer.rb +0 -15
  350. data/app/views/alchemy/admin/contents/_missing.html.erb +0 -17
  351. data/app/views/alchemy/admin/pages/_menu_fields.html.erb +0 -37
  352. data/app/views/alchemy/admin/pages/configure_external.html.erb +0 -32
  353. data/app/views/alchemy/elements/_editor_not_found.html.erb +0 -4
  354. data/app/views/alchemy/navigation/_image_link.html.erb +0 -14
  355. data/app/views/alchemy/navigation/_link.html.erb +0 -19
  356. data/app/views/alchemy/navigation/_renderer.html.erb +0 -29
  357. data/db/migrate/20180226123013_alchemy_four_point_zero.rb +0 -363
  358. data/db/migrate/20180227224537_migrate_tags_to_gutentag.rb +0 -41
  359. data/db/migrate/20180519204655_add_fixed_to_alchemy_elements.rb +0 -6
  360. data/db/migrate/20191016073858_create_alchemy_essence_pages.rb +0 -8
  361. data/db/migrate/20191029212236_create_alchemy_nodes.rb +0 -24
  362. data/db/migrate/20200226081535_add_site_id_to_alchemy_nodes.rb +0 -15
  363. data/lib/alchemy/error_tracking/airbrake_handler.rb +0 -13
  364. data/lib/alchemy/error_tracking.rb +0 -14
  365. data/lib/alchemy/ssl_protection.rb +0 -34
  366. data/lib/alchemy/tasks/helpers.rb +0 -81
  367. data/lib/alchemy/test_support/controller_requests.rb +0 -93
  368. data/lib/alchemy/upgrader/four_point_four.rb +0 -52
  369. data/lib/alchemy/upgrader/four_point_one.rb +0 -42
  370. data/lib/alchemy/upgrader/four_point_six.rb +0 -50
  371. data/lib/alchemy/upgrader/four_point_two.rb +0 -86
  372. data/lib/alchemy/upgrader/tasks/cells_migration.rb +0 -45
  373. data/lib/alchemy/upgrader/tasks/cells_upgrader.rb +0 -166
  374. data/lib/alchemy/upgrader/tasks/element_partial_name_variable_updater.rb +0 -32
  375. data/lib/alchemy/upgrader/tasks/fixed_element_name_finder.rb +0 -31
  376. data/lib/alchemy/upgrader/tasks/harden_acts_as_taggable_on_migrations.rb +0 -27
  377. data/lib/alchemy/upgrader/tasks/picture_gallery_migration.rb +0 -65
  378. data/lib/alchemy/upgrader/tasks/picture_gallery_upgrader.rb +0 -210
  379. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +0 -15
  380. data/lib/rails/generators/alchemy/install/install_generator.rb +0 -60
  381. data/lib/tasks/alchemy/convert.rake +0 -98
  382. data/vendor/assets/javascripts/sortable/Sortable.min.js +0 -2
  383. data/vendor/assets/stylesheets/fontawesome/fa-regular.scss +0 -22
  384. data/vendor/assets/stylesheets/fontawesome/fa-solid.scss +0 -23
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class EssenceNode < BaseRecord
5
+ acts_as_essence(
6
+ ingredient_column: :node,
7
+ preview_text_column: :node_name,
8
+ belongs_to: {
9
+ class_name: "Alchemy::Node",
10
+ foreign_key: :node_id,
11
+ inverse_of: :essence_nodes,
12
+ optional: true,
13
+ },
14
+ )
15
+
16
+ delegate :name, to: :node, prefix: true, allow_nil: true
17
+ end
18
+ end
@@ -2,28 +2,15 @@
2
2
 
3
3
  module Alchemy
4
4
  class EssencePage < BaseRecord
5
- PAGE_ID = /\A\d+\z/
6
-
7
5
  acts_as_essence(
8
6
  ingredient_column: :page,
9
7
  preview_text_method: :name,
10
8
  belongs_to: {
11
- class_name: 'Alchemy::Page',
9
+ class_name: "Alchemy::Page",
12
10
  foreign_key: :page_id,
13
11
  inverse_of: :essence_pages,
14
- optional: true
15
- }
12
+ optional: true,
13
+ },
16
14
  )
17
-
18
- def ingredient=(page)
19
- case page
20
- when PAGE_ID
21
- self.page = Alchemy::Page.new(id: page)
22
- when Alchemy::Page
23
- self.page = page
24
- else
25
- super
26
- end
27
- end
28
15
  end
29
16
  end
@@ -14,8 +14,6 @@
14
14
  # link_title :string
15
15
  # css_class :string
16
16
  # link_target :string
17
- # creator_id :integer
18
- # updater_id :integer
19
17
  # created_at :datetime not null
20
18
  # updated_at :datetime not null
21
19
  # crop_from :string
@@ -26,10 +24,10 @@
26
24
  module Alchemy
27
25
  class EssencePicture < BaseRecord
28
26
  acts_as_essence ingredient_column: :picture, belongs_to: {
29
- class_name: 'Alchemy::Picture',
27
+ class_name: "Alchemy::Picture",
30
28
  foreign_key: :picture_id,
31
29
  inverse_of: :essence_pictures,
32
- optional: true
30
+ optional: true,
33
31
  }
34
32
 
35
33
  delegate :image_file_width, :image_file_height, :image_file, to: :picture
@@ -80,7 +78,7 @@ module Alchemy
80
78
  format: picture.default_render_format,
81
79
  crop_from: crop_from.presence,
82
80
  crop_size: crop_size.presence,
83
- size: content.settings[:size]
81
+ size: content.settings[:size],
84
82
  }.with_indifferent_access
85
83
  end
86
84
 
@@ -90,11 +88,11 @@ module Alchemy
90
88
  # image displayed in the frontend.
91
89
  #
92
90
  # @return [String]
93
- def thumbnail_url(options = {})
91
+ def thumbnail_url
94
92
  return if picture.nil?
95
93
 
96
- crop = crop_values_present? || content.settings_value(:crop, options)
97
- size = render_size || content.settings_value(:size, options)
94
+ crop = crop_values_present? || content.settings[:crop]
95
+ size = render_size || content.settings[:size]
98
96
 
99
97
  options = {
100
98
  size: thumbnail_size(size, crop),
@@ -102,7 +100,7 @@ module Alchemy
102
100
  crop_from: crop_from.presence,
103
101
  crop_size: crop_size.presence,
104
102
  flatten: true,
105
- format: picture.image_file_format
103
+ format: picture.image_file_format,
106
104
  }
107
105
 
108
106
  picture.url(options)
@@ -116,6 +114,7 @@ module Alchemy
116
114
  # @return [String]
117
115
  def preview_text(max = 30)
118
116
  return "" if picture.nil?
117
+
119
118
  picture.name.to_s[0..max - 1]
120
119
  end
121
120
 
@@ -124,6 +123,7 @@ module Alchemy
124
123
  # @return [Hash]
125
124
  def cropping_mask
126
125
  return if crop_from.blank? || crop_size.blank?
126
+
127
127
  crop_from = point_from_string(read_attribute(:crop_from))
128
128
  crop_size = sizes_from_string(read_attribute(:crop_size))
129
129
 
@@ -137,12 +137,12 @@ module Alchemy
137
137
  picture_url(content.settings)
138
138
  end
139
139
 
140
- # Show image cropping link for content and options?
141
- def allow_image_cropping?(options = {})
142
- content && content.settings_value(:crop, options) && picture &&
140
+ # Show image cropping link for content
141
+ def allow_image_cropping?
142
+ content && content.settings[:crop] && picture &&
143
143
  picture.can_be_cropped_to(
144
- content.settings_value(:size, options),
145
- content.settings_value(:upsample, options)
144
+ content.settings[:size],
145
+ content.settings[:upsample],
146
146
  )
147
147
  end
148
148
 
@@ -161,16 +161,17 @@ module Alchemy
161
161
  end
162
162
 
163
163
  def normalize_crop_value(crop_value)
164
- self[crop_value].split('x').map { |n| normalize_number(n) }.join('x')
164
+ self[crop_value].split("x").map { |n| normalize_number(n) }.join("x")
165
165
  end
166
166
 
167
167
  def normalize_number(number)
168
168
  number = number.to_f.round
169
- number < 0 ? 0 : number
169
+ number.negative? ? 0 : number
170
170
  end
171
171
 
172
172
  def replace_newlines
173
173
  return nil if caption.nil?
174
+
174
175
  caption.gsub!(/(\r\n|\r|\n)/, "<br/>")
175
176
  end
176
177
  end
@@ -13,7 +13,7 @@ module Alchemy
13
13
  show_caption: true,
14
14
  disable_link: false,
15
15
  srcset: [],
16
- sizes: []
16
+ sizes: [],
17
17
  }.with_indifferent_access
18
18
 
19
19
  def initialize(content, options = {}, html_options = {})
@@ -33,12 +33,12 @@ module Alchemy
33
33
  output = link_to(output, url_for(essence.link), {
34
34
  title: essence.link_title.presence,
35
35
  target: essence.link_target == "blank" ? "_blank" : nil,
36
- data: {link_target: essence.link_target.presence}
36
+ data: { link_target: essence.link_target.presence },
37
37
  })
38
38
  end
39
39
 
40
40
  if caption
41
- content_tag(:figure, output, {class: essence.css_class.presence}.merge(html_options))
41
+ content_tag(:figure, output, { class: essence.css_class.presence }.merge(html_options))
42
42
  else
43
43
  output
44
44
  end
@@ -48,6 +48,7 @@ module Alchemy
48
48
 
49
49
  def caption
50
50
  return unless show_caption?
51
+
51
52
  @_caption ||= content_tag(:figcaption, essence.caption)
52
53
  end
53
54
 
@@ -57,8 +58,8 @@ module Alchemy
57
58
  alt: essence.alt_tag.presence,
58
59
  title: essence.title.presence,
59
60
  class: caption ? nil : essence.css_class.presence,
60
- srcset: srcset.join(', ').presence,
61
- sizes: options[:sizes].join(', ').presence
61
+ srcset: srcset.join(", ").presence,
62
+ sizes: options[:sizes].join(", ").presence,
62
63
  }.merge(caption ? {} : html_options)
63
64
  )
64
65
  end
@@ -74,7 +75,7 @@ module Alchemy
74
75
  def srcset
75
76
  options[:srcset].map do |size|
76
77
  url = essence.picture_url(size: size)
77
- width, height = size.split('x')
78
+ width, height = size.split("x")
78
79
  width.present? ? "#{url} #{width}w" : "#{url} #{height}h"
79
80
  end
80
81
  end
@@ -8,15 +8,13 @@
8
8
  # body :text
9
9
  # stripped_body :text
10
10
  # public :boolean
11
- # creator_id :integer
12
- # updater_id :integer
13
11
  # created_at :datetime not null
14
12
  # updated_at :datetime not null
15
13
  #
16
14
 
17
15
  module Alchemy
18
16
  class EssenceRichtext < BaseRecord
19
- acts_as_essence preview_text_column: 'stripped_body'
17
+ acts_as_essence preview_text_column: "stripped_body"
20
18
 
21
19
  before_save :strip_content
22
20
 
@@ -8,13 +8,11 @@
8
8
  # value :string
9
9
  # created_at :datetime not null
10
10
  # updated_at :datetime not null
11
- # creator_id :integer
12
- # updater_id :integer
13
11
  #
14
12
 
15
13
  # Provides a select box that stores string values.
16
14
  module Alchemy
17
15
  class EssenceSelect < BaseRecord
18
- acts_as_essence ingredient_column: 'value'
16
+ acts_as_essence ingredient_column: "value"
19
17
  end
20
18
  end
@@ -11,8 +11,6 @@
11
11
  # link_class_name :string
12
12
  # public :boolean default(FALSE)
13
13
  # link_target :string
14
- # creator_id :integer
15
- # updater_id :integer
16
14
  # created_at :datetime not null
17
15
  # updated_at :datetime not null
18
16
  #
@@ -17,6 +17,7 @@ module Alchemy
17
17
 
18
18
  def self.folded_for_user(user)
19
19
  return none unless Alchemy.user_class < ActiveRecord::Base
20
+
20
21
  where(user: user, folded: true)
21
22
  end
22
23
  end
@@ -4,7 +4,7 @@ module Alchemy::Language::Code
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  def code
7
- [language_code, country_code].select(&:present?).join('-')
7
+ [language_code, country_code].select(&:present?).join("-")
8
8
  end
9
9
 
10
10
  def code=(code)
@@ -13,11 +13,11 @@ module Alchemy::Language::Code
13
13
 
14
14
  module ClassMethods
15
15
  def find_by_code(code)
16
- codes = code.split('-')
17
- codes << '' if codes.length == 1
16
+ codes = code.split("-")
17
+ codes << "" if codes.length == 1
18
18
  on_current_site.find_by(
19
19
  language_code: codes[0],
20
- country_code: codes[1]
20
+ country_code: codes[1],
21
21
  )
22
22
  end
23
23
  end
@@ -20,15 +20,18 @@
20
20
  # locale :string
21
21
  #
22
22
 
23
- require_dependency 'alchemy/site'
23
+ require_dependency "alchemy/site"
24
24
 
25
25
  module Alchemy
26
26
  class Language < BaseRecord
27
27
  belongs_to :site
28
- has_many :pages
28
+ has_many :pages, inverse_of: :language
29
+ has_many :nodes, inverse_of: :language
29
30
 
30
31
  before_validation :set_locale, if: -> { locale.blank? }
31
32
 
33
+ has_one :root_page, -> { where(parent: nil, layoutpage: false) }, class_name: "Alchemy::Page"
34
+
32
35
  validates :name, presence: true
33
36
  validates :page_layout, presence: true
34
37
  validates :frontpage_name, presence: true
@@ -54,11 +57,13 @@ module Alchemy
54
57
  after_update :unpublish_pages,
55
58
  if: :should_unpublish_pages?
56
59
 
57
- before_destroy :check_for_default
58
- after_destroy :delete_language_root_page
60
+ before_destroy if: -> { pages.any? } do
61
+ errors.add(:pages, :still_present)
62
+ throw(:abort)
63
+ end
59
64
 
60
- scope :published, -> { where(public: true) }
61
- scope :with_root_page, -> { joins(:pages).where(Page.table_name => {language_root: true}) }
65
+ scope :published, -> { where(public: true) }
66
+ scope :with_root_page, -> { joins(:pages).where(Page.table_name => { language_root: true }) }
62
67
 
63
68
  class << self
64
69
  def on_site(site)
@@ -81,6 +86,8 @@ module Alchemy
81
86
 
82
87
  # The root page of the current language.
83
88
  def current_root_page
89
+ return unless current
90
+
84
91
  current.pages.language_roots.first
85
92
  end
86
93
 
@@ -100,16 +107,6 @@ module Alchemy
100
107
 
101
108
  include Alchemy::Language::Code
102
109
 
103
- # Root page
104
- def root_page
105
- @root_page ||= pages.language_roots.first
106
- end
107
-
108
- # Layout root page
109
- def layout_root_page
110
- @layout_root_page ||= Page.layout_root_for(id)
111
- end
112
-
113
110
  # All available locales matching this language
114
111
  #
115
112
  # Matching either the code (+language_code+ + +country_code+) or the +language_code+
@@ -118,10 +115,14 @@ module Alchemy
118
115
  #
119
116
  def matching_locales
120
117
  @_matching_locales ||= ::I18n.available_locales.select do |locale|
121
- locale.to_s.split('-')[0] == language_code
118
+ locale.to_s.split("-")[0] == language_code
122
119
  end
123
120
  end
124
121
 
122
+ def available_menu_names
123
+ Alchemy::Node.available_menu_names - nodes.reject(&:parent_id).map(&:menu_type)
124
+ end
125
+
125
126
  private
126
127
 
127
128
  def set_locale
@@ -157,36 +158,21 @@ module Alchemy
157
158
  def remove_old_default
158
159
  lang = Language.on_site(site).default
159
160
  return true if lang.nil?
161
+
160
162
  lang.default = false
161
163
  lang.save(validate: false)
162
164
  end
163
165
 
164
166
  def should_set_pages_language?
165
- if active_record_5_1?
166
- saved_change_to_language_code? || saved_change_to_country_code?
167
- else
168
- language_code_changed? || country_code_changed?
169
- end
167
+ saved_change_to_language_code? || saved_change_to_country_code?
170
168
  end
171
169
 
172
170
  def set_pages_language
173
171
  pages.update_all language_code: code
174
172
  end
175
173
 
176
- def check_for_default
177
- raise DefaultLanguageNotDeletable if default?
178
- end
179
-
180
- def delete_language_root_page
181
- root_page.try(:destroy) && layout_root_page.try(:destroy)
182
- end
183
-
184
174
  def should_unpublish_pages?
185
- if active_record_5_1?
186
- saved_changes[:public] == [true, false]
187
- else
188
- changes[:public] == [true, false]
189
- end
175
+ saved_changes[:public] == [true, false]
190
176
  end
191
177
 
192
178
  def unpublish_pages
@@ -13,7 +13,7 @@
13
13
 
14
14
  class Alchemy::LegacyPageUrl < ActiveRecord::Base
15
15
  belongs_to :page,
16
- class_name: 'Alchemy::Page',
16
+ class_name: "Alchemy::Page",
17
17
  required: true
18
18
 
19
19
  validates :urlname,
@@ -22,17 +22,17 @@ module Alchemy
22
22
 
23
23
  attr_accessor :contact_form_id, :ip
24
24
 
25
- config['fields'].each do |field|
25
+ config["fields"].each do |field|
26
26
  attr_accessor field.to_sym
27
27
  end
28
28
 
29
- config['validate_fields'].each do |field|
29
+ config["validate_fields"].each do |field|
30
30
  validates_presence_of field
31
31
 
32
32
  case field.to_sym
33
33
  when /email/
34
34
  validates_format_of field,
35
- with: Alchemy::Config.get('format_matchers')['email'],
35
+ with: Alchemy::Config.get("format_matchers")["email"],
36
36
  if: -> { send(field).present? }
37
37
  when :email_confirmation
38
38
  validates_confirmation_of :email
@@ -4,13 +4,22 @@ module Alchemy
4
4
  class Node < BaseRecord
5
5
  VALID_URL_REGEX = /\A(\/|\D[a-z\+\d\.\-]+:)/
6
6
 
7
+ before_destroy :check_if_related_essence_nodes_present
8
+
7
9
  acts_as_nested_set scope: "language_id", touch: true
8
- stampable stamper_class_name: Alchemy.user_class.name
10
+ stampable stamper_class_name: Alchemy.user_class_name
9
11
 
10
- belongs_to :site, class_name: "Alchemy::Site"
11
12
  belongs_to :language, class_name: "Alchemy::Language"
12
13
  belongs_to :page, class_name: "Alchemy::Page", optional: true, inverse_of: :nodes
13
14
 
15
+ has_one :site, through: :language
16
+
17
+ has_many :essence_nodes, class_name: "Alchemy::EssenceNode", foreign_key: :node_id, inverse_of: :ingredient_association
18
+
19
+ before_validation :translate_root_menu_name, if: -> { root? }
20
+ before_validation :set_menu_type_from_root, unless: -> { root? }
21
+
22
+ validates :menu_type, presence: true
14
23
  validates :name, presence: true, if: -> { page.nil? }
15
24
  validates :url, format: { with: VALID_URL_REGEX }, unless: -> { url.nil? }
16
25
 
@@ -62,11 +71,25 @@ module Alchemy
62
71
  end
63
72
 
64
73
  def to_partial_path
65
- "#{view_folder_name}/wrapper"
74
+ "alchemy/menus/#{menu_type}/node"
75
+ end
76
+
77
+ private
78
+
79
+ def check_if_related_essence_nodes_present
80
+ dependent_essence_nodes = self_and_descendants.flat_map(&:essence_nodes)
81
+ if dependent_essence_nodes.any?
82
+ errors.add(:base, :essence_nodes_present, page_names: dependent_essence_nodes.map(&:page).map(&:name).to_sentence)
83
+ throw(:abort)
84
+ end
85
+ end
86
+
87
+ def translate_root_menu_name
88
+ self.name ||= Alchemy.t(menu_type, scope: :menu_names)
66
89
  end
67
90
 
68
- def view_folder_name
69
- "alchemy/menus/#{name.parameterize.underscore}"
91
+ def set_menu_type_from_root
92
+ self.menu_type = root.menu_type
70
93
  end
71
94
  end
72
95
  end
@@ -15,7 +15,6 @@ module Alchemy
15
15
  # fixed_attributes:
16
16
  # - public_on: nil
17
17
  # - public_until: nil
18
- # - visible: false
19
18
  #
20
19
  class Page::FixedAttributes
21
20
  attr_reader :page
@@ -31,7 +30,7 @@ module Alchemy
31
30
  # @return Hash
32
31
  #
33
32
  def attributes
34
- @_attributes ||= page.definition.fetch('fixed_attributes', {}).symbolize_keys
33
+ @_attributes ||= page.definition.fetch("fixed_attributes", {}).symbolize_keys
35
34
  end
36
35
  alias_method :all, :attributes
37
36
 
@@ -52,6 +51,7 @@ module Alchemy
52
51
  #
53
52
  def fixed?(name)
54
53
  return false if name.nil?
54
+
55
55
  attributes.key?(name.to_sym)
56
56
  end
57
57
 
@@ -59,6 +59,7 @@ module Alchemy
59
59
  #
60
60
  def [](name)
61
61
  return nil if name.nil?
62
+
62
63
  attributes[name.to_sym]
63
64
  end
64
65
  end