alchemy_cms 7.4.10 → 8.0.0.b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (425) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -5
  3. data/app/assets/builds/alchemy/admin/page-select.css +1 -1
  4. data/app/assets/builds/alchemy/admin/print.css +1 -1
  5. data/app/assets/builds/alchemy/admin.css +2 -2
  6. data/app/assets/builds/alchemy/dark-theme.css +1 -0
  7. data/app/assets/builds/alchemy/light-theme.css +1 -0
  8. data/app/assets/builds/alchemy/theme.css +1 -0
  9. data/app/assets/builds/alchemy/welcome.css +1 -1
  10. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
  11. data/app/assets/builds/tinymce/skins/content/alchemy-dark/content.min.css +1 -0
  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/builds/tinymce/skins/ui/alchemy-dark/content.min.css +1 -0
  15. data/app/assets/builds/tinymce/skins/ui/alchemy-dark/skin.min.css +1 -0
  16. data/app/assets/config/alchemy_manifest.js +0 -2
  17. data/app/assets/images/alchemy/element_icons/default.svg +1 -0
  18. data/app/assets/images/alchemy/icons-sprite.svg +1 -0
  19. data/app/components/alchemy/admin/element_select.rb +39 -0
  20. data/app/components/alchemy/admin/resource/applied_filter.rb +29 -0
  21. data/app/components/alchemy/admin/resource/checkbox_filter.rb +36 -0
  22. data/app/components/alchemy/admin/resource/datepicker_filter.rb +42 -0
  23. data/app/components/alchemy/admin/resource/select_filter.rb +43 -0
  24. data/app/components/alchemy/admin/toolbar_button.rb +5 -2
  25. data/app/components/alchemy/ingredients/number_view.rb +18 -0
  26. data/app/controllers/alchemy/admin/attachments_controller.rb +10 -15
  27. data/app/controllers/alchemy/admin/clipboard_controller.rb +2 -6
  28. data/app/controllers/alchemy/admin/elements_controller.rb +3 -1
  29. data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
  30. data/app/controllers/alchemy/admin/pages_controller.rb +15 -15
  31. data/app/controllers/alchemy/admin/pictures_controller.rb +27 -37
  32. data/app/controllers/alchemy/admin/resources_controller.rb +16 -106
  33. data/app/controllers/alchemy/attachments_controller.rb +43 -14
  34. data/app/controllers/alchemy/messages_controller.rb +1 -1
  35. data/app/controllers/alchemy/pages_controller.rb +26 -4
  36. data/app/controllers/concerns/alchemy/admin/resource_filter.rb +93 -0
  37. data/app/decorators/alchemy/element_editor.rb +5 -48
  38. data/app/decorators/alchemy/ingredient_editor.rb +3 -53
  39. data/app/helpers/alchemy/admin/attachments_helper.rb +5 -5
  40. data/app/helpers/alchemy/admin/base_helper.rb +14 -84
  41. data/app/helpers/alchemy/admin/pages_helper.rb +1 -1
  42. data/app/helpers/alchemy/base_helper.rb +0 -30
  43. data/app/helpers/alchemy/elements_block_helper.rb +0 -14
  44. data/app/helpers/alchemy/pages_helper.rb +1 -1
  45. data/{lib → app/helpers}/alchemy/resources_helper.rb +5 -45
  46. data/app/javascript/alchemy_admin/components/action.js +2 -0
  47. data/app/javascript/alchemy_admin/components/alchemy_html_element.js +3 -3
  48. data/app/javascript/alchemy_admin/components/auto_submit.js +20 -0
  49. data/app/javascript/alchemy_admin/components/datepicker.js +18 -7
  50. data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +8 -7
  51. data/app/javascript/alchemy_admin/components/element_editor.js +25 -15
  52. data/app/javascript/alchemy_admin/components/element_select.js +43 -0
  53. data/app/javascript/alchemy_admin/components/index.js +3 -0
  54. data/app/javascript/alchemy_admin/components/link_buttons.js +6 -2
  55. data/app/javascript/alchemy_admin/components/remote_select.js +9 -2
  56. data/app/javascript/alchemy_admin/components/tags_autocomplete.js +5 -1
  57. data/app/javascript/alchemy_admin/components/tinymce.js +93 -14
  58. data/app/javascript/alchemy_admin/components/update_check.js +42 -0
  59. data/app/javascript/alchemy_admin/components/uploader/file_upload.js +15 -8
  60. data/app/javascript/alchemy_admin/components/uploader/progress.js +12 -6
  61. data/app/javascript/alchemy_admin/components/uploader.js +4 -2
  62. data/app/javascript/alchemy_admin/confirm_dialog.js +27 -57
  63. data/app/javascript/alchemy_admin/dialog.js +1 -1
  64. data/app/javascript/alchemy_admin/dirty.js +3 -2
  65. data/app/javascript/alchemy_admin/file_editors.js +1 -1
  66. data/app/javascript/alchemy_admin/i18n.js +15 -16
  67. data/app/javascript/alchemy_admin/image_loader.js +4 -2
  68. data/app/javascript/alchemy_admin/initializer.js +1 -49
  69. data/app/javascript/alchemy_admin/picture_editors.js +7 -4
  70. data/app/javascript/alchemy_admin/picture_selector.js +4 -4
  71. data/app/javascript/alchemy_admin/utils/ajax.js +51 -44
  72. data/app/javascript/alchemy_admin.js +3 -8
  73. data/app/jobs/alchemy/delete_picture_job.rb +12 -0
  74. data/app/models/alchemy/admin/filters/base.rb +38 -0
  75. data/app/models/alchemy/admin/filters/checkbox.rb +24 -0
  76. data/app/models/alchemy/admin/filters/datepicker.rb +53 -0
  77. data/app/models/alchemy/admin/filters/select.rb +70 -0
  78. data/app/models/alchemy/admin/resource_name.rb +27 -0
  79. data/app/models/alchemy/attachment.rb +49 -44
  80. data/app/models/alchemy/base_record.rb +2 -0
  81. data/app/models/alchemy/element/definitions.rb +1 -1
  82. data/app/models/alchemy/element/element_ingredients.rb +6 -6
  83. data/app/models/alchemy/element/presenters.rb +3 -12
  84. data/app/models/alchemy/element.rb +10 -27
  85. data/app/models/alchemy/element_definition.rb +190 -0
  86. data/app/models/alchemy/ingredient.rb +10 -43
  87. data/app/models/alchemy/ingredient_definition.rb +134 -0
  88. data/app/models/alchemy/ingredient_validator.rb +7 -3
  89. data/app/models/alchemy/ingredients/number.rb +19 -0
  90. data/app/models/alchemy/language.rb +2 -21
  91. data/app/models/alchemy/message.rb +3 -7
  92. data/app/models/alchemy/node.rb +1 -1
  93. data/app/models/alchemy/page/{page_layouts.rb → definitions.rb} +12 -19
  94. data/app/models/alchemy/page/fixed_attributes.rb +1 -1
  95. data/app/models/alchemy/page/page_elements.rb +13 -14
  96. data/app/models/alchemy/page/page_naming.rb +3 -11
  97. data/app/models/alchemy/page/page_natures.rb +20 -15
  98. data/app/models/alchemy/page/page_scopes.rb +1 -1
  99. data/app/models/alchemy/page.rb +12 -39
  100. data/app/models/alchemy/page_definition.rb +115 -0
  101. data/app/models/alchemy/picture.rb +71 -99
  102. data/app/models/alchemy/picture_variant.rb +115 -5
  103. data/{lib → app/models}/alchemy/resource.rb +4 -18
  104. data/{lib → app/models}/alchemy/searchable_resource.rb +15 -0
  105. data/app/models/alchemy/site/layout.rb +5 -5
  106. data/app/models/alchemy/site.rb +1 -21
  107. data/app/models/alchemy/storage_adapter/active_storage/attachment_url.rb +41 -0
  108. data/app/models/alchemy/storage_adapter/active_storage/picture_url.rb +55 -0
  109. data/app/models/alchemy/storage_adapter/active_storage/preprocessor.rb +40 -0
  110. data/app/models/alchemy/storage_adapter/active_storage.rb +173 -0
  111. data/app/models/alchemy/{attachment/url.rb → storage_adapter/dragonfly/attachment_url.rb} +12 -12
  112. data/app/models/alchemy/storage_adapter/dragonfly/picture_url.rb +75 -0
  113. data/app/models/alchemy/{picture → storage_adapter/dragonfly}/preprocessor.rb +4 -4
  114. data/app/models/alchemy/storage_adapter/dragonfly.rb +205 -0
  115. data/app/models/alchemy/storage_adapter.rb +74 -0
  116. data/app/models/concerns/alchemy/picture_thumbnails.rb +19 -6
  117. data/app/models/concerns/alchemy/relatable_resource.rb +28 -0
  118. data/app/serializers/alchemy/element_serializer.rb +0 -1
  119. data/app/services/alchemy/dragonfly_to_image_processing.rb +100 -0
  120. data/app/stylesheets/alchemy/_custom-properties.scss +162 -0
  121. data/app/stylesheets/alchemy/_defaults.scss +3 -0
  122. data/app/stylesheets/alchemy/_extends.scss +69 -0
  123. data/app/{assets/stylesheets → stylesheets}/alchemy/_mixins.scss +40 -68
  124. data/app/stylesheets/alchemy/_themes.scss +540 -0
  125. data/app/stylesheets/alchemy/_variables.scss +5 -0
  126. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/archive.scss +45 -42
  127. data/app/stylesheets/alchemy/admin/attachments.scss +17 -0
  128. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/base.scss +20 -15
  129. data/app/stylesheets/alchemy/admin/buttons.scss +135 -0
  130. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/clipboard.scss +2 -2
  131. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dashboard.scss +13 -16
  132. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dialogs.scss +33 -16
  133. data/app/stylesheets/alchemy/admin/element-select.scss +11 -0
  134. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/elements.scss +239 -133
  135. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/errors.scss +5 -5
  136. data/app/stylesheets/alchemy/admin/filters.scss +57 -0
  137. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flatpickr.scss +54 -76
  138. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/form_fields.scss +10 -11
  139. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/forms.scss +28 -21
  140. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/frame.scss +20 -18
  141. data/app/stylesheets/alchemy/admin/hints.scss +5 -0
  142. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/icons.scss +1 -1
  143. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/image_library.scss +21 -61
  144. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/images.scss +1 -1
  145. data/app/stylesheets/alchemy/admin/labels.scss +5 -0
  146. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/lists.scss +3 -3
  147. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/navigation.scss +55 -59
  148. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/node-select.scss +1 -10
  149. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/nodes.scss +21 -18
  150. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/notices.scss +20 -19
  151. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/page-select.scss +18 -2
  152. data/app/stylesheets/alchemy/admin/pagination.scss +137 -0
  153. data/app/stylesheets/alchemy/admin/preview_window.scss +46 -0
  154. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/print.scss +1 -1
  155. data/app/stylesheets/alchemy/admin/resource_info.scss +148 -0
  156. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/search.scss +10 -7
  157. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/selects.scss +85 -46
  158. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/shoelace.scss +37 -68
  159. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/sitemap.scss +39 -34
  160. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tables.scss +9 -7
  161. data/app/stylesheets/alchemy/admin/tags.scss +143 -0
  162. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/toolbar.scss +6 -6
  163. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/typography.scss +3 -6
  164. data/app/{assets/stylesheets → stylesheets}/alchemy/admin/upload.scss +7 -5
  165. data/app/stylesheets/alchemy/admin.scss +44 -0
  166. data/app/stylesheets/alchemy/dark-theme.scss +5 -0
  167. data/app/stylesheets/alchemy/light-theme.scss +6 -0
  168. data/app/stylesheets/alchemy/theme.scss +13 -0
  169. data/app/stylesheets/alchemy/welcome.scss +75 -0
  170. data/app/stylesheets/tinymce/skins/content/alchemy/content.scss +69 -0
  171. data/app/{assets/stylesheets/tinymce/skins/content/alchemy → stylesheets/tinymce/skins/content/alchemy-dark}/content.scss +12 -12
  172. data/app/stylesheets/tinymce/skins/ui/alchemy/content.scss +1 -0
  173. data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/skin.scss +158 -176
  174. data/app/stylesheets/tinymce/skins/ui/alchemy-dark/content.scss +1 -0
  175. data/app/stylesheets/tinymce/skins/ui/alchemy-dark/skin.scss +3784 -0
  176. data/app/views/alchemy/admin/attachments/_files_list.html.erb +22 -12
  177. data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +1 -1
  178. data/app/views/alchemy/admin/attachments/assign.js.erb +4 -3
  179. data/app/views/alchemy/admin/attachments/show.html.erb +55 -43
  180. data/app/views/alchemy/admin/{elements/_clipboard_button.html.erb → clipboard/_button.html.erb} +3 -5
  181. data/app/views/alchemy/admin/clipboard/_update_nested_element_button.turbo_stream.erb +11 -0
  182. data/app/views/alchemy/admin/clipboard/clear.turbo_stream.erb +4 -0
  183. data/app/views/alchemy/admin/clipboard/index.html.erb +15 -13
  184. data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +18 -0
  185. data/app/views/alchemy/admin/clipboard/remove.turbo_stream.erb +9 -0
  186. data/app/views/alchemy/admin/crop.html.erb +1 -1
  187. data/app/views/alchemy/admin/dashboard/info.html.erb +17 -31
  188. data/app/views/alchemy/admin/elements/_element.html.erb +4 -8
  189. data/app/views/alchemy/admin/elements/_form.html.erb +9 -9
  190. data/app/views/alchemy/admin/elements/_header.html.erb +5 -1
  191. data/app/views/alchemy/admin/elements/_toolbar.html.erb +4 -6
  192. data/app/views/alchemy/admin/elements/create.turbo_stream.erb +2 -1
  193. data/app/views/alchemy/admin/elements/index.html.erb +2 -2
  194. data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +3 -16
  195. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +0 -9
  196. data/app/views/alchemy/admin/languages/_form.html.erb +1 -1
  197. data/app/views/alchemy/admin/languages/_table.html.erb +1 -1
  198. data/app/views/alchemy/admin/languages/index.html.erb +5 -2
  199. data/app/views/alchemy/admin/layoutpages/index.html.erb +1 -12
  200. data/app/views/alchemy/admin/pages/_form.html.erb +2 -2
  201. data/app/views/alchemy/admin/pages/_page.html.erb +2 -3
  202. data/app/views/alchemy/admin/pages/_toolbar.html.erb +1 -15
  203. data/app/views/alchemy/admin/pages/index.html.erb +1 -1
  204. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +9 -12
  205. data/app/views/alchemy/admin/partials/_search_form.html.erb +4 -9
  206. data/app/views/alchemy/admin/pictures/_archive.html.erb +16 -29
  207. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +3 -7
  208. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -1
  209. data/app/views/alchemy/admin/pictures/_form.html.erb +1 -1
  210. data/app/views/alchemy/admin/pictures/_infos.html.erb +21 -52
  211. data/app/views/alchemy/admin/pictures/_library_sidebar.html.erb +7 -0
  212. data/app/views/alchemy/admin/pictures/_picture.html.erb +14 -20
  213. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +20 -16
  214. data/app/views/alchemy/admin/pictures/_sorting_select.html.erb +13 -0
  215. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -1
  216. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +1 -6
  217. data/app/views/alchemy/admin/pictures/index.html.erb +5 -19
  218. data/app/views/alchemy/admin/pictures/show.html.erb +10 -5
  219. data/app/views/alchemy/admin/resources/_applied_filters.html.erb +8 -0
  220. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +5 -25
  221. data/app/views/alchemy/admin/resources/_pagination.html.erb +6 -0
  222. data/app/views/alchemy/admin/resources/_per_page_select.html.erb +4 -2
  223. data/app/views/alchemy/admin/resources/_resource_table.html.erb +1 -1
  224. data/app/views/alchemy/admin/resources/_resource_usage_info.html.erb +36 -0
  225. data/app/views/alchemy/admin/resources/_table_header.html.erb +1 -15
  226. data/app/views/alchemy/admin/sites/index.html.erb +5 -1
  227. data/app/views/alchemy/admin/styleguide/index.html.erb +118 -58
  228. data/app/views/alchemy/admin/tags/index.html.erb +1 -1
  229. data/app/views/alchemy/admin/tinymce/_setup.html.erb +7 -7
  230. data/app/{javascript/alchemy_admin/locales/en.js → views/alchemy/admin/translations/_en.js} +5 -2
  231. data/app/views/alchemy/admin/uploader/_button.html.erb +1 -1
  232. data/app/views/alchemy/admin/uploader/_setup.html.erb +4 -4
  233. data/app/views/alchemy/base/error_notice.html.erb +1 -1
  234. data/app/views/alchemy/ingredients/_number_editor.html.erb +24 -0
  235. data/app/views/alchemy/no_index.html.erb +31 -0
  236. data/app/views/alchemy/welcome.html.erb +12 -10
  237. data/app/views/kaminari/alchemy/_first_page.html.erb +5 -3
  238. data/app/views/kaminari/alchemy/_last_page.html.erb +5 -3
  239. data/app/views/kaminari/alchemy/_next_page.html.erb +5 -3
  240. data/app/views/kaminari/alchemy/_paginator.html.erb +18 -13
  241. data/app/views/kaminari/alchemy/_prev_page.html.erb +5 -3
  242. data/app/views/layouts/alchemy/admin.html.erb +25 -24
  243. data/config/alchemy/config.yml +3 -2
  244. data/config/initializers/dragonfly.rb +0 -1
  245. data/config/initializers/mime_types.rb +1 -0
  246. data/config/locales/alchemy.en.yml +57 -21
  247. data/config/routes.rb +0 -2
  248. data/db/migrate/20250905140323_add_created_at_index_to_pictures_and_attachments.rb +14 -0
  249. data/lib/alchemy/admin/preview_url.rb +4 -5
  250. data/lib/alchemy/cache_digests/template_tracker.rb +6 -9
  251. data/lib/alchemy/config_missing.rb +14 -0
  252. data/lib/alchemy/configuration/base_option.rb +24 -0
  253. data/lib/alchemy/configuration/boolean_option.rb +16 -0
  254. data/lib/alchemy/configuration/class_option.rb +15 -0
  255. data/lib/alchemy/configuration/class_set_option.rb +46 -0
  256. data/lib/alchemy/configuration/integer_list_option.rb +13 -0
  257. data/lib/alchemy/configuration/integer_option.rb +12 -0
  258. data/lib/alchemy/configuration/list_option.rb +22 -0
  259. data/lib/alchemy/configuration/regexp_option.rb +11 -0
  260. data/lib/alchemy/configuration/string_list_option.rb +13 -0
  261. data/lib/alchemy/configuration/string_option.rb +11 -0
  262. data/lib/alchemy/configuration.rb +115 -0
  263. data/lib/alchemy/configuration_methods.rb +3 -1
  264. data/lib/alchemy/configurations/default_language.rb +12 -0
  265. data/lib/alchemy/configurations/default_site.rb +10 -0
  266. data/lib/alchemy/configurations/format_matchers.rb +11 -0
  267. data/lib/alchemy/configurations/mailer.rb +16 -0
  268. data/lib/alchemy/configurations/main.rb +223 -0
  269. data/lib/alchemy/configurations/page_cache.rb +19 -0
  270. data/lib/alchemy/configurations/preview.rb +32 -0
  271. data/lib/alchemy/configurations/sitemap.rb +10 -0
  272. data/lib/alchemy/configurations/uploader.rb +34 -0
  273. data/lib/alchemy/engine.rb +81 -24
  274. data/lib/alchemy/hints.rb +3 -7
  275. data/lib/alchemy/install/tasks.rb +0 -12
  276. data/lib/alchemy/on_page_layout.rb +2 -2
  277. data/lib/alchemy/propshaft/tinymce_asset.rb +15 -0
  278. data/lib/alchemy/seeder.rb +2 -2
  279. data/lib/alchemy/tasks/tidy.rb +18 -0
  280. data/lib/alchemy/tasks/usage.rb +4 -4
  281. data/lib/alchemy/test_support/config_stubbing.rb +1 -7
  282. data/lib/alchemy/test_support/factories/attachment_factory.rb +13 -2
  283. data/lib/alchemy/test_support/factories/language_factory.rb +1 -1
  284. data/lib/alchemy/test_support/factories/page_factory.rb +2 -3
  285. data/lib/alchemy/test_support/factories/picture_factory.rb +31 -2
  286. data/lib/alchemy/test_support/factories/site_factory.rb +2 -2
  287. data/lib/alchemy/test_support/having_crop_action_examples.rb +2 -2
  288. data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +80 -26
  289. data/lib/alchemy/test_support/relatable_resource_examples.rb +58 -0
  290. data/lib/alchemy/test_support/shared_ingredient_examples.rb +5 -5
  291. data/lib/alchemy/tinymce.rb +0 -1
  292. data/lib/alchemy/upgrader/eight_zero.rb +14 -0
  293. data/lib/alchemy/upgrader.rb +33 -20
  294. data/lib/alchemy/version.rb +1 -1
  295. data/lib/alchemy.rb +185 -172
  296. data/lib/alchemy_cms.rb +1 -7
  297. data/lib/generators/alchemy/ingredient/ingredient_generator.rb +0 -3
  298. data/lib/generators/alchemy/install/files/_article.html.erb +6 -4
  299. data/lib/generators/alchemy/install/files/alchemy.en.yml +22 -3
  300. data/lib/generators/alchemy/install/files/application.html.erb +5 -0
  301. data/lib/generators/alchemy/install/install_generator.rb +25 -23
  302. data/lib/generators/alchemy/install/templates/alchemy.rb.tt +200 -0
  303. data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +0 -1
  304. data/lib/generators/alchemy/install/templates/elements.yml.tt +3 -1
  305. data/lib/generators/alchemy/install/templates/menus.yml.tt +1 -1
  306. data/lib/generators/alchemy/install/templates/page_layouts.yml.tt +2 -2
  307. data/lib/generators/alchemy/page_layouts/page_layouts_generator.rb +2 -2
  308. data/lib/tasks/alchemy/assets.rake +14 -0
  309. data/lib/tasks/alchemy/tidy.rake +6 -0
  310. data/lib/tasks/alchemy/upgrade.rake +12 -47
  311. data/vendor/assets/stylesheets/tinymce/skins/content/dark/content.min.css +1 -0
  312. data/vendor/assets/stylesheets/tinymce/skins/content/default/content.min.css +1 -0
  313. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide/skin.min.css +1 -0
  314. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide-dark/content.min.css +1 -0
  315. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide-dark/skin.min.css +1 -0
  316. data/vendor/javascript/clipboard.min.js +1 -1
  317. data/vendor/javascript/cropperjs.min.js +1 -1
  318. data/vendor/javascript/handlebars.min.js +3 -3
  319. data/vendor/javascript/jquery.min.js +1 -1
  320. data/vendor/javascript/select2.min.js +3 -3
  321. data/vendor/javascript/shoelace.min.js +92 -76
  322. data/vendor/javascript/sortable.min.js +2 -2
  323. data/vendor/javascript/tinymce.min.js +1 -1
  324. data/vendor/javascript/ungap-custom-elements.min.js +2 -2
  325. metadata +223 -208
  326. data/CHANGELOG.md +0 -2041
  327. data/CODE_OF_CONDUCT.md +0 -13
  328. data/CONTRIBUTING.md +0 -73
  329. data/Gemfile +0 -71
  330. data/Rakefile +0 -102
  331. data/SECURITY.md +0 -13
  332. data/alchemy_cms.gemspec +0 -88
  333. data/app/assets/builds/alchemy/admin/page-select.css.map +0 -1
  334. data/app/assets/builds/alchemy/admin/print.css.map +0 -1
  335. data/app/assets/builds/alchemy/admin.css.map +0 -1
  336. data/app/assets/builds/alchemy/custom-properties.css +0 -1
  337. data/app/assets/builds/alchemy/custom-properties.css.map +0 -1
  338. data/app/assets/builds/alchemy/welcome.css.map +0 -1
  339. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css.map +0 -1
  340. data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css.map +0 -1
  341. data/app/assets/javascripts/alchemy/admin.js +0 -10
  342. data/app/assets/stylesheets/alchemy/_defaults.scss +0 -3
  343. data/app/assets/stylesheets/alchemy/_deprecated_variables.scss +0 -45
  344. data/app/assets/stylesheets/alchemy/_deprecation.scss +0 -17
  345. data/app/assets/stylesheets/alchemy/_extends.scss +0 -62
  346. data/app/assets/stylesheets/alchemy/_variables.scss +0 -201
  347. data/app/assets/stylesheets/alchemy/admin/attachments.scss +0 -40
  348. data/app/assets/stylesheets/alchemy/admin/buttons.scss +0 -123
  349. data/app/assets/stylesheets/alchemy/admin/hints.scss +0 -5
  350. data/app/assets/stylesheets/alchemy/admin/labels.scss +0 -3
  351. data/app/assets/stylesheets/alchemy/admin/pagination.scss +0 -92
  352. data/app/assets/stylesheets/alchemy/admin/preview_window.scss +0 -33
  353. data/app/assets/stylesheets/alchemy/admin/resource_info.scss +0 -42
  354. data/app/assets/stylesheets/alchemy/admin/tags.scss +0 -158
  355. data/app/assets/stylesheets/alchemy/admin.scss +0 -42
  356. data/app/assets/stylesheets/alchemy/custom-properties.css +0 -98
  357. data/app/assets/stylesheets/alchemy/welcome.scss +0 -57
  358. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.css +0 -711
  359. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.css +0 -705
  360. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.min.css +0 -7
  361. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.min.css +0 -7
  362. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.css +0 -29
  363. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.min.css +0 -7
  364. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.css +0 -677
  365. data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.min.css +0 -7
  366. data/app/controllers/alchemy/elements_controller.rb +0 -32
  367. data/app/helpers/alchemy/admin/elements_helper.rb +0 -25
  368. data/app/models/alchemy/element/dom_id.rb +0 -31
  369. data/app/models/alchemy/picture/calculations.rb +0 -49
  370. data/app/models/alchemy/picture/transformations.rb +0 -115
  371. data/app/models/alchemy/picture/url.rb +0 -54
  372. data/app/views/alchemy/admin/attachments/destroy.js.erb +0 -1
  373. data/app/views/alchemy/admin/clipboard/clear.js.erb +0 -3
  374. data/app/views/alchemy/admin/clipboard/insert.js.erb +0 -29
  375. data/app/views/alchemy/admin/clipboard/remove.js.erb +0 -10
  376. data/app/views/alchemy/admin/resources/_filter.html.erb +0 -12
  377. data/app/views/alchemy/admin/resources/_resource.html.erb +0 -34
  378. data/app/views/alchemy/admin/resources/_table.html.erb +0 -29
  379. data/app/views/alchemy/elements/show.html.erb +0 -1
  380. data/app/views/alchemy/elements/show.js.erb +0 -1
  381. data/app/views/alchemy/ingredients/_audio_view.html.erb +0 -1
  382. data/app/views/alchemy/ingredients/_boolean_view.html.erb +0 -1
  383. data/app/views/alchemy/ingredients/_datetime_view.html.erb +0 -3
  384. data/app/views/alchemy/ingredients/_file_view.html.erb +0 -4
  385. data/app/views/alchemy/ingredients/_headline_view.html.erb +0 -4
  386. data/app/views/alchemy/ingredients/_html_view.html.erb +0 -1
  387. data/app/views/alchemy/ingredients/_link_view.html.erb +0 -4
  388. data/app/views/alchemy/ingredients/_node_view.html.erb +0 -1
  389. data/app/views/alchemy/ingredients/_page_view.html.erb +0 -1
  390. data/app/views/alchemy/ingredients/_picture_view.html.erb +0 -4
  391. data/app/views/alchemy/ingredients/_richtext_view.html.erb +0 -3
  392. data/app/views/alchemy/ingredients/_select_view.html.erb +0 -1
  393. data/app/views/alchemy/ingredients/_text_view.html.erb +0 -4
  394. data/app/views/alchemy/ingredients/_video_view.html.erb +0 -3
  395. data/babel.config.js +0 -12
  396. data/bin/importmap +0 -4
  397. data/bin/rails +0 -9
  398. data/bin/rspec +0 -3
  399. data/bin/setup +0 -30
  400. data/bin/start +0 -17
  401. data/bun.lockb +0 -0
  402. data/bundles/shoelace.js +0 -12
  403. data/bundles/tinymce.js +0 -22
  404. data/config/initializers/assets.rb +0 -4
  405. data/eslint.config.js +0 -17
  406. data/lib/alchemy/config.rb +0 -114
  407. data/lib/alchemy/element_definition.rb +0 -73
  408. data/lib/alchemy/page_layout.rb +0 -73
  409. data/lib/alchemy/resource_filter.rb +0 -40
  410. data/lib/alchemy/upgrader/seven_point_four.rb +0 -26
  411. data/lib/alchemy/upgrader/seven_point_three.rb +0 -52
  412. data/lib/alchemy/upgrader/tasks/.keep +0 -0
  413. data/lib/generators/alchemy/ingredient/templates/view.html.erb +0 -1
  414. data/lib/generators/alchemy/install/files/alchemy_admin.js +0 -1
  415. data/lib/generators/alchemy/install/files/all.js +0 -11
  416. data/lib/generators/alchemy/install/files/article.css +0 -25
  417. data/rollup.config.mjs +0 -108
  418. data/vendor/assets/images/remixicon.symbol.svg +0 -11
  419. /data/app/{assets/stylesheets → stylesheets}/alchemy/_fonts.scss +0 -0
  420. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachment-select.scss +0 -0
  421. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flash.scss +0 -0
  422. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/list_filter.scss +0 -0
  423. /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/spinner.scss +0 -0
  424. /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/skintool.json +0 -0
  425. /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/fonts/tinymce-mobile.woff +0 -0
@@ -1,21 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "csv"
4
- require "alchemy/resource"
5
- require "alchemy/resources_helper"
6
- require "alchemy/resource_filter"
7
4
 
8
5
  module Alchemy
9
6
  module Admin
10
7
  class ResourcesController < Alchemy::Admin::BaseController
11
- COMMON_SEARCH_FILTER_EXCLUDES = %i[id utf8 _method _ format].freeze
12
-
13
8
  include Alchemy::ResourcesHelper
9
+ extend Alchemy::Admin::ResourceName
10
+ include Alchemy::Admin::ResourceFilter
14
11
 
15
12
  helper Alchemy::ResourcesHelper, TagsHelper
16
- helper_method :resource_handler, :search_filter_params,
17
- :items_per_page, :items_per_page_options, :resource_has_filters,
18
- :resource_filters_for_select
13
+ helper_method :resource_handler, :items_per_page, :items_per_page_options
19
14
 
20
15
  before_action :load_resource,
21
16
  only: %i[show edit update destroy]
@@ -29,21 +24,20 @@ module Alchemy
29
24
 
30
25
  items = items.includes(*resource_relations_names) if contains_relations?
31
26
  items = items.tagged_with(search_filter_params[:tagged_with]) if search_filter_params[:tagged_with].present?
32
- items = apply_filters(items) if search_filter_params[:filter].present?
33
27
 
34
28
  respond_to do |format|
35
29
  format.html do
36
30
  items = items.page(params[:page] || 1).per(items_per_page)
37
- instance_variable_set(:"@#{resource_handler.resources_name}", items)
31
+ instance_variable_set(:"@#{resources_name}", items)
38
32
  end
39
33
  format.csv do
40
- instance_variable_set(:"@#{resource_handler.resources_name}", items)
34
+ instance_variable_set(:"@#{resources_name}", items)
41
35
  end
42
36
  end
43
37
  end
44
38
 
45
39
  def new
46
- instance_variable_set(:"@#{resource_handler.resource_name}", resource_handler.model.new)
40
+ instance_variable_set(:"@#{resource_name}", resource_handler.model.new)
47
41
  end
48
42
 
49
43
  def show
@@ -54,7 +48,7 @@ module Alchemy
54
48
  end
55
49
 
56
50
  def create
57
- instance_variable_set(:"@#{resource_handler.resource_name}", resource_handler.model.new(resource_params))
51
+ instance_variable_set(:"@#{resource_name}", resource_handler.model.new(resource_params))
58
52
  resource_instance_variable.save
59
53
  render_errors_or_redirect(
60
54
  resource_instance_variable,
@@ -85,62 +79,8 @@ module Alchemy
85
79
  @_resource_handler ||= Alchemy::Resource.new(controller_path, alchemy_module)
86
80
  end
87
81
 
88
- def resource_has_filters
89
- resource_model.respond_to?(:alchemy_resource_filters)
90
- end
91
-
92
- def resource_has_deprecated_filters
93
- resource_model.alchemy_resource_filters.any? { |f| !f.is_a?(Hash) }
94
- end
95
-
96
- def resource_filters
97
- return unless resource_has_filters
98
-
99
- @_resource_filters ||= resource_model.alchemy_resource_filters
100
- end
101
-
102
- def resource_filters_for_select
103
- resource_filters.map do |filter|
104
- ResourceFilter.new(filter, resource_handler.resource_name)
105
- end
106
- end
107
-
108
82
  protected
109
83
 
110
- def apply_filters(items)
111
- sanitize_filter_params!
112
-
113
- search_filter_params[:filter].each do |filter|
114
- if argument_scope_filter?(filter)
115
- items = items.public_send(filter[0], filter[1])
116
- elsif simple_scope_filter?(filter)
117
- items = items.public_send(filter[1])
118
- else
119
- raise "Can't apply filter #{filter[0]}. Either the name or the values must be defined as class methods / scopes on the model."
120
- end
121
- end
122
-
123
- items
124
- end
125
-
126
- def simple_scope_filter?(filter)
127
- resource_model.respond_to?(filter[1])
128
- end
129
-
130
- def argument_scope_filter?(filter)
131
- resource_model.respond_to?(filter[0])
132
- end
133
-
134
- def sanitize_filter_params!
135
- search_filter_params[:filter].reject! do |_, v|
136
- eligible_resource_filter_values.exclude?(v)
137
- end
138
- end
139
-
140
- def eligible_resource_filter_values
141
- resource_filters.map(&:values).flatten!.map!(&:to_s)
142
- end
143
-
144
84
  # Returns a translated +flash[:notice]+ for current controller action.
145
85
  def flash_notice_for_resource_action(action = action_name)
146
86
  return if resource_instance_variable.errors.any?
@@ -174,61 +114,31 @@ module Alchemy
174
114
  end
175
115
 
176
116
  def load_resource
177
- instance_variable_set(:"@#{resource_handler.resource_name}", resource_handler.model.find(params[:id]))
117
+ instance_variable_set(:"@#{resource_name}", resource_handler.model.find(params[:id]))
178
118
  end
179
119
 
180
120
  def authorize_resource
181
121
  authorize!(action_name.to_sym, resource_instance_variable || resource_handler.model)
182
122
  end
183
123
 
184
- # Permits all parameters as default!
185
- #
186
- # THIS IS INSECURE! Although only signed in admin users can send requests anyway, but we should change this.
124
+ # Permits all editable resource attributes as default.
187
125
  #
188
- # Please define this method in your inheriting controller and set the parameters you want to permit.
189
- #
190
- # TODO: Hook this into authorization provider.
126
+ # Define this method in your inheriting controller if you want to permit additional attributes.
191
127
  #
128
+ # @see Alchemy::Resource#editable_attributes
192
129
  def resource_params
193
- params.require(resource_handler.namespaced_resource_name).permit!
194
- end
195
-
196
- def search_filter_params
197
- @_search_filter_params ||= params.except(*COMMON_SEARCH_FILTER_EXCLUDES).permit(*common_search_filter_includes).to_h
198
- end
199
-
200
- def common_search_filter_includes
201
- search_filters = [
202
- {
203
- q: [:s] + permitted_ransack_search_fields
204
- },
205
- :tagged_with,
206
- :page,
207
- :per_page
208
- ]
209
-
210
- if resource_has_filters
211
- search_filters << {
212
- filter: resource_filters.map { |f| f[:name] }
213
- }
214
- end
215
-
216
- search_filters
217
- end
218
-
219
- def permitted_ransack_search_fields
220
- [
221
- resource_handler.search_field_name
222
- ]
130
+ params.require(resource_handler.namespaced_resource_name).permit(
131
+ resource_handler.editable_attributes.map { _1[:name] }
132
+ )
223
133
  end
224
134
 
225
135
  def items_per_page
226
136
  cookies[:alchemy_items_per_page] =
227
- params[:per_page] || cookies[:alchemy_items_per_page] || Alchemy::Config.get(:items_per_page)
137
+ params[:per_page] || cookies[:alchemy_items_per_page] || Alchemy.config.items_per_page
228
138
  end
229
139
 
230
140
  def items_per_page_options
231
- per_page = Alchemy::Config.get(:items_per_page)
141
+ per_page = Alchemy.config.items_per_page
232
142
  [per_page, per_page * 2, per_page * 4]
233
143
  end
234
144
 
@@ -2,37 +2,66 @@
2
2
 
3
3
  module Alchemy
4
4
  class AttachmentsController < BaseController
5
+ if Alchemy.storage_adapter.active_storage?
6
+ include ActiveStorage::Streaming
7
+ end
8
+
5
9
  before_action :load_attachment
6
10
  authorize_resource class: Alchemy::Attachment
7
11
 
12
+ self.etag_with_template_digest = false
13
+
8
14
  # sends file inline. i.e. for viewing pdfs/movies in browser
9
15
  def show
10
- response.headers["Content-Length"] = @attachment.file.size.to_s
11
- send_file(
12
- @attachment.file.path,
13
- {
14
- filename: @attachment.file_name,
15
- type: @attachment.file_mime_type,
16
- disposition: "inline"
17
- }
18
- )
16
+ if Alchemy.storage_adapter.dragonfly?
17
+ send_attachment_file(disposition: :inline)
18
+ else
19
+ send_attachment_blob(disposition: :inline)
20
+ end
19
21
  end
20
22
 
21
23
  # sends file as attachment. aka download
22
24
  def download
25
+ if Alchemy.storage_adapter.dragonfly?
26
+ send_attachment_file(disposition: :attachment)
27
+ else
28
+ send_attachment_blob(disposition: :attachment)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def load_attachment
35
+ @attachment = Attachment.find(params[:id])
36
+ end
37
+
38
+ def send_attachment_file(disposition: :inline)
23
39
  response.headers["Content-Length"] = @attachment.file.size.to_s
40
+
24
41
  send_file(
25
- @attachment.file.path, {
42
+ @attachment.file.path,
43
+ {
26
44
  filename: @attachment.file_name,
27
- type: @attachment.file_mime_type
45
+ type: @attachment.file_mime_type,
46
+ disposition: disposition
28
47
  }
29
48
  )
30
49
  end
31
50
 
32
- private
51
+ def send_attachment_blob(disposition: :inline)
52
+ @blob = @attachment.file.blob
33
53
 
34
- def load_attachment
35
- @attachment = Attachment.find(params[:id])
54
+ if request.headers["Range"].present?
55
+ send_blob_byte_range_data @blob, request.headers["Range"], disposition: disposition
56
+ else
57
+ http_cache_forever public: true do
58
+ response.headers["Accept-Ranges"] = "bytes"
59
+ send_blob_stream @blob, disposition: disposition
60
+ # Rails ActionController::Live removes the Content-Length header,
61
+ # but browsers need that to be able to show a progress bar during download.
62
+ response.headers["Content-Length"] = @blob.byte_size.to_s
63
+ end
64
+ end
36
65
  end
37
66
  end
38
67
  end
@@ -74,7 +74,7 @@ module Alchemy
74
74
  private
75
75
 
76
76
  def mailer_config
77
- Alchemy::Config.get(:mailer)
77
+ Alchemy.config.mailer
78
78
  end
79
79
 
80
80
  def mail_to
@@ -106,7 +106,12 @@ module Alchemy
106
106
  def load_index_page
107
107
  @page ||= Language.current_root_page
108
108
  Current.page = @page
109
- render template: "alchemy/welcome", layout: false if signup_required?
109
+
110
+ if signup_required? && @page.nil?
111
+ render template: "alchemy/welcome", layout: false
112
+ elsif !@page&.public?
113
+ render template: "alchemy/no_index", layout: false
114
+ end
110
115
  end
111
116
 
112
117
  # == Loads page by urlname
@@ -186,13 +191,25 @@ module Alchemy
186
191
  if must_not_cache?
187
192
  expires_now
188
193
  else
189
- expires_in @page.expiration_time, public: !@page.restricted, must_revalidate: true
194
+ expires_in @page.expiration_time, {public: !@page.restricted}.merge(caching_options)
195
+ end
196
+ end
197
+
198
+ def caching_options
199
+ if Alchemy.config.page_cache.stale_while_revalidate
200
+ {
201
+ stale_while_revalidate: Alchemy.config.page_cache.stale_while_revalidate
202
+ }
203
+ else
204
+ {
205
+ must_revalidate: true
206
+ }
190
207
  end
191
208
  end
192
209
 
193
210
  def signup_required?
194
211
  if Alchemy.user_class.respond_to?(:admins)
195
- Alchemy.user_class.admins.empty? && @page.nil?
212
+ Alchemy.user_class.admins.empty?
196
213
  end
197
214
  end
198
215
 
@@ -224,7 +241,12 @@ module Alchemy
224
241
 
225
242
  # don't cache pages if we have flash message to display or the page has caching disabled
226
243
  def must_not_cache?
227
- flash.present? || !@page.cache_page?
244
+ !caching_enabled? || !@page.cache_page? || flash.present?
245
+ end
246
+
247
+ def caching_enabled?
248
+ Rails.application.config.action_controller.perform_caching &&
249
+ Alchemy.config.cache_pages
228
250
  end
229
251
 
230
252
  def page_not_found!
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ module Admin
5
+ module ResourceFilter
6
+ extend ActiveSupport::Concern
7
+
8
+ COMMON_SEARCH_FILTER_EXCLUDES = %i[id utf8 _method _ format].freeze
9
+
10
+ included do
11
+ prepend_before_action :initialize_alchemy_filters
12
+ helper_method :alchemy_filters, :search_form_name, :applied_filters, :search_filter_params, :resource_has_filters
13
+ end
14
+
15
+ class_methods do
16
+ # Adds a filter to the resource.
17
+ # @param name [String, Symbol] The name of the filter.
18
+ # @param type [Symbol] The type of the filter. Can currently be `:select` or `:checkbox`.
19
+ # @param args [Hash] Additional arguments for the filter.
20
+ # @example
21
+ # add_alchemy_filter :by_location, type: :select, options: ->(query) { Location.pluck(:name, :id) }
22
+ # add_alchemy_filter :future, type: :checkbox
23
+ # add_alchemy_filter :by_timeframe, type: :select, options: ["today", "tomorrow"]
24
+ def add_alchemy_filter(name, type:, **args)
25
+ alchemy_filters << "Alchemy::Admin::Filters::#{type.to_s.camelize}".constantize.new(
26
+ name:,
27
+ resource_name:,
28
+ **args
29
+ )
30
+ end
31
+
32
+ def alchemy_filters
33
+ @alchemy_filters ||= []
34
+ end
35
+ end
36
+ delegate :alchemy_filters, to: :class
37
+
38
+ private
39
+
40
+ def initialize_alchemy_filters
41
+ return if alchemy_filters.any?
42
+ return unless resource_model.respond_to?(:alchemy_resource_filters)
43
+ Alchemy::Deprecation.warn(
44
+ "The `alchemy_resource_filters` method is deprecated and will be removed in Alchemy 8.1. " \
45
+ "Please use `add_alchemy_filter` in your controller instead. Make sure to safelist the scopes " \
46
+ "you want to use in the `ransackable_scopes` method of your model."
47
+ )
48
+ resource_model.alchemy_resource_filters.each do |filter_config|
49
+ if resource_model.respond_to?(filter_config[:name])
50
+ self.class.add_alchemy_filter filter_config[:name], type: :select, options: filter_config[:values]
51
+ else
52
+ filter_config[:values].each do |scope|
53
+ self.class.add_alchemy_filter scope, type: :checkbox
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def search_filter_params
60
+ @_search_filter_params ||= params.except(*COMMON_SEARCH_FILTER_EXCLUDES).permit(*common_search_filter_includes).to_h
61
+ end
62
+
63
+ def common_search_filter_includes
64
+ [
65
+ {
66
+ q: [:s] + permitted_ransack_search_fields
67
+ },
68
+ :tagged_with,
69
+ :page,
70
+ :per_page
71
+ ]
72
+ end
73
+
74
+ def permitted_ransack_search_fields
75
+ [
76
+ resource_handler.search_field_name
77
+ ] + alchemy_filters.map(&:name)
78
+ end
79
+
80
+ def resource_has_filters
81
+ alchemy_filters.any?
82
+ end
83
+
84
+ def applied_filters
85
+ return [] unless search_filter_params[:q]
86
+
87
+ alchemy_filters.select do |alchemy_filter|
88
+ search_filter_params[:q][alchemy_filter.name].present?
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -14,7 +14,7 @@ module Alchemy
14
14
  #
15
15
  # @return Array<Alchemy::IngredientEditor>
16
16
  def ingredients
17
- element.definition.fetch(:ingredients, []).map do |ingredient|
17
+ ingredient_definitions.map do |ingredient|
18
18
  Alchemy::IngredientEditor.new(find_or_create_ingredient(ingredient))
19
19
  end
20
20
  end
@@ -22,7 +22,7 @@ module Alchemy
22
22
  # Are any ingredients defined?
23
23
  # @return [Boolean]
24
24
  def has_ingredients_defined?
25
- element.definition.fetch(:ingredients, []).any?
25
+ ingredient_definitions.any?
26
26
  end
27
27
 
28
28
  # Returns the translated ingredient group for displaying in admin editor group headings
@@ -77,56 +77,13 @@ module Alchemy
77
77
  super
78
78
  end
79
79
 
80
- # Returns a deprecation notice for elements marked deprecated
81
- #
82
- # You can either use localizations or pass a String as notice
83
- # in the element definition.
84
- #
85
- # == Custom deprecation notices
86
- #
87
- # Use general element deprecation notice
88
- #
89
- # - name: old_element
90
- # deprecated: true
91
- #
92
- # Add a translation to your locale file for a per element notice.
93
- #
94
- # en:
95
- # alchemy:
96
- # element_deprecation_notices:
97
- # old_element: Foo baz widget is deprecated
98
- #
99
- # or use the global translation that apply to all deprecated elements.
100
- #
101
- # en:
102
- # alchemy:
103
- # element_deprecation_notice: Foo baz widget is deprecated
104
- #
105
- # or pass string as deprecation notice.
106
- #
107
- # - name: old_element
108
- # deprecated: This element will be removed soon.
109
- #
110
- def deprecation_notice
111
- case definition["deprecated"]
112
- when String
113
- definition["deprecated"]
114
- when TrueClass
115
- Alchemy.t(
116
- name,
117
- scope: :element_deprecation_notices,
118
- default: Alchemy.t(:element_deprecated)
119
- )
120
- end
121
- end
122
-
123
80
  private
124
81
 
125
82
  def find_or_create_ingredient(definition)
126
- element.ingredients.detect { |i| i.role == definition[:role] } ||
83
+ element.ingredients.detect { _1.role == definition.role } ||
127
84
  element.ingredients.create!(
128
- role: definition[:role],
129
- type: Alchemy::Ingredient.normalize_type(definition[:type])
85
+ role: definition.role,
86
+ type: Alchemy::Ingredient.normalize_type(definition.type)
130
87
  )
131
88
  end
132
89
  end
@@ -99,62 +99,12 @@ module Alchemy
99
99
  Logger.warn("ingredient #{role} is missing its definition", caller(1..1))
100
100
  Alchemy.t(:ingredient_definition_missing)
101
101
  else
102
- deprecation_notice
103
- end
104
- end
105
-
106
- # Returns a deprecation notice for ingredients marked deprecated
107
- #
108
- # You can either use localizations or pass a String as notice
109
- # in the ingredient definition.
110
- #
111
- # == Custom deprecation notices
112
- #
113
- # Use general ingredient deprecation notice
114
- #
115
- # - name: element_name
116
- # ingredients:
117
- # - role: old_ingredient
118
- # type: Text
119
- # deprecated: true
120
- #
121
- # Add a translation to your locale file for a per ingredient notice.
122
- #
123
- # en:
124
- # alchemy:
125
- # ingredient_deprecation_notices:
126
- # element_name:
127
- # old_ingredient: Foo baz widget is deprecated
128
- #
129
- # or use the global translation that apply to all deprecated ingredients.
130
- #
131
- # en:
132
- # alchemy:
133
- # ingredient_deprecation_notice: Foo baz widget is deprecated
134
- #
135
- # or pass string as deprecation notice.
136
- #
137
- # - name: element_name
138
- # ingredients:
139
- # - role: old_ingredient
140
- # type: Text
141
- # deprecated: This ingredient will be removed soon.
142
- #
143
- def deprecation_notice
144
- case definition[:deprecated]
145
- when String
146
- definition[:deprecated]
147
- when TrueClass
148
- Alchemy.t(
149
- role,
150
- scope: [:ingredient_deprecation_notices, element.name],
151
- default: Alchemy.t(:ingredient_deprecated)
152
- )
102
+ definition.deprecation_notice(element_name: element&.name)
153
103
  end
154
104
  end
155
105
 
156
106
  def validations
157
- definition.fetch(:validate, [])
107
+ definition.validate
158
108
  end
159
109
 
160
110
  def format_validation
@@ -173,7 +123,7 @@ module Alchemy
173
123
  private
174
124
 
175
125
  def form_field_counter
176
- element.definition.fetch(:ingredients, []).index { |i| i[:role] == role }
126
+ element.ingredient_definitions.index { _1.role == role }
177
127
  end
178
128
  end
179
129
  end
@@ -12,12 +12,12 @@ module Alchemy
12
12
 
13
13
  def attachment_preview_size(attachment)
14
14
  case attachment.file_mime_type
15
- when *IMAGE_FILE_TYPES then "600x475"
16
- when *AUDIO_FILE_TYPES then "600x190"
17
- when *VIDEO_FILE_TYPES then "600x485"
18
- when "application/pdf" then "600x600"
15
+ when "application/pdf" then "850x600"
16
+ when *IMAGE_FILE_TYPES then "850x280"
17
+ when *AUDIO_FILE_TYPES then "850x190"
18
+ when *VIDEO_FILE_TYPES then "900x280"
19
19
  else
20
- "600x145"
20
+ "500x165"
21
21
  end
22
22
  end
23
23
  end