alchemy_cms 8.0.0.a → 8.0.0.c

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/app/assets/builds/alchemy/admin/page-select.css +1 -1
  4. data/app/assets/builds/alchemy/admin.css +1 -1
  5. data/app/assets/builds/alchemy/dark-theme.css +1 -0
  6. data/app/assets/builds/alchemy/light-theme.css +1 -0
  7. data/app/assets/builds/alchemy/theme.css +1 -0
  8. data/app/assets/builds/alchemy/welcome.css +1 -1
  9. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
  10. data/app/assets/builds/tinymce/skins/content/alchemy-dark/content.min.css +1 -0
  11. data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -1
  12. data/app/assets/builds/tinymce/skins/ui/alchemy-dark/content.min.css +1 -0
  13. data/app/assets/builds/tinymce/skins/ui/alchemy-dark/skin.min.css +1 -0
  14. data/app/assets/images/alchemy/element_icons/layout-bottom-2-line.svg +1 -0
  15. data/app/assets/images/alchemy/icons-sprite.svg +1 -1
  16. data/app/components/alchemy/admin/element_select.rb +39 -0
  17. data/app/components/alchemy/admin/link_dialog/tabs.rb +1 -1
  18. data/app/components/alchemy/admin/locale_select.rb +38 -0
  19. data/app/components/alchemy/ingredients/datetime_view.rb +4 -2
  20. data/app/controllers/alchemy/admin/attachments_controller.rb +2 -0
  21. data/app/controllers/alchemy/admin/elements_controller.rb +2 -0
  22. data/app/controllers/alchemy/admin/pages_controller.rb +3 -1
  23. data/app/controllers/alchemy/admin/pictures_controller.rb +26 -34
  24. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  25. data/app/controllers/alchemy/pages_controller.rb +19 -2
  26. data/app/controllers/concerns/alchemy/admin/resource_filter.rb +1 -0
  27. data/app/decorators/alchemy/ingredient_editor.rb +9 -1
  28. data/app/helpers/alchemy/admin/attachments_helper.rb +5 -5
  29. data/app/helpers/alchemy/admin/base_helper.rb +0 -7
  30. data/app/helpers/alchemy/admin/form_helper.rb +2 -1
  31. data/app/helpers/alchemy/pages_helper.rb +1 -1
  32. data/app/javascript/alchemy_admin/components/auto_submit.js +20 -0
  33. data/app/javascript/alchemy_admin/components/datepicker.js +8 -5
  34. data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +3 -2
  35. data/app/javascript/alchemy_admin/components/element_editor.js +25 -15
  36. data/app/javascript/alchemy_admin/components/element_select.js +43 -0
  37. data/app/javascript/alchemy_admin/components/index.js +5 -0
  38. data/app/javascript/alchemy_admin/components/link_buttons.js +6 -2
  39. data/app/javascript/alchemy_admin/components/remote_select.js +5 -1
  40. data/app/javascript/alchemy_admin/components/tinymce.js +93 -16
  41. data/app/javascript/alchemy_admin/dialog.js +1 -1
  42. data/app/javascript/alchemy_admin/file_editors.js +1 -1
  43. data/app/javascript/alchemy_admin/image_loader.js +4 -2
  44. data/app/javascript/alchemy_admin/picture_editors.js +7 -4
  45. data/app/javascript/alchemy_admin/picture_selector.js +4 -4
  46. data/app/jobs/alchemy/delete_picture_job.rb +12 -0
  47. data/app/models/alchemy/attachment.rb +2 -9
  48. data/app/models/alchemy/element.rb +1 -0
  49. data/app/models/alchemy/element_definition.rb +31 -0
  50. data/app/models/alchemy/ingredient.rb +1 -1
  51. data/app/models/alchemy/ingredients/boolean.rb +2 -1
  52. data/app/models/alchemy/language.rb +2 -7
  53. data/app/models/alchemy/page/page_naming.rb +4 -11
  54. data/app/models/alchemy/page/page_natures.rb +16 -11
  55. data/app/models/alchemy/page/publisher.rb +1 -1
  56. data/app/models/alchemy/page.rb +1 -6
  57. data/app/models/alchemy/page_definition.rb +1 -1
  58. data/app/models/alchemy/picture.rb +6 -17
  59. data/app/models/alchemy/resource.rb +15 -2
  60. data/app/models/alchemy/site/layout.rb +1 -0
  61. data/app/models/alchemy/site.rb +1 -6
  62. data/app/models/alchemy/storage_adapter/dragonfly/picture_url.rb +7 -2
  63. data/app/models/alchemy/storage_adapter/dragonfly.rb +24 -2
  64. data/app/models/concerns/alchemy/relatable_resource.rb +28 -0
  65. data/app/stylesheets/alchemy/_custom-properties.scss +162 -0
  66. data/app/stylesheets/alchemy/_mixins.scss +12 -24
  67. data/app/stylesheets/alchemy/_themes.scss +540 -0
  68. data/app/stylesheets/alchemy/admin/archive.scss +28 -8
  69. data/app/stylesheets/alchemy/admin/attachments.scss +10 -33
  70. data/app/stylesheets/alchemy/admin/base.scss +4 -1
  71. data/app/stylesheets/alchemy/admin/buttons.scss +7 -32
  72. data/app/stylesheets/alchemy/admin/dashboard.scss +13 -0
  73. data/app/stylesheets/alchemy/admin/dialogs.scss +17 -7
  74. data/app/stylesheets/alchemy/admin/element-select.scss +11 -0
  75. data/app/stylesheets/alchemy/admin/elements.scss +95 -34
  76. data/app/stylesheets/alchemy/admin/filters.scss +8 -9
  77. data/app/stylesheets/alchemy/admin/flatpickr.scss +12 -27
  78. data/app/stylesheets/alchemy/admin/form_fields.scss +0 -15
  79. data/app/stylesheets/alchemy/admin/forms.scss +3 -8
  80. data/app/stylesheets/alchemy/admin/frame.scss +5 -7
  81. data/app/stylesheets/alchemy/admin/icons.scss +0 -9
  82. data/app/stylesheets/alchemy/admin/image_library.scss +13 -55
  83. data/app/stylesheets/alchemy/admin/navigation.scss +1 -11
  84. data/app/stylesheets/alchemy/admin/node-select.scss +1 -10
  85. data/app/stylesheets/alchemy/admin/nodes.scss +6 -2
  86. data/app/stylesheets/alchemy/admin/notices.scss +5 -4
  87. data/app/stylesheets/alchemy/admin/page-select.scss +16 -0
  88. data/app/stylesheets/alchemy/admin/pagination.scss +1 -8
  89. data/app/stylesheets/alchemy/admin/preview_window.scss +12 -1
  90. data/app/stylesheets/alchemy/admin/resource_info.scss +106 -3
  91. data/app/stylesheets/alchemy/admin/search.scss +1 -1
  92. data/app/stylesheets/alchemy/admin/selects.scss +58 -31
  93. data/app/stylesheets/alchemy/admin/shoelace.scss +32 -62
  94. data/app/stylesheets/alchemy/admin/sitemap.scss +7 -18
  95. data/app/stylesheets/alchemy/admin/tables.scss +3 -3
  96. data/app/stylesheets/alchemy/admin/tags.scss +18 -35
  97. data/app/stylesheets/alchemy/admin/toolbar.scss +0 -6
  98. data/app/stylesheets/alchemy/admin/typography.scss +2 -5
  99. data/app/stylesheets/alchemy/admin.scss +1 -1
  100. data/app/stylesheets/alchemy/dark-theme.scss +5 -0
  101. data/app/stylesheets/alchemy/light-theme.scss +6 -0
  102. data/app/stylesheets/alchemy/theme.scss +13 -0
  103. data/app/stylesheets/tinymce/skins/content/alchemy/content.scss +8 -8
  104. data/app/stylesheets/tinymce/skins/content/alchemy-dark/content.scss +70 -0
  105. data/app/stylesheets/tinymce/skins/ui/alchemy/skin.scss +28 -43
  106. data/app/stylesheets/tinymce/skins/ui/alchemy-dark/content.scss +1 -0
  107. data/app/stylesheets/tinymce/skins/ui/alchemy-dark/skin.scss +3784 -0
  108. data/app/views/alchemy/admin/attachments/_files_list.html.erb +20 -10
  109. data/app/views/alchemy/admin/attachments/assign.js.erb +4 -3
  110. data/app/views/alchemy/admin/attachments/show.html.erb +55 -43
  111. data/app/views/alchemy/admin/crop.html.erb +1 -1
  112. data/app/views/alchemy/admin/dashboard/index.html.erb +1 -1
  113. data/app/views/alchemy/admin/dashboard/info.html.erb +36 -6
  114. data/app/views/alchemy/admin/elements/_form.html.erb +9 -9
  115. data/app/views/alchemy/admin/elements/_header.html.erb +12 -10
  116. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +1 -1
  117. data/app/views/alchemy/admin/nodes/_form.html.erb +5 -1
  118. data/app/views/alchemy/admin/pages/info.html.erb +1 -1
  119. data/app/views/alchemy/admin/partials/_search_form.html.erb +1 -0
  120. data/app/views/alchemy/admin/pictures/_archive.html.erb +13 -23
  121. data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +1 -6
  122. data/app/views/alchemy/admin/pictures/_form.html.erb +10 -5
  123. data/app/views/alchemy/admin/pictures/_infos.html.erb +21 -52
  124. data/app/views/alchemy/admin/pictures/_library_sidebar.html.erb +7 -0
  125. data/app/views/alchemy/admin/pictures/_picture.html.erb +15 -16
  126. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +20 -16
  127. data/app/views/alchemy/admin/pictures/_sorting_select.html.erb +13 -0
  128. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -1
  129. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +1 -6
  130. data/app/views/alchemy/admin/pictures/index.html.erb +3 -12
  131. data/app/views/alchemy/admin/pictures/show.html.erb +17 -14
  132. data/app/views/alchemy/admin/pictures/update.turbo_stream.erb +1 -1
  133. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +5 -15
  134. data/app/views/alchemy/admin/resources/_resource_usage_info.html.erb +36 -0
  135. data/app/views/alchemy/admin/styleguide/index.html.erb +118 -66
  136. data/app/views/alchemy/admin/uploader/_button.html.erb +1 -1
  137. data/app/views/alchemy/base/error_notice.html.erb +1 -1
  138. data/app/views/alchemy/ingredients/_page_editor.html.erb +0 -1
  139. data/app/views/alchemy/ingredients/_richtext_editor.html.erb +0 -1
  140. data/app/views/alchemy/ingredients/_select_editor.html.erb +1 -2
  141. data/app/views/layouts/alchemy/admin.html.erb +25 -23
  142. data/config/locales/alchemy.en.yml +26 -8
  143. data/db/migrate/20250905140323_add_created_at_index_to_pictures_and_attachments.rb +14 -0
  144. data/lib/alchemy/configuration/base_option.rb +18 -5
  145. data/lib/alchemy/configuration/boolean_option.rb +2 -5
  146. data/lib/alchemy/configuration/collection_option.rb +69 -0
  147. data/lib/alchemy/configuration/configuration_option.rb +35 -0
  148. data/lib/alchemy/configuration/pathname_option.rb +12 -0
  149. data/lib/alchemy/configuration.rb +44 -6
  150. data/lib/alchemy/configurations/format_matchers.rb +1 -1
  151. data/lib/alchemy/configurations/importmap.rb +11 -0
  152. data/lib/alchemy/configurations/mailer.rb +2 -2
  153. data/lib/alchemy/configurations/main.rb +148 -3
  154. data/lib/alchemy/configurations/page_cache.rb +19 -0
  155. data/lib/alchemy/configurations/uploader.rb +2 -2
  156. data/lib/alchemy/deprecation.rb +1 -1
  157. data/lib/alchemy/engine.rb +43 -21
  158. data/lib/alchemy/install/tasks.rb +0 -12
  159. data/lib/alchemy/name_conversions.rb +6 -0
  160. data/lib/alchemy/tasks/tidy.rb +18 -0
  161. data/lib/alchemy/test_support/config_stubbing.rb +13 -4
  162. data/lib/alchemy/test_support/factories/language_factory.rb +8 -4
  163. data/lib/alchemy/test_support/factories/page_factory.rb +1 -0
  164. data/lib/alchemy/test_support/factories/picture_factory.rb +1 -0
  165. data/lib/alchemy/test_support/relatable_resource_examples.rb +58 -0
  166. data/lib/alchemy/tinymce.rb +0 -1
  167. data/lib/alchemy/version.rb +1 -1
  168. data/lib/alchemy.rb +18 -171
  169. data/lib/generators/alchemy/install/install_generator.rb +21 -10
  170. data/lib/generators/alchemy/install/templates/alchemy.rb.tt +88 -13
  171. data/lib/tasks/alchemy/assets.rake +1 -1
  172. data/lib/tasks/alchemy/tidy.rake +6 -0
  173. data/lib/tasks/alchemy/usage.rake +2 -0
  174. data/vendor/assets/stylesheets/tinymce/skins/content/dark/content.min.css +1 -0
  175. data/vendor/assets/stylesheets/tinymce/skins/content/default/content.min.css +1 -0
  176. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide/skin.min.css +1 -0
  177. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide-dark/content.min.css +1 -0
  178. data/vendor/assets/stylesheets/tinymce/skins/ui/oxide-dark/skin.min.css +1 -0
  179. data/vendor/javascript/clipboard.min.js +1 -1
  180. data/vendor/javascript/cropperjs.min.js +1 -1
  181. data/vendor/javascript/handlebars.min.js +3 -3
  182. data/vendor/javascript/jquery.min.js +1 -1
  183. data/vendor/javascript/select2.min.js +3 -3
  184. data/vendor/javascript/shoelace.min.js +92 -76
  185. data/vendor/javascript/sortable.min.js +2 -2
  186. data/vendor/javascript/tinymce.min.js +1 -1
  187. data/vendor/javascript/ungap-custom-elements.min.js +2 -2
  188. metadata +51 -36
  189. data/CHANGELOG.md +0 -2100
  190. data/CODE_OF_CONDUCT.md +0 -13
  191. data/CONTRIBUTING.md +0 -73
  192. data/Gemfile +0 -78
  193. data/Rakefile +0 -102
  194. data/SECURITY.md +0 -13
  195. data/alchemy_cms.gemspec +0 -97
  196. data/app/assets/builds/alchemy/custom-properties.css +0 -1
  197. data/app/helpers/alchemy/admin/elements_helper.rb +0 -25
  198. data/app/stylesheets/alchemy/custom-properties.css +0 -244
  199. data/bin/importmap +0 -4
  200. data/bin/rails +0 -9
  201. data/bin/rspec +0 -3
  202. data/bin/setup +0 -30
  203. data/bin/start +0 -17
  204. data/bun.lockb +0 -0
  205. data/bundles/remixicon.mjs +0 -153
  206. data/bundles/shoelace.js +0 -12
  207. data/bundles/tinymce.js +0 -22
  208. data/eslint.config.js +0 -18
  209. data/lib/alchemy/configuration/class_set_option.rb +0 -46
  210. data/lib/alchemy/configuration/integer_list_option.rb +0 -13
  211. data/lib/alchemy/configuration/list_option.rb +0 -22
  212. data/lib/alchemy/configuration/string_list_option.rb +0 -13
  213. data/lib/alchemy/upgrader/.keep +0 -0
  214. data/lib/alchemy/upgrader/tasks/.keep +0 -0
  215. data/rollup.config.mjs +0 -108
  216. data/vitest.config.js +0 -21
@@ -0,0 +1,13 @@
1
+ <div id="sorting_select">
2
+ <div class="filter-input">
3
+ <alchemy-auto-submit>
4
+ <%= label_tag "q[s]", t(".label") %>
5
+ <%= select_tag "q[s]", options_for_select([
6
+ [t(".by_latest"), "created_at desc"],
7
+ [t(".alphabetical"), "name asc"]
8
+ ], search_filter_params.dig(:q, :s)),
9
+ form: "resource_search",
10
+ class: "full_width" %>
11
+ </alchemy-auto-submit>
12
+ </div>
13
+ </div>
@@ -1,5 +1,5 @@
1
1
  <% if Alchemy::Picture.tag_counts.any? %>
2
- <h3><%= Alchemy.t("Filter by tag") %></h3>
2
+ <label><%= Alchemy.t("Filter by tag") %></label>
3
3
  <%= render Alchemy::Admin::ListFilter.new(".tag-list li") %>
4
4
  <ul>
5
5
  <%= render_tag_list('Alchemy::Picture') %>
@@ -1,9 +1,4 @@
1
- <%= form_tag update_multiple_admin_pictures_path do %>
2
- <%= hidden_field_tag :q, search_filter_params[:q] %>
3
- <%= hidden_field_tag :size, @size %>
4
- <%= hidden_field_tag :tagged_with, search_filter_params[:tagged_with] %>
5
- <%= hidden_field_tag :filter, search_filter_params[:filter] %>
6
-
1
+ <%= form_tag update_multiple_admin_pictures_path(search_filter_params.merge(size: @size.presence)) do %>
7
2
  <% @pictures.pluck(:id).each do |id| %>
8
3
  <%= hidden_field_tag "picture_ids[]", id %>
9
4
  <% end %>
@@ -17,10 +17,7 @@
17
17
  <%= link_to(
18
18
  render_icon('zoom-out'),
19
19
  alchemy.admin_pictures_path(
20
- size: "small",
21
- q: search_filter_params[:q],
22
- filter: search_filter_params[:filter],
23
- tagged_with: search_filter_params[:tagged_with]
20
+ search_filter_params.merge(size: "small").to_h
24
21
  ),
25
22
  class: "icon_button"
26
23
  ) %>
@@ -29,10 +26,7 @@
29
26
  <%= link_to(
30
27
  render_icon('search'),
31
28
  alchemy.admin_pictures_path(
32
- size: "medium",
33
- q: search_filter_params[:q],
34
- filter: search_filter_params[:filter],
35
- tagged_with: search_filter_params[:tagged_with]
29
+ search_filter_params.merge(size: "medium").to_h
36
30
  ),
37
31
  class: "icon_button"
38
32
  ) %>
@@ -41,10 +35,7 @@
41
35
  <%= link_to(
42
36
  render_icon('zoom-in'),
43
37
  alchemy.admin_pictures_path(
44
- size: "large",
45
- q: search_filter_params[:q],
46
- filter: search_filter_params[:filter],
47
- tagged_with: search_filter_params[:tagged_with]
38
+ search_filter_params.merge(size: "large").to_h
48
39
  ),
49
40
  class: "icon_button"
50
41
  ) %>
@@ -1,16 +1,19 @@
1
1
  <div class="zoomed-picture-background">
2
- <%= image_tag @picture.url || "alchemy/missing-image.svg" %>
2
+ <% if @picture.image_file %>
3
+ <%= image_tag @picture.url %>
4
+ <% else %>
5
+ <alchemy-icon name="file-damage"></alchemy-icon>
6
+ <% end %>
3
7
  </div>
4
8
 
5
9
  <div class="picture-overlay-navigation">
6
10
  <% if @previous %>
7
11
  <%= link_to alchemy.admin_picture_path(
8
- id: @previous,
9
- q: search_filter_params[:q],
10
- page: params[:page],
11
- tagged_with: search_filter_params[:tagged_with],
12
- size: @size,
13
- filter: search_filter_params[:filter]
12
+ :previous,
13
+ search_filter_params.merge(
14
+ picture_index: @previous,
15
+ size: @size
16
+ ).to_h
14
17
  ),
15
18
  class: "previous-picture",
16
19
  remote: true do %>
@@ -19,12 +22,11 @@
19
22
  <% end %>
20
23
  <% if @next %>
21
24
  <%= link_to alchemy.admin_picture_path(
22
- id: @next,
23
- q: search_filter_params[:q],
24
- page: params[:page],
25
- tagged_with: search_filter_params[:tagged_with],
26
- size: @size,
27
- filter: search_filter_params[:filter]
25
+ :next,
26
+ search_filter_params.merge(
27
+ picture_index: @next,
28
+ size: @size
29
+ ).to_h
28
30
  ),
29
31
  class: "next-picture",
30
32
  remote: true do %>
@@ -34,7 +36,8 @@
34
36
  </div>
35
37
 
36
38
  <div class="picture-details-overlay">
37
- <%= render 'form' %>
39
+ <%= render 'form', picture_index: @previous || @next || 0 %>
40
+ <hr>
38
41
  <%= render 'infos' %>
39
42
  </div>
40
43
 
@@ -2,5 +2,5 @@
2
2
  <%= @message[:body] %>
3
3
  </alchemy-growl>
4
4
  <%= turbo_stream.replace("picture_#{@picture.id}") do %>
5
- <%= render "picture", picture: @picture %>
5
+ <%= render "picture", picture: @picture, picture_index: params[:picture_index] %>
6
6
  <% end %>
@@ -1,17 +1,7 @@
1
1
  <div id="filter_bar">
2
- <% alchemy_filters.each do |filter| %>
3
- <%= render filter.input_component(search_filter_params, @query) %>
4
- <% end %>
2
+ <alchemy-auto-submit>
3
+ <% alchemy_filters.each do |filter| %>
4
+ <%= render filter.input_component(search_filter_params, @query) %>
5
+ <% end %>
6
+ </alchemy-auto-submit>
5
7
  </div>
6
-
7
- <script type="module">
8
- // Still using jQuery here, because select2 does emit the event from
9
- // the correct element.
10
- $('#filter_bar').on('change', function(event) {
11
- // We need to dispatch a submit event, so that Turbo that listens
12
- // to it submits the search form us.
13
- const submitEvent = new Event("submit", { bubbles: true, cancelable: true });
14
- event.target.form.dispatchEvent(submitEvent);
15
- return false;
16
- });
17
- </script>
@@ -0,0 +1,36 @@
1
+ <div class="resource-usage-info">
2
+ <% if assignments.any? %>
3
+ <alchemy-message type="info">
4
+ <%= Alchemy.t(resource_name, scope: :used_on_these_pages) %>
5
+ </alchemy-message>
6
+ <ul class="resource_page_list">
7
+ <% assignments.group_by(&:page).each do |page, related_ingredients| %>
8
+ <% if page %>
9
+ <li>
10
+ <h3>
11
+ <%= render_icon 'file' %>
12
+ <p><%= link_to page.name, edit_admin_page_path(page) %></p>
13
+ </h3>
14
+ <ul class="list">
15
+ <% related_ingredients.group_by(&:element).each do |element, related_ingredients| %>
16
+ <li class="<%= cycle('even', 'odd') %>">
17
+ <% page_link = link_to element.display_name_with_preview_text,
18
+ edit_admin_page_path(page, anchor: "element_#{element.id}") %>
19
+ <% ingredients = related_ingredients.map { |p| Alchemy::IngredientEditor.new(p).translated_role }.to_sentence %>
20
+ <%= render_icon(:draggable, style: false) %>
21
+ <p>
22
+ <%== Alchemy.t(:pictures_in_page, page: page_link, pictures: ingredients) %>
23
+ </p>
24
+ </li>
25
+ <% end %>
26
+ </ul>
27
+ </li>
28
+ <% end %>
29
+ <% end %>
30
+ </ul>
31
+ <% else %>
32
+ <%= render_message do %>
33
+ <%= Alchemy.t(resource_name, scope: :not_in_use_yet) %>
34
+ <% end %>
35
+ <% end %>
36
+ </div>
@@ -19,71 +19,6 @@
19
19
 
20
20
  <h1>AlchemyCMS Styleguide</h1>
21
21
 
22
- <h2>Typography</h2>
23
-
24
- <h1>Heading 1</h1>
25
- <h2>Heading 2</h2>
26
- <h3>Heading 3</h3>
27
- <h4>Heading 4</h4>
28
- <h5>Heading 5</h5>
29
-
30
- <p>This is a paragraph, with <a href="#">a link</a>, a <kbd>keyboard shortcut</kbd> and an <code>inline code snippet</code>.</p>
31
-
32
- <h2>Messages</h2>
33
-
34
- <h3>Info Message</h3>
35
-
36
- <%= render_message do %>
37
- <p>Lorem ipsum <strong>dolor</strong> sit amet, consectetur adipiscing elit.</p>
38
- <% end %>
39
-
40
- <h3>Warning Message</h3>
41
-
42
- <%= render_message :warning do %>
43
- <p>Lorem ipsum dolor sit amet, <a href="">consectetur</a> adipiscing elit.</p>
44
- <% end %>
45
-
46
- <h3>Error Message</h3>
47
-
48
- <%= render_message :error do %>
49
- <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
50
- <% end %>
51
-
52
- <h3>Tags Autocomplete</h3>
53
-
54
- <div>
55
- <alchemy-tags-autocomplete>
56
- <input type="text" class="full_width" />
57
- </alchemy-tags-autocomplete>
58
- </div>
59
-
60
- <h3>Growl Messages</h3>
61
-
62
- <%= link_to render_icon(:check), 'javascript:Alchemy.growl("Success message")', class: "icon_button" %>
63
- <%= link_to render_icon(:info), 'javascript:Alchemy.growl("Info message", "info")', class: "icon_button" %>
64
- <%= link_to render_icon(:alert), 'javascript:Alchemy.growl("Warning message", "warn")', class: "icon_button" %>
65
- <%= link_to render_icon(:bug), 'javascript:Alchemy.growl("Error message", "error")', class: "icon_button" %>
66
-
67
- <h2>Dialog Links</h2>
68
-
69
- <sl-tooltip content="Open in Dialog">
70
- <a
71
- href="/admin/styleguide"
72
- is="alchemy-dialog-link"
73
- class="icon_button"
74
- data-dialog-options="{&quot;size&quot;:&quot;800x600&quot;,&quot;title&quot;:&quot;Styleguide in Dialog&quot;}"
75
- >
76
- <%= render_icon "window" %>
77
- </a>
78
- </sl-tooltip>
79
- <a
80
- href="#"
81
- is="alchemy-dialog-link"
82
- class="icon_button disabled"
83
- >
84
- <%= render_icon "window" %>
85
- </a>
86
-
87
22
  <h2>Forms</h2>
88
23
 
89
24
  <div class="panel">
@@ -102,6 +37,13 @@
102
37
  </select>
103
38
  </div>
104
39
 
40
+ <div class="input select">
41
+ <label class="select control-label" for="select">Disabled Select</label>
42
+ <select class="select" id="disabled-select" is="alchemy-select" disabled>
43
+ <option value="">Cannot choose</option>
44
+ </select>
45
+ </div>
46
+
105
47
  <div class="input">
106
48
  <label class="control-label" for="datetime_picker">Date Time Picker</label>
107
49
  <alchemy-datepicker input-type="datetime">
@@ -171,7 +113,7 @@
171
113
  <div class="input text">
172
114
  <label class="text control-label" for="textarea">Textarea</label>
173
115
  <textarea class="text" id="textarea"></textarea>
174
- <small class="hint">With hint</small>
116
+ <small class="hint">With hint and a <a href="/admin/styleguide">link</a> inside.</small>
175
117
  </div>
176
118
 
177
119
  <div class="input text tinymce_container">
@@ -188,6 +130,116 @@
188
130
  </form>
189
131
  </div>
190
132
 
133
+ <h2>Buttons</h2>
134
+
135
+ <p>
136
+ <button class="secondary" disabled>Disabled Secondary</button>
137
+ <button type="submit" is="alchemy-button" disabled>Disabled Primary</button>
138
+ </p>
139
+
140
+ <h2>Selects</h2>
141
+
142
+ <div style="display: flex; gap: 1em;">
143
+ <div style="width: 25%">
144
+ <h3>Select with Search</h3>
145
+ <select is="alchemy-select" class="full_width">
146
+ <option value="1">Option 1</option>
147
+ <option value="2">Option 2</option>
148
+ <option value="3">Option 3</option>
149
+ <option value="4">Option 4</option>
150
+ <option value="5">Option 5</option>
151
+ <option value="6">Option 6</option>
152
+ <option value="7">Option 7</option>
153
+ </select>
154
+ </div>
155
+
156
+ <div style="width: 25%">
157
+ <h3>Page Select</h3>
158
+ <%= render Alchemy::Admin::PageSelect.new do %>
159
+ <input name="page" class="full_width" />
160
+ <% end %>
161
+ </div>
162
+
163
+ <div style="width: 25%">
164
+ <h3>Node Select</h3>
165
+ <%= render Alchemy::Admin::NodeSelect.new do %>
166
+ <input name="node" class="full_width" />
167
+ <% end %>
168
+ </div>
169
+
170
+ <div style="width: 25%">
171
+ <h3>Attachment Select</h3>
172
+ <%= render Alchemy::Admin::AttachmentSelect.new do %>
173
+ <input name="attachment" class="full_width" />
174
+ <% end %>
175
+ </div>
176
+ </div>
177
+
178
+ <h3>Tags Autocomplete</h3>
179
+
180
+ <div style="width: 25%">
181
+ <alchemy-tags-autocomplete>
182
+ <input type="text" class="full_width" />
183
+ </alchemy-tags-autocomplete>
184
+ </div>
185
+
186
+ <h2>Typography</h2>
187
+
188
+ <h1>Heading 1</h1>
189
+ <h2>Heading 2</h2>
190
+ <h3>Heading 3</h3>
191
+ <h4>Heading 4</h4>
192
+ <h5>Heading 5</h5>
193
+
194
+ <p>This is a paragraph, with <a href="#">a link</a>, a <kbd>keyboard shortcut</kbd> and an <code>inline code snippet</code>.</p>
195
+
196
+ <h2>Messages</h2>
197
+
198
+ <h3>Info Message</h3>
199
+
200
+ <%= render_message do %>
201
+ <p>Lorem ipsum <strong>dolor</strong> sit amet, <a href="">consectetur</a> adipiscing elit.</p>
202
+ <% end %>
203
+
204
+ <h3>Warning Message</h3>
205
+
206
+ <%= render_message :warning do %>
207
+ <p>Lorem ipsum dolor sit amet, <a href="">consectetur</a> adipiscing elit.</p>
208
+ <% end %>
209
+
210
+ <h3>Error Message</h3>
211
+
212
+ <%= render_message :error do %>
213
+ <p>Lorem ipsum dolor sit amet, <a href="">consectetur</a> adipiscing elit.</p>
214
+ <% end %>
215
+
216
+ <h3>Growl Messages</h3>
217
+
218
+ <%= link_to render_icon(:check), 'javascript:Alchemy.growl("Success message")', class: "icon_button" %>
219
+ <%= link_to render_icon(:info), 'javascript:Alchemy.growl("Info message", "info")', class: "icon_button" %>
220
+ <%= link_to render_icon(:alert), 'javascript:Alchemy.growl("Warning message", "warn")', class: "icon_button" %>
221
+ <%= link_to render_icon(:bug), 'javascript:Alchemy.growl("Error message", "error")', class: "icon_button" %>
222
+
223
+ <h2>Dialog Links</h2>
224
+
225
+ <sl-tooltip content="Open in Dialog">
226
+ <a
227
+ href="/admin/styleguide"
228
+ is="alchemy-dialog-link"
229
+ class="icon_button"
230
+ data-dialog-options="{&quot;size&quot;:&quot;800x600&quot;,&quot;title&quot;:&quot;Styleguide in Dialog&quot;}"
231
+ >
232
+ <%= render_icon "window" %>
233
+ </a>
234
+ </sl-tooltip>
235
+ <a
236
+ href="#"
237
+ is="alchemy-dialog-link"
238
+ class="icon_button disabled"
239
+ >
240
+ <%= render_icon "window" %>
241
+ </a>
242
+
191
243
  <h2>Tables</h2>
192
244
 
193
245
  <h3>Normal Table</h3>
@@ -1,5 +1,5 @@
1
1
  <% file_types = Alchemy.config.uploader.allowed_filetypes[object.class.model_name.collection] || ['*'] %>
2
- <% accept = file_types == ["*"] ? false : file_types.map {|type| ".#{type}"}.join(", ") %>
2
+ <% accept = file_types.to_a == ["*"] ? nil : file_types.map {|type| ".#{type}"}.join(", ") %>
3
3
 
4
4
  <alchemy-uploader dropzone="<%= local_assigns[:dropzone] || "#main_content" %>">
5
5
  <%= form_for [:admin, object], html: { multipart: true, class: 'upload-button' } do |f| %>
@@ -1,3 +1,3 @@
1
1
  <%= render_message :error do %>
2
- <strong><%= @notice %></strong>
2
+ <%= @notice %>
3
3
  <% end %>
@@ -7,7 +7,6 @@
7
7
  <%= f.text_field :page_id,
8
8
  value: page_editor.page&.id,
9
9
  id: page_editor.form_field_id(:page_id),
10
- required: page_editor.presence_validation?,
11
10
  class: 'full_width' %>
12
11
  <% end %>
13
12
  <% end %>
@@ -9,7 +9,6 @@
9
9
  <%= f.text_area :value,
10
10
  minlength: richtext_editor.length_validation&.fetch(:minimum, nil),
11
11
  maxlength: richtext_editor.length_validation&.fetch(:maximum, nil),
12
- required: richtext_editor.presence_validation?,
13
12
  id: richtext_editor.form_field_id(:value) %>
14
13
  <% end %>
15
14
  <% end %>
@@ -24,8 +24,7 @@
24
24
  <%= f.select :value, options_tags, {}, {
25
25
  id: select_editor.form_field_id,
26
26
  class: ["ingredient-editor-select"],
27
- is: "alchemy-select",
28
- required: select_editor.presence_validation?
27
+ is: "alchemy-select"
29
28
  } %>
30
29
  <% end %>
31
30
  <% end %>
@@ -1,8 +1,9 @@
1
1
  <!DOCTYPE html>
2
- <html lang="<%= ::I18n.locale %>" class="no-js loading-custom-elements alchemy-light">
2
+ <html lang="<%= ::I18n.locale %>" class="no-js loading-custom-elements">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="color-scheme" content="light dark">
6
7
  <title><%= render_alchemy_title %></title>
7
8
  <link rel="shortcut icon" href="<%= asset_path('alchemy/favicon.ico') %>">
8
9
  <link rel="preload" href="<%= asset_path("alchemy/icons-sprite.svg") %>" as="image" type="<%= Mime::Type.lookup_by_extension(:svg) %>" crossorigin>
@@ -10,9 +11,10 @@
10
11
  <meta name="robots" content="noindex">
11
12
  <meta name="turbo-prefetch" content="false">
12
13
  <meta name="turbo-cache-control" content="no-cache">
14
+ <%= stylesheet_link_tag('alchemy/theme', media: 'screen', 'data-turbo-track' => true) %>
13
15
  <%= stylesheet_link_tag('alchemy/admin', media: 'screen', 'data-turbo-track' => true) %>
14
16
  <%= stylesheet_link_tag('alchemy/admin/print', media: 'print', 'data-turbo-track' => true) %>
15
- <% Alchemy.admin_stylesheets.each do |stylesheet| %>
17
+ <% Alchemy.config.admin_stylesheets.each do |stylesheet| %>
16
18
  <%= stylesheet_link_tag(stylesheet, 'data-turbo-track' => true) %>
17
19
  <% end %>
18
20
  <%= yield :stylesheets %>
@@ -27,7 +29,7 @@
27
29
  <%= render 'alchemy/admin/tinymce/setup' %>
28
30
  <%= render 'alchemy/admin/partials/routes' %>
29
31
  <%= javascript_importmap_tags("alchemy_admin", importmap: Alchemy.importmap) %>
30
- <% Alchemy.admin_js_imports.each do |path| %>
32
+ <% Alchemy.config.admin_js_imports.each do |path| %>
31
33
  <script type="module">
32
34
  import "<%= path %>"
33
35
  </script>
@@ -49,23 +51,26 @@
49
51
  <% end %>
50
52
  <%= yield(:alchemy_main_navigation) %>
51
53
  </div>
52
- <div id="logout" class="main_navi_entry">
53
- <% if current_alchemy_user %>
54
- <%= link_to_dialog(
55
- %(
56
- #{render_icon('logout-box-r', class: 'module')}
57
- <label>#{Alchemy.t(:leave)}</label>
58
- ).html_safe,
59
- alchemy.leave_admin_path, {
60
- size: "300x155",
61
- title: Alchemy.t("Leave Alchemy")
62
- }, {'data-alchemy-hotkey' => 'alt+q'}) %>
63
- <% else %>
64
- <%= link_to(alchemy.root_path) do %>
65
- <%= render_icon "logout-box-r", size: "lg" %>
66
- <label><%= Alchemy.t(:leave) %></label>
54
+
55
+ <div id="logout">
56
+ <div class="main_navi_entry">
57
+ <% if current_alchemy_user %>
58
+ <%= link_to_dialog(
59
+ %(
60
+ #{render_icon('logout-box-r', class: 'module')}
61
+ <label>#{Alchemy.t(:leave)}</label>
62
+ ).html_safe,
63
+ alchemy.leave_admin_path, {
64
+ size: "300x155",
65
+ title: Alchemy.t("Leave Alchemy")
66
+ }, {'data-alchemy-hotkey' => 'alt+q'}) %>
67
+ <% else %>
68
+ <%= link_to(alchemy.root_path) do %>
69
+ <%= render_icon "logout-box-r", size: "lg" %>
70
+ <label><%= Alchemy.t(:leave) %></label>
71
+ <% end %>
67
72
  <% end %>
68
- <% end %>
73
+ </div>
69
74
  </div>
70
75
  </div>
71
76
  <% if current_alchemy_user %>
@@ -78,10 +83,7 @@
78
83
  <% end %>
79
84
  <div id="user_info">
80
85
  <%= current_alchemy_user_name %>
81
- <% if Alchemy::I18n.available_locales.length > 1 %>
82
- <%= select_tag 'change_locale',
83
- options_for_select(translations_for_select, ::I18n.locale) %>
84
- <% end %>
86
+ <%= render Alchemy::Admin::LocaleSelect.new %>
85
87
  </div>
86
88
  </div>
87
89
  <div id="toolbar">
@@ -143,7 +143,7 @@ en:
143
143
  without_tag:
144
144
  name: Without tag
145
145
  deletable:
146
- name: Not linked by file content
146
+ name: Not used
147
147
  attachment:
148
148
  by_file_type:
149
149
  name: File Type
@@ -156,6 +156,8 @@ en:
156
156
  name: Recently uploaded only
157
157
  without_tag:
158
158
  name: Without tag
159
+ deletable:
160
+ name: Not used
159
161
 
160
162
  default_ingredient_texts:
161
163
  lorem: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
@@ -224,6 +226,11 @@ en:
224
226
  elements:
225
227
  toolbar:
226
228
  hide: Hide
229
+ pictures:
230
+ sorting_select:
231
+ label: "Sorting"
232
+ by_latest: "Latest"
233
+ alphabetical: "A-Z"
227
234
  add_nested_element: "Add %{name}"
228
235
  anchor: "Anchor"
229
236
  anchor_link_headline: You can link to an anchor from the current page.
@@ -333,7 +340,6 @@ en:
333
340
  "Size": "Size"
334
341
  "Successfully deleted element": "Successfully deleted %{element}"
335
342
  "Tags": "Tags"
336
- "These pictures could not be deleted, because they were in use": "These pictures could not be deleted, because they were in use: %{names}"
337
343
  "This page is locked": "This page is currently locked by %{name}"
338
344
  "Title": "Title"
339
345
  "Trash": "Trash"
@@ -561,6 +567,7 @@ en:
561
567
  password: "Password"
562
568
  paste: "paste"
563
569
  pictures_in_page: "%{page} in %{pictures}"
570
+ pictures_will_be_deleted_now: Pictures will be deleted now
564
571
  place_link: "Add link"
565
572
  player_version: "Flash Player version"
566
573
  "please enter subject and mail address": "Please enter recipient and subject."
@@ -621,6 +628,12 @@ en:
621
628
  tag_list: Tags
622
629
  tags_get_created_if_used_the_first_time: "Tags get created if used the first time."
623
630
  this_picture_is_used_on_these_pages: "This picture is used on following pages"
631
+ used_on_these_pages:
632
+ attachment: "This file is used on following pages"
633
+ picture: "This picture is used on following pages"
634
+ not_in_use_yet:
635
+ attachment: "This file is not in use yet."
636
+ picture: "This picture is not in use yet."
624
637
  title: "Title"
625
638
  to_alchemy: "To Alchemy"
626
639
  unknown: "unknown"
@@ -773,16 +786,19 @@ en:
773
786
  node_ingredients_present: "This menu item is in use inside an Alchemy element on the following pages: %{page_names}."
774
787
  alchemy/site:
775
788
  attributes:
776
- languages:
777
- still_present: "are still attached to this site. Please remove them first."
789
+ base:
790
+ restrict_dependent_destroy:
791
+ has_many: "There are still %{record} attached to this site. Please remove them first."
778
792
  alchemy/language:
779
793
  attributes:
780
- pages:
781
- still_present: "are still attached to this language. Please remove them first."
794
+ base:
795
+ restrict_dependent_destroy:
796
+ has_many: "There are still %{record} attached to this language. Please remove them first."
782
797
  alchemy/page:
783
798
  attributes:
784
- nodes:
785
- still_present: "are still attached to this page. Please remove them first."
799
+ base:
800
+ restrict_dependent_destroy:
801
+ has_many: "There are still %{record} attached to this page. Please remove them first."
786
802
  models:
787
803
  gutentag/tag:
788
804
  one: Tag
@@ -869,6 +885,7 @@ en:
869
885
  public: "Public"
870
886
  locale: Localization
871
887
  code: ISO Code
888
+ nodes: Menu nodes
872
889
  alchemy/legacy_page_url:
873
890
  urlname: "URL-Path"
874
891
  alchemy/node:
@@ -887,6 +904,7 @@ en:
887
904
  meta_description: "Description"
888
905
  meta_keywords: "Keywords"
889
906
  name: "Name"
907
+ nodes: "Menu nodes"
890
908
  page_layout: "Page type"
891
909
  public: "public"
892
910
  restricted: "restricted"
@@ -0,0 +1,14 @@
1
+ class AddCreatedAtIndexToPicturesAndAttachments < ActiveRecord::Migration[7.1]
2
+ disable_ddl_transaction! if connection.adapter_name.match?(/postgres/i)
3
+
4
+ def change
5
+ add_index :alchemy_pictures, :created_at, if_not_exists: true, algorithm: algorithm
6
+ add_index :alchemy_attachments, :created_at, if_not_exists: true, algorithm: algorithm
7
+ end
8
+
9
+ private
10
+
11
+ def algorithm
12
+ connection.adapter_name.match?(/postgres/i) ? :concurrently : nil
13
+ end
14
+ end