alchemy_cms 7.1.7 → 7.2.0.b

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 (305) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +133 -0
  3. data/Gemfile +2 -4
  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/link_view.rb +1 -7
  62. data/app/components/alchemy/ingredients/picture_view.rb +2 -2
  63. data/app/components/alchemy/ingredients/text_view.rb +1 -2
  64. data/app/controllers/alchemy/admin/base_controller.rb +1 -1
  65. data/app/controllers/alchemy/admin/elements_controller.rb +4 -2
  66. data/app/controllers/alchemy/admin/ingredients_controller.rb +2 -0
  67. data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
  68. data/app/controllers/alchemy/admin/legacy_page_urls_controller.rb +12 -4
  69. data/app/controllers/alchemy/admin/nodes_controller.rb +26 -0
  70. data/app/controllers/alchemy/admin/pages_controller.rb +11 -78
  71. data/app/controllers/alchemy/admin/picture_descriptions_controller.rb +15 -0
  72. data/app/controllers/alchemy/admin/pictures_controller.rb +18 -1
  73. data/app/controllers/alchemy/admin/resources_controller.rb +15 -10
  74. data/app/controllers/alchemy/api/attachments_controller.rb +44 -0
  75. data/app/controllers/alchemy/api/pages_controller.rb +10 -6
  76. data/app/controllers/alchemy/base_controller.rb +2 -2
  77. data/app/controllers/alchemy/messages_controller.rb +2 -2
  78. data/app/controllers/alchemy/pages_controller.rb +8 -6
  79. data/app/controllers/concerns/alchemy/admin/current_language.rb +1 -1
  80. data/app/controllers/concerns/alchemy/legacy_page_redirects.rb +1 -1
  81. data/app/decorators/alchemy/element_editor.rb +2 -2
  82. data/app/helpers/alchemy/admin/base_helper.rb +8 -60
  83. data/app/helpers/alchemy/admin/elements_helper.rb +1 -1
  84. data/app/helpers/alchemy/admin/ingredients_helper.rb +1 -1
  85. data/app/helpers/alchemy/base_helper.rb +9 -91
  86. data/app/helpers/alchemy/elements_helper.rb +3 -3
  87. data/app/helpers/alchemy/pages_helper.rb +16 -9
  88. data/app/javascript/alchemy_admin/components/attachment_select.js +24 -0
  89. data/app/javascript/alchemy_admin/components/button.js +3 -0
  90. data/app/javascript/alchemy_admin/components/clipboard_button.js +3 -2
  91. data/app/javascript/alchemy_admin/components/dialog_link.js +10 -7
  92. data/app/javascript/alchemy_admin/components/dom_id_select.js +69 -0
  93. data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +42 -0
  94. data/app/javascript/alchemy_admin/components/element_editor/publish_element_button.js +4 -2
  95. data/app/javascript/alchemy_admin/components/element_editor.js +21 -13
  96. data/app/javascript/alchemy_admin/components/elements_window.js +87 -0
  97. data/app/javascript/alchemy_admin/components/growl.js +13 -0
  98. data/app/javascript/alchemy_admin/components/icon.js +51 -0
  99. data/app/javascript/alchemy_admin/components/index.js +24 -0
  100. data/app/javascript/alchemy_admin/components/ingredient_group.js +6 -0
  101. data/app/javascript/alchemy_admin/components/link_buttons/link_button.js +21 -11
  102. data/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js +2 -1
  103. data/app/javascript/alchemy_admin/components/link_buttons.js +1 -0
  104. data/app/javascript/alchemy_admin/components/list_filter.js +68 -0
  105. data/app/javascript/alchemy_admin/components/message.js +69 -0
  106. data/app/javascript/alchemy_admin/components/node_select.js +1 -1
  107. data/app/javascript/alchemy_admin/components/overlay.js +6 -6
  108. data/app/javascript/alchemy_admin/components/page_select.js +3 -7
  109. data/app/javascript/alchemy_admin/components/preview_window.js +121 -0
  110. data/app/javascript/alchemy_admin/components/remote_select.js +4 -1
  111. data/app/javascript/alchemy_admin/components/select.js +37 -1
  112. data/app/javascript/alchemy_admin/components/tags_autocomplete.js +57 -0
  113. data/app/javascript/alchemy_admin/components/uploader/file_upload.js +4 -3
  114. data/app/javascript/alchemy_admin/components/uploader/progress.js +1 -1
  115. data/app/javascript/alchemy_admin/confirm_dialog.js +133 -0
  116. data/app/javascript/alchemy_admin/dirty.js +19 -14
  117. data/app/javascript/alchemy_admin/fixed_elements.js +24 -0
  118. data/app/javascript/alchemy_admin/growler.js +15 -0
  119. data/app/javascript/alchemy_admin/gui.js +2 -4
  120. data/app/javascript/alchemy_admin/hotkeys.js +60 -0
  121. data/app/javascript/alchemy_admin/image_loader.js +2 -2
  122. data/app/javascript/alchemy_admin/ingredient_anchor_link.js +2 -3
  123. data/app/javascript/alchemy_admin/initializer.js +1 -8
  124. data/app/javascript/alchemy_admin/link_dialog.js +131 -0
  125. data/app/javascript/alchemy_admin/locales/en.js +3 -0
  126. data/app/javascript/alchemy_admin/node_tree.js +4 -3
  127. data/app/javascript/alchemy_admin/page_sorter.js +23 -14
  128. data/app/javascript/alchemy_admin/picture_editors.js +3 -2
  129. data/app/javascript/alchemy_admin/shoelace_theme.js +60 -0
  130. data/app/javascript/alchemy_admin/sitemap.js +9 -3
  131. data/app/javascript/alchemy_admin/sortable_elements.js +4 -6
  132. data/app/javascript/alchemy_admin.js +18 -42
  133. data/app/models/alchemy/current.rb +26 -0
  134. data/app/models/alchemy/element.rb +1 -1
  135. data/app/models/alchemy/ingredients/headline.rb +8 -1
  136. data/app/models/alchemy/ingredients/picture.rb +6 -0
  137. data/app/models/alchemy/language.rb +8 -6
  138. data/app/models/alchemy/node.rb +2 -2
  139. data/app/models/alchemy/page/page_elements.rb +8 -8
  140. data/app/models/alchemy/page/page_layouts.rb +3 -3
  141. data/app/models/alchemy/page/page_natures.rb +13 -9
  142. data/app/models/alchemy/page/page_scopes.rb +2 -2
  143. data/app/models/alchemy/page/publisher.rb +1 -0
  144. data/app/models/alchemy/page.rb +13 -28
  145. data/app/models/alchemy/picture.rb +8 -0
  146. data/app/models/alchemy/picture_description.rb +8 -0
  147. data/app/models/alchemy/picture_variant.rb +1 -1
  148. data/app/models/alchemy/site.rb +10 -7
  149. data/app/serializers/alchemy/attachment_serializer.rb +8 -0
  150. data/app/serializers/alchemy/page_node_serializer.rb +9 -0
  151. data/app/views/alchemy/_menubar.html.erb +1 -1
  152. data/app/views/alchemy/_preview_mode_code.html.erb +1 -1
  153. data/app/views/alchemy/admin/attachments/_tag_list.html.erb +2 -2
  154. data/app/views/alchemy/admin/attachments/archive_overlay.js.erb +0 -1
  155. data/app/views/alchemy/admin/attachments/edit.html.erb +3 -4
  156. data/app/views/alchemy/admin/clipboard/clear.js.erb +1 -1
  157. data/app/views/alchemy/admin/clipboard/index.html.erb +1 -1
  158. data/app/views/alchemy/admin/clipboard/insert.js.erb +1 -1
  159. data/app/views/alchemy/admin/clipboard/remove.js.erb +1 -1
  160. data/app/views/alchemy/admin/dashboard/_locked_pages.html.erb +1 -1
  161. data/app/views/alchemy/admin/dashboard/_sites.html.erb +1 -1
  162. data/app/views/alchemy/admin/dashboard/help.html.erb +48 -12
  163. data/app/views/alchemy/admin/dashboard/index.html.erb +1 -1
  164. data/app/views/alchemy/admin/dashboard/info.html.erb +5 -8
  165. data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +1 -1
  166. data/app/views/alchemy/admin/elements/_element.html.erb +5 -5
  167. data/app/views/alchemy/admin/elements/_footer.html.erb +1 -1
  168. data/app/views/alchemy/admin/elements/_header.html.erb +6 -2
  169. data/app/views/alchemy/admin/elements/_toolbar.html.erb +8 -6
  170. data/app/views/alchemy/admin/elements/create.js.erb +0 -5
  171. data/app/views/alchemy/admin/elements/index.html.erb +70 -34
  172. data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +1 -2
  173. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +3 -5
  174. data/app/views/alchemy/admin/languages/_language.html.erb +1 -1
  175. data/app/views/alchemy/admin/languages/index.html.erb +2 -2
  176. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +18 -18
  177. data/app/views/alchemy/admin/layoutpages/edit.html.erb +3 -4
  178. data/app/views/alchemy/admin/layoutpages/index.html.erb +2 -2
  179. data/app/views/alchemy/admin/legacy_page_urls/_legacy_page_url.html.erb +10 -11
  180. data/app/views/alchemy/admin/legacy_page_urls/_new.html.erb +15 -17
  181. data/app/views/alchemy/admin/legacy_page_urls/_table.html.erb +16 -0
  182. data/app/views/alchemy/admin/legacy_page_urls/_update.turbo_stream.erb +12 -0
  183. data/app/views/alchemy/admin/legacy_page_urls/create.turbo_stream.erb +8 -0
  184. data/app/views/alchemy/admin/legacy_page_urls/destroy.turbo_stream.erb +1 -0
  185. data/app/views/alchemy/admin/legacy_page_urls/edit.html.erb +27 -0
  186. data/app/views/alchemy/admin/legacy_page_urls/show.html.erb +1 -0
  187. data/app/views/alchemy/admin/legacy_page_urls/update.turbo_stream.erb +1 -0
  188. data/app/views/alchemy/admin/nodes/_form.html.erb +12 -11
  189. data/app/views/alchemy/admin/nodes/_label.html.erb +1 -0
  190. data/app/views/alchemy/admin/nodes/_node.html.erb +19 -19
  191. data/app/views/alchemy/admin/nodes/_page_nodes.html.erb +48 -0
  192. data/app/views/alchemy/admin/nodes/_update.turbo_stream.erb +9 -0
  193. data/app/views/alchemy/admin/nodes/create.turbo_stream.erb +1 -0
  194. data/app/views/alchemy/admin/nodes/destroy.turbo_stream.erb +1 -0
  195. data/app/views/alchemy/admin/nodes/index.html.erb +3 -3
  196. data/app/views/alchemy/admin/pages/_form.html.erb +3 -4
  197. data/app/views/alchemy/admin/pages/_legacy_urls.html.erb +4 -15
  198. data/app/views/alchemy/admin/pages/_page.html.erb +39 -39
  199. data/app/views/alchemy/admin/pages/_table_row.html.erb +3 -3
  200. data/app/views/alchemy/admin/pages/_toolbar.html.erb +2 -2
  201. data/app/views/alchemy/admin/pages/configure.html.erb +6 -0
  202. data/app/views/alchemy/admin/pages/edit.html.erb +15 -62
  203. data/app/views/alchemy/admin/pages/unlock.js.erb +2 -2
  204. data/app/views/alchemy/admin/partials/_autocomplete_tag_list.html.erb +3 -1
  205. data/app/views/alchemy/admin/partials/_flash_notices.html.erb +4 -2
  206. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +1 -1
  207. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +5 -2
  208. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +2 -2
  209. data/app/views/alchemy/admin/partials/_search_form.html.erb +2 -2
  210. data/app/views/alchemy/admin/partials/_site_select.html.erb +1 -1
  211. data/app/views/alchemy/admin/picture_descriptions/_form.html.erb +11 -0
  212. data/app/views/alchemy/admin/picture_descriptions/edit.html.erb +6 -0
  213. data/app/views/alchemy/admin/pictures/_form.html.erb +4 -3
  214. data/app/views/alchemy/admin/pictures/_infos.html.erb +1 -1
  215. data/app/views/alchemy/admin/pictures/_picture_description_field.html.erb +29 -0
  216. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +2 -2
  217. data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +0 -2
  218. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +3 -3
  219. data/app/views/alchemy/admin/pictures/show.html.erb +3 -3
  220. data/app/views/alchemy/admin/resources/_form.html.erb +3 -4
  221. data/app/views/alchemy/admin/resources/_tag_list.html.erb +2 -2
  222. data/app/views/alchemy/admin/resources/index.html.erb +2 -2
  223. data/app/views/alchemy/admin/sites/index.html.erb +1 -1
  224. data/app/views/alchemy/admin/styleguide/index.html.erb +29 -24
  225. data/app/views/alchemy/admin/tags/_tag.html.erb +1 -1
  226. data/app/views/alchemy/admin/tags/edit.html.erb +1 -1
  227. data/app/views/alchemy/admin/tags/index.html.erb +1 -1
  228. data/app/views/alchemy/base/500.html.erb +7 -18
  229. data/app/views/alchemy/base/error_notice.html.erb +3 -1
  230. data/app/views/alchemy/ingredients/_boolean_editor.html.erb +1 -1
  231. data/app/views/alchemy/ingredients/_datetime_editor.html.erb +1 -1
  232. data/app/views/alchemy/ingredients/_headline_editor.html.erb +13 -8
  233. data/app/views/alchemy/ingredients/_picture_editor.html.erb +1 -1
  234. data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +1 -1
  235. data/app/views/alchemy/language_links/_language.html.erb +1 -1
  236. data/app/views/kaminari/alchemy/_first_page.html.erb +2 -2
  237. data/app/views/kaminari/alchemy/_gap.html.erb +1 -1
  238. data/app/views/kaminari/alchemy/_last_page.html.erb +2 -2
  239. data/app/views/kaminari/alchemy/_next_page.html.erb +2 -2
  240. data/app/views/kaminari/alchemy/_prev_page.html.erb +2 -2
  241. data/app/views/layouts/alchemy/admin.html.erb +2 -1
  242. data/bundles/shoelace.js +3 -1
  243. data/config/locales/alchemy.en.yml +16 -3
  244. data/config/routes.rb +3 -1
  245. data/db/migrate/20240314105244_create_alchemy_picture_descriptions.rb +11 -0
  246. data/lib/alchemy/configuration_methods.rb +1 -1
  247. data/lib/alchemy/controller_actions.rb +3 -3
  248. data/lib/alchemy/element_definition.rb +10 -6
  249. data/lib/alchemy/engine.rb +19 -2
  250. data/lib/alchemy/page_layout.rb +10 -6
  251. data/lib/alchemy/permissions.rb +3 -2
  252. data/lib/alchemy/seeder.rb +2 -2
  253. data/lib/alchemy/test_support/capybara_helpers.rb +4 -0
  254. data/lib/alchemy/test_support/factories/language_factory.rb +1 -1
  255. data/lib/alchemy/test_support/shared_contexts.rb +8 -0
  256. data/lib/alchemy/tinymce.rb +2 -1
  257. data/lib/alchemy/version.rb +1 -1
  258. data/lib/alchemy.rb +36 -0
  259. data/lib/alchemy_cms.rb +0 -1
  260. data/lib/generators/alchemy/menus/templates/node.html.erb +2 -2
  261. data/lib/generators/alchemy/menus/templates/node.html.haml +2 -2
  262. data/lib/generators/alchemy/menus/templates/node.html.slim +2 -2
  263. data/lib/generators/alchemy/menus/templates/wrapper.html.erb +1 -1
  264. data/lib/generators/alchemy/menus/templates/wrapper.html.haml +1 -1
  265. data/lib/generators/alchemy/menus/templates/wrapper.html.slim +1 -1
  266. data/lib/tasks/alchemy/sitemap.rake +97 -0
  267. data/package.json +8 -8
  268. data/vendor/assets/fonts/remixicon.symbol.svg +11 -0
  269. data/vendor/javascript/shoelace.min.js +333 -118
  270. data/vendor/javascript/sortable.min.js +1 -1
  271. data/vendor/javascript/tinymce.min.js +1 -1
  272. data/vendor/javascript/ungap-custom-elements.min.js +1 -1
  273. metadata +61 -54
  274. data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +0 -85
  275. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +0 -107
  276. data/app/assets/javascripts/alchemy/alchemy.file_progress.js.coffee +0 -66
  277. data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +0 -45
  278. data/app/assets/javascripts/alchemy/alchemy.growler.js.coffee +0 -24
  279. data/app/assets/javascripts/alchemy/alchemy.hotkeys.js.coffee +0 -49
  280. data/app/assets/javascripts/alchemy/alchemy.initializer.js.coffee +0 -0
  281. data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +0 -230
  282. data/app/assets/javascripts/alchemy/alchemy.list_filter.js.coffee +0 -49
  283. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +0 -82
  284. data/app/assets/javascripts/alchemy/alchemy.string_extension.js.coffee +0 -11
  285. data/app/assets/javascripts/alchemy/templates/page.hbs +0 -19
  286. data/app/javascript/alchemy_admin/tags_autocomplete.js +0 -46
  287. data/app/models/alchemy/tree_node.rb +0 -7
  288. data/app/views/alchemy/admin/elements/destroy.js.erb +0 -8
  289. data/app/views/alchemy/admin/legacy_page_urls/_form.html.erb +0 -5
  290. data/app/views/alchemy/admin/legacy_page_urls/create.js.erb +0 -9
  291. data/app/views/alchemy/admin/legacy_page_urls/destroy.js.erb +0 -6
  292. data/app/views/alchemy/admin/legacy_page_urls/update.js.erb +0 -2
  293. data/app/views/alchemy/admin/pages/_anchor_link.html.erb +0 -22
  294. data/app/views/alchemy/admin/pages/_external_link.html.erb +0 -31
  295. data/app/views/alchemy/admin/pages/_file_link.html.erb +0 -31
  296. data/app/views/alchemy/admin/pages/_internal_link.html.erb +0 -35
  297. data/app/views/alchemy/admin/pages/link.html.erb +0 -26
  298. data/app/views/alchemy/admin/partials/_flash.html.erb +0 -4
  299. data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +0 -29
  300. data/vendor/assets/fonts/remixicon.eot +0 -0
  301. data/vendor/assets/fonts/remixicon.svg +0 -7816
  302. data/vendor/assets/fonts/remixicon.ttf +0 -0
  303. data/vendor/assets/fonts/remixicon.woff +0 -0
  304. data/vendor/assets/fonts/remixicon.woff2 +0 -0
  305. data/vendor/assets/stylesheets/remixicon.scss +0 -10480
@@ -250,6 +250,7 @@ en:
250
250
  "Confirm new password": "Confirm new password"
251
251
  "Copy": "Copy"
252
252
  "Could not load Adobe Flash® Plugin!": "Could not load Adobe Flash® Plugin!"
253
+ "Create node on parent:": "Create node on parent:"
253
254
  "Currently locked pages": "Currently locked pages"
254
255
  "Default language has to be public": "Default language has to be public"
255
256
  "Delete image": "Delete image"
@@ -286,6 +287,7 @@ en:
286
287
  "New": "New"
287
288
  "New Element": "New element"
288
289
  "New page": "New page"
290
+ "No menu node for this page found": "No menu node for this page found"
289
291
  "No page links for this page found": "No page links for this page found"
290
292
  "New password": "New password"
291
293
  "New Tag": "New tag"
@@ -300,7 +302,6 @@ en:
300
302
  "Page deleted": "%{name} deleted"
301
303
  "Page saved": "%{name} saved"
302
304
  "Page cache flushed": "Page cache flushed"
303
- "Pages order saved": "Pages order saved"
304
305
  "Password": "Password"
305
306
  "Password reset": "Password reset"
306
307
  "Paste from clipboard": "Paste from clipboard"
@@ -411,6 +412,9 @@ en:
411
412
  delete_node: "Delete this menu node"
412
413
  delete_page: "Delete this page"
413
414
  delete_tag: "Delete tag"
415
+ search_page: "Search page"
416
+ search_node: "Search menu node"
417
+ search_tag: "Search tag"
414
418
  document: "File"
415
419
  download_csv: "Download CSV"
416
420
  download_file: "Download file '%{filename}'"
@@ -587,7 +591,12 @@ en:
587
591
  replace: replace
588
592
  replace_file: Replace file
589
593
  "Replaced Tag": "Tag '%{old_tag}' was replaced with '%{new_tag}'"
594
+ "%{resource_name} successfully %{action}": "%{resource_name} successfully %{action}."
590
595
  resources:
596
+ actions:
597
+ created: "created"
598
+ updated: "updated"
599
+ removed: "removed"
591
600
  relation_select:
592
601
  blank: "- none -"
593
602
  right: "right"
@@ -656,7 +665,6 @@ en:
656
665
  "Successfully updated": "Successfully updated"
657
666
  "Successfully removed": "Successfully removed"
658
667
  "Nothing found": "Nothing found."
659
- complete: "Abgeschlossen"
660
668
  "Update available": "Update available"
661
669
  "Update status unavailable": "Update status unavailable"
662
670
  "Uploading": "Uploading"
@@ -790,7 +798,7 @@ en:
790
798
  other: "Elements"
791
799
  alchemy/ingredients/headline:
792
800
  one: Headline
793
- other: Headline
801
+ other: Headlines
794
802
  alchemy/legacy_page_url:
795
803
  one: "Link"
796
804
  other: "Links"
@@ -809,6 +817,9 @@ en:
809
817
  alchemy/picture:
810
818
  one: Picture
811
819
  other: Pictures
820
+ alchemy/picture_description:
821
+ one: Picture Description
822
+ other: Picture Descriptions
812
823
  alchemy/user:
813
824
  one: User
814
825
  other: User
@@ -899,6 +910,8 @@ en:
899
910
  image_file_size: "Filesize"
900
911
  name: "Name"
901
912
  tag_list: Tags
913
+ alchemy/picture_description:
914
+ text: "Description"
902
915
  alchemy/site:
903
916
  name: "Name"
904
917
  host: "Primary Host"
data/config/routes.rb CHANGED
@@ -22,7 +22,6 @@ Alchemy::Engine.routes.draw do
22
22
  resources :pages do
23
23
  resources :elements
24
24
  collection do
25
- post :order
26
25
  post :flush
27
26
  post :copy_language_tree
28
27
  get :create_language
@@ -65,6 +64,8 @@ Alchemy::Engine.routes.draw do
65
64
  end
66
65
  end
67
66
 
67
+ resources :picture_descriptions, only: [:index, :edit]
68
+
68
69
  resources :attachments, except: [:new] do
69
70
  member do
70
71
  get :download
@@ -116,6 +117,7 @@ Alchemy::Engine.routes.draw do
116
117
  resources :elements, only: :show
117
118
 
118
119
  namespace :api, defaults: {format: "json"} do
120
+ resources :attachments, only: [:index]
119
121
  resources :ingredients, only: [:index]
120
122
 
121
123
  resources :elements, only: [:index, :show]
@@ -0,0 +1,11 @@
1
+ class CreateAlchemyPictureDescriptions < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :alchemy_picture_descriptions do |t|
4
+ t.belongs_to :picture, null: false, foreign_key: {to_table: :alchemy_pictures}
5
+ t.belongs_to :language, null: false, foreign_key: {to_table: :alchemy_languages}
6
+ t.text :text
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -28,7 +28,7 @@ module Alchemy
28
28
  # matches the current I18n.locale then the prefix os omitted.
29
29
  # Also, if only one published language exists.
30
30
  #
31
- def prefix_locale?(locale = Language.current&.code)
31
+ def prefix_locale?(locale = Current.language&.code)
32
32
  return false unless locale
33
33
 
34
34
  multi_language? && locale != ::I18n.default_locale.to_s
@@ -56,7 +56,7 @@ module Alchemy
56
56
  # can be scoped against it.
57
57
  #
58
58
  def set_current_alchemy_site
59
- Site.current = current_alchemy_site
59
+ Current.site = current_alchemy_site
60
60
  end
61
61
 
62
62
  # Sets the current language for Alchemy.
@@ -83,11 +83,11 @@ module Alchemy
83
83
  Language.find_by_code(id_or_code)
84
84
  end
85
85
 
86
- # Stores language in +Language.current+
86
+ # Stores language in +Current.language+
87
87
  #
88
88
  def store_current_alchemy_language(language)
89
89
  if language&.id
90
- Language.current = language
90
+ Current.language = language
91
91
  end
92
92
  end
93
93
  end
@@ -42,6 +42,16 @@ module Alchemy
42
42
  all.detect { |a| a["name"] == name }
43
43
  end
44
44
 
45
+ def reset!
46
+ @definitions = nil
47
+ end
48
+
49
+ # The absolute +elements.yml+ file path
50
+ # @return [Pathname]
51
+ def definitions_file_path
52
+ Rails.root.join("config", "alchemy", "elements.yml")
53
+ end
54
+
45
55
  private
46
56
 
47
57
  # Reads the element definitions from +config/alchemy/elements.yml+.
@@ -58,12 +68,6 @@ module Alchemy
58
68
  "Could not find elements.yml file! Please run `rails generate alchemy:install`"
59
69
  end
60
70
  end
61
-
62
- # Returns the elements.yml file path
63
- #
64
- def definitions_file_path
65
- Rails.root.join "config/alchemy/elements.yml"
66
- end
67
71
  end
68
72
  end
69
73
  end
@@ -35,6 +35,23 @@ module Alchemy
35
35
  end
36
36
  end
37
37
 
38
+ initializer "alchemy.watch_definition_changes" do |app|
39
+ elements_reloader = app.config.file_watcher.new([ElementDefinition.definitions_file_path]) do
40
+ Rails.logger.info "[#{engine_name}] Reloading Element Definitions."
41
+ ElementDefinition.reset!
42
+ end
43
+ page_layouts_reloader = app.config.file_watcher.new([PageLayout.layouts_file_path]) do
44
+ Rails.logger.info "[#{engine_name}] Reloading Page Layouts."
45
+ PageLayout.reset!
46
+ end
47
+ [elements_reloader, page_layouts_reloader].each do |reloader|
48
+ app.reloaders << reloader
49
+ app.reloader.to_run do
50
+ reloader.execute_if_updated
51
+ end
52
+ end
53
+ end
54
+
38
55
  # Gutentag downcases all tags before save
39
56
  # and Gutentag validations are not case sensitive.
40
57
  # But we support having tags with uppercase characters.
@@ -47,8 +64,8 @@ module Alchemy
47
64
  initializer "alchemy.ransack" do
48
65
  Ransack.configure do |config|
49
66
  config.custom_arrows = {
50
- up_arrow: '<i class="ri-1x ri-arrow-up-line"></i>',
51
- down_arrow: '<i class="ri-1x ri-arrow-down-line"></i>'
67
+ up_arrow: '<alchemy-icon name="arrow-up" size="1x"></alchemy-icon>',
68
+ down_arrow: '<alchemy-icon name="arrow-down" size="1x"></alchemy-icon>'
52
69
  }
53
70
  end
54
71
  end
@@ -41,6 +41,16 @@ module Alchemy
41
41
  all.detect { |a| a["name"].casecmp(name).zero? }
42
42
  end
43
43
 
44
+ def reset!
45
+ @definitions = nil
46
+ end
47
+
48
+ # The absolute +page_layouts.yml+ file path
49
+ # @return [Pathname]
50
+ def layouts_file_path
51
+ Rails.root.join("config", "alchemy", "page_layouts.yml")
52
+ end
53
+
44
54
  private
45
55
 
46
56
  # Reads the layout definitions from +config/alchemy/page_layouts.yml+.
@@ -58,12 +68,6 @@ module Alchemy
58
68
  raise LoadError, "Could not find page_layouts.yml file! Please run `rails generate alchemy:install`"
59
69
  end
60
70
  end
61
-
62
- # Returns the page_layouts.yml file path
63
- #
64
- def layouts_file_path
65
- Rails.root.join "config/alchemy/page_layouts.yml"
66
- end
67
71
  end
68
72
  end
69
73
  end
@@ -42,7 +42,7 @@ module Alchemy
42
42
  end
43
43
 
44
44
  can :read, Alchemy::Page, Alchemy::Page.published.not_restricted.from_current_site do |p|
45
- p.public? && !p.restricted? && p.site == Alchemy::Site.current
45
+ p.public? && !p.restricted? && p.site == Alchemy::Current.site
46
46
  end
47
47
  end
48
48
  end
@@ -65,7 +65,7 @@ module Alchemy
65
65
  end
66
66
 
67
67
  can :read, Alchemy::Page, Alchemy::Page.published.from_current_site do |p|
68
- p.public? && p.site == Alchemy::Site.current
68
+ p.public? && p.site == Alchemy::Current.site
69
69
  end
70
70
  end
71
71
  end
@@ -157,6 +157,7 @@ module Alchemy
157
157
  end
158
158
 
159
159
  can :manage, Alchemy::Picture
160
+ can :manage, Alchemy::PictureDescription
160
161
  can :manage, Alchemy::Attachment
161
162
  can :manage, Alchemy::Tag
162
163
  can :index, Alchemy::Language
@@ -31,7 +31,7 @@ module Alchemy
31
31
  log "There are already pages present in your database. " \
32
32
  "Please use `rake db:reset' if you want to rebuild your database.", :skip
33
33
  else
34
- create_default_site! unless Alchemy::Site.default
34
+ create_default_site! unless Alchemy::Site.first
35
35
  create_default_language! unless Alchemy::Language.default
36
36
  seed_pages if contentpages.present?
37
37
  seed_layoutpages if layoutpages.present?
@@ -126,7 +126,7 @@ module Alchemy
126
126
  page_layout: default_language["page_layout"],
127
127
  public: true,
128
128
  default: true,
129
- site: Alchemy::Site.default
129
+ site: Alchemy::Site.first
130
130
  )
131
131
  else
132
132
  raise DefaultLanguageNotFoundError
@@ -54,6 +54,10 @@ module Alchemy
54
54
  find(%([content="#{content}"] > a)).click
55
55
  end
56
56
 
57
+ def click_icon(name)
58
+ find(%(alchemy-icon[name="#{name}"])).click
59
+ end
60
+
57
61
  private
58
62
 
59
63
  def within_entire_page(&block)
@@ -10,7 +10,7 @@ FactoryBot.define do
10
10
 
11
11
  public { true }
12
12
 
13
- site { Alchemy::Site.default || create(:alchemy_site, :default) }
13
+ site { Alchemy::Site.first || create(:alchemy_site, :default) }
14
14
 
15
15
  trait :klingon do
16
16
  name { "Klingon" }
@@ -14,3 +14,11 @@ RSpec.shared_context "with invalid file" do
14
14
  end
15
15
  end
16
16
  end
17
+
18
+ RSpec.shared_context "in preview mode" do
19
+ around do |example|
20
+ Alchemy::Current.preview_page = page
21
+ example.run
22
+ Alchemy::Current.preview_page = nil
23
+ end
24
+ end
@@ -33,7 +33,8 @@ module Alchemy
33
33
  entity_encoding: "raw",
34
34
  paste_as_text: true,
35
35
  element_format: "html",
36
- branding: false
36
+ branding: false,
37
+ license_key: "gpl"
37
38
  }
38
39
 
39
40
  class << self
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "7.1.7"
4
+ VERSION = "7.2.0.b"
5
5
 
6
6
  def self.version
7
7
  VERSION
data/lib/alchemy.rb CHANGED
@@ -102,4 +102,40 @@ module Alchemy
102
102
  # JS Importmap instance
103
103
  singleton_class.attr_accessor :importmap
104
104
  self.importmap = Importmap::Map.new
105
+
106
+ # Configure tabs in the link dialog
107
+ #
108
+ # With this configuration that tabs in the link dialog can be extended
109
+ # without overwriting or defacing the Admin Interface.
110
+ #
111
+ # == Example
112
+ #
113
+ # # components/acme/link_tab.rb
114
+ # module Acme
115
+ # class LinkTab < ::Alchemy::Admin::LinkDialog::BaseTab
116
+ # def title
117
+ # "Awesome Tab Title"
118
+ # end
119
+ #
120
+ # def name
121
+ # :unique_name
122
+ # end
123
+ #
124
+ # def fields
125
+ # [ title_input, target_select ]
126
+ # end
127
+ # end
128
+ # end
129
+ #
130
+ # # config/initializers/alchemy.rb
131
+ # Alchemy.link_dialog_tabs << Acme::LinkTab
132
+ #
133
+ def self.link_dialog_tabs
134
+ @_link_dialog_tabs ||= Set.new([
135
+ Alchemy::Admin::LinkDialog::InternalTab,
136
+ Alchemy::Admin::LinkDialog::AnchorTab,
137
+ Alchemy::Admin::LinkDialog::ExternalTab,
138
+ Alchemy::Admin::LinkDialog::FileTab
139
+ ])
140
+ end
105
141
  end
data/lib/alchemy_cms.rb CHANGED
@@ -17,7 +17,6 @@ require "jquery-rails"
17
17
  require "kaminari"
18
18
  require "non_stupid_digest_assets"
19
19
  require "ransack"
20
- require "request_store"
21
20
  require "responders"
22
21
  require "sassc-rails"
23
22
  require "simple_form"
@@ -1,8 +1,8 @@
1
- <%% cache [node, @page, @preview_mode] do %>
1
+ <%% cache [node, @page, Alchemy::Current.preview_page?] do %>
2
2
  <%%= content_tag :li, class: ['nav-item', node.children.any? ? 'dropdown' : nil].compact do %>
3
3
  <%%= link_to_if node.url,
4
4
  node.name,
5
- @preview_mode ? 'javascript: void(0)' : node.url,
5
+ Alchemy::Current.preview_page? ? 'javascript: void(0)' : node.url,
6
6
  class: ['nav-link', current_page?(node.url) ? 'active' : nil].compact,
7
7
  title: node.title,
8
8
  target: node.external? ? '_blank' : nil,
@@ -1,9 +1,9 @@
1
- - cache [node, @page, @preview_mode] do
1
+ - cache [node, @page, Alchemy::Current.preview_page?] do
2
2
  = content_tag :li,
3
3
  class: ['nav-item', node.children.any? ? 'dropdown' : nil].compact do
4
4
  = link_to_if node.url,
5
5
  node.name,
6
- @preview_mode ? 'javascript: void(0)' : node.url,
6
+ Alchemy::Current.preview_page? ? 'javascript: void(0)' : node.url,
7
7
  class: ['nav-link', current_page?(node.url) ? 'active' : nil].compact,
8
8
  title: node.title,
9
9
  target: node.external? ? '_blank' : nil,
@@ -1,9 +1,9 @@
1
- - cache [node, @page, @preview_mode] do
1
+ - cache [node, @page, Alchemy::Current.preview_page?] do
2
2
  = content_tag :li,
3
3
  class: ['nav-item', node.children.any? ? 'dropdown' : nil].compact do
4
4
  = link_to_if node.url,
5
5
  node.name,
6
- @preview_mode ? 'javascript: void(0)' : node.url,
6
+ Alchemy::Current.preview_page? ? 'javascript: void(0)' : node.url,
7
7
  class: ['nav-link', current_page?(node.url) ? 'active' : nil].compact,
8
8
  title: node.title,
9
9
  target: node.external? ? '_blank' : nil,
@@ -1,4 +1,4 @@
1
- <%% cache [menu, @page, @preview_mode] do %>
1
+ <%% cache [menu, @page, Alchemy::Current.preview_page?] do %>
2
2
  <ul class="nav">
3
3
  <%%= render partial: menu.to_partial_path,
4
4
  collection: menu.children.includes(:page, :children),
@@ -1,4 +1,4 @@
1
- - cache [menu, @page, @preview_mode] do
1
+ - cache [menu, @page, Alchemy::Current.preview_page?] do
2
2
  %ul.nav
3
3
  = render partial: menu.to_partial_path,
4
4
  collection: menu.children.includes(:page, :children),
@@ -1,4 +1,4 @@
1
- - cache [menu, @page, @preview_mode] do
1
+ - cache [menu, @page, Alchemy::Current.preview_page?] do
2
2
  ul.nav
3
3
  = render partial: menu.to_partial_path,
4
4
  collection: menu.children.includes(:page, :children),
@@ -0,0 +1,97 @@
1
+ class SiteMapIssueFinder
2
+ attr_reader :save
3
+ attr_accessor :depth_issue_page_ids, :lft_rgt_issue_page_ids
4
+
5
+ def initialize(save: false)
6
+ @save = save
7
+ @depth_issue_page_ids = Set.new
8
+ @lft_rgt_issue_page_ids = Set.new
9
+ end
10
+
11
+ def depth_issues
12
+ puts "\nFind Page Depth Issues"
13
+ puts "======================\n"
14
+
15
+ Alchemy::Page.each_with_level(Alchemy::Page.root.self_and_descendants) do |page, level|
16
+ not_the_same_level = level != page.depth
17
+ print not_the_same_level ? "F" : "."
18
+ if not_the_same_level
19
+ depth_issue_page_ids << page.id
20
+ page.update_column(:depth, level) if save
21
+ end
22
+ end
23
+
24
+ print_conclusion(depth_issue_page_ids, "depth")
25
+ end
26
+
27
+ def lft_rgt_issues
28
+ puts "\nFind Page Left/Right Value Issues"
29
+ puts "=================================\n"
30
+
31
+ Alchemy::Page.roots.each do |root_page|
32
+ traverse_children(root_page, root_page.lft)
33
+ end
34
+
35
+ print_conclusion(lft_rgt_issue_page_ids, "left/right value")
36
+ end
37
+
38
+ private
39
+
40
+ def print_conclusion(page_ids, field_description)
41
+ puts "\n\n"
42
+ if page_ids.length > 0
43
+ if save
44
+ puts "Found and corrected #{field_description} for #{page_ids.length} page(s)!"
45
+ else
46
+ puts "Following Pages have an incorrect #{field_description}: #{page_ids.join(", ")}"
47
+ end
48
+ else
49
+ puts "All pages have the correct #{field_description}!\n"
50
+ end
51
+ puts "\n"
52
+ end
53
+
54
+ def fix_page_field(page, field, counter)
55
+ wrong_counter = page[field] != counter
56
+ print wrong_counter ? "F" : "."
57
+ if wrong_counter
58
+ lft_rgt_issue_page_ids << page.id
59
+ page.update_column(field, counter) if save
60
+ end
61
+ end
62
+
63
+ ##
64
+ # @param [Alchemy::Page] page
65
+ # @param [Integer] counter
66
+ # @return [Integer]
67
+ def traverse_children(page, counter)
68
+ page.children.order(lft: :asc).each do |child_page|
69
+ counter += 1
70
+ fix_page_field(child_page, :lft, counter)
71
+ counter = traverse_children(child_page, counter) unless child_page.leaf?
72
+
73
+ counter += 1
74
+ fix_page_field(child_page, :rgt, counter)
75
+ end
76
+
77
+ counter
78
+ end
79
+ end
80
+
81
+ namespace :alchemy do
82
+ namespace :sitemap do
83
+ desc "Get an overview over all sitemap issues"
84
+ task anomalies: [:environment] do
85
+ finder = SiteMapIssueFinder.new
86
+ finder.lft_rgt_issues
87
+ finder.depth_issues
88
+ end
89
+
90
+ desc "Fix nested set sitemap issues"
91
+ task fix: [:environment] do
92
+ finder = SiteMapIssueFinder.new(save: true)
93
+ finder.lft_rgt_issues
94
+ finder.depth_issues
95
+ end
96
+ end
97
+ end
data/package.json CHANGED
@@ -11,28 +11,28 @@
11
11
  "license": "BSD-3-Clause",
12
12
  "dependencies": {
13
13
  "@rails/ujs": "^7.1.3",
14
- "@shoelace-style/shoelace": "^2.13.1",
14
+ "@shoelace-style/shoelace": "^2.14.0",
15
15
  "@ungap/custom-elements": "^1.3.0",
16
16
  "clipboard": "^2.0.11",
17
17
  "flatpickr": "^4.6.13",
18
18
  "keymaster": "^1.6.2",
19
19
  "sortablejs": "^1.15.2",
20
- "tinymce": "^6.8.3"
20
+ "tinymce": "^7.1.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@babel/core": "^7.23.7",
24
- "@babel/preset-env": "^7.23.7",
23
+ "@babel/core": "^7.24.5",
24
+ "@babel/preset-env": "^7.24.5",
25
25
  "@rollup/plugin-commonjs": "^25.0.7",
26
26
  "@rollup/plugin-node-resolve": "^15.2.3",
27
27
  "@rollup/plugin-terser": "^0.4.4",
28
28
  "babel-jest": "^29.6.4",
29
- "eslint": "^8.56.0",
29
+ "eslint": "^8.57.0",
30
30
  "jest": "^29.6.4",
31
31
  "jest-environment-jsdom": "^29.6.4",
32
32
  "jquery": "^3.7.1",
33
- "jsdom-testing-mocks": "^1.11.0",
34
- "prettier": "^3.2.4",
35
- "rollup": "^4.9.5",
33
+ "jsdom-testing-mocks": "^1.13.0",
34
+ "prettier": "^3.2.5",
35
+ "rollup": "^4.17.2",
36
36
  "xhr-mock": "^2.5.1"
37
37
  },
38
38
  "jest": {