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
@@ -4,13 +4,13 @@ module Alchemy
4
4
  class Page < BaseRecord
5
5
  # Module concerning page layouts
6
6
  #
7
- module PageLayouts
7
+ module Definitions
8
8
  extend ActiveSupport::Concern
9
9
 
10
10
  module ClassMethods
11
11
  # Register a custom page layouts repository
12
12
  #
13
- # The default repository is Alchemy::PageLayout
13
+ # The default repository is Alchemy::PageDefinition
14
14
  #
15
15
  def layouts_repository=(klass)
16
16
  @_layouts_repository = klass
@@ -19,8 +19,10 @@ module Alchemy
19
19
  # Returns page layouts ready for Rails' select form helper.
20
20
  #
21
21
  def layouts_for_select(language_id, layoutpages: false)
22
- @map_array = []
23
- mapped_layouts_for_select(selectable_layouts(language_id, layoutpages: layoutpages))
22
+ layouts = selectable_layouts(language_id, layoutpages: layoutpages)
23
+ layouts.map do |layout|
24
+ [layout.human_name, layout.name]
25
+ end
24
26
  end
25
27
 
26
28
  # Returns all layouts that can be used for creating a new page.
@@ -36,9 +38,9 @@ module Alchemy
36
38
  @language_id = language_id
37
39
  layouts_repository.all.select do |layout|
38
40
  if layoutpages
39
- layout["layoutpage"] && layout_available?(layout)
41
+ layout.layoutpage && layout_available?(layout)
40
42
  else
41
- !layout["layoutpage"] && layout_available?(layout)
43
+ !layout.layoutpage && layout_available?(layout)
42
44
  end
43
45
  end
44
46
  end
@@ -62,28 +64,19 @@ module Alchemy
62
64
  private
63
65
 
64
66
  def layouts_repository
65
- @_layouts_repository ||= PageLayout
66
- end
67
-
68
- # Maps given layouts for Rails select form helper.
69
- #
70
- def mapped_layouts_for_select(layouts)
71
- layouts.each do |layout|
72
- @map_array << [human_layout_name(layout["name"]), layout["name"]]
73
- end
74
- @map_array
67
+ @_layouts_repository ||= PageDefinition
75
68
  end
76
69
 
77
70
  # Returns true if the given layout is unique and not already taken or it should be hidden.
78
71
  #
79
72
  def layout_available?(layout)
80
- !layout["hide"] && !already_taken?(layout) && available_on_site?(layout)
73
+ !layout.hide && !already_taken?(layout) && available_on_site?(layout)
81
74
  end
82
75
 
83
76
  # Returns true if this layout is unique and already taken by another page.
84
77
  #
85
78
  def already_taken?(layout)
86
- layout["unique"] && page_with_layout_existing?(layout["name"])
79
+ layout.unique && page_with_layout_existing?(layout.name)
87
80
  end
88
81
 
89
82
  # Returns true if one page already has the given layout
@@ -106,7 +99,7 @@ module Alchemy
106
99
  return false unless Alchemy::Current.site
107
100
 
108
101
  Alchemy::Current.site.definition.blank? ||
109
- Alchemy::Current.site.definition.fetch("page_layouts", []).include?(layout["name"])
102
+ Alchemy::Current.site.definition.fetch("page_layouts", []).include?(layout.name)
110
103
  end
111
104
  end
112
105
  end
@@ -31,7 +31,7 @@ module Alchemy
31
31
  # @return Hash
32
32
  #
33
33
  def attributes
34
- @_attributes ||= page.definition.fetch("fixed_attributes", {}).symbolize_keys
34
+ @_attributes ||= page.definition.fixed_attributes.symbolize_keys
35
35
  end
36
36
  alias_method :all, :attributes
37
37
 
@@ -81,8 +81,8 @@ module Alchemy
81
81
  #
82
82
  def available_element_definitions(only_element_named = nil)
83
83
  @_available_element_definitions ||= if only_element_named
84
- definition = Element.definition_by_name(only_element_named)
85
- element_definitions_by_name(definition["nestable_elements"])
84
+ element_definition = Element.definition_by_name(only_element_named)
85
+ element_definitions_by_name(element_definition.nestable_elements)
86
86
  else
87
87
  element_definitions.dup
88
88
  end
@@ -100,7 +100,7 @@ module Alchemy
100
100
  # All names of elements that can actually be placed on current page.
101
101
  #
102
102
  def available_element_names
103
- @_available_element_names ||= available_element_definitions.map { |e| e["name"] }
103
+ @_available_element_names ||= available_element_definitions.map(&:name)
104
104
  end
105
105
 
106
106
  # Available element definitions excluding nested unique elements.
@@ -109,7 +109,7 @@ module Alchemy
109
109
  @_available_elements = if parent
110
110
  parents_unique_nested_elements = parent.nested_elements.where(unique: true).pluck(:name)
111
111
  available_element_definitions(parent.name).reject do |e|
112
- parents_unique_nested_elements.include? e["name"]
112
+ parents_unique_nested_elements.include?(e.name)
113
113
  end
114
114
  else
115
115
  available_element_definitions
@@ -129,10 +129,9 @@ module Alchemy
129
129
  #
130
130
  def descendent_element_definitions
131
131
  definitions = element_definitions_by_name(element_definition_names)
132
- definitions.select { |d| d.key?("nestable_elements") }.each do |d|
133
- definitions += element_definitions_by_name(d["nestable_elements"])
134
- end
135
- definitions.uniq { |d| d["name"] }
132
+ definitions.select { _1.nestable_elements.any? }.flat_map do |d|
133
+ element_definitions_by_name(d.nestable_elements)
134
+ end.uniq(&:name)
136
135
  end
137
136
 
138
137
  # All names of elements that are defined in the page definition.
@@ -145,7 +144,7 @@ module Alchemy
145
144
  # elements: [headline, contactform]
146
145
  #
147
146
  def element_definition_names
148
- definition["elements"] || []
147
+ definition.elements || []
149
148
  end
150
149
 
151
150
  # Element definitions with given name(s)
@@ -161,7 +160,7 @@ module Alchemy
161
160
  if names.to_s == "all"
162
161
  Element.definitions
163
162
  else
164
- Element.definitions.select { |e| names.include? e["name"] }
163
+ Element.definitions.select { names.include?(_1.name) }
165
164
  end
166
165
  end
167
166
 
@@ -181,7 +180,7 @@ module Alchemy
181
180
  # And if so, it generates them.
182
181
  #
183
182
  def generate_elements
184
- definition.fetch("autogenerate", []).each do |element_name|
183
+ definition.autogenerate&.each do |element_name|
185
184
  Element.create(page: self, page_version: draft_version, name: element_name)
186
185
  end
187
186
  end
@@ -190,7 +189,7 @@ module Alchemy
190
189
  #
191
190
  def delete_unique_element_definitions!
192
191
  @_available_element_definitions.delete_if do |element|
193
- element["unique"] && @_existing_element_names.include?(element["name"])
192
+ element.unique && @_existing_element_names.include?(element.name)
194
193
  end
195
194
  end
196
195
 
@@ -198,8 +197,8 @@ module Alchemy
198
197
  #
199
198
  def delete_outnumbered_element_definitions!
200
199
  @_available_element_definitions.delete_if do |element|
201
- outnumbered = @_existing_element_names.select { |name| name == element["name"] }
202
- element["amount"] && outnumbered.count >= element["amount"].to_i
200
+ outnumbered = @_existing_element_names.select { |name| name == element.name }
201
+ element.amount && outnumbered.count >= element.amount
203
202
  end
204
203
  end
205
204
  end
@@ -17,8 +17,7 @@ module Alchemy
17
17
  presence: true, uniqueness: {scope: [:parent_id], case_sensitive: false, unless: -> { parent_id.nil? }}
18
18
  validates :urlname,
19
19
  uniqueness: {scope: [:language_id, :layoutpage], if: -> { urlname.present? }, case_sensitive: false},
20
- exclusion: {in: RESERVED_URLNAMES},
21
- length: {minimum: 3, if: -> { urlname.present? }}
20
+ exclusion: {in: RESERVED_URLNAMES}
22
21
 
23
22
  before_save :set_title,
24
23
  if: -> { title.blank? }
@@ -74,17 +73,10 @@ module Alchemy
74
73
  self[:title] = name
75
74
  end
76
75
 
77
- # Converts the given name into an url friendly string.
76
+ # Returns the full nested urlname.
78
77
  #
79
- # Names shorter than 3 will be filled up with dashes,
80
- # so it does not collidate with the language code.
81
- #
82
- def converted_url_name
83
- url_name = convert_to_urlname(slug.blank? ? name : slug)
84
- url_name.rjust(3, "-")
85
- end
86
-
87
78
  def nested_url_name
79
+ converted_url_name = convert_to_urlname(slug.blank? ? name : slug)
88
80
  if parent&.language_root?
89
81
  converted_url_name
90
82
  else
@@ -13,8 +13,21 @@ module Alchemy
13
13
  language.public? && !!public_version&.public?
14
14
  end
15
15
 
16
+ # Cache-Control max-age duration in seconds.
17
+ #
18
+ # You can set this via the `ALCHEMY_PAGE_CACHE_MAX_AGE` environment variable,
19
+ # in the `Alchemy.config.page_cache_max_age` configuration option,
20
+ # or in the pages definition in `config/alchemy/page_layouts.yml` file.
21
+ #
22
+ # Defaults to 600 seconds.
16
23
  def expiration_time
17
- public_until ? public_until - Time.current : nil
24
+ return 0 unless cache_page?
25
+
26
+ if definition.cache.to_s.match?(/\d+/)
27
+ definition.cache.to_i
28
+ else
29
+ Alchemy.config.page_cache.max_age
30
+ end
18
31
  end
19
32
 
20
33
  def rootpage?
@@ -38,13 +51,13 @@ module Alchemy
38
51
  # @returns Array
39
52
  #
40
53
  def has_limited_editors?
41
- definition["editable_by"].present?
54
+ definition.editable_by.present?
42
55
  end
43
56
 
44
57
  def editor_roles
45
58
  return unless has_limited_editors?
46
59
 
47
- definition["editable_by"]
60
+ definition.editable_by
48
61
  end
49
62
 
50
63
  # True if page locked_at timestamp and locked_by id are set
@@ -80,10 +93,10 @@ module Alchemy
80
93
 
81
94
  # Returns the self#page_layout definition from config/alchemy/page_layouts.yml file.
82
95
  def definition
83
- definition = PageLayout.get(page_layout)
96
+ definition = PageDefinition.get(page_layout)
84
97
  if definition.nil?
85
98
  log_warning "Page definition for `#{page_layout}` not found. Please check `page_layouts.yml` file."
86
- return {}
99
+ return PageDefinition.new
87
100
  end
88
101
  definition
89
102
  end
@@ -144,17 +157,9 @@ module Alchemy
144
157
  # @returns Boolean
145
158
  #
146
159
  def cache_page?
147
- return false unless caching_enabled?
148
-
149
- page_layout = PageLayout.get(self.page_layout)
150
- page_layout["cache"] != false && page_layout["searchresults"] != true
151
- end
152
-
153
- private
160
+ return false if !public? || restricted?
154
161
 
155
- def caching_enabled?
156
- Alchemy::Config.get(:cache_pages) &&
157
- Rails.application.config.action_controller.perform_caching
162
+ definition.cache != false && definition.searchresults != true
158
163
  end
159
164
  end
160
165
  end
@@ -124,7 +124,7 @@ module Alchemy
124
124
  end
125
125
 
126
126
  def ransackable_scopes(_auth_object)
127
- [:published, :contentpages, :from_current_site, :searchables, :layoutpages]
127
+ [:published, :not_public, :restricted, :contentpages, :from_current_site, :searchables, :layoutpages, :by_page_layout]
128
128
  end
129
129
  end
130
130
  end
@@ -36,7 +36,7 @@
36
36
  #
37
37
 
38
38
  require_dependency "alchemy/page/fixed_attributes"
39
- require_dependency "alchemy/page/page_layouts"
39
+ require_dependency "alchemy/page/definitions"
40
40
  require_dependency "alchemy/page/page_scopes"
41
41
  require_dependency "alchemy/page/page_natures"
42
42
  require_dependency "alchemy/page/page_naming"
@@ -44,7 +44,6 @@ require_dependency "alchemy/page/page_elements"
44
44
 
45
45
  module Alchemy
46
46
  class Page < BaseRecord
47
- include Alchemy::Hints
48
47
  include Alchemy::Logger
49
48
  include Alchemy::Taggable
50
49
 
@@ -90,7 +89,7 @@ module Alchemy
90
89
 
91
90
  acts_as_nested_set(dependent: :destroy, scope: [:layoutpage, :language_id])
92
91
 
93
- stampable stamper_class_name: Alchemy.user_class.name
92
+ stampable stamper_class_name: Alchemy.user_class_name
94
93
 
95
94
  belongs_to :language
96
95
 
@@ -116,7 +115,7 @@ module Alchemy
116
115
  has_many :site_languages, through: :site, source: :languages
117
116
  has_many :folded_pages, dependent: :destroy
118
117
  has_many :legacy_urls, class_name: "Alchemy::LegacyPageUrl", dependent: :destroy
119
- has_many :nodes, class_name: "Alchemy::Node", inverse_of: :page
118
+ has_many :nodes, class_name: "Alchemy::Node", inverse_of: :page, dependent: :restrict_with_error
120
119
  has_many :versions, class_name: "Alchemy::PageVersion", inverse_of: :page, dependent: :destroy
121
120
  has_one :draft_version, -> { drafts }, class_name: "Alchemy::PageVersion"
122
121
  has_one :public_version, -> { published }, class_name: "Alchemy::PageVersion", autosave: -> { persisted? }
@@ -133,11 +132,6 @@ module Alchemy
133
132
  before_create -> { versions.build },
134
133
  if: -> { versions.none? }
135
134
 
136
- before_destroy if: -> { nodes.any? } do
137
- errors.add(:nodes, :still_present)
138
- throw(:abort)
139
- end
140
-
141
135
  before_save :set_language_code,
142
136
  if: -> { language.present? }
143
137
 
@@ -156,7 +150,7 @@ module Alchemy
156
150
  after_update :touch_nodes
157
151
 
158
152
  # Concerns
159
- include PageLayouts
153
+ include Definitions
160
154
  include PageScopes
161
155
  include PageNatures
162
156
  include PageNaming
@@ -164,6 +158,7 @@ module Alchemy
164
158
 
165
159
  # site_name accessor
166
160
  delegate :name, to: :site, prefix: true, allow_nil: true
161
+ delegate :has_hint?, :hint, to: :definition
167
162
 
168
163
  # Class methods
169
164
  #
@@ -183,37 +178,10 @@ module Alchemy
183
178
  @_url_path_class = klass
184
179
  end
185
180
 
186
- def alchemy_resource_filters
187
- [
188
- {
189
- name: :by_page_layout,
190
- values: PageLayout.all.map { |p| [Alchemy.t(p["name"], scope: "page_layout_names"), p["name"]] }
191
- },
192
- {
193
- name: :status,
194
- values: %w[published not_public restricted]
195
- }
196
- ]
197
- end
198
-
199
181
  def searchable_alchemy_resource_attributes
200
182
  %w[name urlname title]
201
183
  end
202
184
 
203
- # Used to store the current page previewed in the edit page template.
204
- # @deprecated Use {Alchemy::Current#preview_page=} instead.
205
- def current_preview=(page)
206
- Current.preview_page = page
207
- end
208
- deprecate "current_preview=": :"Alchemy::Current.preview_page=", deprecator: Alchemy::Deprecation
209
-
210
- # Returns the current page previewed in the edit page template.
211
- # @deprecated Use {Alchemy::Current#preview_page} instead.
212
- def current_preview
213
- Current.preview_page
214
- end
215
- deprecate current_preview: :"Alchemy::Current.preview_page", deprecator: Alchemy::Deprecation
216
-
217
185
  # @return the language root page for given language id.
218
186
  # @param language_id [Fixnum]
219
187
  #
@@ -265,13 +233,13 @@ module Alchemy
265
233
 
266
234
  clipboard_pages = all_from_clipboard(clipboard)
267
235
  allowed_page_layouts = Alchemy::Page.selectable_layouts(language_id, layoutpages: layoutpages)
268
- allowed_page_layout_names = allowed_page_layouts.collect { |p| p["name"] }
236
+ allowed_page_layout_names = allowed_page_layouts.collect(&:name)
269
237
  clipboard_pages.select { |cp| allowed_page_layout_names.include?(cp.page_layout) }
270
238
  end
271
239
 
272
240
  def link_target_options
273
241
  options = [[Alchemy.t(:default, scope: "link_target_options"), ""]]
274
- link_target_options = Config.get(:link_target_options)
242
+ link_target_options = Alchemy.config.link_target_options
275
243
  link_target_options.each do |option|
276
244
  # add an underscore to the options to provide the default syntax
277
245
  # @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target
@@ -280,6 +248,11 @@ module Alchemy
280
248
  end
281
249
  options
282
250
  end
251
+
252
+ # Allow all string and text attributes to be searchable by Ransack.
253
+ def ransackable_attributes(_auth_object = nil)
254
+ searchable_alchemy_resource_attributes + ["updated_at"]
255
+ end
283
256
  end
284
257
 
285
258
  # Instance methods
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Alchemy
4
+ class PageDefinition
5
+ include ActiveModel::Model
6
+ include ActiveModel::Attributes
7
+ include Alchemy::Hints
8
+
9
+ extend ActiveModel::Translation
10
+
11
+ attribute :name, :string
12
+ attribute :elements, default: []
13
+ attribute :autogenerate, default: []
14
+ attribute :layoutpage, :boolean, default: false
15
+ attribute :unique, :boolean, default: false
16
+ attribute :cache, default: true
17
+ attribute :insert_elements_at, :string, default: "bottom"
18
+ attribute :fixed_attributes, default: {}
19
+ attribute :searchable, :boolean, default: true
20
+ attribute :searchresults, :boolean, default: false
21
+ attribute :hide, :boolean, default: false
22
+ attribute :editable_by
23
+ attribute :hint
24
+
25
+ validates :name,
26
+ presence: true,
27
+ format: {
28
+ with: /\A[a-z_-]+\z/
29
+ }
30
+
31
+ delegate :[], to: :attributes
32
+ delegate :blank?, to: :name
33
+
34
+ class << self
35
+ # Returns all page layouts.
36
+ #
37
+ # They are defined in +config/alchemy/page_layout.yml+ file.
38
+ #
39
+ def all
40
+ @definitions ||= read_definitions_file.map { new(**_1) }
41
+ end
42
+
43
+ def map(...)
44
+ all.map(...)
45
+ end
46
+ alias_method :collect, :map
47
+
48
+ # Add additional page definitions to collection.
49
+ #
50
+ # Useful for extending the page layouts from an Alchemy module.
51
+ #
52
+ # === Usage Example
53
+ #
54
+ # Call +Alchemy::PageDefinition.add(your_definition)+ in your engine.rb file.
55
+ #
56
+ # @param [Array || Hash]
57
+ # You can pass a single layout definition as Hash, or a collection of page layouts as Array.
58
+ #
59
+ def add(definition)
60
+ all
61
+ @definitions += Array.wrap(definition).map { new(**_1) }
62
+ end
63
+
64
+ # Returns one page definition by given name.
65
+ #
66
+ def get(name)
67
+ return new if name.blank?
68
+
69
+ all.detect { _1.name.casecmp(name).zero? }
70
+ end
71
+
72
+ def reset!
73
+ @definitions = nil
74
+ end
75
+
76
+ # The absolute +page_layouts.yml+ file path
77
+ # @return [Pathname]
78
+ def layouts_file_path
79
+ Rails.root.join("config", "alchemy", "page_layouts.yml")
80
+ end
81
+
82
+ private
83
+
84
+ # Reads the layout definitions from +config/alchemy/page_layouts.yml+.
85
+ #
86
+ def read_definitions_file
87
+ if File.exist?(layouts_file_path)
88
+ Array.wrap(
89
+ YAML.safe_load(
90
+ ERB.new(File.read(layouts_file_path)).result,
91
+ permitted_classes: YAML_PERMITTED_CLASSES,
92
+ aliases: true
93
+ ) || []
94
+ )
95
+ else
96
+ raise LoadError, "Could not find page_layouts.yml file! Please run `rails generate alchemy:install`"
97
+ end
98
+ end
99
+ end
100
+
101
+ def human_name
102
+ Alchemy::Page.human_layout_name(name)
103
+ end
104
+
105
+ def attributes
106
+ super.with_indifferent_access
107
+ end
108
+
109
+ private
110
+
111
+ def hint_translation_scope
112
+ :page_hints
113
+ end
114
+ end
115
+ end