alchemy_cms 7.1.6 → 7.2.0.b

Sign up to get free protection for your applications and to get access to all the features.
Files changed (315) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +141 -0
  3. data/Gemfile +2 -2
  4. data/LICENSE +1 -1
  5. data/README.md +5 -6
  6. data/SECURITY.md +1 -1
  7. data/alchemy_cms.gemspec +3 -4
  8. data/app/assets/javascripts/alchemy/admin.js +0 -9
  9. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +5 -15
  10. data/app/assets/javascripts/alchemy/alchemy.image_overlay.coffee +5 -4
  11. data/app/assets/javascripts/alchemy/templates/index.js +0 -1
  12. data/app/assets/javascripts/alchemy/templates/node_folder.hbs +1 -1
  13. data/app/assets/javascripts/alchemy/templates/page_folder.hbs +1 -1
  14. data/app/assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js +20 -7
  15. data/app/assets/stylesheets/alchemy/_custom-properties.scss +12 -0
  16. data/app/assets/stylesheets/alchemy/_mixins.scss +10 -6
  17. data/app/assets/stylesheets/alchemy/_variables.scss +3 -0
  18. data/app/assets/stylesheets/alchemy/admin.scss +2 -2
  19. data/app/assets/stylesheets/alchemy/archive.scss +4 -3
  20. data/app/assets/stylesheets/alchemy/attachment-select.scss +19 -0
  21. data/app/assets/stylesheets/alchemy/base.scss +31 -18
  22. data/app/assets/stylesheets/alchemy/buttons.scss +3 -4
  23. data/app/assets/stylesheets/alchemy/dashboard.scss +1 -1
  24. data/app/assets/stylesheets/alchemy/dialogs.scss +2 -5
  25. data/app/assets/stylesheets/alchemy/elements.scss +76 -46
  26. data/app/assets/stylesheets/alchemy/flash.scss +20 -70
  27. data/app/assets/stylesheets/alchemy/forms.scss +41 -36
  28. data/app/assets/stylesheets/alchemy/frame.scss +12 -3
  29. data/app/assets/stylesheets/alchemy/icons.scss +34 -2
  30. data/app/assets/stylesheets/alchemy/image_library.scss +18 -9
  31. data/app/assets/stylesheets/alchemy/{filter_field.scss → list_filter.scss} +8 -7
  32. data/app/assets/stylesheets/alchemy/lists.scss +1 -1
  33. data/app/assets/stylesheets/alchemy/navigation.scss +9 -12
  34. data/app/assets/stylesheets/alchemy/node-select.scss +1 -1
  35. data/app/assets/stylesheets/alchemy/nodes.scss +15 -13
  36. data/app/assets/stylesheets/alchemy/notices.scss +56 -39
  37. data/app/assets/stylesheets/alchemy/page-select.scss +1 -4
  38. data/app/assets/stylesheets/alchemy/pagination.scss +11 -1
  39. data/app/assets/stylesheets/alchemy/preview_window.scss +7 -3
  40. data/app/assets/stylesheets/alchemy/search.scss +4 -4
  41. data/app/assets/stylesheets/alchemy/selects.scss +13 -7
  42. data/app/assets/stylesheets/alchemy/shoelace.scss +33 -2
  43. data/app/assets/stylesheets/alchemy/sitemap.scss +155 -159
  44. data/app/assets/stylesheets/alchemy/tables.scss +49 -12
  45. data/app/assets/stylesheets/alchemy/tags.scss +17 -11
  46. data/app/assets/stylesheets/alchemy/toolbar.scss +2 -2
  47. data/app/assets/stylesheets/alchemy/typography.scss +41 -22
  48. data/app/assets/stylesheets/alchemy/upload.scss +5 -4
  49. data/app/components/alchemy/admin/attachment_select.rb +39 -0
  50. data/app/components/alchemy/admin/icon.rb +72 -0
  51. data/app/components/alchemy/admin/link_dialog/anchor_tab.rb +41 -0
  52. data/app/components/alchemy/admin/link_dialog/base_tab.rb +75 -0
  53. data/app/components/alchemy/admin/link_dialog/external_tab.rb +42 -0
  54. data/app/components/alchemy/admin/link_dialog/file_tab.rb +45 -0
  55. data/app/components/alchemy/admin/link_dialog/internal_tab.rb +66 -0
  56. data/app/components/alchemy/admin/link_dialog/tabs.rb +33 -0
  57. data/app/components/alchemy/admin/list_filter.rb +42 -0
  58. data/app/components/alchemy/admin/message.rb +19 -0
  59. data/app/components/alchemy/admin/tags_autocomplete.rb +25 -0
  60. data/app/components/alchemy/admin/toolbar_button.rb +111 -0
  61. data/app/components/alchemy/ingredients/audio_view.rb +1 -1
  62. data/app/components/alchemy/ingredients/base_view.rb +1 -1
  63. data/app/components/alchemy/ingredients/boolean_view.rb +1 -1
  64. data/app/components/alchemy/ingredients/datetime_view.rb +1 -1
  65. data/app/components/alchemy/ingredients/file_view.rb +1 -1
  66. data/app/components/alchemy/ingredients/headline_view.rb +16 -7
  67. data/app/components/alchemy/ingredients/link_view.rb +1 -7
  68. data/app/components/alchemy/ingredients/page_view.rb +1 -1
  69. data/app/components/alchemy/ingredients/picture_view.rb +3 -3
  70. data/app/components/alchemy/ingredients/richtext_view.rb +1 -1
  71. data/app/components/alchemy/ingredients/text_view.rb +2 -3
  72. data/app/components/alchemy/ingredients/video_view.rb +1 -1
  73. data/app/controllers/alchemy/admin/base_controller.rb +1 -1
  74. data/app/controllers/alchemy/admin/elements_controller.rb +4 -2
  75. data/app/controllers/alchemy/admin/ingredients_controller.rb +2 -0
  76. data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
  77. data/app/controllers/alchemy/admin/legacy_page_urls_controller.rb +12 -4
  78. data/app/controllers/alchemy/admin/nodes_controller.rb +26 -0
  79. data/app/controllers/alchemy/admin/pages_controller.rb +11 -78
  80. data/app/controllers/alchemy/admin/picture_descriptions_controller.rb +15 -0
  81. data/app/controllers/alchemy/admin/pictures_controller.rb +18 -1
  82. data/app/controllers/alchemy/admin/resources_controller.rb +15 -10
  83. data/app/controllers/alchemy/api/attachments_controller.rb +44 -0
  84. data/app/controllers/alchemy/api/pages_controller.rb +10 -6
  85. data/app/controllers/alchemy/base_controller.rb +2 -2
  86. data/app/controllers/alchemy/messages_controller.rb +2 -2
  87. data/app/controllers/alchemy/pages_controller.rb +8 -6
  88. data/app/controllers/concerns/alchemy/admin/current_language.rb +1 -1
  89. data/app/controllers/concerns/alchemy/legacy_page_redirects.rb +1 -1
  90. data/app/decorators/alchemy/element_editor.rb +2 -2
  91. data/app/helpers/alchemy/admin/base_helper.rb +8 -60
  92. data/app/helpers/alchemy/admin/elements_helper.rb +1 -1
  93. data/app/helpers/alchemy/admin/ingredients_helper.rb +1 -1
  94. data/app/helpers/alchemy/base_helper.rb +9 -91
  95. data/app/helpers/alchemy/elements_helper.rb +3 -3
  96. data/app/helpers/alchemy/pages_helper.rb +16 -9
  97. data/app/javascript/alchemy_admin/components/attachment_select.js +24 -0
  98. data/app/javascript/alchemy_admin/components/button.js +3 -0
  99. data/app/javascript/alchemy_admin/components/clipboard_button.js +3 -2
  100. data/app/javascript/alchemy_admin/components/dialog_link.js +10 -7
  101. data/app/javascript/alchemy_admin/components/dom_id_select.js +69 -0
  102. data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +42 -0
  103. data/app/javascript/alchemy_admin/components/element_editor/publish_element_button.js +4 -2
  104. data/app/javascript/alchemy_admin/components/element_editor.js +21 -13
  105. data/app/javascript/alchemy_admin/components/elements_window.js +87 -0
  106. data/app/javascript/alchemy_admin/components/growl.js +13 -0
  107. data/app/javascript/alchemy_admin/components/icon.js +51 -0
  108. data/app/javascript/alchemy_admin/components/index.js +24 -0
  109. data/app/javascript/alchemy_admin/components/ingredient_group.js +6 -0
  110. data/app/javascript/alchemy_admin/components/link_buttons/link_button.js +21 -11
  111. data/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js +2 -1
  112. data/app/javascript/alchemy_admin/components/link_buttons.js +1 -0
  113. data/app/javascript/alchemy_admin/components/list_filter.js +68 -0
  114. data/app/javascript/alchemy_admin/components/message.js +69 -0
  115. data/app/javascript/alchemy_admin/components/node_select.js +1 -1
  116. data/app/javascript/alchemy_admin/components/overlay.js +6 -6
  117. data/app/javascript/alchemy_admin/components/page_select.js +3 -7
  118. data/app/javascript/alchemy_admin/components/preview_window.js +121 -0
  119. data/app/javascript/alchemy_admin/components/remote_select.js +4 -1
  120. data/app/javascript/alchemy_admin/components/select.js +37 -1
  121. data/app/javascript/alchemy_admin/components/tags_autocomplete.js +57 -0
  122. data/app/javascript/alchemy_admin/components/uploader/file_upload.js +4 -3
  123. data/app/javascript/alchemy_admin/components/uploader/progress.js +1 -1
  124. data/app/javascript/alchemy_admin/confirm_dialog.js +133 -0
  125. data/app/javascript/alchemy_admin/dirty.js +19 -14
  126. data/app/javascript/alchemy_admin/fixed_elements.js +24 -0
  127. data/app/javascript/alchemy_admin/growler.js +15 -0
  128. data/app/javascript/alchemy_admin/gui.js +2 -4
  129. data/app/javascript/alchemy_admin/hotkeys.js +60 -0
  130. data/app/javascript/alchemy_admin/image_loader.js +2 -2
  131. data/app/javascript/alchemy_admin/ingredient_anchor_link.js +2 -3
  132. data/app/javascript/alchemy_admin/initializer.js +1 -8
  133. data/app/javascript/alchemy_admin/link_dialog.js +131 -0
  134. data/app/javascript/alchemy_admin/locales/en.js +3 -0
  135. data/app/javascript/alchemy_admin/node_tree.js +4 -3
  136. data/app/javascript/alchemy_admin/page_sorter.js +23 -14
  137. data/app/javascript/alchemy_admin/picture_editors.js +3 -2
  138. data/app/javascript/alchemy_admin/shoelace_theme.js +60 -0
  139. data/app/javascript/alchemy_admin/sitemap.js +9 -3
  140. data/app/javascript/alchemy_admin/sortable_elements.js +4 -6
  141. data/app/javascript/alchemy_admin.js +18 -42
  142. data/app/models/alchemy/current.rb +26 -0
  143. data/app/models/alchemy/element.rb +1 -1
  144. data/app/models/alchemy/ingredients/headline.rb +8 -1
  145. data/app/models/alchemy/ingredients/picture.rb +6 -0
  146. data/app/models/alchemy/language.rb +8 -6
  147. data/app/models/alchemy/node.rb +2 -2
  148. data/app/models/alchemy/page/page_elements.rb +8 -8
  149. data/app/models/alchemy/page/page_layouts.rb +3 -3
  150. data/app/models/alchemy/page/page_natures.rb +13 -9
  151. data/app/models/alchemy/page/page_scopes.rb +2 -2
  152. data/app/models/alchemy/page/publisher.rb +1 -0
  153. data/app/models/alchemy/page.rb +15 -28
  154. data/app/models/alchemy/picture.rb +8 -0
  155. data/app/models/alchemy/picture_description.rb +8 -0
  156. data/app/models/alchemy/picture_variant.rb +1 -1
  157. data/app/models/alchemy/site.rb +10 -7
  158. data/app/serializers/alchemy/attachment_serializer.rb +8 -0
  159. data/app/serializers/alchemy/page_node_serializer.rb +9 -0
  160. data/app/views/alchemy/_menubar.html.erb +1 -1
  161. data/app/views/alchemy/_preview_mode_code.html.erb +1 -1
  162. data/app/views/alchemy/admin/attachments/_tag_list.html.erb +2 -2
  163. data/app/views/alchemy/admin/attachments/archive_overlay.js.erb +0 -1
  164. data/app/views/alchemy/admin/attachments/edit.html.erb +3 -4
  165. data/app/views/alchemy/admin/clipboard/clear.js.erb +1 -1
  166. data/app/views/alchemy/admin/clipboard/index.html.erb +1 -1
  167. data/app/views/alchemy/admin/clipboard/insert.js.erb +1 -1
  168. data/app/views/alchemy/admin/clipboard/remove.js.erb +1 -1
  169. data/app/views/alchemy/admin/dashboard/_locked_pages.html.erb +1 -1
  170. data/app/views/alchemy/admin/dashboard/_sites.html.erb +1 -1
  171. data/app/views/alchemy/admin/dashboard/help.html.erb +48 -12
  172. data/app/views/alchemy/admin/dashboard/index.html.erb +1 -1
  173. data/app/views/alchemy/admin/dashboard/info.html.erb +5 -8
  174. data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +1 -1
  175. data/app/views/alchemy/admin/elements/_element.html.erb +5 -5
  176. data/app/views/alchemy/admin/elements/_footer.html.erb +1 -1
  177. data/app/views/alchemy/admin/elements/_header.html.erb +6 -2
  178. data/app/views/alchemy/admin/elements/_toolbar.html.erb +8 -6
  179. data/app/views/alchemy/admin/elements/create.js.erb +1 -5
  180. data/app/views/alchemy/admin/elements/index.html.erb +70 -34
  181. data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +1 -2
  182. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +3 -5
  183. data/app/views/alchemy/admin/languages/_language.html.erb +1 -1
  184. data/app/views/alchemy/admin/languages/index.html.erb +2 -2
  185. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +18 -18
  186. data/app/views/alchemy/admin/layoutpages/edit.html.erb +3 -4
  187. data/app/views/alchemy/admin/layoutpages/index.html.erb +2 -2
  188. data/app/views/alchemy/admin/legacy_page_urls/_legacy_page_url.html.erb +10 -11
  189. data/app/views/alchemy/admin/legacy_page_urls/_new.html.erb +15 -17
  190. data/app/views/alchemy/admin/legacy_page_urls/_table.html.erb +16 -0
  191. data/app/views/alchemy/admin/legacy_page_urls/_update.turbo_stream.erb +12 -0
  192. data/app/views/alchemy/admin/legacy_page_urls/create.turbo_stream.erb +8 -0
  193. data/app/views/alchemy/admin/legacy_page_urls/destroy.turbo_stream.erb +1 -0
  194. data/app/views/alchemy/admin/legacy_page_urls/edit.html.erb +27 -0
  195. data/app/views/alchemy/admin/legacy_page_urls/show.html.erb +1 -0
  196. data/app/views/alchemy/admin/legacy_page_urls/update.turbo_stream.erb +1 -0
  197. data/app/views/alchemy/admin/nodes/_form.html.erb +12 -11
  198. data/app/views/alchemy/admin/nodes/_label.html.erb +1 -0
  199. data/app/views/alchemy/admin/nodes/_node.html.erb +19 -19
  200. data/app/views/alchemy/admin/nodes/_page_nodes.html.erb +48 -0
  201. data/app/views/alchemy/admin/nodes/_update.turbo_stream.erb +9 -0
  202. data/app/views/alchemy/admin/nodes/create.turbo_stream.erb +1 -0
  203. data/app/views/alchemy/admin/nodes/destroy.turbo_stream.erb +1 -0
  204. data/app/views/alchemy/admin/nodes/index.html.erb +3 -3
  205. data/app/views/alchemy/admin/pages/_form.html.erb +3 -4
  206. data/app/views/alchemy/admin/pages/_legacy_urls.html.erb +4 -15
  207. data/app/views/alchemy/admin/pages/_page.html.erb +39 -39
  208. data/app/views/alchemy/admin/pages/_table_row.html.erb +3 -3
  209. data/app/views/alchemy/admin/pages/_toolbar.html.erb +2 -2
  210. data/app/views/alchemy/admin/pages/configure.html.erb +6 -0
  211. data/app/views/alchemy/admin/pages/edit.html.erb +15 -62
  212. data/app/views/alchemy/admin/pages/unlock.js.erb +2 -2
  213. data/app/views/alchemy/admin/partials/_autocomplete_tag_list.html.erb +3 -1
  214. data/app/views/alchemy/admin/partials/_flash_notices.html.erb +4 -2
  215. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +1 -1
  216. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +5 -2
  217. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +2 -2
  218. data/app/views/alchemy/admin/partials/_search_form.html.erb +2 -2
  219. data/app/views/alchemy/admin/partials/_site_select.html.erb +1 -1
  220. data/app/views/alchemy/admin/picture_descriptions/_form.html.erb +11 -0
  221. data/app/views/alchemy/admin/picture_descriptions/edit.html.erb +6 -0
  222. data/app/views/alchemy/admin/pictures/_form.html.erb +4 -3
  223. data/app/views/alchemy/admin/pictures/_infos.html.erb +1 -1
  224. data/app/views/alchemy/admin/pictures/_picture_description_field.html.erb +29 -0
  225. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +2 -2
  226. data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +0 -2
  227. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +3 -3
  228. data/app/views/alchemy/admin/pictures/show.html.erb +3 -3
  229. data/app/views/alchemy/admin/resources/_form.html.erb +3 -4
  230. data/app/views/alchemy/admin/resources/_tag_list.html.erb +2 -2
  231. data/app/views/alchemy/admin/resources/index.html.erb +2 -2
  232. data/app/views/alchemy/admin/sites/index.html.erb +1 -1
  233. data/app/views/alchemy/admin/styleguide/index.html.erb +29 -24
  234. data/app/views/alchemy/admin/tags/_tag.html.erb +1 -1
  235. data/app/views/alchemy/admin/tags/edit.html.erb +1 -1
  236. data/app/views/alchemy/admin/tags/index.html.erb +1 -1
  237. data/app/views/alchemy/base/500.html.erb +7 -18
  238. data/app/views/alchemy/base/error_notice.html.erb +3 -1
  239. data/app/views/alchemy/ingredients/_boolean_editor.html.erb +1 -1
  240. data/app/views/alchemy/ingredients/_datetime_editor.html.erb +1 -1
  241. data/app/views/alchemy/ingredients/_headline_editor.html.erb +13 -8
  242. data/app/views/alchemy/ingredients/_picture_editor.html.erb +1 -1
  243. data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +1 -1
  244. data/app/views/alchemy/language_links/_language.html.erb +1 -1
  245. data/app/views/kaminari/alchemy/_first_page.html.erb +2 -2
  246. data/app/views/kaminari/alchemy/_gap.html.erb +1 -1
  247. data/app/views/kaminari/alchemy/_last_page.html.erb +2 -2
  248. data/app/views/kaminari/alchemy/_next_page.html.erb +2 -2
  249. data/app/views/kaminari/alchemy/_prev_page.html.erb +2 -2
  250. data/app/views/layouts/alchemy/admin.html.erb +2 -1
  251. data/bundles/shoelace.js +3 -1
  252. data/config/locales/alchemy.en.yml +16 -3
  253. data/config/routes.rb +3 -1
  254. data/db/migrate/20240314105244_create_alchemy_picture_descriptions.rb +11 -0
  255. data/lib/alchemy/configuration_methods.rb +1 -1
  256. data/lib/alchemy/controller_actions.rb +3 -3
  257. data/lib/alchemy/element_definition.rb +10 -6
  258. data/lib/alchemy/engine.rb +19 -2
  259. data/lib/alchemy/modules.rb +2 -2
  260. data/lib/alchemy/page_layout.rb +10 -6
  261. data/lib/alchemy/permissions.rb +3 -2
  262. data/lib/alchemy/seeder.rb +2 -2
  263. data/lib/alchemy/test_support/capybara_helpers.rb +4 -0
  264. data/lib/alchemy/test_support/factories/language_factory.rb +1 -1
  265. data/lib/alchemy/test_support/shared_contexts.rb +8 -0
  266. data/lib/alchemy/tinymce.rb +2 -1
  267. data/lib/alchemy/version.rb +1 -1
  268. data/lib/alchemy.rb +36 -0
  269. data/lib/alchemy_cms.rb +0 -1
  270. data/lib/generators/alchemy/menus/templates/node.html.erb +2 -2
  271. data/lib/generators/alchemy/menus/templates/node.html.haml +2 -2
  272. data/lib/generators/alchemy/menus/templates/node.html.slim +2 -2
  273. data/lib/generators/alchemy/menus/templates/wrapper.html.erb +1 -1
  274. data/lib/generators/alchemy/menus/templates/wrapper.html.haml +1 -1
  275. data/lib/generators/alchemy/menus/templates/wrapper.html.slim +1 -1
  276. data/lib/tasks/alchemy/sitemap.rake +97 -0
  277. data/package.json +8 -8
  278. data/vendor/assets/fonts/remixicon.symbol.svg +11 -0
  279. data/vendor/javascript/shoelace.min.js +333 -118
  280. data/vendor/javascript/sortable.min.js +1 -1
  281. data/vendor/javascript/tinymce.min.js +1 -1
  282. data/vendor/javascript/ungap-custom-elements.min.js +1 -1
  283. metadata +61 -54
  284. data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +0 -85
  285. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +0 -107
  286. data/app/assets/javascripts/alchemy/alchemy.file_progress.js.coffee +0 -66
  287. data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +0 -45
  288. data/app/assets/javascripts/alchemy/alchemy.growler.js.coffee +0 -24
  289. data/app/assets/javascripts/alchemy/alchemy.hotkeys.js.coffee +0 -49
  290. data/app/assets/javascripts/alchemy/alchemy.initializer.js.coffee +0 -0
  291. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +0 -230
  292. data/app/assets/javascripts/alchemy/alchemy.list_filter.js.coffee +0 -49
  293. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +0 -82
  294. data/app/assets/javascripts/alchemy/alchemy.string_extension.js.coffee +0 -11
  295. data/app/assets/javascripts/alchemy/templates/page.hbs +0 -19
  296. data/app/javascript/alchemy_admin/tags_autocomplete.js +0 -46
  297. data/app/models/alchemy/tree_node.rb +0 -7
  298. data/app/views/alchemy/admin/elements/destroy.js.erb +0 -8
  299. data/app/views/alchemy/admin/legacy_page_urls/_form.html.erb +0 -5
  300. data/app/views/alchemy/admin/legacy_page_urls/create.js.erb +0 -9
  301. data/app/views/alchemy/admin/legacy_page_urls/destroy.js.erb +0 -6
  302. data/app/views/alchemy/admin/legacy_page_urls/update.js.erb +0 -2
  303. data/app/views/alchemy/admin/pages/_anchor_link.html.erb +0 -22
  304. data/app/views/alchemy/admin/pages/_external_link.html.erb +0 -31
  305. data/app/views/alchemy/admin/pages/_file_link.html.erb +0 -31
  306. data/app/views/alchemy/admin/pages/_internal_link.html.erb +0 -35
  307. data/app/views/alchemy/admin/pages/link.html.erb +0 -26
  308. data/app/views/alchemy/admin/partials/_flash.html.erb +0 -4
  309. data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +0 -29
  310. data/vendor/assets/fonts/remixicon.eot +0 -0
  311. data/vendor/assets/fonts/remixicon.svg +0 -7816
  312. data/vendor/assets/fonts/remixicon.ttf +0 -0
  313. data/vendor/assets/fonts/remixicon.woff +0 -0
  314. data/vendor/assets/fonts/remixicon.woff2 +0 -0
  315. data/vendor/assets/stylesheets/remixicon.scss +0 -10480
@@ -7,13 +7,22 @@ module Alchemy
7
7
  end
8
8
 
9
9
  def call
10
- content_tag "h#{@level || ingredient.level}",
11
- ingredient.value,
12
- id: ingredient.dom_id.presence,
13
- class: [
14
- ingredient.size ? "h#{ingredient.size}" : nil,
15
- html_options[:class]
16
- ]
10
+ content_tag tag_name, id: dom_id, class: css_classes do
11
+ ingredient.value
12
+ end.html_safe
13
+ end
14
+
15
+ private
16
+
17
+ def tag_name = "h#{@level || ingredient.level}"
18
+
19
+ def dom_id = ingredient.dom_id.presence
20
+
21
+ def css_classes
22
+ [
23
+ ingredient.size ? "h#{ingredient.size}" : nil,
24
+ html_options[:class]
25
+ ]
17
26
  end
18
27
  end
19
28
  end
@@ -12,13 +12,7 @@ module Alchemy
12
12
  end
13
13
 
14
14
  def call
15
- link_to(link_text, value, {target: link_target}.merge(html_options))
16
- end
17
-
18
- private
19
-
20
- def link_target
21
- (ingredient.link_target == "blank") ? "_blank" : nil
15
+ link_to(link_text, value, {target: ingredient.link_target.presence}.merge(html_options)).html_safe
22
16
  end
23
17
  end
24
18
  end
@@ -4,7 +4,7 @@ module Alchemy
4
4
  delegate :page, to: :ingredient
5
5
 
6
6
  def call
7
- link_to page.name, alchemy.show_page_path(urlname: page.urlname)
7
+ link_to(page.name, alchemy.show_page_path(urlname: page.urlname)).html_safe
8
8
  end
9
9
 
10
10
  def render?
@@ -57,7 +57,7 @@ module Alchemy
57
57
  content_tag(:figure, output, {class: ingredient.css_class.presence}.merge(html_options))
58
58
  else
59
59
  output
60
- end
60
+ end.html_safe
61
61
  end
62
62
 
63
63
  private
@@ -65,7 +65,7 @@ module Alchemy
65
65
  def caption
66
66
  return unless show_caption?
67
67
 
68
- @_caption ||= content_tag(:figcaption, ingredient.caption)
68
+ @_caption ||= content_tag(:figcaption, ingredient.caption.html_safe)
69
69
  end
70
70
 
71
71
  def src
@@ -101,7 +101,7 @@ module Alchemy
101
101
  end
102
102
 
103
103
  def alt_text
104
- ingredient.alt_tag.presence || html_options.delete(:alt) || ingredient.picture.name&.humanize
104
+ html_options.delete(:alt) || ingredient.alt_text
105
105
  end
106
106
  end
107
107
  end
@@ -15,7 +15,7 @@ module Alchemy
15
15
  ingredient.stripped_body
16
16
  else
17
17
  value.to_s.html_safe
18
- end
18
+ end.html_safe
19
19
  end
20
20
  end
21
21
  end
@@ -21,10 +21,9 @@ module Alchemy
21
21
  link_to(value, url_for(link), {
22
22
  id: dom_id.presence,
23
23
  title: link_title,
24
- target: ((link_target == "blank") ? "_blank" : nil),
25
- data: {link_target: link_target}
24
+ target: link_target
26
25
  }.merge(html_options))
27
- end
26
+ end.html_safe
28
27
  end
29
28
 
30
29
  private
@@ -6,7 +6,7 @@ module Alchemy
6
6
  def call
7
7
  content_tag(:video, html_options) do
8
8
  tag(:source, src: src, type: attachment.file_mime_type)
9
- end
9
+ end.html_safe
10
10
  end
11
11
 
12
12
  def render?
@@ -33,7 +33,7 @@ module Alchemy
33
33
 
34
34
  # Disable layout rendering for xhr requests.
35
35
  def set_layout
36
- request.xhr? ? false : "alchemy/admin"
36
+ (request.xhr? || turbo_frame_request?) ? false : "alchemy/admin"
37
37
  end
38
38
 
39
39
  # Handles exceptions
@@ -76,9 +76,11 @@ module Alchemy
76
76
  end
77
77
 
78
78
  def destroy
79
- @richtext_ids = @element.richtext_ingredients_ids
80
79
  @element.destroy
81
- @notice = Alchemy.t("Successfully deleted element") % {element: @element.display_name}
80
+
81
+ render json: {
82
+ message: Alchemy.t("Successfully deleted element") % {element: @element.display_name}
83
+ }
82
84
  end
83
85
 
84
86
  def publish
@@ -10,6 +10,8 @@ module Alchemy
10
10
  helper "Alchemy::Admin::Ingredients"
11
11
 
12
12
  def edit
13
+ @language = Alchemy::Language.find_by(id: params[:language_id]) ||
14
+ Alchemy::Current.language
13
15
  end
14
16
 
15
17
  def update
@@ -46,7 +46,7 @@ module Alchemy
46
46
  private
47
47
 
48
48
  def load_current_site
49
- @current_site = Alchemy::Site.current
49
+ @current_site = Alchemy::Current.site
50
50
  if @current_site.nil?
51
51
  flash[:warning] = Alchemy.t("Please create a site first.")
52
52
  do_redirect_to admin_sites_path
@@ -9,13 +9,17 @@ module Alchemy
9
9
  end
10
10
 
11
11
  def create
12
- @legacy_page_url = @page.legacy_urls.build(legacy_page_url_params)
13
- @legacy_page_url.save
12
+ @legacy_page_url = @page.legacy_urls.create(legacy_page_url_params)
13
+ @message = message_for_resource_action
14
+ end
15
+
16
+ def show
14
17
  end
15
18
 
16
19
  def update
17
20
  @legacy_page_url = LegacyPageUrl.find(params[:id])
18
21
  if @legacy_page_url.update(legacy_page_url_params)
22
+ @message = message_for_resource_action
19
23
  render :update
20
24
  else
21
25
  render :edit
@@ -23,8 +27,8 @@ module Alchemy
23
27
  end
24
28
 
25
29
  def destroy
26
- @legacy_page_url = LegacyPageUrl.find(params[:id])
27
- @legacy_page_url.destroy
30
+ @page.legacy_urls.destroy(@legacy_page_url)
31
+ @message = message_for_resource_action
28
32
  end
29
33
 
30
34
  private
@@ -33,6 +37,10 @@ module Alchemy
33
37
  @page = Page.find(params[:page_id])
34
38
  end
35
39
 
40
+ def load_resource
41
+ @legacy_page_url = LegacyPageUrl.find(params[:id])
42
+ end
43
+
36
44
  def legacy_page_url_params
37
45
  params.require(:legacy_page_url).permit(:urlname)
38
46
  end
@@ -16,6 +16,32 @@ module Alchemy
16
16
  )
17
17
  end
18
18
 
19
+ def create
20
+ if turbo_frame_request?
21
+ @page = Alchemy::Page.find(resource_params[:page_id])
22
+ @node = @page.nodes.build(resource_params)
23
+ if @node.valid?
24
+ @node.save
25
+ flash_notice_for_resource_action(:create)
26
+ else
27
+ flash[:error] = @node.errors.full_messages.join(", ")
28
+ end
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def destroy
35
+ if turbo_frame_request?
36
+ @node = Alchemy::Node.find(params[:id])
37
+ @page = @node.page
38
+ @page.nodes.destroy(@node)
39
+ flash_notice_for_resource_action(:destroy)
40
+ else
41
+ super
42
+ end
43
+ end
44
+
19
45
  private
20
46
 
21
47
  def resource_params
@@ -7,11 +7,11 @@ module Alchemy
7
7
 
8
8
  helper "alchemy/pages"
9
9
 
10
- before_action :load_resource, except: [:index, :flush, :new, :order, :create, :copy_language_tree, :link]
10
+ before_action :load_resource, except: [:index, :flush, :new, :create, :copy_language_tree, :link]
11
11
 
12
12
  authorize_resource class: Alchemy::Page, except: [:index, :tree]
13
13
 
14
- before_action only: [:index, :tree, :flush, :new, :order, :create, :copy_language_tree] do
14
+ before_action only: [:index, :tree, :flush, :new, :create, :copy_language_tree] do
15
15
  authorize! :index, :alchemy_admin_pages
16
16
  end
17
17
 
@@ -21,7 +21,7 @@ module Alchemy
21
21
  except: [:show]
22
22
 
23
23
  before_action :set_root_page,
24
- only: [:index, :show, :order]
24
+ only: [:index, :show]
25
25
 
26
26
  before_action :set_preview_mode, only: [:show]
27
27
 
@@ -66,7 +66,7 @@ module Alchemy
66
66
  # Used by page preview iframe in Page#edit view.
67
67
  #
68
68
  def show
69
- Page.current_preview = @page
69
+ Current.preview_page = @page
70
70
  # Setting the locale to pages language, so the page content has it's correct translations.
71
71
  ::I18n.locale = @page.language.locale
72
72
  render(layout: Alchemy::Config.get(:admin_page_preview_layout) || "application")
@@ -112,7 +112,9 @@ module Alchemy
112
112
  klass.new(routes: Alchemy::Engine.routes).url_for(@page)
113
113
  ]
114
114
  end
115
+ @preview_url = @preview_urls.first.last
115
116
  @layoutpage = @page.layoutpage?
117
+ Alchemy::Current.language = @page.language
116
118
  end
117
119
 
118
120
  # Set page configuration like page names, meta tags and states.
@@ -163,9 +165,7 @@ module Alchemy
163
165
  end
164
166
 
165
167
  def link
166
- @attachments = Attachment.all.collect { |f|
167
- [f.name, download_attachment_path(id: f.id, name: f.slug)]
168
- }
168
+ render LinkDialog::Tabs.new(**link_dialog_params)
169
169
  end
170
170
 
171
171
  def fold
@@ -212,23 +212,6 @@ module Alchemy
212
212
  redirect_to admin_pages_path
213
213
  end
214
214
 
215
- # Receives a JSON object representing a language tree to be ordered
216
- # and updates all pages in that language structure to their correct indexes
217
- def order
218
- neworder = JSON.parse(params[:set])
219
- tree = create_tree(neworder, @page_root)
220
-
221
- Alchemy::Page.transaction do
222
- tree.each do |key, node|
223
- dbitem = Page.find(key)
224
- dbitem.update_node!(node)
225
- end
226
- end
227
-
228
- flash[:notice] = Alchemy.t("Pages order saved")
229
- do_redirect_to admin_pages_path
230
- end
231
-
232
215
  def flush
233
216
  @current_language.pages.flushables.update_all(published_at: Time.current)
234
217
  # We need to ensure, that also all layoutpages get the +published_at+ timestamp set,
@@ -261,60 +244,6 @@ module Alchemy
261
244
  Page.language_root_for(params[:languages][:old_lang_id])
262
245
  end
263
246
 
264
- # Returns the current left index and the aggregated hash of tree nodes indexed by page id visited so far
265
- #
266
- # Visits a batch of children nodes, assigns them the correct ordering indexes and spuns recursively the same
267
- # procedure on their children, if any
268
- #
269
- # @param [Array]
270
- # An array of children nodes to be visited
271
- # @param [Integer]
272
- # The lft attribute that should be given to the first node in the array
273
- # @param [Integer]
274
- # The page id of the parent of this batch of children nodes
275
- # @param [Integer]
276
- # The depth at which these children reside
277
- # @param [Hash]
278
- # A Hash of TreeNode's indexed by their page ids
279
- # @param [String]
280
- # The url for the parent node of these children
281
- # @param [Boolean]
282
- # Whether these children reside in a restricted branch according to their ancestors
283
- #
284
- def visit_nodes(nodes, my_left, parent, depth, tree, url, restricted)
285
- nodes.each do |item|
286
- my_right = my_left + 1
287
- my_restricted = item["restricted"] || restricted
288
- urls = process_url(url, item)
289
-
290
- if item["children"]
291
- my_right, tree = visit_nodes(item["children"], my_left + 1, item["id"], depth + 1, tree, urls[:children_path], my_restricted)
292
- end
293
-
294
- tree[item["id"]] = TreeNode.new(my_left, my_right, parent, depth, urls[:my_urlname], my_restricted)
295
- my_left = my_right + 1
296
- end
297
-
298
- [my_left, tree]
299
- end
300
-
301
- # Returns a Hash of TreeNode's indexed by their page ids
302
- #
303
- # Grabs the array representing a tree structure of pages passed as a parameter,
304
- # visits it and creates a map of TreeNodes indexed by page id featuring Nested Set
305
- # ordering information consisting of the left, right, depth and parent_id indexes as
306
- # well as a node's url and restricted status
307
- #
308
- # @param [Array]
309
- # An Array representing a tree of Alchemy::Page's
310
- # @param [Alchemy::Page]
311
- # The root page for the language being ordered
312
- #
313
- def create_tree(items, rootpage)
314
- _, tree = visit_nodes(items, rootpage.lft + 1, rootpage.id, rootpage.depth + 1, {}, "", rootpage.restricted)
315
- tree
316
- end
317
-
318
247
  # Returns a pair, the path that a given tree node should take, and the path its children should take
319
248
  #
320
249
  # This function will add a node's own slug into their ancestor's path
@@ -355,6 +284,10 @@ module Alchemy
355
284
  params.require(:page).permit(*secure_attributes)
356
285
  end
357
286
 
287
+ def link_dialog_params
288
+ params.permit([:url, :selected_tab, :link_title, :link_target])
289
+ end
290
+
358
291
  def secure_attributes
359
292
  if can?(:create, Alchemy::Page)
360
293
  Page::PERMITTED_ATTRIBUTES + [:language_root, :parent_id, :language_id, :language_code]
@@ -0,0 +1,15 @@
1
+ module Alchemy
2
+ module Admin
3
+ class PictureDescriptionsController < Alchemy::Admin::ResourcesController
4
+ def edit
5
+ @picture_description = @picture.descriptions.find_or_initialize_by(language_id: params[:language_id])
6
+ end
7
+
8
+ private
9
+
10
+ def load_resource
11
+ @picture = Alchemy::Picture.find(params[:picture_id])
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,6 +5,7 @@ module Alchemy
5
5
  class PicturesController < Alchemy::Admin::ResourcesController
6
6
  include UploaderResponses
7
7
  include ArchiveOverlay
8
+ include CurrentLanguage
8
9
 
9
10
  helper "alchemy/admin/tags"
10
11
 
@@ -33,6 +34,9 @@ module Alchemy
33
34
  @previous = filtered_pictures.where("name < ?", @picture.name).last
34
35
  @next = filtered_pictures.where("name > ?", @picture.name).first
35
36
  @assignments = @picture.picture_ingredients.joins(element: :page)
37
+ @picture_description = @picture.descriptions.find_or_initialize_by(
38
+ language_id: Alchemy::Current.language.id
39
+ )
36
40
 
37
41
  render action: "show"
38
42
  end
@@ -193,7 +197,20 @@ module Alchemy
193
197
  end
194
198
 
195
199
  def picture_params
196
- params.require(:picture).permit(:image_file, :upload_hash, :name, :tag_list)
200
+ params.require(:picture).permit(
201
+ :image_file,
202
+ :upload_hash,
203
+ :name,
204
+ {
205
+ descriptions_attributes: [
206
+ :id,
207
+ :text,
208
+ :language_id,
209
+ :picture_id
210
+ ]
211
+ },
212
+ :tag_list
213
+ )
197
214
  end
198
215
 
199
216
  def picture_url_params
@@ -141,23 +141,28 @@ module Alchemy
141
141
  resource_filters.map(&:values).flatten
142
142
  end
143
143
 
144
- # Returns a translated +flash[:notice]+.
145
- # The key should look like "Modelname successfully created|updated|destroyed."
146
- def flash_notice_for_resource_action(action = params[:action])
144
+ # Returns a translated +flash[:notice]+ for current controller action.
145
+ def flash_notice_for_resource_action(action = action_name)
147
146
  return if resource_instance_variable.errors.any?
148
147
 
148
+ flash[:notice] = message_for_resource_action(action)
149
+ end
150
+
151
+ # Returns a translated message for a +flash[:notice]+.
152
+ # The key should look like "Modelname successfully created|updated|destroyed."
153
+ def message_for_resource_action(action = action_name)
149
154
  case action.to_sym
150
155
  when :create
151
- verb = "created"
156
+ verb = Alchemy.t("created", scope: "resources.actions")
152
157
  when :update
153
- verb = "updated"
158
+ verb = Alchemy.t("updated", scope: "resources.actions")
154
159
  when :destroy
155
- verb = "removed"
160
+ verb = Alchemy.t("removed", scope: "resources.actions")
156
161
  end
157
- flash[:notice] = Alchemy.t(
158
- "#{resource_handler.resource_name.classify} successfully #{verb}",
159
- default: Alchemy.t("Successfully #{verb}")
160
- )
162
+ Alchemy.t("%{resource_name} successfully %{action}",
163
+ resource_name: resource_handler.model.model_name.human,
164
+ action: verb,
165
+ default: Alchemy.t("Successfully #{verb}"))
161
166
  end
162
167
 
163
168
  def is_alchemy_module?
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class Api::AttachmentsController < Api::BaseController
5
+ def index
6
+ authorize! :index, Attachment
7
+
8
+ @attachments = Attachment.all
9
+ @attachments = @attachments.ransack(params[:q]).result
10
+
11
+ if params[:page]
12
+ @attachments = @attachments.page(params[:page]).per(params[:per_page])
13
+ end
14
+
15
+ render json: @attachments, adapter: :json, root: "data", meta: meta_data
16
+ end
17
+
18
+ private
19
+
20
+ def meta_data
21
+ {
22
+ total_count: total_count_value,
23
+ per_page: per_page_value,
24
+ page: page_value
25
+ }
26
+ end
27
+
28
+ def total_count_value
29
+ params[:page] ? @attachments.total_count : @attachments.size
30
+ end
31
+
32
+ def per_page_value
33
+ if params[:page]
34
+ (params[:per_page] || Kaminari.config.default_per_page).to_i
35
+ else
36
+ @attachments.size
37
+ end
38
+ end
39
+
40
+ def page_value
41
+ params[:page] ? params[:page].to_i : 1
42
+ end
43
+ end
44
+ end
@@ -49,9 +49,13 @@ module Alchemy
49
49
  def move
50
50
  @page = Page.find(params[:id])
51
51
  authorize! :update, @page
52
- target_parent_page = Page.find(params[:target_parent_id])
53
- @page.move_to_child_with_index(target_parent_page, params[:new_position])
54
- render json: @page, serializer: PageSerializer
52
+ begin
53
+ target_parent_page = Page.find(params[:target_parent_id])
54
+ @page.move_to_child_with_index(target_parent_page, params[:new_position])
55
+ render json: @page, serializer: PageNodeSerializer
56
+ rescue => err
57
+ render json: {message: err.message}, status: 422
58
+ end
55
59
  end
56
60
 
57
61
  private
@@ -66,11 +70,11 @@ module Alchemy
66
70
  end
67
71
 
68
72
  def load_page_by_urlname
69
- return unless Language.current
73
+ return unless Current.language
70
74
 
71
- Language.current.pages.where(
75
+ Current.language.pages.where(
72
76
  urlname: params[:urlname],
73
- language_code: params[:locale] || Language.current.code
77
+ language_code: params[:locale] || Current.language.code
74
78
  ).includes(page_includes).first
75
79
  end
76
80
 
@@ -25,9 +25,9 @@ module Alchemy
25
25
  # Sets +I18n.locale+ to current Alchemy language.
26
26
  #
27
27
  def set_locale
28
- return unless Language.current
28
+ return unless Current.language
29
29
 
30
- ::I18n.locale = Language.current&.locale
30
+ ::I18n.locale = Current.language&.locale
31
31
  end
32
32
 
33
33
  def not_found_error!(msg = "Not found \"#{request.fullpath}\"")
@@ -99,7 +99,7 @@ module Alchemy
99
99
  end
100
100
  redirect_to show_page_path(
101
101
  urlname: urlname,
102
- locale: prefix_locale? ? Language.current.code : nil
102
+ locale: prefix_locale? ? Current.language.code : nil
103
103
  )
104
104
  end
105
105
 
@@ -117,7 +117,7 @@ module Alchemy
117
117
  end
118
118
 
119
119
  def get_page
120
- @page = Language.current.pages.find_by(page_layout: mailer_config["page_layout_name"])
120
+ @page = Current.language.pages.find_by(page_layout: mailer_config["page_layout_name"])
121
121
  if @page.blank?
122
122
  raise "Page for page_layout #{mailer_config["page_layout_name"]} not found"
123
123
  end
@@ -105,6 +105,7 @@ module Alchemy
105
105
  #
106
106
  def load_index_page
107
107
  @page ||= Language.current_root_page
108
+ Current.page = @page
108
109
  render template: "alchemy/welcome", layout: false if signup_required?
109
110
  end
110
111
 
@@ -118,16 +119,17 @@ module Alchemy
118
119
  # @return NilClass
119
120
  #
120
121
  def load_page
121
- page_not_found! unless Language.current
122
+ page_not_found! unless Current.language
122
123
 
123
- @page ||= Language.current.pages.contentpages.find_by(
124
+ @page ||= Current.language.pages.contentpages.find_by(
124
125
  urlname: params[:urlname],
125
- language_code: params[:locale] || Language.current.code
126
+ language_code: params[:locale] || Current.language.code
126
127
  )
128
+ Current.page = @page
127
129
  end
128
130
 
129
131
  def enforce_locale
130
- redirect_permanently_to page_locale_redirect_url(locale: Language.current.code)
132
+ redirect_permanently_to page_locale_redirect_url(locale: Current.language.code)
131
133
  end
132
134
 
133
135
  def locale_prefix_missing?
@@ -135,7 +137,7 @@ module Alchemy
135
137
  end
136
138
 
137
139
  def default_locale?
138
- Language.current.code.to_sym == ::I18n.default_locale.to_sym
140
+ Current.language.code.to_sym == ::I18n.default_locale.to_sym
139
141
  end
140
142
 
141
143
  # Page url with or without locale while keeping all additional params
@@ -214,7 +216,7 @@ module Alchemy
214
216
  def render_fresh_page?
215
217
  must_not_cache? || stale?(
216
218
  etag: page_etag,
217
- last_modified: @page.published_at,
219
+ last_modified: @page.last_modified_at,
218
220
  public: !@page.restricted,
219
221
  template: "pages/show"
220
222
  )
@@ -15,7 +15,7 @@ module Alchemy
15
15
  @current_language = if session[:alchemy_language_id].present?
16
16
  set_alchemy_language(session[:alchemy_language_id])
17
17
  else
18
- Alchemy::Language.current
18
+ Current.language
19
19
  end
20
20
  if @current_language.nil?
21
21
  flash[:warning] = Alchemy.t("Please create a language first.")
@@ -46,7 +46,7 @@ module Alchemy
46
46
  LegacyPageUrl.joins(:page).where(
47
47
  :urlname => urlname,
48
48
  Page.table_name => {
49
- language_id: Language.current.id
49
+ language_id: Current.language.id
50
50
  }
51
51
  )
52
52
  end
@@ -59,8 +59,8 @@ module Alchemy
59
59
  compact? ? "compact" : nil,
60
60
  deprecated? ? "deprecated" : nil,
61
61
  fixed? ? "is-fixed" : "not-fixed",
62
- public? ? "visible" : "hidden"
63
- ].join(" ")
62
+ public? ? nil : "element-hidden"
63
+ ]
64
64
  end
65
65
 
66
66
  # Tells us, if we should show the element footer and form inputs.