alchemy_cms 7.4.6 → 8.0.0.a

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.

Potentially problematic release.


This version of alchemy_cms might be problematic. Click here for more details.

Files changed (349) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -0
  3. data/Gemfile +13 -6
  4. data/README.md +13 -5
  5. data/alchemy_cms.gemspec +14 -5
  6. data/app/assets/builds/alchemy/admin/page-select.css +1 -1
  7. data/app/assets/builds/alchemy/admin/print.css +1 -1
  8. data/app/assets/builds/alchemy/admin.css +2 -2
  9. data/app/assets/builds/alchemy/custom-properties.css +1 -1
  10. data/app/assets/builds/alchemy/welcome.css +1 -1
  11. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
  12. data/app/assets/builds/tinymce/skins/ui/alchemy/content.min.css +1 -0
  13. data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -1
  14. data/app/assets/config/alchemy_manifest.js +0 -2
  15. data/app/assets/images/alchemy/icons-sprite.svg +1 -0
  16. data/app/components/alchemy/admin/resource/applied_filter.rb +29 -0
  17. data/app/components/alchemy/admin/resource/checkbox_filter.rb +36 -0
  18. data/app/components/alchemy/admin/resource/datepicker_filter.rb +42 -0
  19. data/app/components/alchemy/admin/resource/select_filter.rb +43 -0
  20. data/app/components/alchemy/admin/toolbar_button.rb +5 -2
  21. data/app/components/alchemy/ingredients/number_view.rb +18 -0
  22. data/app/controllers/alchemy/admin/attachments_controller.rb +8 -15
  23. data/app/controllers/alchemy/admin/clipboard_controller.rb +2 -6
  24. data/app/controllers/alchemy/admin/elements_controller.rb +1 -1
  25. data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
  26. data/app/controllers/alchemy/admin/pages_controller.rb +15 -15
  27. data/app/controllers/alchemy/admin/pictures_controller.rb +9 -5
  28. data/app/controllers/alchemy/admin/resources_controller.rb +16 -106
  29. data/app/controllers/alchemy/attachments_controller.rb +43 -14
  30. data/app/controllers/alchemy/messages_controller.rb +1 -1
  31. data/app/controllers/alchemy/pages_controller.rb +7 -2
  32. data/app/controllers/concerns/alchemy/admin/resource_filter.rb +92 -0
  33. data/app/decorators/alchemy/element_editor.rb +5 -48
  34. data/app/decorators/alchemy/ingredient_editor.rb +3 -53
  35. data/app/helpers/alchemy/admin/base_helper.rb +14 -84
  36. data/app/helpers/alchemy/admin/elements_helper.rb +4 -4
  37. data/app/helpers/alchemy/admin/pages_helper.rb +1 -1
  38. data/app/helpers/alchemy/base_helper.rb +0 -30
  39. data/app/helpers/alchemy/elements_block_helper.rb +0 -14
  40. data/app/helpers/alchemy/pages_helper.rb +1 -1
  41. data/{lib → app/helpers}/alchemy/resources_helper.rb +5 -45
  42. data/app/javascript/alchemy_admin/components/action.js +2 -0
  43. data/app/javascript/alchemy_admin/components/alchemy_html_element.js +3 -3
  44. data/app/javascript/alchemy_admin/components/datepicker.js +10 -2
  45. data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +7 -7
  46. data/app/javascript/alchemy_admin/components/element_editor.js +1 -1
  47. data/app/javascript/alchemy_admin/components/index.js +1 -0
  48. data/app/javascript/alchemy_admin/components/remote_select.js +4 -1
  49. data/app/javascript/alchemy_admin/components/tags_autocomplete.js +5 -1
  50. data/app/javascript/alchemy_admin/components/tinymce.js +4 -2
  51. data/app/javascript/alchemy_admin/components/update_check.js +42 -0
  52. data/app/javascript/alchemy_admin/components/uploader/file_upload.js +15 -8
  53. data/app/javascript/alchemy_admin/components/uploader/progress.js +12 -6
  54. data/app/javascript/alchemy_admin/components/uploader.js +4 -2
  55. data/app/javascript/alchemy_admin/confirm_dialog.js +27 -57
  56. data/app/javascript/alchemy_admin/dirty.js +3 -2
  57. data/app/javascript/alchemy_admin/i18n.js +15 -16
  58. data/app/javascript/alchemy_admin/initializer.js +1 -49
  59. data/app/javascript/alchemy_admin/utils/ajax.js +51 -44
  60. data/app/javascript/alchemy_admin.js +3 -8
  61. data/app/models/alchemy/admin/filters/base.rb +38 -0
  62. data/app/models/alchemy/admin/filters/checkbox.rb +24 -0
  63. data/app/models/alchemy/admin/filters/datepicker.rb +53 -0
  64. data/app/models/alchemy/admin/filters/select.rb +70 -0
  65. data/app/models/alchemy/admin/resource_name.rb +27 -0
  66. data/app/models/alchemy/attachment.rb +51 -34
  67. data/app/models/alchemy/base_record.rb +2 -0
  68. data/app/models/alchemy/element/definitions.rb +1 -1
  69. data/app/models/alchemy/element/element_ingredients.rb +6 -6
  70. data/app/models/alchemy/element/presenters.rb +3 -12
  71. data/app/models/alchemy/element.rb +9 -27
  72. data/app/models/alchemy/element_definition.rb +160 -0
  73. data/app/models/alchemy/ingredient.rb +10 -43
  74. data/app/models/alchemy/ingredient_definition.rb +134 -0
  75. data/app/models/alchemy/ingredient_validator.rb +7 -3
  76. data/app/models/alchemy/ingredients/number.rb +19 -0
  77. data/app/models/alchemy/language.rb +0 -14
  78. data/app/models/alchemy/message.rb +3 -7
  79. data/app/models/alchemy/node.rb +1 -1
  80. data/app/models/alchemy/page/{page_layouts.rb → definitions.rb} +12 -19
  81. data/app/models/alchemy/page/fixed_attributes.rb +1 -1
  82. data/app/models/alchemy/page/page_elements.rb +13 -14
  83. data/app/models/alchemy/page/page_natures.rb +7 -7
  84. data/app/models/alchemy/page/page_scopes.rb +1 -1
  85. data/app/models/alchemy/page.rb +11 -33
  86. data/app/models/alchemy/page_definition.rb +115 -0
  87. data/app/models/alchemy/picture.rb +69 -79
  88. data/app/models/alchemy/picture_variant.rb +115 -5
  89. data/{lib → app/models}/alchemy/resource.rb +4 -18
  90. data/{lib → app/models}/alchemy/searchable_resource.rb +15 -0
  91. data/app/models/alchemy/site/layout.rb +5 -5
  92. data/app/models/alchemy/site.rb +0 -15
  93. data/app/models/alchemy/storage_adapter/active_storage/attachment_url.rb +41 -0
  94. data/app/models/alchemy/storage_adapter/active_storage/picture_url.rb +55 -0
  95. data/app/models/alchemy/storage_adapter/active_storage/preprocessor.rb +40 -0
  96. data/app/models/alchemy/storage_adapter/active_storage.rb +173 -0
  97. data/app/models/alchemy/{attachment/url.rb → storage_adapter/dragonfly/attachment_url.rb} +12 -12
  98. data/app/models/alchemy/{picture/url.rb → storage_adapter/dragonfly/picture_url.rb} +28 -12
  99. data/app/models/alchemy/{picture → storage_adapter/dragonfly}/preprocessor.rb +4 -4
  100. data/app/models/alchemy/storage_adapter/dragonfly.rb +183 -0
  101. data/app/models/alchemy/storage_adapter.rb +74 -0
  102. data/app/models/concerns/alchemy/picture_thumbnails.rb +19 -6
  103. data/app/serializers/alchemy/element_serializer.rb +0 -1
  104. data/app/services/alchemy/dragonfly_to_image_processing.rb +100 -0
  105. data/app/stylesheets/alchemy/_defaults.scss +3 -0
  106. data/app/stylesheets/alchemy/_extends.scss +69 -0
  107. data/app/{assets/stylesheets → stylesheets}/alchemy/_mixins.scss +36 -49
  108. data/app/stylesheets/alchemy/_variables.scss +5 -0
  109. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/archive.scss +20 -37
  110. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/base.scss +16 -14
  111. data/app/stylesheets/alchemy/admin/buttons.scss +160 -0
  112. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/clipboard.scss +2 -2
  113. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dashboard.scss +13 -16
  114. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dialogs.scss +23 -16
  115. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/elements.scss +150 -105
  116. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/errors.scss +5 -5
  117. data/app/stylesheets/alchemy/admin/filters.scss +58 -0
  118. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flatpickr.scss +53 -60
  119. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/form_fields.scss +21 -7
  120. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/forms.scss +31 -19
  121. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/frame.scss +20 -16
  122. data/app/stylesheets/alchemy/admin/hints.scss +5 -0
  123. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/icons.scss +10 -1
  124. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/image_library.scss +10 -8
  125. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/images.scss +1 -1
  126. data/app/stylesheets/alchemy/admin/labels.scss +5 -0
  127. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/lists.scss +3 -3
  128. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/navigation.scss +61 -55
  129. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/nodes.scss +21 -18
  130. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/notices.scss +18 -18
  131. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/page-select.scss +2 -2
  132. data/app/stylesheets/alchemy/admin/pagination.scss +144 -0
  133. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/preview_window.scss +8 -6
  134. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/print.scss +1 -1
  135. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/resource_info.scss +8 -5
  136. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/search.scss +9 -6
  137. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/selects.scss +49 -37
  138. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/shoelace.scss +5 -6
  139. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/sitemap.scss +38 -33
  140. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tables.scss +6 -4
  141. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tags.scss +6 -4
  142. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/toolbar.scss +12 -6
  143. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/typography.scss +2 -2
  144. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/upload.scss +7 -5
  145. data/app/stylesheets/alchemy/admin.scss +44 -0
  146. data/app/stylesheets/alchemy/custom-properties.css +244 -0
  147. data/app/stylesheets/alchemy/welcome.scss +75 -0
  148. data/app/{assets/stylesheets → stylesheets}/tinymce/skins/content/alchemy/content.scss +8 -9
  149. data/app/stylesheets/tinymce/skins/ui/alchemy/content.scss +1 -0
  150. data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/skin.scss +133 -136
  151. data/app/views/alchemy/admin/attachments/_files_list.html.erb +2 -2
  152. data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +1 -1
  153. data/app/views/alchemy/admin/{elements/_clipboard_button.html.erb → clipboard/_button.html.erb} +3 -5
  154. data/app/views/alchemy/admin/clipboard/_update_nested_element_button.turbo_stream.erb +11 -0
  155. data/app/views/alchemy/admin/clipboard/clear.turbo_stream.erb +4 -0
  156. data/app/views/alchemy/admin/clipboard/index.html.erb +15 -13
  157. data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +18 -0
  158. data/app/views/alchemy/admin/clipboard/remove.turbo_stream.erb +9 -0
  159. data/app/views/alchemy/admin/dashboard/info.html.erb +17 -31
  160. data/app/views/alchemy/admin/elements/_element.html.erb +4 -8
  161. data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
  162. data/app/views/alchemy/admin/elements/_header.html.erb +1 -0
  163. data/app/views/alchemy/admin/elements/_toolbar.html.erb +4 -6
  164. data/app/views/alchemy/admin/elements/create.turbo_stream.erb +2 -1
  165. data/app/views/alchemy/admin/elements/index.html.erb +2 -2
  166. data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +3 -16
  167. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +0 -9
  168. data/app/views/alchemy/admin/languages/_form.html.erb +1 -1
  169. data/app/views/alchemy/admin/languages/_table.html.erb +1 -1
  170. data/app/views/alchemy/admin/languages/index.html.erb +5 -2
  171. data/app/views/alchemy/admin/layoutpages/index.html.erb +1 -12
  172. data/app/views/alchemy/admin/pages/_form.html.erb +2 -2
  173. data/app/views/alchemy/admin/pages/_page.html.erb +2 -3
  174. data/app/views/alchemy/admin/pages/_toolbar.html.erb +1 -15
  175. data/app/views/alchemy/admin/pages/index.html.erb +1 -1
  176. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +9 -12
  177. data/app/views/alchemy/admin/partials/_search_form.html.erb +4 -9
  178. data/app/views/alchemy/admin/pictures/_archive.html.erb +4 -7
  179. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +2 -1
  180. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -1
  181. data/app/views/alchemy/admin/pictures/index.html.erb +2 -7
  182. data/app/views/alchemy/admin/resources/_applied_filters.html.erb +8 -0
  183. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +11 -21
  184. data/app/views/alchemy/admin/resources/_pagination.html.erb +6 -0
  185. data/app/views/alchemy/admin/resources/_per_page_select.html.erb +4 -2
  186. data/app/views/alchemy/admin/resources/_resource_table.html.erb +1 -1
  187. data/app/views/alchemy/admin/resources/_table_header.html.erb +1 -15
  188. data/app/views/alchemy/admin/sites/index.html.erb +5 -1
  189. data/app/views/alchemy/admin/styleguide/index.html.erb +8 -0
  190. data/app/views/alchemy/admin/tags/index.html.erb +1 -1
  191. data/app/views/alchemy/admin/tinymce/_setup.html.erb +7 -7
  192. data/app/{javascript/alchemy_admin/locales/en.js → views/alchemy/admin/translations/_en.js} +5 -2
  193. data/app/views/alchemy/admin/uploader/_button.html.erb +1 -1
  194. data/app/views/alchemy/admin/uploader/_setup.html.erb +4 -4
  195. data/app/views/alchemy/base/redirect.js.erb +1 -1
  196. data/app/views/alchemy/ingredients/_number_editor.html.erb +24 -0
  197. data/app/views/alchemy/no_index.html.erb +31 -0
  198. data/app/views/alchemy/welcome.html.erb +12 -10
  199. data/app/views/kaminari/alchemy/_first_page.html.erb +5 -3
  200. data/app/views/kaminari/alchemy/_last_page.html.erb +5 -3
  201. data/app/views/kaminari/alchemy/_next_page.html.erb +5 -3
  202. data/app/views/kaminari/alchemy/_paginator.html.erb +18 -13
  203. data/app/views/kaminari/alchemy/_prev_page.html.erb +5 -3
  204. data/app/views/layouts/alchemy/admin.html.erb +5 -9
  205. data/bun.lockb +0 -0
  206. data/bundles/remixicon.mjs +153 -0
  207. data/config/alchemy/config.yml +3 -2
  208. data/config/initializers/dragonfly.rb +0 -1
  209. data/config/initializers/mime_types.rb +1 -0
  210. data/config/locales/alchemy.en.yml +32 -14
  211. data/config/routes.rb +0 -2
  212. data/eslint.config.js +2 -1
  213. data/lib/alchemy/admin/preview_url.rb +4 -5
  214. data/lib/alchemy/cache_digests/template_tracker.rb +6 -9
  215. data/lib/alchemy/config_missing.rb +14 -0
  216. data/lib/alchemy/configuration/base_option.rb +24 -0
  217. data/lib/alchemy/configuration/boolean_option.rb +16 -0
  218. data/lib/alchemy/configuration/class_option.rb +15 -0
  219. data/lib/alchemy/configuration/class_set_option.rb +46 -0
  220. data/lib/alchemy/configuration/integer_list_option.rb +13 -0
  221. data/lib/alchemy/configuration/integer_option.rb +12 -0
  222. data/lib/alchemy/configuration/list_option.rb +22 -0
  223. data/lib/alchemy/configuration/regexp_option.rb +11 -0
  224. data/lib/alchemy/configuration/string_list_option.rb +13 -0
  225. data/lib/alchemy/configuration/string_option.rb +11 -0
  226. data/lib/alchemy/configuration.rb +115 -0
  227. data/lib/alchemy/configuration_methods.rb +3 -1
  228. data/lib/alchemy/configurations/default_language.rb +12 -0
  229. data/lib/alchemy/configurations/default_site.rb +10 -0
  230. data/lib/alchemy/configurations/format_matchers.rb +11 -0
  231. data/lib/alchemy/configurations/mailer.rb +16 -0
  232. data/lib/alchemy/configurations/main.rb +216 -0
  233. data/lib/alchemy/configurations/preview.rb +32 -0
  234. data/lib/alchemy/configurations/sitemap.rb +10 -0
  235. data/lib/alchemy/configurations/uploader.rb +34 -0
  236. data/lib/alchemy/engine.rb +65 -17
  237. data/lib/alchemy/hints.rb +3 -7
  238. data/lib/alchemy/on_page_layout.rb +2 -2
  239. data/lib/alchemy/propshaft/tinymce_asset.rb +15 -0
  240. data/lib/alchemy/seeder.rb +2 -2
  241. data/lib/alchemy/tasks/usage.rb +4 -4
  242. data/lib/alchemy/test_support/config_stubbing.rb +1 -7
  243. data/lib/alchemy/test_support/factories/attachment_factory.rb +13 -2
  244. data/lib/alchemy/test_support/factories/language_factory.rb +1 -1
  245. data/lib/alchemy/test_support/factories/page_factory.rb +2 -3
  246. data/lib/alchemy/test_support/factories/picture_factory.rb +30 -2
  247. data/lib/alchemy/test_support/factories/site_factory.rb +2 -2
  248. data/lib/alchemy/test_support/having_crop_action_examples.rb +2 -2
  249. data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +80 -26
  250. data/lib/alchemy/test_support/shared_ingredient_examples.rb +5 -5
  251. data/lib/alchemy/upgrader/.keep +0 -0
  252. data/lib/alchemy/upgrader/eight_zero.rb +14 -0
  253. data/lib/alchemy/upgrader.rb +33 -20
  254. data/lib/alchemy/version.rb +1 -1
  255. data/lib/alchemy.rb +192 -170
  256. data/lib/alchemy_cms.rb +1 -7
  257. data/lib/generators/alchemy/ingredient/ingredient_generator.rb +0 -3
  258. data/lib/generators/alchemy/install/files/_article.html.erb +6 -4
  259. data/lib/generators/alchemy/install/files/alchemy.en.yml +22 -3
  260. data/lib/generators/alchemy/install/files/application.html.erb +5 -0
  261. data/lib/generators/alchemy/install/install_generator.rb +5 -14
  262. data/lib/generators/alchemy/install/templates/alchemy.rb.tt +196 -0
  263. data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +0 -1
  264. data/lib/generators/alchemy/install/templates/elements.yml.tt +3 -1
  265. data/lib/generators/alchemy/install/templates/menus.yml.tt +1 -1
  266. data/lib/generators/alchemy/install/templates/page_layouts.yml.tt +2 -2
  267. data/lib/generators/alchemy/page_layouts/page_layouts_generator.rb +2 -2
  268. data/lib/tasks/alchemy/assets.rake +14 -0
  269. data/lib/tasks/alchemy/upgrade.rake +12 -47
  270. data/vendor/javascript/tinymce.min.js +1 -1
  271. data/vitest.config.js +21 -0
  272. metadata +181 -180
  273. data/app/assets/builds/alchemy/admin/page-select.css.map +0 -1
  274. data/app/assets/builds/alchemy/admin/print.css.map +0 -1
  275. data/app/assets/builds/alchemy/admin.css.map +0 -1
  276. data/app/assets/builds/alchemy/custom-properties.css.map +0 -1
  277. data/app/assets/builds/alchemy/welcome.css.map +0 -1
  278. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css.map +0 -1
  279. data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css.map +0 -1
  280. data/app/assets/javascripts/alchemy/admin.js +0 -10
  281. data/app/assets/stylesheets/alchemy/_defaults.scss +0 -3
  282. data/app/assets/stylesheets/alchemy/_deprecated_variables.scss +0 -45
  283. data/app/assets/stylesheets/alchemy/_deprecation.scss +0 -17
  284. data/app/assets/stylesheets/alchemy/_extends.scss +0 -62
  285. data/app/assets/stylesheets/alchemy/_variables.scss +0 -201
  286. data/app/assets/stylesheets/alchemy/admin/buttons.scss +0 -124
  287. data/app/assets/stylesheets/alchemy/admin/hints.scss +0 -5
  288. data/app/assets/stylesheets/alchemy/admin/labels.scss +0 -3
  289. data/app/assets/stylesheets/alchemy/admin/pagination.scss +0 -92
  290. data/app/assets/stylesheets/alchemy/admin.scss +0 -42
  291. data/app/assets/stylesheets/alchemy/custom-properties.css +0 -98
  292. data/app/assets/stylesheets/alchemy/welcome.scss +0 -57
  293. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.css +0 -711
  294. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.css +0 -705
  295. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.min.css +0 -7
  296. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.min.css +0 -7
  297. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.css +0 -29
  298. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.min.css +0 -7
  299. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.css +0 -677
  300. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.min.css +0 -7
  301. data/app/controllers/alchemy/elements_controller.rb +0 -32
  302. data/app/models/alchemy/element/dom_id.rb +0 -31
  303. data/app/models/alchemy/picture/calculations.rb +0 -49
  304. data/app/models/alchemy/picture/transformations.rb +0 -115
  305. data/app/views/alchemy/admin/attachments/destroy.js.erb +0 -1
  306. data/app/views/alchemy/admin/clipboard/clear.js.erb +0 -3
  307. data/app/views/alchemy/admin/clipboard/insert.js.erb +0 -29
  308. data/app/views/alchemy/admin/clipboard/remove.js.erb +0 -10
  309. data/app/views/alchemy/admin/resources/_filter.html.erb +0 -12
  310. data/app/views/alchemy/admin/resources/_resource.html.erb +0 -34
  311. data/app/views/alchemy/admin/resources/_table.html.erb +0 -29
  312. data/app/views/alchemy/elements/show.html.erb +0 -1
  313. data/app/views/alchemy/elements/show.js.erb +0 -1
  314. data/app/views/alchemy/ingredients/_audio_view.html.erb +0 -1
  315. data/app/views/alchemy/ingredients/_boolean_view.html.erb +0 -1
  316. data/app/views/alchemy/ingredients/_datetime_view.html.erb +0 -3
  317. data/app/views/alchemy/ingredients/_file_view.html.erb +0 -4
  318. data/app/views/alchemy/ingredients/_headline_view.html.erb +0 -4
  319. data/app/views/alchemy/ingredients/_html_view.html.erb +0 -1
  320. data/app/views/alchemy/ingredients/_link_view.html.erb +0 -4
  321. data/app/views/alchemy/ingredients/_node_view.html.erb +0 -1
  322. data/app/views/alchemy/ingredients/_page_view.html.erb +0 -1
  323. data/app/views/alchemy/ingredients/_picture_view.html.erb +0 -4
  324. data/app/views/alchemy/ingredients/_richtext_view.html.erb +0 -3
  325. data/app/views/alchemy/ingredients/_select_view.html.erb +0 -1
  326. data/app/views/alchemy/ingredients/_text_view.html.erb +0 -4
  327. data/app/views/alchemy/ingredients/_video_view.html.erb +0 -3
  328. data/babel.config.js +0 -12
  329. data/config/initializers/assets.rb +0 -4
  330. data/lib/alchemy/config.rb +0 -114
  331. data/lib/alchemy/element_definition.rb +0 -73
  332. data/lib/alchemy/page_layout.rb +0 -73
  333. data/lib/alchemy/resource_filter.rb +0 -40
  334. data/lib/alchemy/upgrader/seven_point_four.rb +0 -26
  335. data/lib/alchemy/upgrader/seven_point_three.rb +0 -52
  336. data/lib/generators/alchemy/ingredient/templates/view.html.erb +0 -1
  337. data/lib/generators/alchemy/install/files/alchemy_admin.js +0 -1
  338. data/lib/generators/alchemy/install/files/all.js +0 -11
  339. data/lib/generators/alchemy/install/files/article.css +0 -25
  340. data/vendor/assets/images/remixicon.symbol.svg +0 -11
  341. /data/app/{assets/stylesheets → stylesheets}/alchemy/_fonts.scss +0 -0
  342. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachment-select.scss +0 -0
  343. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachments.scss +0 -0
  344. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flash.scss +0 -0
  345. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/list_filter.scss +0 -0
  346. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/node-select.scss +0 -0
  347. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/spinner.scss +0 -0
  348. /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/skintool.json +0 -0
  349. /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/fonts/tinymce-mobile.woff +0 -0
@@ -86,8 +86,8 @@ RSpec.shared_examples_for "having picture thumbnails" do
86
86
  allow(record).to receive(:settings) { {} }
87
87
  end
88
88
 
89
- it "adds them to the url" do
90
- expect(picture_url).to match(/\?foo=baz/)
89
+ it "does not add them to the url" do
90
+ expect(picture_url).to_not match(/\?foo=baz/)
91
91
  end
92
92
  end
93
93
  end
@@ -112,7 +112,7 @@ RSpec.shared_examples_for "having picture thumbnails" do
112
112
  describe "#picture_url_options" do
113
113
  subject(:picture_url_options) { record.picture_url_options }
114
114
 
115
- let(:picture) { build_stubbed(:alchemy_picture) }
115
+ let(:picture) { build(:alchemy_picture) }
116
116
 
117
117
  it { is_expected.to be_a(HashWithIndifferentAccess) }
118
118
 
@@ -418,7 +418,7 @@ RSpec.shared_examples_for "having picture thumbnails" do
418
418
  end
419
419
 
420
420
  context "with picture assigned" do
421
- let(:picture) { build_stubbed(:alchemy_picture) }
421
+ let(:picture) { build(:alchemy_picture) }
422
422
 
423
423
  let(:default_mask) do
424
424
  [
@@ -432,8 +432,8 @@ RSpec.shared_examples_for "having picture thumbnails" do
432
432
  let(:settings) { {} }
433
433
 
434
434
  before do
435
- picture.image_file_width = 300
436
- picture.image_file_height = 250
435
+ allow(picture).to receive(:image_file_width) { 300 }
436
+ allow(picture).to receive(:image_file_height) { 250 }
437
437
  allow(record).to receive(:settings) { settings }
438
438
  end
439
439
 
@@ -555,8 +555,8 @@ RSpec.shared_examples_for "having picture thumbnails" do
555
555
  let(:settings) { {crop: true, size: size} }
556
556
 
557
557
  before do
558
- picture.image_file_width = 200
559
- picture.image_file_height = 100
558
+ allow(picture).to receive(:image_file_width) { 200 }
559
+ allow(picture).to receive(:image_file_height) { 100 }
560
560
  end
561
561
 
562
562
  context "size 200x50" do
@@ -619,45 +619,99 @@ RSpec.shared_examples_for "having picture thumbnails" do
619
619
  end
620
620
 
621
621
  describe "#allow_image_cropping?" do
622
- let(:picture) do
623
- stub_model(Alchemy::Picture, image_file_width: 400, image_file_height: 300)
622
+ let(:picture) { Alchemy::Picture.new }
623
+ let(:image_file_width) { 400 }
624
+ let(:image_file_height) { 300 }
625
+ let(:crop) { false }
626
+ let(:size) { "400x300" }
627
+ let(:upsample) { false }
628
+
629
+ before do
630
+ allow(picture).to receive(:image_file_width) { image_file_width }
631
+ allow(picture).to receive(:image_file_height) { image_file_height }
632
+ allow(record).to receive(:settings) { {crop:, size:, upsample:} }
624
633
  end
625
634
 
626
635
  subject { record.allow_image_cropping? }
627
636
 
628
- it { is_expected.to be_falsy }
637
+ shared_context "with image file" do
638
+ before do
639
+ if Alchemy.storage_adapter.dragonfly?
640
+ expect(picture).to receive(:image_file) { fixture_file_upload("image.png") }
641
+ elsif Alchemy.storage_adapter.active_storage?
642
+ expect(picture.image_file).to receive(:attached?) { true }
643
+ end
644
+ end
645
+ end
629
646
 
630
- context "with picture assigned" do
647
+ shared_context "without image file" do
631
648
  before do
632
- allow(record).to receive(:picture) { picture }
649
+ if Alchemy.storage_adapter.dragonfly?
650
+ expect(picture).to receive(:image_file) { nil }
651
+ elsif Alchemy.storage_adapter.active_storage?
652
+ expect(picture.image_file).to receive(:attached?) { false }
653
+ end
633
654
  end
655
+ end
656
+
657
+ context "with crop set to false" do
658
+ let(:crop) { false }
634
659
 
635
660
  it { is_expected.to be_falsy }
661
+ end
662
+
663
+ context "with crop set to true" do
664
+ let(:crop) { true }
636
665
 
637
- context "and with image larger than crop size" do
666
+ context "with picture assigned" do
638
667
  before do
639
- allow(picture).to receive(:can_be_cropped_to?) { true }
668
+ allow(record).to receive(:picture) { picture }
640
669
  end
641
670
 
642
- it { is_expected.to be_falsy }
671
+ context "and image smaller or equal to crop size" do
672
+ context "if picture.image_file is nil" do
673
+ include_context "without image file"
643
674
 
644
- context "with crop set to true" do
645
- before do
646
- allow(record).to receive(:settings) { {crop: true} }
675
+ it { is_expected.to be_falsy }
647
676
  end
648
677
 
649
- context "if picture.image_file is nil" do
650
- before do
651
- expect(picture).to receive(:image_file) { nil }
652
- end
678
+ context "if picture.image_file is present" do
679
+ include_context "with image file"
653
680
 
654
681
  it { is_expected.to be_falsy }
682
+
683
+ context "but with upsample set to true" do
684
+ let(:upsample) { true }
685
+
686
+ it { is_expected.to be(true) }
687
+ end
655
688
  end
689
+ end
656
690
 
657
- context "if picture.image_file is present" do
658
- let(:picture) { build_stubbed(:alchemy_picture) }
691
+ context "and with image larger than crop size" do
692
+ let(:image_file_width) { 1201 }
693
+ let(:image_file_height) { 481 }
694
+
695
+ it { is_expected.to be_falsy }
659
696
 
660
- it { is_expected.to be(true) }
697
+ context "with crop set to true" do
698
+ context "if picture.image_file is nil" do
699
+ include_context "without image file"
700
+
701
+ it { is_expected.to be_falsy }
702
+ end
703
+
704
+ context "if picture.image_file is present" do
705
+ include_context "with image file"
706
+
707
+ it { is_expected.to be(true) }
708
+
709
+ context "with size setting being nil" do
710
+ let(:size) { nil }
711
+
712
+ it { is_expected.to be_falsey }
713
+ end
714
+ end
661
715
  end
662
716
  end
663
717
  end
@@ -29,11 +29,11 @@ RSpec.shared_examples_for "an alchemy ingredient" do
29
29
  context "with element" do
30
30
  before do
31
31
  expect(element).to receive(:ingredient_definition_for).at_least(:once) do
32
- {
32
+ Alchemy::IngredientDefinition.new(
33
33
  settings: {
34
34
  linkable: true
35
35
  }
36
- }.with_indifferent_access
36
+ )
37
37
  end
38
38
  end
39
39
 
@@ -47,19 +47,19 @@ RSpec.shared_examples_for "an alchemy ingredient" do
47
47
  context "without element" do
48
48
  let(:element) { nil }
49
49
 
50
- it { is_expected.to eq({}) }
50
+ it { is_expected.to be_a(Alchemy::IngredientDefinition) }
51
51
  end
52
52
 
53
53
  context "with element" do
54
54
  let(:definition) do
55
- {
55
+ Alchemy::IngredientDefinition.new(
56
56
  role: "headline",
57
57
  type: "Text",
58
58
  default: "Hello World",
59
59
  settings: {
60
60
  linkable: true
61
61
  }
62
- }.with_indifferent_access
62
+ )
63
63
  end
64
64
 
65
65
  before do
File without changes
@@ -0,0 +1,14 @@
1
+ module Alchemy
2
+ class Upgrader
3
+ module EightZero
4
+ def mention_alchemy_config_initializer
5
+ todo <<~TEXT, "Configuration has changed to initializer"
6
+ Check the new configuration file `./config/initializers/alchemy.rb` and
7
+ update values from your `config/alchemy/config.yml` file.
8
+
9
+ Then you can safely remove the `config/alchemy/config.yml` file.
10
+ TEXT
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,29 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "alchemy/shell"
4
+ require "thor"
4
5
 
5
6
  module Alchemy
6
7
  class Upgrader
7
- extend Alchemy::Shell
8
-
9
- Dir["#{File.dirname(__FILE__)}/upgrader/*.rb"].sort.each { |f| require f }
10
-
11
- class << self
12
- def copy_new_config_file
13
- desc "Copy configuration file."
14
- config_file = Rails.root.join("config/alchemy/config.yml")
15
- default_config = File.join(File.dirname(__FILE__), "../../config/alchemy/config.yml")
16
- if !File.exist? config_file
17
- log "No configuration file found. Creating it."
18
- FileUtils.cp default_config, Rails.root.join("config/alchemy/config.yml")
19
- elsif FileUtils.identical? default_config, config_file
20
- log "Configuration file already present.", :skip
21
- else
22
- log "Custom configuration file found."
23
- FileUtils.cp default_config, Rails.root.join("config/alchemy/config.yml.defaults")
24
- log "Copied new default configuration file."
25
- todo "Check the default configuration file (./config/alchemy/config.yml.defaults) for new configuration options and insert them into your config file.", "Configuration has changed"
26
- end
8
+ include Alchemy::Shell
9
+ include Thor::Base
10
+ include Thor::Actions
11
+
12
+ Dir["#{File.dirname(__FILE__)}/upgrader/*.rb"].sort.each { require(_1) }
13
+
14
+ VERSION_MODULE_MAP = {
15
+ "8.0" => "Alchemy::Upgrader::EightZero"
16
+ }
17
+
18
+ source_root Alchemy::Engine.root.join("lib/generators/alchemy/install")
19
+
20
+ def initialize(version)
21
+ super()
22
+ self.class.include VERSION_MODULE_MAP[version.to_s].constantize
23
+ end
24
+
25
+ def update_config
26
+ desc "Copy configuration file."
27
+ @default_config = Alchemy::Configurations::Main.new
28
+ template("templates/alchemy.rb.tt", "config/initializers/alchemy.rb")
29
+ end
30
+
31
+ def run_migrations
32
+ ActiveRecord::Migration.check_all_pending!
33
+ rescue ActiveRecord::PendingMigrationError
34
+ desc "Pending Database migrations."
35
+ if yes?("Run database migrations now? (y/N)")
36
+ log "Migrating Database..."
37
+ Rake::Task["db:migrate"].invoke
38
+ else
39
+ log "Don't forget to run database migrations later with rake `db:migrate`.", :skip
27
40
  end
28
41
  end
29
42
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "7.4.6"
4
+ VERSION = "8.0.0.a"
5
5
 
6
6
  def self.version
7
7
  VERSION
data/lib/alchemy.rb CHANGED
@@ -2,190 +2,212 @@
2
2
 
3
3
  require "alchemy/admin/preview_url"
4
4
  require "importmap-rails"
5
+ require "alchemy/configurations/main"
6
+ require "alchemy/config_missing"
5
7
 
6
8
  module Alchemy
9
+ include Alchemy::ConfigMissing
10
+ extend Alchemy::ConfigMissing
11
+
7
12
  YAML_PERMITTED_CLASSES = %w[Symbol Date Regexp]
8
13
 
9
- # Define page preview sources
10
- #
11
- # A preview source is a Ruby class returning an URL
12
- # that is used as source for the preview frame in the
13
- # admin UI.
14
- #
15
- # == Example
16
- #
17
- # # lib/acme/preview_source.rb
18
- # class Acme::PreviewSource < Alchemy::Admin::PreviewUrl
19
- # def url_for(page)
20
- # if page.site.name == "Next"
21
- # "https://user:#{ENV['PREVIEW_HTTP_PASS']}@next.acme.com"
22
- # else
23
- # "https://www.acme.com"
24
- # end
25
- # end
26
- # end
27
- #
28
- # # config/initializers/alchemy.rb
29
- # require "acme/preview_source"
30
- # Alchemy.preview_sources << Acme::PreviewSource
31
- #
32
- # # config/locales/de.yml
33
- # de:
34
- # activemodel:
35
- # models:
36
- # acme/preview_source: Acme Vorschau
37
- #
38
- def self.preview_sources
39
- @_preview_sources ||= Set.new << Alchemy::Admin::PreviewUrl
40
- end
14
+ # JS Importmap instance
15
+ singleton_class.attr_accessor :importmap
16
+ self.importmap = Importmap::Map.new
41
17
 
42
- def self.preview_sources=(sources)
43
- @_preview_sources = Array(sources)
44
- end
18
+ class << self
19
+ def config
20
+ @_config ||= Alchemy::Configurations::Main.new
21
+ end
45
22
 
46
- # Additional JS modules to be imported in the Alchemy admin UI
47
- #
48
- # Be sure to also pin the modules with +Alchemy.importmap+.
49
- #
50
- # == Example
51
- #
52
- # Alchemy.importmap.pin "flatpickr/de",
53
- # to: "https://ga.jspm.io/npm:flatpickr@4.6.13/dist/l10n/de.js"
54
- #
55
- # Alchemy.admin_js_imports << "flatpickr/de"
56
- #
57
- def self.admin_js_imports
58
- @_admin_js_imports ||= Set.new
59
- end
23
+ def configure(&blk)
24
+ yield config
25
+ end
60
26
 
61
- def self.admin_js_imports=(sources)
62
- @_admin_js_imports = Set[sources]
63
- end
27
+ enable_searchable_deprecation_msg = "Use `Alchemy.config.show_page_searchable_checkbox` instead."
28
+ def enable_searchable = config.show_page_searchable_checkbox
29
+ deprecate :enable_searchable= => enable_searchable_deprecation_msg, :deprecator => Alchemy::Deprecation
64
30
 
65
- # Additional importmaps to be included in the Alchemy admin UI
66
- #
67
- # Be sure to also pin modules with +Alchemy.importmap+.
68
- #
69
- # == Example
70
- #
71
- # # config/alchemy/importmap.rb
72
- # Alchemy.importmap.pin "alchemy_solidus", to: "alchemy_solidus.js", preload: true
73
- # Alchemy.importmap.pin_all_from Alchemy::Solidus::Engine.root.join("app/javascript/alchemy_solidus"),
74
- # under: "alchemy_solidus",
75
- # preload: true
76
- #
77
- # # lib/alchemy/solidus/engine.rb
78
- # initializer "alchemy_solidus.assets", before: "alchemy.importmap" do |app|
79
- # Alchemy.admin_importmaps.add({
80
- # importmap_path: root.join("config/importmap.rb"),
81
- # source_paths: [
82
- # root.join("app/javascript")
83
- # ],
84
- # name: "alchemy_solidus"
85
- # })
86
- # app.config.assets.precompile << "alchemy_solidus/manifest.js"
87
- # end
88
- #
89
- # @return [Set<Hash>]
90
- def self.admin_importmaps
91
- @_admin_importmaps ||= Set.new([{
92
- importmap_path: Engine.root.join("config/importmap.rb"),
93
- source_paths: [
94
- Engine.root.join("app/javascript"),
95
- Engine.root.join("vendor/javascript")
96
- ],
97
- name: "alchemy_admin"
98
- }])
99
- end
31
+ def enable_searchable=(other)
32
+ config.show_page_searchable_checkbox = other
33
+ end
34
+ deprecate enable_searchable: enable_searchable_deprecation_msg, deprecator: Alchemy::Deprecation
100
35
 
101
- # Additional stylesheets to be included in the Alchemy admin UI
102
- #
103
- # == Example
104
- #
105
- # # lib/alchemy/devise/engine.rb
106
- # initializer "alchemy.devise.stylesheets", before: "alchemy.admin_stylesheets" do
107
- # Alchemy.admin_stylesheets << "alchemy/devise/admin.css"
108
- # end
109
- #
110
- # @return [Set<String>]
111
- def self.admin_stylesheets
112
- @_admin_stylesheets ||= Set.new(["alchemy/admin/custom.css"])
113
- end
36
+ # Define page preview sources
37
+ #
38
+ # A preview source is a Ruby class returning an URL
39
+ # that is used as source for the preview frame in the
40
+ # admin UI.
41
+ #
42
+ # == Example
43
+ #
44
+ # # lib/acme/preview_source.rb
45
+ # class Acme::PreviewSource < Alchemy::Admin::PreviewUrl
46
+ # def url_for(page)
47
+ # if page.site.name == "Next"
48
+ # "https://user:#{ENV['PREVIEW_HTTP_PASS']}@next.acme.com"
49
+ # else
50
+ # "https://www.acme.com"
51
+ # end
52
+ # end
53
+ # end
54
+ #
55
+ # # config/initializers/alchemy.rb
56
+ # require "acme/preview_source"
57
+ # Alchemy.preview_sources << Acme::PreviewSource
58
+ #
59
+ # # config/locales/de.yml
60
+ # de:
61
+ # activemodel:
62
+ # models:
63
+ # acme/preview_source: Acme Vorschau
64
+ #
65
+ def preview_sources
66
+ @_preview_sources ||= Set.new << Alchemy::Admin::PreviewUrl
67
+ end
114
68
 
115
- # Define page publish targets
116
- #
117
- # A publish target is a ActiveJob that gets performed
118
- # whenever a user clicks the publish page button.
119
- #
120
- # Use this to trigger deployment hooks of external
121
- # services in an asychronous way.
122
- #
123
- # == Example
124
- #
125
- # # app/jobs/publish_job.rb
126
- # class PublishJob < ApplicationJob
127
- # def perform(page)
128
- # RestClient.post(ENV['BUILD_HOOK_URL'])
129
- # end
130
- # end
131
- #
132
- # # config/initializers/alchemy.rb
133
- # Alchemy.publish_targets << PublishJob
134
- #
135
- def self.publish_targets
136
- @_publish_targets ||= Set.new
137
- end
69
+ def preview_sources=(sources)
70
+ @_preview_sources = Array(sources)
71
+ end
138
72
 
139
- # Enable full text search configuration
140
- #
141
- # It enables a searchable checkbox in the page form to toggle
142
- # the searchable field. These information can used in a search
143
- # plugin (e.g. https://github.com/AlchemyCMS/alchemy-pg_search).
144
- #
145
- # == Example
146
- #
147
- # # config/initializers/alchemy.rb
148
- # Alchemy.enable_searchable = true
149
- #
150
- mattr_accessor :enable_searchable, default: false
73
+ # Additional JS modules to be imported in the Alchemy admin UI
74
+ #
75
+ # Be sure to also pin the modules with +Alchemy.importmap+.
76
+ #
77
+ # == Example
78
+ #
79
+ # Alchemy.importmap.pin "flatpickr/de",
80
+ # to: "https://ga.jspm.io/npm:flatpickr@4.6.13/dist/l10n/de.js"
81
+ #
82
+ # Alchemy.admin_js_imports << "flatpickr/de"
83
+ #
84
+ def admin_js_imports
85
+ @_admin_js_imports ||= Set.new
86
+ end
151
87
 
152
- # JS Importmap instance
153
- singleton_class.attr_accessor :importmap
154
- self.importmap = Importmap::Map.new
88
+ def admin_js_imports=(sources)
89
+ @_admin_js_imports = Set[sources]
90
+ end
155
91
 
156
- # Configure tabs in the link dialog
157
- #
158
- # With this configuration that tabs in the link dialog can be extended
159
- # without overwriting or defacing the Admin Interface.
160
- #
161
- # == Example
162
- #
163
- # # components/acme/link_tab.rb
164
- # module Acme
165
- # class LinkTab < ::Alchemy::Admin::LinkDialog::BaseTab
166
- # def title
167
- # "Awesome Tab Title"
168
- # end
169
- #
170
- # def name
171
- # :unique_name
172
- # end
173
- #
174
- # def fields
175
- # [ title_input, target_select ]
176
- # end
177
- # end
178
- # end
92
+ # Additional importmaps to be included in the Alchemy admin UI
93
+ #
94
+ # Be sure to also pin modules with +Alchemy.importmap+.
95
+ #
96
+ # == Example
97
+ #
98
+ # # config/alchemy/importmap.rb
99
+ # Alchemy.importmap.pin "alchemy_solidus", to: "alchemy_solidus.js", preload: true
100
+ # Alchemy.importmap.pin_all_from Alchemy::Solidus::Engine.root.join("app/javascript/alchemy_solidus"),
101
+ # under: "alchemy_solidus",
102
+ # preload: true
103
+ #
104
+ # # lib/alchemy/solidus/engine.rb
105
+ # initializer "alchemy_solidus.assets", before: "alchemy.importmap" do |app|
106
+ # Alchemy.admin_importmaps.add({
107
+ # importmap_path: root.join("config/importmap.rb"),
108
+ # source_paths: [
109
+ # root.join("app/javascript")
110
+ # ],
111
+ # name: "alchemy_solidus"
112
+ # })
113
+ # app.config.assets.precompile << "alchemy_solidus/manifest.js"
114
+ # end
115
+ #
116
+ # @return [Set<Hash>]
117
+ def admin_importmaps
118
+ @_admin_importmaps ||= Set.new([{
119
+ importmap_path: Engine.root.join("config/importmap.rb"),
120
+ source_paths: [
121
+ Engine.root.join("app/javascript"),
122
+ Engine.root.join("vendor/javascript")
123
+ ],
124
+ name: "alchemy_admin"
125
+ }])
126
+ end
127
+
128
+ # Additional stylesheets to be included in the Alchemy admin UI
129
+ #
130
+ # == Example
131
+ #
132
+ # # lib/alchemy/devise/engine.rb
133
+ # initializer "alchemy.devise.stylesheets", before: "alchemy.admin_stylesheets" do
134
+ # Alchemy.admin_stylesheets << "alchemy/devise/admin.css"
135
+ # end
136
+ #
137
+ # @return [Set<String>]
138
+ def admin_stylesheets
139
+ @_admin_stylesheets ||= Set.new(["alchemy/admin/custom.css"])
140
+ end
141
+
142
+ # Define page publish targets
143
+ #
144
+ # A publish target is a ActiveJob that gets performed
145
+ # whenever a user clicks the publish page button.
146
+ #
147
+ # Use this to trigger deployment hooks of external
148
+ # services in an asychronous way.
149
+ #
150
+ # == Example
151
+ #
152
+ # # app/jobs/publish_job.rb
153
+ # class PublishJob < ApplicationJob
154
+ # def perform(page)
155
+ # RestClient.post(ENV['BUILD_HOOK_URL'])
156
+ # end
157
+ # end
158
+ #
159
+ # # config/initializers/alchemy.rb
160
+ # Alchemy.publish_targets << PublishJob
161
+ #
162
+ def publish_targets
163
+ @_publish_targets ||= Set.new
164
+ end
165
+
166
+ # Configure tabs in the link dialog
167
+ #
168
+ # With this configuration that tabs in the link dialog can be extended
169
+ # without overwriting or defacing the Admin Interface.
170
+ #
171
+ # == Example
172
+ #
173
+ # # components/acme/link_tab.rb
174
+ # module Acme
175
+ # class LinkTab < ::Alchemy::Admin::LinkDialog::BaseTab
176
+ # def title
177
+ # "Awesome Tab Title"
178
+ # end
179
+ #
180
+ # def name
181
+ # :unique_name
182
+ # end
183
+ #
184
+ # def fields
185
+ # [ title_input, target_select ]
186
+ # end
187
+ # end
188
+ # end
189
+ #
190
+ # # config/initializers/alchemy.rb
191
+ # Alchemy.link_dialog_tabs << Acme::LinkTab
192
+ #
193
+ def link_dialog_tabs
194
+ @_link_dialog_tabs ||= Set.new([
195
+ Alchemy::Admin::LinkDialog::InternalTab,
196
+ Alchemy::Admin::LinkDialog::AnchorTab,
197
+ Alchemy::Admin::LinkDialog::ExternalTab,
198
+ Alchemy::Admin::LinkDialog::FileTab
199
+ ])
200
+ end
201
+ end
202
+
203
+ # The storage adapter for Pictures and Attachments
179
204
  #
180
- # # config/initializers/alchemy.rb
181
- # Alchemy.link_dialog_tabs << Acme::LinkTab
205
+ # Chose between 'active_storage' (default) or 'dragonfly' (legacy)
182
206
  #
183
- def self.link_dialog_tabs
184
- @_link_dialog_tabs ||= Set.new([
185
- Alchemy::Admin::LinkDialog::InternalTab,
186
- Alchemy::Admin::LinkDialog::AnchorTab,
187
- Alchemy::Admin::LinkDialog::ExternalTab,
188
- Alchemy::Admin::LinkDialog::FileTab
189
- ])
207
+ # Can be set via 'ALCHEMY_STORAGE_ADAPTER' env var.
208
+ def self.storage_adapter
209
+ @_storage_adapter ||= Alchemy::StorageAdapter.new(
210
+ ENV.fetch("ALCHEMY_STORAGE_ADAPTER", Alchemy.config.storage_adapter)
211
+ )
190
212
  end
191
213
  end