alchemy_cms 7.3.4 → 7.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -1
  3. data/Gemfile +10 -3
  4. data/README.md +2 -2
  5. data/Rakefile +2 -0
  6. data/alchemy_cms.gemspec +1 -4
  7. data/app/assets/builds/alchemy/admin.css +9 -1
  8. data/app/assets/builds/alchemy/admin.css.map +1 -1
  9. data/app/assets/builds/alchemy/custom-properties.css +1 -1
  10. data/app/assets/builds/alchemy/custom-properties.css.map +1 -1
  11. data/app/assets/builds/alchemy/preview.min.js +1 -0
  12. data/app/assets/builds/alchemy/welcome.css +1 -1
  13. data/app/assets/builds/alchemy/welcome.css.map +1 -1
  14. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
  15. data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css.map +1 -1
  16. data/app/assets/config/alchemy_manifest.js +0 -4
  17. data/app/assets/javascripts/alchemy/admin.js +8 -6
  18. data/app/assets/stylesheets/alchemy/admin/elements.scss +43 -7
  19. data/app/assets/stylesheets/alchemy/admin/forms.scss +4 -0
  20. data/app/assets/stylesheets/alchemy/admin/image_library.scss +40 -26
  21. data/app/assets/stylesheets/alchemy/admin/navigation.scss +9 -1
  22. data/app/assets/stylesheets/alchemy/admin/preview_window.scss +22 -17
  23. data/app/assets/stylesheets/alchemy/admin.scss +1 -1
  24. data/app/assets/stylesheets/alchemy/custom-properties.css +2 -1
  25. data/app/components/alchemy/ingredients/link_view.rb +7 -1
  26. data/app/components/alchemy/ingredients/picture_view.rb +5 -2
  27. data/app/components/alchemy/ingredients/text_view.rb +4 -1
  28. data/app/components/concerns/alchemy/ingredients/link_target.rb +18 -0
  29. data/app/controllers/alchemy/admin/base_controller.rb +34 -5
  30. data/app/controllers/alchemy/admin/elements_controller.rb +2 -2
  31. data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
  32. data/app/controllers/alchemy/admin/layoutpages_controller.rb +1 -0
  33. data/app/controllers/alchemy/admin/pages_controller.rb +6 -6
  34. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  35. data/app/controllers/alchemy/elements_controller.rb +3 -0
  36. data/app/helpers/alchemy/admin/form_helper.rb +1 -1
  37. data/app/helpers/alchemy/admin/navigation_helper.rb +22 -1
  38. data/app/javascript/alchemy_admin/components/action.js +2 -1
  39. data/app/javascript/alchemy_admin/components/dialog_link.js +3 -18
  40. data/app/javascript/alchemy_admin/components/element_editor.js +9 -0
  41. data/app/javascript/alchemy_admin/components/elements_window.js +34 -0
  42. data/app/javascript/alchemy_admin/components/elements_window_handle.js +65 -0
  43. data/app/javascript/alchemy_admin/components/icon.js +2 -2
  44. data/app/javascript/alchemy_admin/components/index.js +1 -0
  45. data/app/javascript/alchemy_admin/components/preview_window.js +5 -5
  46. data/app/javascript/alchemy_admin/components/uploader/file_upload.js +1 -1
  47. data/app/javascript/alchemy_admin/confirm_dialog.js +9 -11
  48. data/app/javascript/alchemy_admin/dialog.js +329 -0
  49. data/app/javascript/alchemy_admin/hotkeys.js +3 -2
  50. data/app/javascript/alchemy_admin/image_cropper.js +57 -40
  51. data/app/javascript/alchemy_admin/image_overlay.js +73 -0
  52. data/app/javascript/alchemy_admin/initializer.js +51 -2
  53. data/app/javascript/alchemy_admin/link_dialog.js +2 -1
  54. data/app/javascript/alchemy_admin/node_tree.js +3 -1
  55. data/app/javascript/alchemy_admin/page_sorter.js +1 -1
  56. data/app/javascript/alchemy_admin/picture_selector.js +2 -1
  57. data/app/javascript/alchemy_admin/shoelace_theme.js +2 -2
  58. data/app/javascript/alchemy_admin/templates/compiled.js +1 -0
  59. data/app/javascript/alchemy_admin.js +10 -6
  60. data/app/javascript/preview.js +117 -0
  61. data/app/models/alchemy/image_cropper_settings.rb +3 -4
  62. data/app/views/alchemy/_menubar.html.erb +1 -1
  63. data/app/views/alchemy/_preview_mode_code.html.erb +1 -1
  64. data/app/views/alchemy/admin/crop.html.erb +19 -16
  65. data/app/views/alchemy/admin/dashboard/info.html.erb +1 -1
  66. data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +9 -8
  67. data/app/views/alchemy/admin/elements/_clipboard_button.html.erb +14 -0
  68. data/app/views/alchemy/admin/elements/_element.html.erb +2 -0
  69. data/app/views/alchemy/admin/elements/_form.html.erb +15 -13
  70. data/app/views/alchemy/admin/elements/create.turbo_stream.erb +34 -0
  71. data/app/views/alchemy/admin/elements/index.html.erb +3 -15
  72. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +1 -1
  73. data/app/views/alchemy/admin/layoutpages/edit.html.erb +7 -5
  74. data/app/views/alchemy/admin/nodes/_form.html.erb +1 -1
  75. data/app/views/alchemy/admin/pages/_current_page.html.erb +1 -1
  76. data/app/views/alchemy/admin/pages/_form.html.erb +43 -40
  77. data/app/views/alchemy/admin/pages/_locked_page.html.erb +1 -1
  78. data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +1 -1
  79. data/app/views/alchemy/admin/pages/_sitemap.html.erb +1 -1
  80. data/app/views/alchemy/admin/pages/_table.html.erb +2 -2
  81. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  82. data/app/views/alchemy/admin/pages/update.turbo_stream.erb +39 -0
  83. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +3 -4
  84. data/app/views/alchemy/admin/pictures/_picture_description_field.html.erb +7 -5
  85. data/app/views/alchemy/admin/pictures/index.html.erb +13 -9
  86. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +1 -1
  87. data/app/views/layouts/alchemy/admin.html.erb +8 -4
  88. data/bun.lockb +0 -0
  89. data/bundles/tinymce.js +2 -0
  90. data/config/alchemy/config.yml +3 -3
  91. data/config/alchemy/modules.yml +7 -6
  92. data/config/importmap.rb +4 -0
  93. data/config/routes.rb +1 -1
  94. data/lib/alchemy/engine.rb +6 -0
  95. data/lib/alchemy/modules.rb +0 -27
  96. data/lib/alchemy/resource.rb +14 -4
  97. data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +10 -10
  98. data/lib/alchemy/tinymce.rb +2 -1
  99. data/lib/alchemy/upgrader/seven_point_four.rb +26 -0
  100. data/lib/alchemy/version.rb +1 -1
  101. data/lib/alchemy.rb +14 -0
  102. data/lib/alchemy_cms.rb +0 -2
  103. data/lib/generators/alchemy/ingredient/ingredient_generator.rb +5 -0
  104. data/lib/generators/alchemy/ingredient/templates/view.html.erb +1 -1
  105. data/lib/generators/alchemy/ingredient/templates/view_component.rb.tt +10 -0
  106. data/lib/generators/alchemy/install/install_generator.rb +0 -1
  107. data/lib/generators/alchemy/install/templates/elements.yml.tt +1 -1
  108. data/lib/tasks/alchemy/upgrade.rake +19 -20
  109. data/rollup.config.mjs +44 -1
  110. data/vendor/javascript/cropperjs.min.js +10 -0
  111. data/vendor/javascript/handlebars.min.js +29 -0
  112. data/vendor/javascript/jquery.min.js +2 -0
  113. data/vendor/javascript/select2.min.js +23 -0
  114. data/vendor/javascript/tinymce.min.js +1 -1
  115. metadata +40 -94
  116. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +0 -271
  117. data/app/assets/javascripts/alchemy/alchemy.image_overlay.coffee +0 -54
  118. data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +0 -97
  119. data/app/assets/javascripts/alchemy/preview.js +0 -1
  120. data/app/assets/javascripts/alchemy/templates/index.js +0 -2
  121. data/app/javascript/alchemy_admin/gui.js +0 -12
  122. data/app/views/alchemy/admin/elements/create.js.erb +0 -35
  123. data/app/views/alchemy/admin/pages/update.js.erb +0 -43
  124. data/lib/alchemy/upgrader/seven_point_zero.rb +0 -36
  125. data/vendor/assets/images/Jcrop.gif +0 -0
  126. data/vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js +0 -7
  127. data/vendor/assets/javascripts/jquery_plugins/select2.js +0 -3729
  128. data/vendor/assets/stylesheets/jquery.Jcrop.min.css +0 -2
  129. data/vendor/assets/stylesheets/tinymce/skins/content/default/content.min.css +0 -1
  130. /data/app/{assets/javascripts/alchemy → javascript/alchemy_admin}/templates/node_folder.hbs +0 -0
  131. /data/app/{assets/javascripts/alchemy → javascript/alchemy_admin}/templates/page_folder.hbs +0 -0
  132. /data/app/{assets/javascripts/tinymce/icons/remixicons/icons.js → javascript/tinymce/icons/remixicons/index.js} +0 -0
  133. /data/app/{assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js → javascript/tinymce/plugins/alchemy_link/index.js} +0 -0
  134. /data/vendor/assets/{fonts → images}/remixicon.symbol.svg +0 -0
@@ -4,6 +4,7 @@
4
4
  data-element-name="<%= element.name %>"
5
5
  class="<%= element.css_classes.join(" ") %>"
6
6
  <%= element.compact? ? "compact" : nil %>
7
+ <%= local_assigns[:created] ? "created" : nil %>
7
8
  <%= element.fixed? ? "fixed" : nil %>
8
9
  >
9
10
  <% unless element.fixed? %>
@@ -68,6 +69,7 @@
68
69
  <% if element.nestable_elements.any? %>
69
70
  <div class="nestable-elements">
70
71
  <%= content_tag :div,
72
+ id: "element_#{element.id}_nested_elements",
71
73
  class: "nested-elements", data: {
72
74
  'droppable-elements' => element.nestable_elements.join(' '),
73
75
  'element-name' => element.name
@@ -3,18 +3,20 @@
3
3
  <%= Alchemy.t(:no_more_elements_to_add) %>
4
4
  <% end %>
5
5
  <%- else -%>
6
- <%= alchemy_form_for [:admin, @element] do |form| %>
7
- <%= form.hidden_field :page_version_id %>
8
- <%= form.input :name,
9
- label: Alchemy.t(:element_of_type),
10
- collection: elements_for_select(@elements),
11
- prompt: Alchemy.t(:select_element),
12
- selected: (@elements.first if @elements.count == 1),
13
- input_html: {is: 'alchemy-select', autofocus: true} %>
14
- <% if @elements.count == 1 %>
15
- <%= form.hidden_field :name, value: @elements.first[:name] %>
16
- <% end %>
17
- <%= form.hidden_field :parent_element_id, value: @parent_element.try(:id) %>
18
- <%= form.submit Alchemy.t(:add) %>
6
+ <%= turbo_frame_tag @element do %>
7
+ <%= alchemy_form_for [:admin, @element], remote: false do |form| %>
8
+ <%= form.hidden_field :page_version_id %>
9
+ <%= form.input :name,
10
+ label: Alchemy.t(:element_of_type),
11
+ collection: elements_for_select(@elements),
12
+ prompt: Alchemy.t(:select_element),
13
+ selected: (@elements.first if @elements.count == 1),
14
+ input_html: {is: 'alchemy-select', autofocus: true} %>
15
+ <% if @elements.count == 1 %>
16
+ <%= form.hidden_field :name, value: @elements.first[:name] %>
17
+ <% end %>
18
+ <%= form.hidden_field :parent_element_id, value: @parent_element.try(:id) %>
19
+ <%= form.submit Alchemy.t(:add) %>
20
+ <%- end -%>
19
21
  <%- end -%>
20
22
  <%- end -%>
@@ -0,0 +1,34 @@
1
+ <% opts = {
2
+ partial: "alchemy/admin/elements/element",
3
+ locals: {
4
+ element: Alchemy::ElementEditor.new(@element),
5
+ created: true
6
+ }
7
+ } %>
8
+
9
+ <% if @element.fixed? %>
10
+ <% target = "fixed_element_#{@element.id}" %>
11
+ <% elsif @element.parent_element %>
12
+ <% target = "element_#{@element.parent_element_id}_nested_elements" %>
13
+ <% else %>
14
+ <% target = "main-content-elements" %>
15
+ <% end %>
16
+
17
+ <%- if @cut_element_id -%>
18
+ <%= turbo_stream.remove "element_#{@cut_element_id}" %>
19
+ <% end %>
20
+
21
+ <% if @insert_at_top %>
22
+ <%= turbo_stream.prepend target, **opts %>
23
+ <% else %>
24
+ <%= turbo_stream.append target, **opts %>
25
+ <% end %>
26
+
27
+ <%= turbo_stream.replace "clipboard_button",
28
+ partial: "alchemy/admin/elements/clipboard_button" %>
29
+
30
+ <alchemy-growl>
31
+ <%= Alchemy.t(:successfully_added_element) %>
32
+ </alchemy-growl>
33
+
34
+ <alchemy-action name="closeCurrentDialog"></alchemy-action>
@@ -1,4 +1,5 @@
1
1
  <%= turbo_frame_tag "alchemy_elements_window" do %>
2
+ <alchemy-elements-window-handle></alchemy-elements-window-handle>
2
3
  <alchemy-elements-window>
3
4
  <div class="elements-window-toolbar">
4
5
  <%= render Alchemy::Admin::ToolbarButton.new(
@@ -12,20 +13,7 @@
12
13
  },
13
14
  if_permitted_to: [:create, Alchemy::Element]
14
15
  ) %>
15
- <%= render Alchemy::Admin::ToolbarButton.new(
16
- url: alchemy.admin_clipboard_path(remarkable_type: "elements"),
17
- label: Alchemy.t("Show clipboard"),
18
- icon: :clipboard,
19
- icon_style: clipboard_empty?("elements") ? "line" : "fill",
20
- dialog_options: {
21
- title: Alchemy.t("Clipboard"),
22
- size: "400x305"
23
- },
24
- link_options: {
25
- id: "clipboard_button"
26
- },
27
- if_permitted_to: [:index, :alchemy_admin_clipboard]
28
- ) %>
16
+ <%= render "alchemy/admin/elements/clipboard_button" %>
29
17
  <sl-tooltip content="<%= Alchemy.t("Collapse all elements") %>" placement="top-end" class="right">
30
18
  <button id="collapse-all-elements-button" class="icon_button">
31
19
  <alchemy-icon name="contract-up-down"></alchemy-icon>
@@ -53,7 +41,7 @@
53
41
  <%= render @elements.map { |element| Alchemy::ElementEditor.new(element) } %>
54
42
  </sl-tab-panel>
55
43
  <% @fixed_elements.each do |element| %>
56
- <sl-tab-panel name="fixed-element-<%= element.id %>" style="--padding: 0" class="scrollable-elements">
44
+ <sl-tab-panel id="fixed_element_<%= element.id %>" name="fixed-element-<%= element.id %>" style="--padding: 0" class="scrollable-elements">
57
45
  <%= render Alchemy::ElementEditor.new(element) %>
58
46
  </sl-tab-panel>
59
47
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <%= f.input :caption, as: ingredient.settings[:caption_as_textarea] ? 'text' : 'string' %>
2
2
  <%= f.input :title %>
3
- <%= f.input :alt_tag, as: :text, placeholder: ingredient.alt_text(language: @language) %>
3
+ <%= f.input :alt_tag, as: :text, placeholder: ingredient.alt_text(language: @language), input_html: {rows: 4} %>
4
4
  <%- if ingredient.settings[:sizes].present? && ingredient.settings[:srcset].blank? -%>
5
5
  <%= f.input :render_size,
6
6
  collection: [
@@ -1,7 +1,9 @@
1
- <%= alchemy_form_for [:admin, @page], url: alchemy.admin_layoutpage_path(@page), class: 'edit_page' do |f| %>
2
- <%= f.input :name, autofocus: true %>
3
- <%= render Alchemy::Admin::TagsAutocomplete.new do %>
4
- <%= f.input :tag_list, input_html: { value: f.object.tag_list.join(",") } %>
1
+ <%= turbo_frame_tag @page do %>
2
+ <%= alchemy_form_for [:admin, @page], url: alchemy.admin_layoutpage_path(@page), class: 'edit_page', remote: false do |f| %>
3
+ <%= f.input :name, autofocus: true %>
4
+ <%= render Alchemy::Admin::TagsAutocomplete.new do %>
5
+ <%= f.input :tag_list, input_html: { value: f.object.tag_list.join(",") } %>
6
+ <% end %>
7
+ <%= f.submit Alchemy.t(:save) %>
5
8
  <% end %>
6
- <%= f.submit Alchemy.t(:save) %>
7
9
  <% end %>
@@ -27,7 +27,7 @@
27
27
  <%= f.submit button_label %>
28
28
  <% end %>
29
29
 
30
- <script>
30
+ <script type="module">
31
31
  const nodeName = document.getElementById("node_name")
32
32
  const nodeUrl = document.getElementById("node_url")
33
33
  const form = document.getElementById("node_form")
@@ -1,4 +1,4 @@
1
- <div class="page_status_and_name" id="page_<%= current_page.id %>_status">
1
+ <div class="page_status_and_name" id="locked_page_<%= current_page.id %>">
2
2
  <%= render 'alchemy/admin/pages/page_infos', page: current_page %>
3
3
  <%= render 'alchemy/admin/pages/page_status', page: current_page %>
4
4
  </div>
@@ -1,55 +1,58 @@
1
- <%= alchemy_form_for [:admin, @page], class: 'edit_page' do |f| %>
2
- <% unless @page.language_root? || @page.layoutpage %>
3
- <%= render Alchemy::Admin::PageSelect.new(@page.parent) do %>
4
- <%= f.input :parent_id, required: true %>
1
+ <%= turbo_frame_tag @page do %>
2
+ <%= alchemy_form_for [:admin, @page], class: 'edit_page', remote: false do |f| %>
3
+ <% unless @page.language_root? || @page.layoutpage %>
4
+ <%= render Alchemy::Admin::PageSelect.new(@page.parent) do %>
5
+ <%= f.input :parent_id, required: true %>
6
+ <% end %>
5
7
  <% end %>
6
- <% end %>
7
8
 
8
- <div class="input check_boxes">
9
- <label class="control-label"><%= Alchemy.t(:page_status) %></label>
10
- <div class="control_group">
11
- <%= render 'alchemy/admin/pages/publication_fields' %>
12
- <%= page_status_checkbox(@page, :restricted) %>
13
- <% if configuration(:sitemap)['show_flag'] %>
14
- <%= page_status_checkbox(@page, :sitemap) %>
15
- <% end %>
9
+ <div class="input check_boxes">
10
+ <label class="control-label"><%= Alchemy.t(:page_status) %></label>
11
+ <div class="control_group">
12
+ <%= render 'alchemy/admin/pages/publication_fields' %>
13
+ <%= page_status_checkbox(@page, :restricted) %>
14
+ <% if configuration(:sitemap)['show_flag'] %>
15
+ <%= page_status_checkbox(@page, :sitemap) %>
16
+ <% end %>
17
+ </div>
16
18
  </div>
17
- </div>
18
19
 
19
- <%= f.input :name, autofocus: true %>
20
- <%= f.input :urlname, as: 'string', input_html: {value: @page.slug}, label: Alchemy::Page.human_attribute_name(:slug) %>
21
- <alchemy-char-counter max-chars="60">
22
- <%= f.input :title %>
23
- </alchemy-char-counter>
20
+ <%= f.input :name, autofocus: true %>
21
+ <%= f.input :urlname, as: 'string', input_html: {value: @page.slug}, label: Alchemy::Page.human_attribute_name(:slug) %>
22
+ <alchemy-char-counter max-chars="60">
23
+ <%= f.input :title %>
24
+ </alchemy-char-counter>
25
+
26
+ <% if Alchemy.enable_searchable %>
27
+ <div class="input check_boxes">
28
+ <label class="control-label"><%= Alchemy.t(:fulltext_search) %></label>
29
+ <div class="control_group">
30
+ <%= page_status_checkbox(@page, :searchable) %>
31
+ </div>
32
+ </div>
33
+ <% end %>
24
34
 
25
- <% if Alchemy.enable_searchable %>
26
35
  <div class="input check_boxes">
27
- <label class="control-label"><%= Alchemy.t(:fulltext_search) %></label>
36
+ <label class="control-label"><%= Alchemy.t(:search_engines) %></label>
28
37
  <div class="control_group">
29
- <%= page_status_checkbox(@page, :searchable) %>
38
+ <%= page_status_checkbox(@page, :robot_index) %>
39
+ <%= page_status_checkbox(@page, :robot_follow) %>
30
40
  </div>
31
41
  </div>
32
- <% end %>
33
42
 
34
- <div class="input check_boxes">
35
- <label class="control-label"><%= Alchemy.t(:search_engines) %></label>
36
- <div class="control_group">
37
- <%= page_status_checkbox(@page, :robot_index) %>
38
- <%= page_status_checkbox(@page, :robot_follow) %>
39
- </div>
40
- </div>
43
+ <alchemy-char-counter max-chars="160">
44
+ <%= f.input :meta_description, as: 'text' %>
45
+ </alchemy-char-counter>
41
46
 
42
- <alchemy-char-counter max-chars="160">
43
- <%= f.input :meta_description, as: 'text' %>
44
- </alchemy-char-counter>
47
+ <%= f.input :meta_keywords,
48
+ as: 'text',
49
+ hint: Alchemy.t('pages.update.comma_seperated') %>
45
50
 
46
- <%= f.input :meta_keywords,
47
- as: 'text',
48
- hint: Alchemy.t('pages.update.comma_seperated') %>
51
+ <%= render Alchemy::Admin::TagsAutocomplete.new do %>
52
+ <%= f.input :tag_list, input_html: { value: f.object.tag_list.join(",") } %>
53
+ <% end %>
49
54
 
50
- <%= render Alchemy::Admin::TagsAutocomplete.new do %>
51
- <%= f.input :tag_list, input_html: { value: f.object.tag_list.join(",") } %>
55
+ <%= hidden_field_tag :view, params[:view] %>
56
+ <%= f.submit Alchemy.t(:save) %>
52
57
  <% end %>
53
-
54
- <%= f.submit Alchemy.t(:save) %>
55
58
  <% end %>
@@ -1,4 +1,4 @@
1
- <% if @page == locked_page %>
1
+ <% if action_name == "edit" && @page == locked_page %>
2
2
  <%= render 'alchemy/admin/pages/current_page', current_page: @page %>
3
3
  <% else %>
4
4
  <div class="locked_page wide" id="locked_page_<%= locked_page.id %>">
@@ -19,7 +19,7 @@
19
19
  </label>
20
20
  </div>
21
21
 
22
- <script type="text/javascript">
22
+ <script type="module">
23
23
  $(function() {
24
24
  $("#page_layout").on("change", function(e) {
25
25
  var url = "<%= alchemy.admin_pages_path(search_filter_params.except(:page_layout, :page).merge(view: "list")) %>";
@@ -19,7 +19,7 @@
19
19
  {{/each}}
20
20
  </script>
21
21
 
22
- <script type="text/javascript">
22
+ <script type="module">
23
23
  $(function() {
24
24
  Alchemy.currentSitemap = new Alchemy.Sitemap({
25
25
  url: '<%= alchemy.tree_admin_pages_path %>',
@@ -67,7 +67,7 @@
67
67
  <% table.with_action :configure, Alchemy.t(:edit_page_properties) do |page| %>
68
68
  <%= link_to_dialog(
69
69
  render_icon(:cog),
70
- alchemy.configure_admin_page_path(page),
70
+ alchemy.configure_admin_page_path(page, view: "list"),
71
71
  {
72
72
  title: Alchemy.t(:edit_page_properties),
73
73
  size: '450x680'
@@ -91,7 +91,7 @@
91
91
  <% end %>
92
92
 
93
93
 
94
- <script type="text/javascript">
94
+ <script type="module">
95
95
  $(function() {
96
96
  Alchemy.PagePublicationFields();
97
97
  });
@@ -144,7 +144,7 @@
144
144
  <% end %>
145
145
 
146
146
  <% content_for :javascripts do %>
147
- <script type="text/javascript" charset="utf-8">
147
+ <script type="module">
148
148
  $(document).one('turbo:load', function() {
149
149
  $('#unlock_page_form, #publish_page_form').on('submit', function(event) {
150
150
  var not_dirty = Alchemy.checkPageDirtyness(this);
@@ -0,0 +1,39 @@
1
+ <alchemy-growl><%= @notice %></alchemy-action>
2
+ <alchemy-action name="closeCurrentDialog"></alchemy-action>
3
+
4
+ <% if @while_page_edit -%>
5
+ <%= turbo_stream.replace "locked_page_#{@page.id}" do %>
6
+ <%= render("alchemy/admin/pages/current_page", current_page: @page) %>
7
+ <% end %>
8
+ <alchemy-action name="reloadPreview"></alchemy-action>
9
+ <% else %>
10
+ <%= turbo_stream.replace "locked_page_#{@page.id}" do %>
11
+ <%= render("alchemy/admin/pages/locked_page", locked_page: @page) %>
12
+ <% end %>
13
+
14
+ <% if @view == "list" %>
15
+ <turbo-stream action="refresh"></turbo-stream>
16
+ <% elsif @page.parent_id != @old_parent_id -%>
17
+ <%= turbo_stream.append "sitemap" do %>
18
+ <script type="module">
19
+ Alchemy.currentSitemap.load(<%= @page.get_language_root.id %>);
20
+ </script>
21
+ <% end %>
22
+ <% else -%>
23
+ <% if @page.layoutpage %>
24
+ <%= turbo_stream.replace "page_#{@page.id}" do %>
25
+ <%= render("alchemy/admin/layoutpages/layoutpage", layoutpage: @page) %>
26
+ <% end %>
27
+ <% else %>
28
+ <%= turbo_stream.append "sitemap" do %>
29
+ <script type="module">
30
+ const page = document.getElementById('page_<%= @page.id %>');
31
+ const page_html = "<%= j render('page', page: @page) %>".replace(/__ID__/g, "<%= @page.id %>");
32
+ const compiler = Handlebars.compile(page_html);
33
+ const tree = <%== @tree.to_json %>;
34
+ page.outerHTML = compiler(tree.pages[0]);
35
+ </script>
36
+ <% end %>
37
+ <% end %>
38
+ <% end -%>
39
+ <% end %>
@@ -6,10 +6,9 @@
6
6
  <% elsif navigation["inline_image"] %>
7
7
  <%== navigation["inline_image"] %>
8
8
  <% elsif navigation["icon"] %>
9
- <%# Cannot use the render_icon helper, because the navigation["icon"] includes the style %>
10
- <svg class="icon">
11
- <use href="<%= asset_path("remixicon.symbol.svg") %>#ri-<%= navigation["icon"] %>" />
12
- </svg>
9
+ <%= content_tag :"alchemy-icon", nil,
10
+ name: navigation["icon"],
11
+ "icon-style": navigation["icon-style"] %>
13
12
  <% else %>
14
13
  <%= render_icon :table %>
15
14
  <% end %>
@@ -21,9 +21,11 @@
21
21
  <script type="module">
22
22
  const select = document.querySelector("#language_id")
23
23
 
24
- select.addEventListener("change", () => {
25
- const url = new URL(select.dataset.url)
26
- url.searchParams.set("language_id", select.value)
27
- Turbo.visit(url, { frame: "picture_descriptions" })
28
- })
24
+ if (select) {
25
+ select.addEventListener("change", () => {
26
+ const url = new URL(select.dataset.url)
27
+ url.searchParams.set("language_id", select.value)
28
+ Turbo.visit(url, { frame: "picture_descriptions" })
29
+ })
30
+ }
29
31
  </script>
@@ -86,16 +86,20 @@
86
86
  </div>
87
87
 
88
88
  <% content_for :javascripts do %>
89
- <script type="text/javascript" charset="utf-8">
90
- $(function() {
91
- Alchemy.pictureSelector();
92
- $('#picture_archive').on("click", ".thumbnail_background", function(event) {
93
- var url = $(this).attr('href');
94
- var overlay = new Alchemy.ImageOverlay(url);
95
- overlay.open();
96
- event.preventDefault();
97
- return false;
89
+ <script type="module">
90
+ import ImageOverlay from "alchemy_admin/image_overlay";
91
+ import pictureSelector from "alchemy_admin/picture_selector";
92
+ import { on } from "alchemy_admin/utils/events";
93
+
94
+ pictureSelector();
95
+ on("click", "#picture_archive", ".thumbnail_background", (event) => {
96
+ const url = event.target.closest("a")?.href;
97
+ const overlay = new ImageOverlay(url, {
98
+ size: `${window.innerWidth}x${window.innerHeight}`,
99
+ padding: false
98
100
  });
101
+ overlay.open();
102
+ event.preventDefault();
99
103
  });
100
104
  </script>
101
105
  <% end %>
@@ -2,7 +2,7 @@
2
2
  <%= render partial: "filter", collection: resource_filters_for_select %>
3
3
  </div>
4
4
 
5
- <script type="text/javascript">
5
+ <script type="module">
6
6
  $(function() {
7
7
  $('select', '#filter_bar').on('change', function(e) {
8
8
  var $this = $(this);
@@ -5,15 +5,17 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title><%= render_alchemy_title %></title>
7
7
  <link rel="shortcut icon" href="<%= asset_path('alchemy/favicon.ico') %>">
8
+ <link rel="preload" href="<%= asset_path("remixicon.symbol.svg") %>" as="image" type="<%= Mime::Type.lookup_by_extension(:svg) %>" crossorigin>
8
9
  <%= csrf_meta_tag %>
9
10
  <meta name="robots" content="noindex">
10
- <meta name="alchemy-icon-sprite" content="<%= asset_path("remixicon.symbol.svg") %>">
11
11
  <meta name="turbo-prefetch" content="false">
12
12
  <meta name="turbo-cache-control" content="no-cache">
13
13
  <%= stylesheet_link_tag('alchemy/custom-properties', media: 'screen', 'data-turbo-track' => true) %>
14
14
  <%= stylesheet_link_tag('alchemy/admin', media: 'screen', 'data-turbo-track' => true) %>
15
15
  <%= stylesheet_link_tag('alchemy/admin/print', media: 'print', 'data-turbo-track' => true) %>
16
- <%= stylesheet_link_tag('alchemy/admin/custom', 'data-turbo-track' => true) %>
16
+ <% Alchemy.admin_stylesheets.each do |stylesheet| %>
17
+ <%= stylesheet_link_tag(stylesheet, 'data-turbo-track' => true) %>
18
+ <% end %>
17
19
  <%= yield :stylesheets %>
18
20
  <script>
19
21
  // Global Alchemy JavaScript object.
@@ -32,7 +34,9 @@
32
34
  <% end %>
33
35
  <%= yield :javascript_includes %>
34
36
  </head>
35
- <%= content_tag :body, id: 'alchemy', class: alchemy_body_class + ["alchemy-light"] do %>
37
+ <%= content_tag :body, id: 'alchemy',
38
+ class: alchemy_body_class + ["alchemy-light"],
39
+ style: cookies["alchemy-elements-window-width"] && "--elements-window-width: #{cookies["alchemy-elements-window-width"]}px" do %>
36
40
  <noscript>
37
41
  <h1><%= Alchemy.t(:javascript_disabled_headline) %></h1>
38
42
  <p><%= Alchemy.t(:javascript_disabled_text) %></p>
@@ -89,7 +93,7 @@
89
93
  <div id="main_content">
90
94
  <%= yield %>
91
95
  </div>
92
- <script>
96
+ <script type="module">
93
97
  // Setting the correct locale for select2 dropdown replacement.
94
98
  $.extend($.fn.select2.defaults, $.fn.select2.locales['<%= ::I18n.locale %>']);
95
99
  </script>
data/bun.lockb CHANGED
Binary file
data/bundles/tinymce.js CHANGED
@@ -3,6 +3,7 @@ import tinymce from "tinymce"
3
3
 
4
4
  /* Default icons are required. After that, import custom icons if applicable */
5
5
  import "tinymce/icons/default"
6
+ import "tinymce/icons/remixicons"
6
7
 
7
8
  /* Required TinyMCE components */
8
9
  import "tinymce/themes/silver"
@@ -16,5 +17,6 @@ import "tinymce/plugins/directionality"
16
17
  import "tinymce/plugins/fullscreen"
17
18
  import "tinymce/plugins/link"
18
19
  import "tinymce/plugins/lists"
20
+ import "tinymce/plugins/alchemy_link"
19
21
 
20
22
  export default tinymce
@@ -199,9 +199,9 @@ link_target_options: [blank]
199
199
  # validates_format_of :url, with: Alchemy::Config.get('format_matchers')['url']
200
200
  #
201
201
  format_matchers:
202
- email: !ruby/regexp '/\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/'
203
- url: !ruby/regexp '/\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix'
204
- link_url: !ruby/regexp '/^(tel:|mailto:|\/|[a-z]+:\/\/)/'
202
+ email: !ruby/regexp /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/
203
+ url: !ruby/regexp /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix
204
+ link_url: !ruby/regexp /^(tel:|mailto:|\/|[a-z]+:\/\/)/
205
205
 
206
206
  # The layout used for rendering the +alchemy/admin/pages#show+ action.
207
207
  admin_page_preview_layout: application
@@ -7,7 +7,7 @@
7
7
  name: Home
8
8
  controller: "/alchemy/admin/dashboard"
9
9
  action: index
10
- icon: home-2-line
10
+ icon: home-2
11
11
 
12
12
  - name: pages
13
13
  engine_name: alchemy
@@ -16,7 +16,7 @@
16
16
  name: "modules.pages"
17
17
  controller: "/alchemy/admin/pages"
18
18
  action: index
19
- icon: pages-line
19
+ icon: pages
20
20
  sub_navigation:
21
21
  - name: "modules.pages"
22
22
  controller: "/alchemy/admin/pages"
@@ -35,7 +35,7 @@
35
35
  name: "modules.menus"
36
36
  controller: "/alchemy/admin/nodes"
37
37
  action: index
38
- icon: menu-2-line
38
+ icon: menu-2
39
39
 
40
40
  - name: languages
41
41
  engine_name: alchemy
@@ -45,6 +45,7 @@
45
45
  controller: "/alchemy/admin/languages"
46
46
  action: index
47
47
  icon: translate-2
48
+ icon-style: none
48
49
 
49
50
  - name: sites
50
51
  engine_name: alchemy
@@ -53,7 +54,7 @@
53
54
  name: "modules.sites"
54
55
  controller: "/alchemy/admin/sites"
55
56
  action: index
56
- icon: global-line
57
+ icon: global
57
58
 
58
59
  - name: tags
59
60
  engine_name: alchemy
@@ -62,7 +63,7 @@
62
63
  name: "modules.tags"
63
64
  controller: "/alchemy/admin/tags"
64
65
  action: index
65
- icon: price-tag-3-line
66
+ icon: price-tag-3
66
67
 
67
68
  - name: archive
68
69
  engine_name: alchemy
@@ -71,7 +72,7 @@
71
72
  controller: "/alchemy/admin/pictures"
72
73
  action: index
73
74
  name: "modules.library"
74
- icon: archive-drawer-line
75
+ icon: archive-drawer
75
76
  sub_navigation:
76
77
  - name: "modules.pictures"
77
78
  controller: "/alchemy/admin/pictures"
data/config/importmap.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  pin "@ungap/custom-elements", to: "ungap-custom-elements.min.js", preload: true # @1.3.0
2
2
  pin "clipboard", to: "clipboard.min.js", preload: true
3
+ pin "cropperjs", to: "cropperjs.min.js", preload: true
3
4
  pin "flatpickr", to: "flatpickr.min.js", preload: true # @4.6.13
5
+ pin "handlebars", to: "handlebars.min.js", preload: true # @4.7.8
6
+ pin "jquery", to: "jquery.min.js", preload: true
4
7
  pin "keymaster", to: "keymaster.min.js", preload: true
8
+ pin "select2", to: "select2.min.js", preload: true
5
9
  pin "sortablejs", to: "sortable.min.js", preload: true # @1.15.1
6
10
  pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
7
11
  pin "shoelace", to: "shoelace.min.js", preload: true
data/config/routes.rb CHANGED
@@ -88,7 +88,7 @@ Alchemy::Engine.routes.draw do
88
88
  end
89
89
  end
90
90
 
91
- resource :clipboard, only: :index, controller: "clipboard" do
91
+ resource :clipboard, only: [], controller: "clipboard" do
92
92
  collection do
93
93
  get :index
94
94
  delete :clear
@@ -20,6 +20,12 @@ module Alchemy
20
20
  NonStupidDigestAssets.whitelist += [/^tinymce\//]
21
21
  end
22
22
 
23
+ initializer "alchemy.admin_stylesheets" do |app|
24
+ Alchemy.admin_stylesheets.each do |stylesheet|
25
+ app.config.assets.precompile << stylesheet
26
+ end
27
+ end
28
+
23
29
  initializer "alchemy.importmap" do |app|
24
30
  watch_paths = []
25
31