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
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dragonfly_svg"
4
3
  require "alchemy/dragonfly/processors/crop_resize"
5
4
  require "alchemy/dragonfly/processors/auto_orient"
6
5
  require "alchemy/dragonfly/processors/thumbnail"
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Mime::Type.register "image/svg+xml", :svg
4
+ Mime::Type.register "image/webp", :webp
@@ -89,6 +89,7 @@ en:
89
89
  image/tiff: TIFF Image
90
90
  image/x-psd: Photoshop File
91
91
  image/svg+xml: SVG Image
92
+ image/webp: WebP Image
92
93
  text/plain: Plain Text Document
93
94
  text/x-vcard: vCard
94
95
  video/mp4: MPEG-4 Video
@@ -104,41 +105,57 @@ en:
104
105
  filters:
105
106
  page:
106
107
  by_page_layout:
108
+ all: All page types
107
109
  name: Page Type
108
110
  status:
111
+ all: Do not filter
109
112
  name: Status
110
113
  values:
111
114
  not_public: Unpublished
112
115
  published: Published
113
116
  restricted: Restricted
117
+ not_public:
118
+ name: Unpublished
119
+ published:
120
+ name: Published
121
+ restricted:
122
+ name: Restricted
123
+ updated_at_lteq:
124
+ name: Updated before
125
+ updated_at_gteq:
126
+ name: Updated after
114
127
  picture:
115
128
  by_file_format:
129
+ all: Show all file types
116
130
  name: File Type
117
131
  values:
132
+ <<: *mime_types
118
133
  gif: GIF Image
119
134
  jpeg: JPG Image
120
135
  png: PNG Image
121
136
  svg: SVG Image
122
137
  tiff: TIFF Image
123
138
  webp: WebP Image
124
- misc:
125
- name: Miscellaneous
126
- values:
127
- last_upload: Last upload only
128
- recent: Recently uploaded only
129
- without_tag: Without tag
130
- deletable: Not linked by file content
139
+ last_upload:
140
+ name: Last upload only
141
+ recent:
142
+ name: Recently uploaded only
143
+ without_tag:
144
+ name: Without tag
145
+ deletable:
146
+ name: Not linked by file content
131
147
  attachment:
132
148
  by_file_type:
133
149
  name: File Type
134
150
  values:
135
151
  <<: *mime_types
136
- misc:
137
- name: Miscellaneous
138
- values:
139
- last_upload: Last upload only
140
- recent: Recently uploaded only
141
- without_tag: Without tag
152
+ all: Show all file types
153
+ last_upload:
154
+ name: Last upload only
155
+ recent:
156
+ name: Recently uploaded only
157
+ without_tag:
158
+ name: Without tag
142
159
 
143
160
  default_ingredient_texts:
144
161
  lorem: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
@@ -681,7 +698,7 @@ en:
681
698
  time:
682
699
  formats:
683
700
  alchemy:
684
- default: "%m.%d.%Y %H:%M"
701
+ default: "%Y-%m-%d %H:%M"
685
702
  ingredient_date: "%Y-%m-%d"
686
703
  page_status: "%m.%d.%Y %H:%M"
687
704
  short_datetime: "%d %b %H:%M"
@@ -719,6 +736,7 @@ en:
719
736
  attributes:
720
737
  image_file:
721
738
  blank: "Please attach a picture."
739
+ too_big: must not be larger than %{maximum}MB
722
740
  alchemy/user:
723
741
  attributes:
724
742
  email:
data/config/routes.rb CHANGED
@@ -68,7 +68,6 @@ Alchemy::Engine.routes.draw do
68
68
 
69
69
  resources :attachments, except: [:new] do
70
70
  member do
71
- get :download
72
71
  put :assign
73
72
  end
74
73
  end
@@ -114,7 +113,6 @@ Alchemy::Engine.routes.draw do
114
113
  as: :show_attachment
115
114
 
116
115
  resources :messages, only: [:index, :new, :create]
117
- resources :elements, only: :show
118
116
 
119
117
  namespace :api, defaults: {format: "json"} do
120
118
  resources :attachments, only: [:index]
data/eslint.config.js CHANGED
@@ -1,5 +1,6 @@
1
- module.exports = [
1
+ export default [
2
2
  {
3
+ ignores: ["spec/**/*"],
3
4
  rules: {
4
5
  "no-restricted-imports": [
5
6
  "error",
@@ -41,7 +41,6 @@ module Alchemy
41
41
 
42
42
  def url_for(page)
43
43
  @preview_config = preview_config_for(page)
44
-
45
44
  if @preview_config && uri
46
45
  uri_class.build(
47
46
  host: uri.host,
@@ -60,10 +59,10 @@ module Alchemy
60
59
  attr_reader :routes
61
60
 
62
61
  def preview_config_for(page)
63
- preview_config = Alchemy::Config.get(:preview)
62
+ preview_config = Alchemy.config.preview
64
63
  return unless preview_config
65
64
 
66
- preview_config[page.site.name] || preview_config
65
+ preview_config.for_site(page.site) || preview_config
67
66
  end
68
67
 
69
68
  def uri
@@ -81,8 +80,8 @@ module Alchemy
81
80
  end
82
81
 
83
82
  def userinfo
84
- auth = @preview_config["auth"]
85
- auth ? "#{auth["username"]}:#{auth["password"]}" : nil
83
+ auth = @preview_config.auth
84
+ auth.username ? "#{auth["username"]}:#{auth["password"]}" : nil
86
85
  end
87
86
  end
88
87
  end
@@ -14,15 +14,12 @@ module Alchemy
14
14
  def dependencies
15
15
  case @name.to_s
16
16
  when /^alchemy\/pages\/show/
17
- PageLayout.all.map { |p| "alchemy/page_layouts/_#{p["name"]}" }
17
+ PageDefinition.all.map { "alchemy/page_layouts/_#{_1.name}" }
18
18
  when /^alchemy\/page_layouts\/_(\w+)/
19
- page_layout = PageLayout.get($1)
20
- layout_elements = page_layout.fetch("elements", [])
21
- layout_elements.map { |name| "alchemy/elements/_#{name}" }
19
+ page_definition = PageDefinition.get($1)
20
+ page_definition.elements.map { "alchemy/elements/_#{_1}" }
22
21
  when /^alchemy\/elements\/_(\w+)/
23
- ingredient_types($1).map { |type|
24
- "alchemy/ingredients/_#{type.underscore}_view"
25
- }.uniq
22
+ ingredient_types($1).map { "alchemy/ingredients/_#{_1.underscore}_view" }.tap(&:uniq!)
26
23
  else
27
24
  ActionView::DependencyTracker::ERBTracker.call(@name, @template)
28
25
  end
@@ -31,10 +28,10 @@ module Alchemy
31
28
  private
32
29
 
33
30
  def ingredient_types(name)
34
- element = Element.definitions.detect { |e| e["name"] == name }
31
+ element = Element.definitions.detect { _1.name == name }
35
32
  return [] unless element
36
33
 
37
- element.fetch("ingredients", []).collect { |c| c["type"] }
34
+ element.ingredients.map(&:type)
38
35
  end
39
36
  end
40
37
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module ConfigMissing
5
+ def const_missing(missing_const_name)
6
+ if missing_const_name == :Config
7
+ Alchemy::Deprecation.warn("Alchemy::Config is deprecated. Use Alchemy.config instead.")
8
+ Alchemy.config
9
+ else
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class Configuration
5
+ class BaseOption
6
+ def self.value_class
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def initialize(value:, name:, **args)
11
+ @name = name
12
+ @value = validate(value) unless value.nil?
13
+ end
14
+ attr_reader :name, :value
15
+
16
+ private
17
+
18
+ def validate(value)
19
+ raise TypeError, "#{name} must be set as a #{self.class.value_class.name}, given #{value.inspect}" unless value.is_a?(self.class.value_class)
20
+ value
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "alchemy/configuration/base_option"
4
+
5
+ module Alchemy
6
+ class Configuration
7
+ class BooleanOption < BaseOption
8
+ private
9
+
10
+ def validate(value)
11
+ raise TypeError, "#{name} must be a Boolean, given #{value.inspect}" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
12
+ value
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "alchemy/configuration/base_option"
4
+
5
+ module Alchemy
6
+ class Configuration
7
+ class ClassOption < BaseOption
8
+ def self.value_class
9
+ String
10
+ end
11
+
12
+ def value = @value.constantize
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "alchemy/configuration/base_option"
4
+
5
+ module Alchemy
6
+ class Configuration
7
+ class ClassSetOption < BaseOption
8
+ include Enumerable
9
+
10
+ def value = self
11
+
12
+ def <<(klass)
13
+ @value << klass.to_s
14
+ end
15
+
16
+ def concat(klasses)
17
+ klasses.each do |klass|
18
+ self << klass
19
+ end
20
+
21
+ self
22
+ end
23
+
24
+ delegate :clear, :empty?, to: :@value
25
+
26
+ def delete(object)
27
+ @value.delete(object.to_s)
28
+ end
29
+
30
+ def each
31
+ @value.each do |klass|
32
+ yield klass.constantize
33
+ end
34
+ end
35
+
36
+ def as_json = @value
37
+
38
+ private
39
+
40
+ def validate(value)
41
+ raise TypeError, "each #{name} must be set as a String" unless value.all? { _1.is_a?(String) }
42
+ value
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "alchemy/configuration/list_option"
4
+
5
+ module Alchemy
6
+ class Configuration
7
+ class IntegerListOption < ListOption
8
+ def self.item_class
9
+ Integer
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "alchemy/configuration/base_option"
4
+ module Alchemy
5
+ class Configuration
6
+ class IntegerOption < BaseOption
7
+ def self.value_class
8
+ Integer
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "alchemy/configuration/base_option"
4
+
5
+ module Alchemy
6
+ class Configuration
7
+ class ListOption < BaseOption
8
+ def self.item_class
9
+ raise NotImplementedError
10
+ end
11
+
12
+ private
13
+
14
+ def validate(value)
15
+ unless value.is_a?(Array) && value.all? { _1.is_a?(self.class.item_class) }
16
+ raise TypeError, "#{@name} must be an Array of #{self.class.item_class.name.downcase.pluralize}, given #{value.inspect}"
17
+ end
18
+ value
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class Configuration
5
+ class RegexpOption < BaseOption
6
+ def self.value_class
7
+ Regexp
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "alchemy/configuration/list_option"
4
+
5
+ module Alchemy
6
+ class Configuration
7
+ class StringListOption < ListOption
8
+ def self.item_class
9
+ String
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class Configuration
5
+ class StringOption < BaseOption
6
+ def self.value_class
7
+ String
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext/string"
5
+
6
+ require "alchemy/configuration/boolean_option"
7
+ require "alchemy/configuration/class_option"
8
+ require "alchemy/configuration/class_set_option"
9
+ require "alchemy/configuration/integer_option"
10
+ require "alchemy/configuration/integer_list_option"
11
+ require "alchemy/configuration/regexp_option"
12
+ require "alchemy/configuration/string_list_option"
13
+ require "alchemy/configuration/string_option"
14
+
15
+ module Alchemy
16
+ class Configuration
17
+ def initialize(configuration_hash = {})
18
+ set(configuration_hash)
19
+ end
20
+
21
+ def set(configuration_hash)
22
+ configuration_hash.each do |key, value|
23
+ send(:"#{key}=", value)
24
+ end
25
+ end
26
+
27
+ alias_method :get, :send
28
+ alias_method :[], :get
29
+
30
+ def show = self
31
+
32
+ def fetch(key, default = nil)
33
+ get(key) || default
34
+ end
35
+
36
+ def set_from_yaml(file)
37
+ set(
38
+ YAML.safe_load(
39
+ ERB.new(File.read(file)).result,
40
+ permitted_classes: YAML_PERMITTED_CLASSES,
41
+ aliases: true
42
+ ) || {}
43
+ )
44
+ end
45
+
46
+ def to_h
47
+ self.class.defined_options.map do |option|
48
+ [option, send(option)]
49
+ end.concat(
50
+ self.class.defined_configurations.map do |configuration|
51
+ [configuration, send(configuration).to_h]
52
+ end
53
+ ).to_h
54
+ end
55
+
56
+ class << self
57
+ def defined_configurations = []
58
+
59
+ def defined_options = []
60
+
61
+ def configuration(name, configuration_class)
62
+ # The defined configurations on a class are all those defined directly on
63
+ # that class as well as those defined on ancestors.
64
+ # We store these as a class instance variable on each class which has a
65
+ # configuration. super() collects configurations defined on ancestors.
66
+ singleton_configurations = (@defined_singleton_configurations ||= [])
67
+ singleton_configurations << name.to_sym
68
+
69
+ define_singleton_method :defined_configurations do
70
+ super() + singleton_configurations
71
+ end
72
+
73
+ define_method(name) do
74
+ unless instance_variable_get(:"@#{name}")
75
+ send(:"#{name}=", configuration_class.new)
76
+ end
77
+ instance_variable_get(:"@#{name}")
78
+ end
79
+
80
+ define_method(:"#{name}=") do |value|
81
+ if value.is_a?(configuration_class)
82
+ instance_variable_set(:"@#{name}", value)
83
+ else
84
+ send(name).set(value)
85
+ end
86
+ end
87
+ end
88
+
89
+ def option(name, type, default: nil, **args)
90
+ klass = "Alchemy::Configuration::#{type.to_s.camelize}Option".constantize
91
+ # The defined options on a class are all those defined directly on
92
+ # that class as well as those defined on ancestors.
93
+ # We store these as a class instance variable on each class which has a
94
+ # option. super() collects options defined on ancestors.
95
+ singleton_options = (@defined_singleton_options ||= [])
96
+ singleton_options << name.to_sym
97
+
98
+ define_singleton_method :defined_options do
99
+ super() + singleton_options
100
+ end
101
+
102
+ define_method(name) do
103
+ unless instance_variable_defined?(:"@#{name}")
104
+ send(:"#{name}=", default)
105
+ end
106
+ instance_variable_get(:"@#{name}").value
107
+ end
108
+
109
+ define_method(:"#{name}=") do |value|
110
+ instance_variable_set(:"@#{name}", klass.new(value:, name:, **args))
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -12,9 +12,11 @@ module Alchemy
12
12
  #
13
13
  # Config file is in +config/alchemy/config.yml+
14
14
  #
15
+ # @deprecated - Use Alchemy.config.get instead
15
16
  def configuration(name)
16
- Config.get(name)
17
+ Alchemy.config.get(name)
17
18
  end
19
+ deprecate configuration: "Alchemy.config.get", deprecator: Alchemy::Deprecation
18
20
 
19
21
  # Returns true if more than one language is published on current site.
20
22
  #
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module Configurations
5
+ class DefaultLanguage < Alchemy::Configuration
6
+ option :name, :string, default: "English"
7
+ option :code, :string, default: "en"
8
+ option :page_layout, :string, default: "index"
9
+ option :frontpage_name, :string, default: "Index"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module Configurations
5
+ class DefaultSite < Alchemy::Configuration
6
+ option :name, :string, default: "Default Site"
7
+ option :host, :string, default: "*"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module Configurations
5
+ class FormatMatchers < Alchemy::Configuration
6
+ option :email, :regexp, default: /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/
7
+ option :url, :regexp, default: /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix
8
+ option :link_url, :regexp, default: /^(tel:|mailto:|\/|[a-z]+:\/\/)/
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module Configurations
5
+ class Mailer < Alchemy::Configuration
6
+ option :page_layout_name, :string, default: "contact"
7
+ option :forward_to_page, :boolean, default: false
8
+ option :mail_success_page, :string, default: "thanks"
9
+ option :mail_from, :string, default: "your.mail@your-domain.com"
10
+ option :mail_to, :string, default: "your.mail@your-domain.com"
11
+ option :subject, :string, default: "A new contact form message"
12
+ option :fields, :string_list, default: %w[salutation firstname lastname address zip city phone email message]
13
+ option :validate_fields, :string_list, default: %w[lastname email]
14
+ end
15
+ end
16
+ end