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
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module Admin
5
+ module Filters
6
+ class Select < Base
7
+ attr_reader :options
8
+
9
+ # Creates a resource filter that displays as a select.
10
+ # @param name [String] The name of the filter.
11
+ # @param resource_name [String] The name of the resource.
12
+ # @param options [Proc, Array] A proc that returns the options for the select, or an array of options.
13
+ def initialize(name:, resource_name:, options:)
14
+ super(name:, resource_name:)
15
+ @options = options_to_proc(options)
16
+ end
17
+
18
+ # Returns a select filter component.
19
+ # @param params [Hash] The search filter params.
20
+ # @param query [Ransack::Search] The current search query.
21
+ # @return [ Alchemy::Admin::Resource::SelectFilter] The select filter component.
22
+ def input_component(params, query)
23
+ Alchemy::Admin::Resource::SelectFilter.new(
24
+ name:,
25
+ resource_name:,
26
+ label: translated_name,
27
+ include_blank:,
28
+ options: get_options_for_select(query),
29
+ params:
30
+ )
31
+ end
32
+
33
+ private
34
+
35
+ def include_blank
36
+ Alchemy.t(:all, scope: [:filters, resource_name, name])
37
+ end
38
+
39
+ def options_to_proc(options)
40
+ if options.is_a? Proc
41
+ options
42
+ else
43
+ ->(_query) { options }
44
+ end
45
+ end
46
+
47
+ def get_options_for_select(query)
48
+ options_for_select = options.call(query)
49
+ # The result of the query is an Array of Arrays, where the first element is the translated name and the second element is the value.
50
+ # If the first element is an Array, we assume that the options are already translated.
51
+ if options_for_select.first.is_a? Array
52
+ options_for_select
53
+ # If the values are translatable, we translate them.
54
+ elsif Alchemy.t(:values, scope: [:filters, resource_name, name])
55
+ options_for_select.map do |value|
56
+ [Alchemy.t(value.to_sym, scope: [:filters, resource_name, name, :values]), value]
57
+ end
58
+ # Otherwise we return the options as they are.
59
+ else
60
+ options_for_select.map { |option| [option, option] }
61
+ end
62
+ end
63
+
64
+ def translated_value(value, query)
65
+ get_options_for_select(query).detect { |option| option[1].to_s == value }&.first
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module Admin
5
+ module ResourceName
6
+ def resource_model_name
7
+ resource_array.join("/").classify
8
+ end
9
+
10
+ def resource_name
11
+ resources_name.singularize
12
+ end
13
+
14
+ def resource_array
15
+ controller_path_array.reject { |el| el == "admin" }
16
+ end
17
+
18
+ def resources_name
19
+ resource_array.last
20
+ end
21
+
22
+ def controller_path_array
23
+ controller_path.split("/")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -24,11 +24,9 @@ module Alchemy
24
24
  include Alchemy::Taggable
25
25
  include Alchemy::TouchElements
26
26
 
27
- dragonfly_accessor :file, app: :alchemy_attachments do
28
- after_assign { |f| write_attribute(:file_mime_type, f.mime_type) }
29
- end
27
+ include Alchemy.storage_adapter.attachment_class_methods
30
28
 
31
- stampable stamper_class_name: Alchemy.user_class.name
29
+ stampable stamper_class_name: Alchemy.user_class_name
32
30
 
33
31
  has_many :file_ingredients,
34
32
  class_name: "Alchemy::Ingredients::File",
@@ -38,7 +36,10 @@ module Alchemy
38
36
  has_many :elements, through: :file_ingredients
39
37
  has_many :pages, through: :elements
40
38
 
41
- scope :by_file_type, ->(file_type) { where(file_mime_type: file_type) }
39
+ scope :by_file_type, ->(file_type) do
40
+ Alchemy.storage_adapter.by_file_type_scope(file_type)
41
+ end
42
+
42
43
  scope :recent, -> { where("#{table_name}.created_at > ?", Time.current - 24.hours).order(:created_at) }
43
44
  scope :without_tag, -> { left_outer_joins(:taggings).where(gutentag_taggings: {id: nil}) }
44
45
 
@@ -46,9 +47,8 @@ module Alchemy
46
47
  class << self
47
48
  # The class used to generate URLs for attachments
48
49
  #
49
- # @see Alchemy::Attachment::Url
50
50
  def url_class
51
- @_url_class ||= Alchemy::Attachment::Url
51
+ @_url_class ||= Alchemy.storage_adapter.attachment_url_class
52
52
  end
53
53
 
54
54
  # Set a different attachment url class
@@ -58,19 +58,6 @@ module Alchemy
58
58
  @_url_class = klass
59
59
  end
60
60
 
61
- def alchemy_resource_filters
62
- [
63
- {
64
- name: :by_file_type,
65
- values: distinct.pluck(:file_mime_type).map { |type| [Alchemy.t(type, scope: "mime_types"), type] }.sort_by(&:first)
66
- },
67
- {
68
- name: :misc,
69
- values: %w[recent last_upload without_tag]
70
- }
71
- ]
72
- end
73
-
74
61
  def last_upload
75
62
  last_id = Attachment.maximum(:id)
76
63
  return Attachment.all unless last_id
@@ -79,33 +66,43 @@ module Alchemy
79
66
  end
80
67
 
81
68
  def searchable_alchemy_resource_attributes
82
- %w[name file_name]
69
+ Alchemy.storage_adapter.searchable_alchemy_resource_attributes(name)
70
+ end
71
+
72
+ def ransackable_attributes(_auth_object = nil)
73
+ Alchemy.storage_adapter.ransackable_attributes(name)
74
+ end
75
+
76
+ def ransackable_associations(_auth_object = nil)
77
+ Alchemy.storage_adapter.ransackable_associations(name)
78
+ end
79
+
80
+ def file_types(scope = all)
81
+ Alchemy.storage_adapter.file_formats(name, scope:)
83
82
  end
84
83
 
85
84
  def allowed_filetypes
86
- Config.get(:uploader).fetch("allowed_filetypes", {}).fetch("alchemy/attachments", [])
85
+ Alchemy.config.uploader.allowed_filetypes.alchemy_attachments
86
+ end
87
+
88
+ def ransackable_scopes(_auth_object = nil)
89
+ %i[by_file_type recent last_upload without_tag]
87
90
  end
88
91
  end
89
92
 
90
93
  validates_presence_of :file
91
- validates_size_of :file, maximum: Config.get(:uploader)["file_size_limit"].megabytes
92
- validates_property :ext,
93
- of: :file,
94
- in: allowed_filetypes,
95
- case_sensitive: false,
96
- message: Alchemy.t("not a valid file"),
94
+ validates_size_of :file, maximum: Alchemy.config.uploader.file_size_limit.megabytes
95
+ validate :file_type_allowed,
97
96
  unless: -> { self.class.allowed_filetypes.include?("*") }
98
97
 
99
- before_save :set_name, if: :file_name_changed?
98
+ before_save :set_name, if: -> { Alchemy.storage_adapter.set_attachment_name?(self) }
100
99
 
101
100
  scope :with_file_type, ->(file_type) { where(file_mime_type: file_type) }
102
101
 
103
102
  # Instance methods
104
103
 
105
104
  def url(options = {})
106
- if file
107
- self.class.url_class.new(self).call(options)
108
- end
105
+ self.class.url_class.new(self).call(options)
109
106
  end
110
107
 
111
108
  # An url save filename without format suffix
@@ -118,9 +115,23 @@ module Alchemy
118
115
  pages.any? && pages.not_restricted.blank?
119
116
  end
120
117
 
118
+ # File name
119
+ def file_name
120
+ Alchemy.storage_adapter.file_name(self)
121
+ end
122
+
123
+ # File size
124
+ def file_size
125
+ Alchemy.storage_adapter.file_size(self)
126
+ end
127
+
128
+ def file_mime_type
129
+ Alchemy.storage_adapter.file_mime_type(self)
130
+ end
131
+
121
132
  # File format suffix
122
133
  def extension
123
- file_name.split(".").last
134
+ Alchemy.storage_adapter.file_extension(self)
124
135
  end
125
136
 
126
137
  alias_method :suffix, :extension
@@ -156,8 +167,14 @@ module Alchemy
156
167
 
157
168
  private
158
169
 
170
+ def file_type_allowed
171
+ unless extension&.in?(self.class.allowed_filetypes)
172
+ errors.add(:file, Alchemy.t("not a valid file"))
173
+ end
174
+ end
175
+
159
176
  def set_name
160
- self.name = convert_to_humanized_name(file_name, file.ext)
177
+ self.name ||= convert_to_humanized_name(file_name, extension)
161
178
  end
162
179
  end
163
180
  end
@@ -6,6 +6,8 @@ module Alchemy
6
6
  end
7
7
 
8
8
  class BaseRecord < ActiveRecord::Base
9
+ include ConfigMissing
10
+ extend ConfigMissing
9
11
  extend Alchemy::SearchableResource
10
12
 
11
13
  self.abstract_class = true
@@ -46,7 +46,7 @@ module Alchemy
46
46
  else
47
47
  log_warning "Could not find element definition for #{name}. " \
48
48
  "Please check your elements.yml file!"
49
- {}
49
+ ElementDefinition.new
50
50
  end
51
51
  end
52
52
  end
@@ -53,16 +53,16 @@ module Alchemy
53
53
 
54
54
  # Returns all element ingredient definitions from the +elements.yml+ file
55
55
  def ingredient_definitions
56
- definition.fetch(:ingredients, [])
56
+ definition.ingredients
57
57
  end
58
58
 
59
59
  # Returns the definition for given ingredient role
60
60
  def ingredient_definition_for(role)
61
61
  if ingredient_definitions.blank?
62
- log_warning "Element #{name} is missing the ingredient definition for #{role}"
63
62
  nil
64
63
  else
65
- ingredient_definitions.find { |d| d[:role] == role.to_s }
64
+ ingredient_definitions.find { _1.role == role.to_s } ||
65
+ log_warning("Element #{name} is missing the ingredient definition for #{role}")
66
66
  end
67
67
  end
68
68
 
@@ -99,10 +99,10 @@ module Alchemy
99
99
 
100
100
  # Builds ingredients for this element as described in the +elements.yml+
101
101
  def build_ingredients
102
- ingredient_definitions.each do |attributes|
102
+ ingredient_definitions.each do |definition|
103
103
  ingredients.build(
104
- role: attributes[:role],
105
- type: Alchemy::Ingredient.normalize_type(attributes[:type])
104
+ role: definition.role,
105
+ type: Alchemy::Ingredient.normalize_type(definition.type)
106
106
  )
107
107
  end
108
108
  end
@@ -33,7 +33,7 @@ module Alchemy
33
33
  # @see Alchemy::Element::Presenters#display_name_for
34
34
  #
35
35
  def display_name
36
- self.class.display_name_for(definition["name"] || name)
36
+ self.class.display_name_for(definition.name || name)
37
37
  end
38
38
 
39
39
  # Returns a preview text for element.
@@ -78,15 +78,6 @@ module Alchemy
78
78
  "#{display_name}: #{preview_text(maxlength)}"
79
79
  end
80
80
 
81
- # Returns a dom id used for elements html id tag.
82
- # @deprecated
83
- def dom_id
84
- if caller.none? { |l| l =~ Regexp.new("alchemy/elements_block_helper.rb:117:in `element_view_for'") }
85
- Alchemy::Deprecation.warn("dom_id is deprecated and will be removed from Alchemy 8.0. Please pass an id to the element_view_for helper instead.")
86
- end
87
- self.class.dom_id_class.new(self).call
88
- end
89
-
90
81
  # The ingredient that's used for element's preview text.
91
82
  #
92
83
  # It tries to find one of element's ingredients that is defined +as_element_title+.
@@ -113,8 +104,8 @@ module Alchemy
113
104
  def first_ingredient_by_definition
114
105
  return if ingredient_definitions.empty?
115
106
 
116
- role = ingredient_definitions.first["role"]
117
- ingredients.detect { |ingredient| ingredient.role == role }
107
+ role = ingredient_definitions.first.role
108
+ ingredients.detect { _1.role == role }
118
109
  end
119
110
  end
120
111
  end
@@ -30,7 +30,6 @@ module Alchemy
30
30
 
31
31
  include Alchemy::Logger
32
32
  include Alchemy::Taggable
33
- include Alchemy::Hints
34
33
 
35
34
  FORBIDDEN_DEFINITION_ATTRIBUTES = [
36
35
  "amount",
@@ -55,7 +54,7 @@ module Alchemy
55
54
  #
56
55
  acts_as_list scope: [:page_version_id, :fixed, :parent_element_id]
57
56
 
58
- stampable stamper_class_name: Alchemy.user_class.name
57
+ stampable stamper_class_name: Alchemy.user_class_name
59
58
 
60
59
  before_destroy :delete_all_nested_elements
61
60
 
@@ -107,6 +106,7 @@ module Alchemy
107
106
  scope :not_nested, -> { where(parent_element_id: nil) }
108
107
 
109
108
  delegate :restricted?, to: :page, allow_nil: true
109
+ delegate :deprecation_notice, :has_hint?, :hint, to: :definition
110
110
 
111
111
  # Concerns
112
112
  include Definitions
@@ -131,27 +131,9 @@ module Alchemy
131
131
  raise(ElementDefinitionError, attributes)
132
132
  end
133
133
 
134
- super(element_definition.merge(element_attributes).except(*FORBIDDEN_DEFINITION_ATTRIBUTES))
134
+ super(element_definition.attributes.merge(element_attributes).except(*FORBIDDEN_DEFINITION_ATTRIBUTES))
135
135
  end
136
136
 
137
- # The class responsible for the +dom_id+ of elements.
138
- # Defaults to +Alchemy::Element::DomId+.
139
- # @deprecated
140
- def dom_id_class
141
- if caller.none? { |l| l =~ Regexp.new("alchemy/element/presenters.rb:87:in `dom_id'") }
142
- Alchemy::Deprecation.warn("dom_id_class is deprecated and will be removed from Alchemy 8.0. Please pass an id to the element_view_for helper instead.")
143
- end
144
- @_dom_id_class || DomId
145
- end
146
-
147
- # Register a custom +DomId+ class responsible for the +dom_id+ of elements.
148
- # Defaults to +Alchemy::Element::DomId+.
149
- # @deprecated
150
- def dom_id_class=(klass)
151
- @_dom_id_class = klass
152
- end
153
- deprecate :dom_id_class=, deprecator: Alchemy::Deprecation
154
-
155
137
  # This methods does a copy of source and all its ingredients.
156
138
  #
157
139
  # == Options
@@ -185,7 +167,7 @@ module Alchemy
185
167
  def all_from_clipboard_for_parent_element(clipboard, parent_element)
186
168
  return none if clipboard.nil? || parent_element.nil?
187
169
 
188
- all_from_clipboard(clipboard).where(name: parent_element.definition["nestable_elements"])
170
+ all_from_clipboard(clipboard).where(name: parent_element.definition.nestable_elements)
189
171
  end
190
172
  end
191
173
 
@@ -229,7 +211,7 @@ module Alchemy
229
211
 
230
212
  # Returns true if the definition of this element has a taggable true value.
231
213
  def taggable?
232
- definition["taggable"] == true
214
+ definition.taggable == true
233
215
  end
234
216
 
235
217
  # The opposite of folded?
@@ -239,7 +221,7 @@ module Alchemy
239
221
 
240
222
  # Defined as compact element?
241
223
  def compact?
242
- definition["compact"] == true
224
+ definition.compact
243
225
  end
244
226
 
245
227
  # Defined as deprecated element?
@@ -271,7 +253,7 @@ module Alchemy
271
253
  #
272
254
  # @return Boolean
273
255
  def deprecated?
274
- !!definition["deprecated"]
256
+ !!definition.deprecated
275
257
  end
276
258
 
277
259
  # The element's view partial is dependent from its name
@@ -294,13 +276,13 @@ module Alchemy
294
276
 
295
277
  # A collection of element names that can be nested inside this element.
296
278
  def nestable_elements
297
- definition.fetch("nestable_elements", [])
279
+ definition.nestable_elements
298
280
  end
299
281
 
300
282
  private
301
283
 
302
284
  def generate_nested_elements
303
- definition.fetch("autogenerate", []).each do |nestable_element|
285
+ definition.autogenerate.each do |nestable_element|
304
286
  if nestable_elements.include?(nestable_element)
305
287
  Element.create(page_version: page_version, parent_element_id: id, name: nestable_element)
306
288
  else
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class ElementDefinition
5
+ include ActiveModel::Model
6
+ include ActiveModel::Attributes
7
+ include Alchemy::Hints
8
+
9
+ extend ActiveModel::Translation
10
+
11
+ attribute :name, :string
12
+ attribute :unique, :boolean, default: false
13
+ attribute :amount, :integer, default: Float::INFINITY
14
+ attribute :taggable, :boolean, default: false
15
+ attribute :compact, :boolean, default: false
16
+ attribute :fixed, :boolean, default: false
17
+ attribute :ingredients, default: []
18
+ attribute :nestable_elements, default: []
19
+ attribute :autogenerate, default: []
20
+ attribute :deprecated
21
+ attribute :message
22
+ attribute :warning
23
+ attribute :hint
24
+
25
+ validates :name,
26
+ presence: true,
27
+ format: {
28
+ with: /\A[a-z_-]+\z/
29
+ }
30
+
31
+ delegate :blank?, to: :name
32
+
33
+ class << self
34
+ # Returns the definitions from elements.yml file.
35
+ #
36
+ # Place a +elements.yml+ file inside your apps +config/alchemy+ folder to define
37
+ # your own set of elements
38
+ #
39
+ def all
40
+ @definitions ||= read_definitions_file.map { new(**_1) }
41
+ end
42
+
43
+ # Add additional page definitions to collection.
44
+ #
45
+ # Useful for extending the elements from an Alchemy module.
46
+ #
47
+ # === Usage Example
48
+ #
49
+ # Call +Alchemy::ElementDefinition.add(your_definition)+ in your engine.rb file.
50
+ #
51
+ # @param [Array || Hash]
52
+ # You can pass a single element definition as Hash, or a collection of elements as Array.
53
+ #
54
+ def add(definition)
55
+ all
56
+ @definitions += Array.wrap(definition).map { new(**_1) }
57
+ end
58
+
59
+ # Returns one element definition by given name.
60
+ #
61
+ def get(name)
62
+ return new if name.blank?
63
+
64
+ all.detect { _1.name.casecmp(name).zero? }
65
+ end
66
+
67
+ def reset!
68
+ @definitions = nil
69
+ end
70
+
71
+ # The absolute +elements.yml+ file path
72
+ # @return [Pathname]
73
+ def definitions_file_path
74
+ Rails.root.join("config", "alchemy", "elements.yml")
75
+ end
76
+
77
+ private
78
+
79
+ def definitions_file
80
+ File.read(definitions_file_path)
81
+ end
82
+
83
+ def definitions_file_exist?
84
+ File.exist?(definitions_file_path)
85
+ end
86
+
87
+ # Reads the element definitions from +config/alchemy/elements.yml+.
88
+ #
89
+ def read_definitions_file
90
+ if definitions_file_exist?
91
+ YAML.safe_load(
92
+ ERB.new(definitions_file).result,
93
+ permitted_classes: YAML_PERMITTED_CLASSES,
94
+ aliases: true
95
+ ) || []
96
+ else
97
+ raise LoadError,
98
+ "Could not find elements.yml file! Please run `rails generate alchemy:install`"
99
+ end
100
+ end
101
+ end
102
+
103
+ def attributes
104
+ super.with_indifferent_access
105
+ end
106
+
107
+ def ingredients
108
+ super.map { IngredientDefinition.new(**_1) }
109
+ end
110
+
111
+ # Returns a deprecation notice for elements marked deprecated
112
+ #
113
+ # You can either use localizations or pass a String as notice
114
+ # in the element definition.
115
+ #
116
+ # == Custom deprecation notices
117
+ #
118
+ # Use general element deprecation notice
119
+ #
120
+ # - name: old_element
121
+ # deprecated: true
122
+ #
123
+ # Add a translation to your locale file for a per element notice.
124
+ #
125
+ # en:
126
+ # alchemy:
127
+ # element_deprecation_notices:
128
+ # old_element: Foo baz widget is deprecated
129
+ #
130
+ # or use the global translation that apply to all deprecated elements.
131
+ #
132
+ # en:
133
+ # alchemy:
134
+ # element_deprecation_notice: Foo baz widget is deprecated
135
+ #
136
+ # or pass string as deprecation notice.
137
+ #
138
+ # - name: old_element
139
+ # deprecated: This element will be removed soon.
140
+ #
141
+ def deprecation_notice
142
+ case deprecated
143
+ when String
144
+ deprecated
145
+ when TrueClass
146
+ Alchemy.t(
147
+ name,
148
+ scope: :element_deprecation_notices,
149
+ default: Alchemy.t(:element_deprecated)
150
+ )
151
+ end
152
+ end
153
+
154
+ private
155
+
156
+ def hint_translation_scope
157
+ :element_hints
158
+ end
159
+ end
160
+ end