alchemy_cms 8.0.12 → 8.1.0

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.
Files changed (286) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -10
  3. data/app/assets/builds/alchemy/admin.css +1 -1
  4. data/app/assets/builds/alchemy/dark-theme.css +1 -1
  5. data/app/assets/builds/alchemy/light-theme.css +1 -1
  6. data/app/assets/builds/alchemy/preview.min.js +1 -1
  7. data/app/assets/builds/alchemy/theme.css +1 -1
  8. data/app/{views/alchemy/admin/elements/_element.html.erb → components/alchemy/admin/element_editor.html.erb} +34 -29
  9. data/app/components/alchemy/admin/element_editor.rb +115 -0
  10. data/app/components/alchemy/admin/element_select.rb +12 -9
  11. data/app/components/alchemy/admin/ingredient_editor.rb +54 -0
  12. data/app/components/alchemy/admin/list_filter.rb +16 -5
  13. data/app/components/alchemy/admin/page_node.html.erb +214 -0
  14. data/app/components/alchemy/admin/page_node.rb +70 -0
  15. data/app/components/alchemy/admin/picture_thumbnail.rb +36 -0
  16. data/app/components/alchemy/admin/publish_page_button.html.erb +15 -0
  17. data/app/components/alchemy/admin/publish_page_button.rb +54 -0
  18. data/app/{helpers/alchemy/admin/tags_helper.rb → components/alchemy/admin/tags_list.rb} +19 -11
  19. data/app/components/alchemy/admin/toolbar_button.rb +17 -13
  20. data/app/components/alchemy/ingredients/audio_editor.rb +8 -0
  21. data/app/components/alchemy/ingredients/base_editor.rb +222 -0
  22. data/app/components/alchemy/ingredients/boolean_editor.rb +21 -0
  23. data/app/components/alchemy/ingredients/color_editor.rb +80 -0
  24. data/app/components/alchemy/ingredients/color_view.rb +13 -0
  25. data/app/components/alchemy/ingredients/datetime_editor.rb +28 -0
  26. data/app/components/alchemy/ingredients/file_editor.rb +69 -0
  27. data/app/components/alchemy/ingredients/headline_editor.rb +88 -0
  28. data/app/components/alchemy/ingredients/html_editor.rb +11 -0
  29. data/app/components/alchemy/ingredients/link_editor.rb +29 -0
  30. data/app/components/alchemy/ingredients/node_editor.rb +23 -0
  31. data/app/components/alchemy/ingredients/number_editor.rb +28 -0
  32. data/app/components/alchemy/ingredients/page_editor.rb +19 -0
  33. data/app/components/alchemy/ingredients/picture_editor.rb +81 -0
  34. data/app/components/alchemy/ingredients/richtext_editor.rb +31 -0
  35. data/app/components/alchemy/ingredients/select_editor.rb +37 -0
  36. data/app/components/alchemy/ingredients/select_view.rb +7 -0
  37. data/app/components/alchemy/ingredients/text_editor.rb +41 -0
  38. data/app/components/alchemy/ingredients/video_editor.rb +8 -0
  39. data/app/controllers/alchemy/admin/attachments_controller.rb +8 -15
  40. data/app/controllers/alchemy/admin/base_controller.rb +7 -18
  41. data/app/controllers/alchemy/admin/clipboard_controller.rb +15 -11
  42. data/app/controllers/alchemy/admin/dashboard_controller.rb +2 -2
  43. data/app/controllers/alchemy/admin/elements_controller.rb +34 -32
  44. data/app/controllers/alchemy/admin/ingredients_controller.rb +1 -0
  45. data/app/controllers/alchemy/admin/languages_controller.rb +0 -3
  46. data/app/controllers/alchemy/admin/layoutpages_controller.rb +2 -1
  47. data/app/controllers/alchemy/admin/legacy_page_urls_controller.rb +1 -1
  48. data/app/controllers/alchemy/admin/nodes_controller.rb +24 -1
  49. data/app/controllers/alchemy/admin/pages_controller.rb +36 -42
  50. data/app/controllers/alchemy/admin/pictures_controller.rb +4 -28
  51. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  52. data/app/controllers/alchemy/api/ingredients_controller.rb +1 -1
  53. data/app/controllers/alchemy/api/pages_controller.rb +5 -3
  54. data/app/controllers/alchemy/base_controller.rb +6 -6
  55. data/app/controllers/alchemy/pages_controller.rb +12 -6
  56. data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +0 -1
  57. data/app/controllers/concerns/alchemy/admin/clipboard.rb +57 -0
  58. data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +2 -2
  59. data/app/controllers/concerns/alchemy/site_redirects.rb +1 -1
  60. data/app/decorators/alchemy/ingredient_editor.rb +37 -4
  61. data/app/helpers/alchemy/admin/base_helper.rb +10 -6
  62. data/app/helpers/alchemy/admin/ingredients_helper.rb +6 -3
  63. data/app/helpers/alchemy/base_helper.rb +1 -1
  64. data/app/helpers/alchemy/pages_helper.rb +1 -1
  65. data/app/javascript/alchemy_admin/components/action.js +5 -1
  66. data/app/javascript/alchemy_admin/components/color_select.js +73 -0
  67. data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +11 -3
  68. data/app/javascript/alchemy_admin/components/element_editor/publish_element_button.js +7 -2
  69. data/app/javascript/alchemy_admin/components/element_editor.js +11 -12
  70. data/app/javascript/alchemy_admin/components/element_select.js +39 -17
  71. data/app/javascript/alchemy_admin/components/elements_window.js +0 -2
  72. data/app/javascript/alchemy_admin/components/file_editor.js +26 -0
  73. data/app/javascript/alchemy_admin/components/index.js +9 -0
  74. data/app/javascript/alchemy_admin/components/list_filter.js +57 -8
  75. data/app/javascript/alchemy_admin/components/message.js +9 -3
  76. data/app/javascript/alchemy_admin/components/page_node.js +119 -0
  77. data/app/javascript/alchemy_admin/{page_publication_fields.js → components/page_publication_fields.js} +9 -8
  78. data/app/javascript/alchemy_admin/{picture_editors.js → components/picture_editor.js} +30 -45
  79. data/app/javascript/alchemy_admin/components/picture_thumbnail.js +107 -0
  80. data/app/javascript/alchemy_admin/components/publish_page_button.js +41 -0
  81. data/app/javascript/alchemy_admin/components/select.js +3 -1
  82. data/app/javascript/alchemy_admin/components/sitemap.js +210 -0
  83. data/app/javascript/alchemy_admin/{sortable_elements.js → components/sortable_elements.js} +22 -25
  84. data/app/javascript/alchemy_admin/components/tinymce.js +10 -5
  85. data/app/javascript/alchemy_admin/components/uploader.js +30 -0
  86. data/app/javascript/alchemy_admin/image_overlay.js +0 -2
  87. data/app/javascript/alchemy_admin/initializer.js +0 -3
  88. data/app/javascript/alchemy_admin/link_dialog.js +1 -6
  89. data/app/javascript/alchemy_admin/templates/compiled.js +1 -1
  90. data/app/javascript/alchemy_admin/utils/ajax.js +15 -3
  91. data/app/javascript/alchemy_admin.js +0 -6
  92. data/app/models/alchemy/attachment.rb +4 -44
  93. data/app/models/alchemy/element/definitions.rb +1 -2
  94. data/app/models/alchemy/element/element_ingredients.rb +6 -2
  95. data/app/models/alchemy/element.rb +54 -13
  96. data/app/models/alchemy/element_definition.rb +4 -1
  97. data/app/models/alchemy/elements_repository.rb +6 -0
  98. data/app/models/alchemy/folded_page.rb +2 -2
  99. data/app/models/alchemy/ingredient.rb +38 -1
  100. data/app/models/alchemy/ingredient_definition.rb +4 -1
  101. data/app/models/alchemy/ingredient_validator.rb +6 -2
  102. data/app/models/alchemy/ingredients/color.rb +10 -0
  103. data/app/models/alchemy/ingredients/headline.rb +2 -17
  104. data/app/models/alchemy/ingredients/picture.rb +4 -4
  105. data/app/models/alchemy/ingredients/select.rb +19 -0
  106. data/app/models/alchemy/language/code.rb +0 -1
  107. data/app/models/alchemy/node.rb +28 -1
  108. data/app/models/alchemy/page/page_naming.rb +0 -7
  109. data/app/models/alchemy/page/page_natures.rb +7 -3
  110. data/app/models/alchemy/page/page_scopes.rb +13 -1
  111. data/app/models/alchemy/page/publisher.rb +14 -2
  112. data/app/models/alchemy/page.rb +102 -23
  113. data/app/models/alchemy/page_definition.rb +4 -1
  114. data/app/models/alchemy/page_version.rb +22 -6
  115. data/app/models/alchemy/picture.rb +10 -11
  116. data/app/models/alchemy/picture_variant.rb +1 -3
  117. data/app/models/alchemy/resource.rb +1 -1
  118. data/app/models/alchemy/storage_adapter/active_storage.rb +14 -2
  119. data/app/models/alchemy/storage_adapter/dragonfly.rb +12 -0
  120. data/app/models/alchemy/storage_adapter.rb +2 -0
  121. data/app/models/concerns/alchemy/picture_thumbnails.rb +4 -4
  122. data/app/models/concerns/alchemy/publishable.rb +54 -0
  123. data/app/models/concerns/alchemy/relatable_resource.rb +4 -14
  124. data/app/serializers/alchemy/page_tree_serializer.rb +11 -31
  125. data/app/services/alchemy/copy_page.rb +17 -0
  126. data/app/services/alchemy/duplicate_element.rb +1 -1
  127. data/app/services/alchemy/page_tree_preloader.rb +105 -0
  128. data/app/stylesheets/alchemy/_extends.scss +3 -9
  129. data/app/stylesheets/alchemy/_mixins.scss +3 -1
  130. data/app/stylesheets/alchemy/_themes.scss +19 -10
  131. data/app/stylesheets/alchemy/admin/archive.scss +1 -0
  132. data/app/stylesheets/alchemy/admin/base.scss +5 -2
  133. data/app/stylesheets/alchemy/admin/buttons.scss +3 -3
  134. data/app/stylesheets/alchemy/admin/element-select.scss +18 -0
  135. data/app/stylesheets/alchemy/admin/elements.scss +123 -23
  136. data/app/stylesheets/alchemy/admin/errors.scss +1 -1
  137. data/app/stylesheets/alchemy/admin/flash.scss +6 -4
  138. data/app/stylesheets/alchemy/admin/images.scss +9 -5
  139. data/app/stylesheets/alchemy/admin/list_filter.scss +4 -4
  140. data/app/stylesheets/alchemy/admin/navigation.scss +1 -1
  141. data/app/stylesheets/alchemy/admin/notices.scss +1 -2
  142. data/app/stylesheets/alchemy/admin/selects.scss +36 -21
  143. data/app/stylesheets/alchemy/admin/shoelace.scss +14 -1
  144. data/app/stylesheets/alchemy/admin/sitemap.scss +11 -3
  145. data/app/stylesheets/alchemy/admin/tags.scss +3 -1
  146. data/app/stylesheets/alchemy/admin/toolbar.scss +1 -1
  147. data/app/views/alchemy/_edit_mode.html.erb +1 -1
  148. data/app/views/alchemy/_menubar.html.erb +1 -1
  149. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +35 -31
  150. data/app/views/alchemy/admin/attachments/_files_list.html.erb +1 -1
  151. data/app/views/alchemy/admin/attachments/_library_sidebar.html.erb +6 -0
  152. data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +1 -1
  153. data/app/views/alchemy/admin/attachments/_replace_button.html.erb +1 -8
  154. data/app/views/alchemy/admin/attachments/_sorting_select.html.erb +13 -0
  155. data/app/views/alchemy/admin/attachments/_tag_list.html.erb +2 -3
  156. data/app/views/alchemy/admin/attachments/index.html.erb +5 -11
  157. data/app/views/alchemy/admin/attachments/show.html.erb +1 -1
  158. data/app/views/alchemy/admin/clipboard/_button.html.erb +1 -0
  159. data/app/views/alchemy/admin/clipboard/index.html.erb +4 -5
  160. data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +1 -1
  161. data/app/views/alchemy/admin/crop.html.erb +5 -7
  162. data/app/views/alchemy/admin/dashboard/widgets/_locked_pages.html.erb +1 -1
  163. data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +6 -6
  164. data/app/views/alchemy/admin/elements/_fixed_element.html.erb +1 -1
  165. data/app/views/alchemy/admin/elements/_footer.html.erb +7 -1
  166. data/app/views/alchemy/admin/elements/_header.html.erb +5 -5
  167. data/app/views/alchemy/admin/elements/_toolbar.html.erb +33 -8
  168. data/app/views/alchemy/admin/elements/create.turbo_stream.erb +10 -10
  169. data/app/views/alchemy/admin/elements/index.html.erb +29 -16
  170. data/app/views/alchemy/admin/elements/new.html.erb +2 -2
  171. data/app/views/alchemy/admin/ingredients/update.turbo_stream.erb +3 -5
  172. data/app/views/alchemy/admin/leave.html.erb +1 -1
  173. data/app/views/alchemy/admin/nodes/_node.html.erb +19 -0
  174. data/app/views/alchemy/admin/nodes/edit.html.erb +1 -1
  175. data/app/views/alchemy/admin/nodes/index.html.erb +3 -1
  176. data/app/views/alchemy/admin/nodes/new.html.erb +14 -1
  177. data/app/views/alchemy/admin/pages/_current_page.html.erb +3 -1
  178. data/app/views/alchemy/admin/pages/_form.html.erb +21 -9
  179. data/app/views/alchemy/admin/pages/_page_status.html.erb +1 -1
  180. data/app/views/alchemy/admin/pages/_publication_fields.html.erb +28 -26
  181. data/app/views/alchemy/admin/pages/_table.html.erb +0 -7
  182. data/app/views/alchemy/admin/pages/_toolbar.html.erb +3 -6
  183. data/app/views/alchemy/admin/pages/edit.html.erb +5 -11
  184. data/app/views/alchemy/admin/pages/flush.turbo_stream.erb +2 -0
  185. data/app/views/alchemy/admin/pages/fold.turbo_stream.erb +5 -0
  186. data/app/views/alchemy/admin/pages/index.html.erb +5 -3
  187. data/app/views/alchemy/admin/pages/new.html.erb +2 -12
  188. data/app/views/alchemy/admin/pages/publish.turbo_stream.erb +12 -0
  189. data/app/views/alchemy/admin/pages/tree.html.erb +13 -0
  190. data/app/views/alchemy/admin/pages/update.turbo_stream.erb +5 -16
  191. data/app/views/alchemy/admin/partials/_flash_notices.html.erb +1 -1
  192. data/app/views/alchemy/admin/partials/{_remote_search_form.html.erb → _overlay_search_form.html.erb} +1 -2
  193. data/app/views/alchemy/admin/partials/_paste_from_clipboard_form.html.erb +12 -0
  194. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +24 -21
  195. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +18 -26
  196. data/app/views/alchemy/admin/pictures/_picture.html.erb +12 -16
  197. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +3 -6
  198. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +2 -3
  199. data/app/views/alchemy/admin/pictures/index.html.erb +0 -1
  200. data/app/views/alchemy/admin/pictures/update.turbo_stream.erb +1 -1
  201. data/app/views/alchemy/admin/resources/_resource_usage_info.html.erb +1 -1
  202. data/app/views/alchemy/admin/resources/_tag_list.html.erb +2 -3
  203. data/app/views/alchemy/admin/styleguide/index.html.erb +25 -20
  204. data/app/views/alchemy/admin/tags/edit.html.erb +1 -1
  205. data/app/views/alchemy/admin/tinymce/_setup.html.erb +2 -2
  206. data/app/views/alchemy/admin/uploader/_button.html.erb +1 -15
  207. data/app/views/alchemy/attachments/show.html.erb +1 -1
  208. data/app/views/alchemy/base/permission_denied.js.erb +1 -1
  209. data/app/views/alchemy/ingredients/shared/_anchor.html.erb +9 -7
  210. data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +12 -5
  211. data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +10 -11
  212. data/app/views/alchemy/language_links/_spacer.html.erb +1 -1
  213. data/app/views/alchemy/messages_mailer/new.html.erb +1 -1
  214. data/app/views/alchemy/welcome.html.erb +1 -1
  215. data/config/locales/alchemy.en.yml +12 -3
  216. data/config/routes.rb +2 -2
  217. data/db/migrate/20230123112425_add_searchable_to_alchemy_pages.rb +1 -1
  218. data/db/migrate/20230505132743_add_indexes_to_alchemy_pictures.rb +1 -1
  219. data/db/migrate/20231113104432_create_page_mutexes.rb +1 -1
  220. data/db/migrate/20240314105244_create_alchemy_picture_descriptions.rb +1 -1
  221. data/db/migrate/20250626160259_add_unique_index_to_picture_descriptions.rb +1 -1
  222. data/db/migrate/20250905140323_add_created_at_index_to_pictures_and_attachments.rb +1 -1
  223. data/db/migrate/20251106150010_convert_select_value_for_multiple.rb +11 -0
  224. data/db/migrate/20260102121232_add_metadata_to_page_versions.rb +9 -0
  225. data/db/migrate/20260115164704_add_publication_timestamps_to_alchemy_elements.rb +30 -0
  226. data/db/migrate/20260115164705_add_index_to_element_publication_timestamps.rb +13 -0
  227. data/lib/alchemy/ability_helper.rb +1 -3
  228. data/lib/alchemy/auth_accessors.rb +51 -117
  229. data/lib/alchemy/configuration.rb +1 -2
  230. data/lib/alchemy/configurations/main.rb +63 -0
  231. data/lib/alchemy/controller_actions.rb +2 -3
  232. data/lib/alchemy/engine.rb +9 -12
  233. data/lib/alchemy/error_tracking/error_logger.rb +1 -1
  234. data/lib/alchemy/errors.rb +1 -1
  235. data/lib/alchemy/logger.rb +34 -4
  236. data/lib/alchemy/name_conversions.rb +0 -6
  237. data/lib/alchemy/seeder.rb +2 -2
  238. data/lib/alchemy/tasks/usage.rb +4 -4
  239. data/lib/alchemy/test_support/factories/page_version_factory.rb +3 -0
  240. data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +30 -0
  241. data/lib/alchemy/test_support/shared_ingredient_editor_examples.rb +26 -6
  242. data/lib/alchemy/test_support/shared_publishable_examples.rb +114 -0
  243. data/lib/alchemy/upgrader/eight_one.rb +56 -0
  244. data/lib/alchemy/upgrader.rb +9 -1
  245. data/lib/alchemy/version.rb +1 -1
  246. data/lib/alchemy.rb +1 -4
  247. data/lib/alchemy_cms.rb +0 -1
  248. data/lib/generators/alchemy/elements/templates/view.html.erb +3 -3
  249. data/lib/generators/alchemy/ingredient/ingredient_generator.rb +6 -8
  250. data/lib/generators/alchemy/ingredient/templates/editor_component.rb.tt +22 -0
  251. data/lib/generators/alchemy/page_layouts/templates/layout.html.erb +1 -1
  252. data/lib/generators/alchemy/site_layouts/templates/layout.html.erb +1 -1
  253. data/lib/tasks/alchemy/upgrade.rake +21 -7
  254. data/vendor/javascript/shoelace.min.js +713 -31
  255. data/vendor/javascript/tinymce.min.js +1 -1
  256. metadata +104 -84
  257. data/app/decorators/alchemy/element_editor.rb +0 -90
  258. data/app/helpers/alchemy/admin/pictures_helper.rb +0 -14
  259. data/app/javascript/alchemy_admin/file_editors.js +0 -28
  260. data/app/javascript/alchemy_admin/image_loader.js +0 -54
  261. data/app/javascript/alchemy_admin/page_sorter.js +0 -71
  262. data/app/javascript/alchemy_admin/sitemap.js +0 -154
  263. data/app/javascript/alchemy_admin/templates/page_folder.hbs +0 -3
  264. data/app/views/alchemy/admin/attachments/archive_overlay.js.erb +0 -4
  265. data/app/views/alchemy/admin/pages/_page.html.erb +0 -163
  266. data/app/views/alchemy/admin/pages/_sitemap.html.erb +0 -30
  267. data/app/views/alchemy/admin/pages/flush.js.erb +0 -2
  268. data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +0 -5
  269. data/app/views/alchemy/admin/pictures/index.js.erb +0 -2
  270. data/app/views/alchemy/ingredients/_audio_editor.html.erb +0 -5
  271. data/app/views/alchemy/ingredients/_boolean_editor.html.erb +0 -11
  272. data/app/views/alchemy/ingredients/_datetime_editor.html.erb +0 -20
  273. data/app/views/alchemy/ingredients/_file_editor.html.erb +0 -52
  274. data/app/views/alchemy/ingredients/_headline_editor.html.erb +0 -44
  275. data/app/views/alchemy/ingredients/_html_editor.html.erb +0 -8
  276. data/app/views/alchemy/ingredients/_link_editor.html.erb +0 -30
  277. data/app/views/alchemy/ingredients/_node_editor.html.erb +0 -13
  278. data/app/views/alchemy/ingredients/_number_editor.html.erb +0 -24
  279. data/app/views/alchemy/ingredients/_page_editor.html.erb +0 -13
  280. data/app/views/alchemy/ingredients/_picture_editor.html.erb +0 -59
  281. data/app/views/alchemy/ingredients/_richtext_editor.html.erb +0 -15
  282. data/app/views/alchemy/ingredients/_select_editor.html.erb +0 -31
  283. data/app/views/alchemy/ingredients/_text_editor.html.erb +0 -29
  284. data/app/views/alchemy/ingredients/_video_editor.html.erb +0 -5
  285. data/lib/generators/alchemy/ingredient/templates/editor.html.erb +0 -14
  286. /data/{lib → app/models}/alchemy/permissions.rb +0 -0
@@ -32,7 +32,7 @@
32
32
  <select class="select" id="select" is="alchemy-select">
33
33
  <option value="">Please choose</option>
34
34
  <option value="1">Option 1</option>
35
- <option selected="selected" value="2">Option 2</option>
35
+ <option selected value="2">Option 2</option>
36
36
  <option value="3">Option 3</option>
37
37
  </select>
38
38
  </div>
@@ -47,7 +47,7 @@
47
47
  <div class="input">
48
48
  <label class="control-label" for="datetime_picker">Date Time Picker</label>
49
49
  <alchemy-datepicker input-type="datetime">
50
- <input type="text" id="datetime_picker">
50
+ <input type="text" id="datetime_picker" autocomplete="off">
51
51
  </alchemy-datepicker>
52
52
  </div>
53
53
 
@@ -57,12 +57,12 @@
57
57
  <div class="input-row">
58
58
  <div class="input-column">
59
59
  <alchemy-datepicker input-type="date">
60
- <input type="text" id="date_picker">
60
+ <input type="text" id="date_picker" autocomplete="off">
61
61
  </alchemy-datepicker>
62
62
  </div>
63
63
  <div class="input-column">
64
64
  <alchemy-datepicker input-type="time">
65
- <input type="text" id="time_picker">
65
+ <input type="text" id="time_picker" autocomplete="off">
66
66
  </alchemy-datepicker>
67
67
  </div>
68
68
  </div>
@@ -71,23 +71,23 @@
71
71
 
72
72
  <div class="input">
73
73
  <label class="control-label" for="optional_string">String</label>
74
- <input type="text" id="optional_string">
74
+ <input type="text" id="optional_string" autocomplete="off">
75
75
  </div>
76
76
  <div class="input required">
77
77
  <label class="required control-label" for="required_string">
78
78
  Required String<abbr title="required">*</abbr>
79
79
  </label>
80
- <input type="text" id="required_string">
80
+ <input type="text" id="required_string" autocomplete="off">
81
81
  </div>
82
82
  <div class="input field_with_errors">
83
83
  <label class="control-label" for="string_with_error">String with error</label>
84
- <input type="text" id="string_with_error">
84
+ <input type="text" id="string_with_error" autocomplete="off">
85
85
  <small class="error">Error message</small>
86
86
  </div>
87
87
  <alchemy-char-counter max-chars="60">
88
88
  <div class="input">
89
89
  <label class="control-label" for="string_with_counter">String with char counter</label>
90
- <input type="text" id="string_with_counter">
90
+ <input type="text" id="string_with_counter" autocomplete="off">
91
91
  </div>
92
92
  </alchemy-char-counter>
93
93
 
@@ -95,7 +95,7 @@
95
95
  <label class="control-label">Check Boxes</label>
96
96
  <div class="control_group">
97
97
  <label class="checkbox">
98
- <input type="checkbox" value="1" checked="checked">
98
+ <input type="checkbox" value="1" checked>
99
99
  Checked
100
100
  </label>
101
101
  <label class="checkbox">
@@ -140,7 +140,7 @@
140
140
  <h2>Selects</h2>
141
141
 
142
142
  <div style="display: flex; gap: 1em;">
143
- <div style="width: 25%">
143
+ <div style="width: 20%">
144
144
  <h3>Select with Search</h3>
145
145
  <select is="alchemy-select" class="full_width">
146
146
  <option value="1">Option 1</option>
@@ -153,24 +153,29 @@
153
153
  </select>
154
154
  </div>
155
155
 
156
- <div style="width: 25%">
156
+ <div style="width: 20%">
157
157
  <h3>Page Select</h3>
158
158
  <%= render Alchemy::Admin::PageSelect.new do %>
159
- <input name="page" class="full_width" />
159
+ <input name="page" class="full_width">
160
160
  <% end %>
161
161
  </div>
162
162
 
163
- <div style="width: 25%">
163
+ <div style="width: 20%">
164
+ <h3>Element Select</h3>
165
+ <%= render Alchemy::Admin::ElementSelect.new(Alchemy::Element.definitions) %>
166
+ </div>
167
+
168
+ <div style="width: 20%">
164
169
  <h3>Node Select</h3>
165
170
  <%= render Alchemy::Admin::NodeSelect.new do %>
166
- <input name="node" class="full_width" />
171
+ <input name="node" class="full_width">
167
172
  <% end %>
168
173
  </div>
169
174
 
170
- <div style="width: 25%">
175
+ <div style="width: 20%">
171
176
  <h3>Attachment Select</h3>
172
177
  <%= render Alchemy::Admin::AttachmentSelect.new do %>
173
- <input name="attachment" class="full_width" />
178
+ <input name="attachment" class="full_width">
174
179
  <% end %>
175
180
  </div>
176
181
  </div>
@@ -179,7 +184,7 @@
179
184
 
180
185
  <div style="width: 25%">
181
186
  <alchemy-tags-autocomplete>
182
- <input type="text" class="full_width" />
187
+ <input type="text" class="full_width" autocomplete="off">
183
188
  </alchemy-tags-autocomplete>
184
189
  </div>
185
190
 
@@ -198,19 +203,19 @@
198
203
  <h3>Info Message</h3>
199
204
 
200
205
  <%= render_message do %>
201
- <p>Lorem ipsum <strong>dolor</strong> sit amet, <a href="">consectetur</a> adipiscing elit.</p>
206
+ <p>Lorem ipsum <strong>dolor</strong> sit amet, <a href="#">consectetur</a> adipiscing elit.</p>
202
207
  <% end %>
203
208
 
204
209
  <h3>Warning Message</h3>
205
210
 
206
211
  <%= render_message :warning do %>
207
- <p>Lorem ipsum dolor sit amet, <a href="">consectetur</a> adipiscing elit.</p>
212
+ <p>Lorem ipsum dolor sit amet, <a href="#">consectetur</a> adipiscing elit.</p>
208
213
  <% end %>
209
214
 
210
215
  <h3>Error Message</h3>
211
216
 
212
217
  <%= render_message :error do %>
213
- <p>Lorem ipsum dolor sit amet, <a href="">consectetur</a> adipiscing elit.</p>
218
+ <p>Lorem ipsum dolor sit amet, <a href="#">consectetur</a> adipiscing elit.</p>
214
219
  <% end %>
215
220
 
216
221
  <h3>Growl Messages</h3>
@@ -13,7 +13,7 @@
13
13
  <div class="input tags">
14
14
  <label class="control-label"><%= Alchemy.t('Tags') %></label>
15
15
  <div class="control_group" id="tags_tag_list">
16
- <%= render Alchemy::Admin::ListFilter.new(".tag-list li") %>
16
+ <%= render Alchemy::Admin::ListFilter.new(".tag-list li", placeholder: Alchemy.t("Filter tags")) %>
17
17
  <ul class="tags tag-list">
18
18
  <%= render partial: "radio_tag", collection: @tags, locals: {name: "tag"} %>
19
19
  </ul>
@@ -1,8 +1,8 @@
1
1
  <% tinymce_base_path = "#{Rails.application.config.assets.prefix}/tinymce" %>
2
2
 
3
- <link rel="preload" href="<%= asset_path("#{tinymce_base_path}/skins/ui/alchemy/skin.min.css") %>" as="style" />
3
+ <link rel="preload" href="<%= asset_path("#{tinymce_base_path}/skins/ui/alchemy/skin.min.css") %>" as="style">
4
4
  <% if Alchemy::Tinymce.init[:content_css] %>
5
- <link rel="preload" href="<%= asset_path(Alchemy::Tinymce.init[:content_css]) %>" as="style" />
5
+ <link rel="preload" href="<%= asset_path(Alchemy::Tinymce.init[:content_css]) %>" as="style">
6
6
  <% end %>
7
7
  <% Alchemy::Tinymce.preloadable_plugins.each do |plugin| %>
8
8
  <link rel="preload" href="<%= asset_path("#{tinymce_base_path}/plugins/#{plugin}/plugin.min.js") %>" as="script">
@@ -1,7 +1,7 @@
1
1
  <% file_types = Alchemy.config.uploader.allowed_filetypes[object.class.model_name.collection] || ['*'] %>
2
2
  <% accept ||= file_types.to_a == ["*"] ? nil : file_types.map {|type| ".#{type}"}.join(", ") %>
3
3
 
4
- <alchemy-uploader dropzone="<%= local_assigns[:dropzone] || "#main_content" %>">
4
+ <alchemy-uploader redirect-url="<%= redirect_url %>" dropzone="<%= local_assigns[:dropzone] || "#main_content" %>">
5
5
  <%= form_for [:admin, object], html: { multipart: true, class: 'upload-button' } do |f| %>
6
6
  <%= f.file_field file_attribute,
7
7
  class: 'fileupload fileupload--field', multiple: true, accept: accept,
@@ -15,17 +15,3 @@
15
15
  <% end %>
16
16
  <% end %>
17
17
  </alchemy-uploader>
18
-
19
- <script type='text/javascript'>
20
- document.querySelector("alchemy-uploader").addEventListener("Alchemy.upload.successful", (evt) => {
21
- setTimeout(() => {
22
- var url = '<%= redirect_url.html_safe %>';
23
- evt.target.uploadProgress.visible = false;
24
- <% if local_assigns[:in_dialog] %>
25
- $.get(url, null, null, 'script');
26
- <% else %>
27
- Turbo.visit(url);
28
- <% end %>
29
- }, 1000)
30
- })
31
- </script>
@@ -1 +1 @@
1
- <%#= @attachment.public_filename %>
1
+ <%#= @attachment.public_filename %>
@@ -1,3 +1,3 @@
1
1
  Alchemy.closeCurrentDialog(function() {
2
- Turbo.visit('<%= Alchemy.login_path %>');
2
+ Turbo.visit('<%= Alchemy.config.login_path %>');
3
3
  });
@@ -1,9 +1,11 @@
1
1
  <span class="edit-ingredient-anchor-link">
2
- <%= link_to_dialog render_icon(:bookmark, { style: ingredient_editor.dom_id.present? ? "fill" : "line" }),
3
- alchemy.edit_admin_ingredient_path(id: ingredient_editor.id),
4
- {
5
- title: Alchemy.t(:edit_anchor),
6
- size: "380x125"
7
- },
8
- title: Alchemy.t(:edit_anchor) %>
2
+ <% disabled = cannot?(:edit, ingredient) %>
3
+ <%= content_tag "sl-tooltip", content: Alchemy.t(:edit_anchor), disabled: disabled do %>
4
+ <%= link_to_dialog render_icon(:bookmark, { style: ingredient.dom_id.present? ? "fill" : "line" }),
5
+ disabled ? nil : alchemy.edit_admin_ingredient_path(id: ingredient.id),
6
+ {
7
+ title: Alchemy.t(:edit_anchor),
8
+ size: "380x125"
9
+ } %>
10
+ <% end %>
9
11
  </span>
@@ -1,10 +1,17 @@
1
- <%= content_tag "alchemy-link-buttons", class: local_assigns[:wrapper_class], data: {ingredient_id: ingredient_editor.id} do %>
2
- <%= content_tag "sl-tooltip", content: Alchemy.t(:place_link) do %>
1
+ <% disabled = cannot?(:edit, ingredient) %>
2
+ <%= content_tag "alchemy-link-buttons", class: local_assigns[:wrapper_class], data: {ingredient_id: ingredient.id} do %>
3
+ <%= content_tag "sl-tooltip",
4
+ content: Alchemy.t(:place_link),
5
+ disabled: disabled do %>
3
6
  <%= content_tag "button", "", is: "alchemy-link-button",
4
- class: [ingredient_editor.linked? ? "linked" : nil] %>
7
+ disabled: disabled,
8
+ class: [ingredient.linked? ? "linked" : nil] %>
5
9
  <% end %>
6
- <%= content_tag "sl-tooltip", content: Alchemy.t(:unlink) do %>
10
+ <%= content_tag "sl-tooltip",
11
+ content: Alchemy.t(:unlink),
12
+ disabled: disabled do %>
7
13
  <%= content_tag "button", "", is: "alchemy-unlink-button",
8
- class: [ingredient_editor.linked? ? "linked" : "disabled"] %>
14
+ disabled: disabled,
15
+ class: [ingredient.linked? ? "linked" : "disabled"] %>
9
16
  <% end %>
10
17
  <% end %>
@@ -1,27 +1,26 @@
1
1
  <% linkable = picture_editor.settings[:linkable] != false %>
2
2
  <% croppable = picture_editor.allow_image_cropping? %>
3
+ <% disabled = cannot?(:edit, picture_editor.ingredient) %>
3
4
 
4
- <%= content_tag "sl-tooltip", content: Alchemy.t("Edit Picturemask"), placement: "top-start", disabled: !croppable do %>
5
+ <%= content_tag "sl-tooltip", content: Alchemy.t("Edit Picturemask"), placement: "top-start", disabled: !croppable || disabled do %>
5
6
  <%= link_to_dialog render_icon(:crop),
6
- alchemy.crop_admin_ingredient_path(picture_editor.ingredient, {
7
+ (croppable && !disabled) ? alchemy.crop_admin_ingredient_path(picture_editor.ingredient, {
7
8
  crop_from_form_field_id: picture_editor.form_field_id(:crop_from),
8
9
  crop_size_form_field_id: picture_editor.form_field_id(:crop_size),
9
10
  picture_id: picture_editor.picture&.id
10
- }), {
11
+ }) : nil, {
11
12
  size: "1080x615",
12
13
  title: Alchemy.t("Edit Picturemask"),
13
14
  image_loader: false,
14
15
  padding: false
15
16
  }, {
16
- class: croppable ? "crop_link icon_button" : "disabled crop_link icon_button" ,
17
- tabindex: croppable ? nil : "-1",
18
- onclick: "return false"
17
+ class: "crop_link icon_button"
19
18
  } %>
20
19
  <% end %>
21
20
 
22
- <%= content_tag "sl-tooltip", content: picture_editor.picture ? Alchemy.t(:swap_image) : Alchemy.t(:insert_image) do %>
21
+ <%= content_tag "sl-tooltip", content: picture_editor.picture ? Alchemy.t(:swap_image) : Alchemy.t(:insert_image), disabled: disabled do %>
23
22
  <%= link_to_dialog render_icon("image-add"),
24
- alchemy.admin_pictures_path(
23
+ disabled ? nil : alchemy.admin_pictures_path(
25
24
  form_field_id: picture_editor.form_field_id(:picture_id)
26
25
  ),
27
26
  {
@@ -33,15 +32,15 @@
33
32
  <% end %>
34
33
 
35
34
  <% if linkable %>
36
- <%= render "alchemy/ingredients/shared/link_tools", ingredient_editor: picture_editor %>
35
+ <%= render "alchemy/ingredients/shared/link_tools", ingredient: picture_editor.ingredient %>
37
36
  <% else %>
38
37
  <span class="icon_button disabled"><%= render_icon("link", style: "m") %></span>
39
38
  <span class="icon_button disabled"><%= render_icon("link-unlink", style: "m") %></span>
40
39
  <% end %>
41
40
 
42
- <%= content_tag "sl-tooltip", content: Alchemy.t(:edit_image_properties), placement: "top-end" do %>
41
+ <%= content_tag "sl-tooltip", content: Alchemy.t(:edit_image_properties), placement: "top-end", disabled: disabled do %>
43
42
  <%= link_to_dialog render_icon(:edit),
44
- alchemy.edit_admin_ingredient_path(id: picture_editor.id, language_id: @page.language_id),
43
+ disabled ? nil : alchemy.edit_admin_ingredient_path(id: picture_editor.id, language_id: @page.language_id),
45
44
  {
46
45
  title: Alchemy.t(:edit_image_properties),
47
46
  size: "380x255"
@@ -1 +1 @@
1
- <%= options[:spacer].html_safe %>
1
+ <%= options[:spacer].html_safe %>
@@ -1 +1 @@
1
- <%= render_elements %>
1
+ <%= render_elements %>
@@ -14,7 +14,7 @@
14
14
  <%= Alchemy.t("Before creating your first page you need an admin user") %>.
15
15
  </p>
16
16
  <p>
17
- <%= link_to Alchemy.t("Create admin user"), Alchemy.signup_path, class: "button" %>
17
+ <%= link_to Alchemy.t("Create admin user"), Alchemy.config.signup_path, class: "button" %>
18
18
  </p>
19
19
  <h4><%= Alchemy.t("More resources") %></h4>
20
20
  <ul>
@@ -225,6 +225,11 @@ en:
225
225
  aliases: Enter additional domains this site should be accessible by. Please separate them by space or new line.
226
226
 
227
227
  admin:
228
+ attachments:
229
+ sorting_select:
230
+ label: "Sorting"
231
+ by_latest: "Latest"
232
+ alphabetical: "A-Z"
228
233
  elements:
229
234
  toolbar:
230
235
  hide: Hide
@@ -233,6 +238,9 @@ en:
233
238
  label: "Sorting"
234
239
  by_latest: "Latest"
235
240
  alphabetical: "A-Z"
241
+ publish_page_button:
242
+ publish_page: "Publish page"
243
+ publish_changes: "Publish changes"
236
244
  add_nested_element: "Add %{name}"
237
245
  anchor: "Anchor"
238
246
  anchor_link_headline: You can link to an anchor from the current page.
@@ -354,9 +362,9 @@ en:
354
362
  "Visit page": "Visit page"
355
363
  "Warning!": "Warning!"
356
364
  ingredient_deprecated: "WARNING! This field is deprecated and will be removed soon. Please do not use it anymore."
357
- element_definition_missing: "WARNING! Missing element definition. Please check your elements.yml file."
365
+ element_definition_missing: "WARNING: Missing element definition! Please check your element definitions."
358
366
  element_deprecated: "WARNING! This element is deprecated and will be removed soon. Please do not use it anymore."
359
- page_definition_missing: "WARNING! Missing page layout definition. Please check your page_layouts.yml file."
367
+ page_definition_missing: "WARNING: Missing page layout definition! Please check your page definitions."
360
368
  "Welcome to Alchemy": "Welcome to Alchemy"
361
369
  "Who else is online": "Who else is online"
362
370
  "Yes": "Yes"
@@ -391,7 +399,6 @@ en:
391
399
  confirm_to_delete_menu: "Do you really want to delete this menu?"
392
400
  confirm_to_delete_node: "Do you really want to delete this menu node?"
393
401
  confirm_to_delete_page: "Do you really want to delete this page?"
394
- confirm_to_flush_cache: "Do you really want to flush the page cache? This will might take a while and can make first page requests slow."
395
402
  ingredient_validations_headline: "Please check marked fields below"
396
403
  copy: "copy"
397
404
  copy_element: "Copy this element"
@@ -408,6 +415,7 @@ en:
408
415
  create_node: "Add a menu node"
409
416
  create_page: "Create a new subpage"
410
417
  currently_edited_by: "This page is locked by"
418
+ custom_color: "Custom color"
411
419
  cut_element: "Cut this element."
412
420
  delete_file: "Delete this file from server."
413
421
  delete_image: "Remove this image"
@@ -531,6 +539,7 @@ en:
531
539
  alchemy/node: "<h2>You do not have any menus yet.</h2><p>With menus your users can navigate your website. You can have multiple menus on your website. How many depends on your website template.</p><p>Please chose which menu you want to create first.</p>"
532
540
  alchemy/site: "<h2>You do not have any websites yet.</h2><p>In order to serve requests you need to create a website first.</p><p>This form has been filled with sensible defaults that work in most cases.</p>"
533
541
  no_search_results: "Your search did not return any results."
542
+ no_unpublished_changes: "No unpublished changes"
534
543
  "not a valid image": "This is not an valid image."
535
544
  "or": "or"
536
545
  or_replace_it_with_an_existing_tag: "Or replace it with an existing tag"
data/config/routes.rb CHANGED
@@ -7,7 +7,7 @@ Alchemy::Engine.routes.draw do
7
7
 
8
8
  get "/sitemap.xml", to: "pages#sitemap", format: "xml"
9
9
 
10
- scope Alchemy.admin_path, {constraints: Alchemy.admin_constraints} do
10
+ scope Alchemy.admin_path, constraints: Alchemy.admin_constraints do
11
11
  get "/", to: redirect("#{Alchemy.admin_path}/dashboard"), as: :admin
12
12
  get "/dashboard", to: "admin/dashboard#index", as: :admin_dashboard
13
13
  get "/dashboard/info", to: "admin/dashboard#info", as: :dashboard_info
@@ -16,7 +16,7 @@ Alchemy::Engine.routes.draw do
16
16
  get "/leave", to: "admin/base#leave", as: :leave_admin
17
17
  end
18
18
 
19
- namespace :admin, {path: Alchemy.admin_path, constraints: Alchemy.admin_constraints} do
19
+ namespace :admin, path: Alchemy.admin_path, constraints: Alchemy.admin_constraints do
20
20
  resources :nodes
21
21
 
22
22
  resources :pages do
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class AddSearchableToAlchemyPages < ActiveRecord::Migration[6.0]
3
+ class AddSearchableToAlchemyPages < ActiveRecord::Migration[7.2]
4
4
  def change
5
5
  return if column_exists?(:alchemy_pages, :searchable)
6
6
 
@@ -1,4 +1,4 @@
1
- class AddIndexesToAlchemyPictures < ActiveRecord::Migration[6.1]
1
+ class AddIndexesToAlchemyPictures < ActiveRecord::Migration[7.2]
2
2
  def change
3
3
  add_index :alchemy_pictures, :name, if_not_exists: true
4
4
  add_index :alchemy_pictures, :image_file_name, if_not_exists: true
@@ -1,4 +1,4 @@
1
- class CreatePageMutexes < ActiveRecord::Migration[6.1]
1
+ class CreatePageMutexes < ActiveRecord::Migration[7.2]
2
2
  def change
3
3
  create_table :alchemy_page_mutexes do |t|
4
4
  t.references :page, null: false, index: {unique: true}, foreign_key: {to_table: :alchemy_pages}
@@ -1,4 +1,4 @@
1
- class CreateAlchemyPictureDescriptions < ActiveRecord::Migration[7.0]
1
+ class CreateAlchemyPictureDescriptions < ActiveRecord::Migration[7.2]
2
2
  def change
3
3
  create_table :alchemy_picture_descriptions do |t|
4
4
  t.belongs_to :picture, null: false, foreign_key: {to_table: :alchemy_pictures}
@@ -1,4 +1,4 @@
1
- class AddUniqueIndexToPictureDescriptions < ActiveRecord::Migration[7.0]
1
+ class AddUniqueIndexToPictureDescriptions < ActiveRecord::Migration[7.2]
2
2
  disable_ddl_transaction! if connection.adapter_name.match?(/postgres/i)
3
3
 
4
4
  def change
@@ -1,4 +1,4 @@
1
- class AddCreatedAtIndexToPicturesAndAttachments < ActiveRecord::Migration[7.1]
1
+ class AddCreatedAtIndexToPicturesAndAttachments < ActiveRecord::Migration[7.2]
2
2
  disable_ddl_transaction! if connection.adapter_name.match?(/postgres/i)
3
3
 
4
4
  def change
@@ -0,0 +1,11 @@
1
+ class ConvertSelectValueForMultiple < ActiveRecord::Migration[7.1]
2
+ def up
3
+ say_with_time "Converting Alchemy::Ingredients::Select values to multiple" do
4
+ update <<-SQL.squish
5
+ UPDATE alchemy_ingredients
6
+ SET value = '["' || value || '"]'
7
+ WHERE type = 'Alchemy::Ingredients::Select' AND value NOT LIKE '["%"]';
8
+ SQL
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddMetadataToPageVersions < ActiveRecord::Migration[7.2]
4
+ def change
5
+ add_column :alchemy_page_versions, :title, :string
6
+ add_column :alchemy_page_versions, :meta_description, :text
7
+ add_column :alchemy_page_versions, :meta_keywords, :text
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ class AddPublicationTimestampsToAlchemyElements < ActiveRecord::Migration[7.2]
2
+ def up
3
+ add_column :alchemy_elements, :public_on, :datetime
4
+ add_column :alchemy_elements, :public_until, :datetime
5
+
6
+ say_with_time "Populating publication dates" do
7
+ update <<-SQL.squish
8
+ UPDATE alchemy_elements
9
+ SET public_on = created_at
10
+ WHERE public = #{connection.quoted_true}
11
+ SQL
12
+ end
13
+ end
14
+
15
+ def down
16
+ say_with_time "Reverting publication dates" do
17
+ update <<-SQL.squish
18
+ UPDATE alchemy_elements
19
+ SET public = CASE
20
+ WHEN public_on IS NOT NULL AND public_on <= CURRENT_TIMESTAMP
21
+ THEN #{connection.quoted_true}
22
+ ELSE #{connection.quoted_false}
23
+ END
24
+ SQL
25
+ end
26
+
27
+ remove_column :alchemy_elements, :public_until
28
+ remove_column :alchemy_elements, :public_on
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ class AddIndexToElementPublicationTimestamps < ActiveRecord::Migration[7.2]
2
+ disable_ddl_transaction! if connection.adapter_name.match?(/postgres/i)
3
+
4
+ def change
5
+ add_index :alchemy_elements, [:public_on, :public_until], algorithm: algorithm
6
+ end
7
+
8
+ private
9
+
10
+ def algorithm
11
+ connection.adapter_name.match?(/postgres/i) ? :concurrently : nil
12
+ end
13
+ end
@@ -14,9 +14,7 @@ module Alchemy::AbilityHelper
14
14
  def current_ability
15
15
  @current_ability ||= begin
16
16
  alchemy_permissions = Alchemy::Permissions.new(current_alchemy_user)
17
- Alchemy.registered_abilities.each do |klass|
18
- # Ensure to avoid issues with Rails constant lookup.
19
- klass = "::#{klass}".constantize
17
+ Alchemy.config.abilities.each do |klass|
20
18
  alchemy_permissions.merge(klass.new(current_alchemy_user))
21
19
  end
22
20
  alchemy_permissions