alchemy_cms 5.3.0 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/app/assets/javascripts/alchemy/admin.js +0 -1
  4. data/app/assets/javascripts/alchemy/templates/index.js +1 -0
  5. data/app/assets/javascripts/alchemy/templates/page.hbs +17 -7
  6. data/app/assets/javascripts/alchemy/templates/page_folder.hbs +3 -0
  7. data/app/assets/stylesheets/alchemy/page-select.scss +29 -4
  8. data/app/assets/stylesheets/alchemy/sitemap.scss +2 -6
  9. data/app/controllers/alchemy/admin/pages_controller.rb +8 -12
  10. data/app/controllers/alchemy/api/pages_controller.rb +14 -4
  11. data/app/serializers/alchemy/page_serializer.rb +7 -1
  12. data/app/serializers/alchemy/page_tree_serializer.rb +3 -3
  13. data/app/views/alchemy/admin/pages/_page.html.erb +111 -133
  14. data/app/views/alchemy/admin/pages/_sitemap.html.erb +2 -8
  15. data/app/views/alchemy/admin/pages/_toolbar.html.erb +0 -12
  16. data/app/views/alchemy/admin/pages/index.html.erb +1 -1
  17. data/app/views/alchemy/admin/partials/_routes.html.erb +8 -1
  18. data/app/views/alchemy/essences/_essence_page_editor.html.erb +1 -1
  19. data/config/locales/alchemy.en.yml +0 -3
  20. data/config/routes.rb +4 -2
  21. data/lib/alchemy/permissions.rb +0 -1
  22. data/lib/alchemy/version.rb +1 -1
  23. data/package/src/page_sorter.js +68 -0
  24. data/package/src/sitemap.js +55 -36
  25. data/package.json +1 -1
  26. metadata +4 -6
  27. data/app/assets/javascripts/alchemy/alchemy.page_sorter.js +0 -24
  28. data/app/views/alchemy/admin/pages/fold.js.erb +0 -2
  29. data/app/views/alchemy/admin/pages/sort.html.erb +0 -19
  30. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +0 -434
@@ -292,7 +292,6 @@ en:
292
292
  "Site successfully removed": "Website successfully removed."
293
293
  "Site successfully updated": "Website successfully updated."
294
294
  "Size": "Size"
295
- "Sort pages": "Reorder pages"
296
295
  "Successfully added content": "Successfully added %{content}"
297
296
  "Successfully deleted content": "Successfully deleted %{content}"
298
297
  "Tags": "Tags"
@@ -394,7 +393,6 @@ en:
394
393
  enter_external_link: "Please enter the URL you want to link with"
395
394
  explain_cropping: "<p>Move the frame and change its size with the mouse or arrow keys to adjust the image mask. Click on \"apply\" when you are satisfied with your selection.</p><p>If you want to return to the original centered image mask like it was defined in the layout, click \"reset\" and \"apply\" afterwards.</p>"
396
395
  explain_publishing: "Publish the page and remove the cached version from the server."
397
- explain_sitemap_dragndrop_sorting: "Tip: Drag the pages at the icon in order to sort them."
398
396
  explain_unlocking: "Leave page and unlock it for other users."
399
397
  external_link_notice_1: "Please enter the complete url with http:// or a similar protocol."
400
398
  external_link_notice_2: "To refer a path from your website url, start with a /."
@@ -553,7 +551,6 @@ en:
553
551
  robot_follow: "robot may follow links."
554
552
  robot_index: "allow robot to index."
555
553
  save: "Save"
556
- "save order": "Save order"
557
554
  saved_link: "Link saved."
558
555
  search: "search"
559
556
  search_engines: "Search engines"
data/config/routes.rb CHANGED
@@ -28,13 +28,12 @@ Alchemy::Engine.routes.draw do
28
28
  post :copy_language_tree
29
29
  get :create_language
30
30
  get :link
31
- get :sort
32
31
  get :tree
33
32
  end
34
33
  member do
35
34
  post :unlock
36
35
  post :publish
37
- post :fold
36
+ patch :fold
38
37
  post :visit
39
38
  get :configure
40
39
  get :preview
@@ -145,6 +144,9 @@ Alchemy::Engine.routes.draw do
145
144
  collection do
146
145
  get :nested
147
146
  end
147
+ member do
148
+ patch :move
149
+ end
148
150
  end
149
151
 
150
152
  get "/pages/*urlname(.:format)" => "pages#show", as: "page"
@@ -149,7 +149,6 @@ module Alchemy
149
149
  :copy_language_tree,
150
150
  :flush,
151
151
  :order,
152
- :sort,
153
152
  :switch_language,
154
153
  ], Alchemy::Page
155
154
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "5.3.0"
4
+ VERSION = "5.3.1"
5
5
 
6
6
  def self.version
7
7
  VERSION
@@ -0,0 +1,68 @@
1
+ import Sortable from "sortablejs"
2
+
3
+ function onFinishDragging(evt) {
4
+ const pageId = evt.item.dataset.pageId
5
+ const url = Alchemy.routes.move_admin_page_path(pageId)
6
+ const data = {
7
+ target_parent_id: evt.to.dataset.parentId,
8
+ new_position: evt.newIndex
9
+ }
10
+
11
+ fetch(url, {
12
+ method: "PATCH",
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ Accept: "application/json"
16
+ },
17
+ body: JSON.stringify(data)
18
+ })
19
+ .then(async (response) => {
20
+ const pageData = await response.json()
21
+ const pageEl = document.getElementById(`page_${pageId}`)
22
+ const urlPathEl = pageEl.querySelector(".sitemap_url")
23
+
24
+ Alchemy.growl(Alchemy.t("Successfully moved page"))
25
+ urlPathEl.textContent = pageData.url_path
26
+ displayPageFolders()
27
+ })
28
+ .catch((error) => {
29
+ Alchemy.growl(error.message || error, "error")
30
+ })
31
+ }
32
+
33
+ export function displayPageFolders() {
34
+ document.querySelectorAll("li.sitemap-item").forEach((el) => {
35
+ const pageFolderEl = el.querySelector(".page_folder")
36
+ const list = el.querySelector(".children")
37
+ const page = {
38
+ folded: el.dataset.folded === "true",
39
+ id: el.dataset.pageId,
40
+ type: el.dataset.type
41
+ }
42
+
43
+ if (list.children.length > 0 || page.folded) {
44
+ pageFolderEl.outerHTML = HandlebarsTemplates.page_folder({ page })
45
+ } else {
46
+ pageFolderEl.innerHTML = ""
47
+ }
48
+ })
49
+ }
50
+
51
+ export function createSortables(sortables) {
52
+ sortables.forEach((el) => {
53
+ new Sortable(el, {
54
+ group: "pages",
55
+ animation: 150,
56
+ fallbackOnBody: true,
57
+ swapThreshold: 0.65,
58
+ handle: ".handle",
59
+ onEnd: onFinishDragging
60
+ })
61
+ })
62
+ }
63
+
64
+ export default function () {
65
+ const sortables = document.querySelectorAll("ul.children")
66
+ displayPageFolders()
67
+ createSortables(sortables)
68
+ }
@@ -1,12 +1,14 @@
1
1
  // The admin sitemap Alchemy class
2
+ import PageSorter from "./page_sorter"
3
+ import { on } from "./utils/events"
4
+ import { createSortables, displayPageFolders } from "./page_sorter"
2
5
 
3
6
  export default class Sitemap {
4
7
  // Storing some objects.
5
8
  constructor(options) {
6
- const list_template_regexp = new RegExp("/" + options.page_root_id, "g")
7
9
  const list_template_html = document
8
10
  .getElementById("sitemap-list")
9
- .innerHTML.replace(list_template_regexp, "/{{id}}")
11
+ .innerHTML.replace(/__ID__/g, "{{id}}")
10
12
  this.search_field = document.querySelector(".search_input_field")
11
13
  this.filter_field_clear = document.querySelector(".search_field_clear")
12
14
  this.filter_field_clear.removeAttribute("href")
@@ -24,56 +26,68 @@ export default class Sitemap {
24
26
 
25
27
  // Loads the sitemap
26
28
  load(pageId) {
27
- const spinner = this.options.spinner || new Alchemy.Spinner("medium")
29
+ const spinner = new Alchemy.Spinner("medium")
28
30
  const spinTarget = this.sitemap_wrapper
29
31
  spinTarget.innerHTML = ""
30
32
  spinner.spin(spinTarget)
31
- this.fetch(
32
- `${this.options.url}?id=${pageId}&full=${this.options.full}`
33
- ).then(async (response) => {
34
- this.render(await response.json())
35
- spinner.stop()
36
- })
33
+ fetch(`${this.options.url}?id=${pageId}`)
34
+ .then(async (response) => {
35
+ this.render(await response.json())
36
+ this.handlePageFolders()
37
+ spinner.stop()
38
+ })
39
+ .catch(this.errorHandler)
37
40
  }
38
41
 
39
- // Reload the sitemap for a specific branch
40
- reload(pageId) {
41
- const spinner = new Alchemy.Spinner("small")
42
- const spinTarget = document.getElementById(`fold_button_${pageId}`)
43
- spinTarget.querySelector(".far").remove()
44
- spinner.spin(spinTarget)
45
- this.fetch(`${this.options.url}?id=${pageId}`).then(async (response) => {
46
- this.render(await response.json(), pageId)
47
- spinner.stop()
48
- })
49
- }
42
+ // Watch page folder clicks and re-render the page branch
43
+ handlePageFolders() {
44
+ on(
45
+ "click",
46
+ "#sitemap",
47
+ ".page_folder",
48
+ function (evt) {
49
+ const spinner = new Alchemy.Spinner("small")
50
+ const pageFolder = evt.target.closest(".page_folder")
51
+ const pageId = pageFolder.dataset.pageId
52
+ pageFolder.innerHTML = ""
53
+ spinner.spin(pageFolder)
50
54
 
51
- fetch(url) {
52
- return fetch(url).catch((error) => console.warn(`Request failed: ${error}`))
55
+ fetch(Alchemy.routes.fold_admin_page_path(pageId), {
56
+ method: "PATCH",
57
+ headers: {
58
+ Accept: "application/json"
59
+ }
60
+ })
61
+ .then(async (response) => {
62
+ this.reRender(pageId, await response.json())
63
+ spinner.stop()
64
+ })
65
+ .catch(this.errorHandler)
66
+ }.bind(this)
67
+ )
53
68
  }
54
69
 
55
70
  // Renders the sitemap
56
- render(data, foldingId) {
57
- let renderTarget, renderTemplate
71
+ render(data) {
72
+ const renderTarget = this.sitemap_wrapper
73
+ const renderTemplate = this.template
58
74
 
59
- if (foldingId) {
60
- renderTarget = document.getElementById(`page_${foldingId}`)
61
- renderTemplate = this.list_template
62
- renderTarget.outerHTML = renderTemplate({ children: data.pages })
63
- } else {
64
- renderTarget = this.sitemap_wrapper
65
- renderTemplate = this.template
66
- renderTarget.innerHTML = renderTemplate({ children: data.pages })
67
- }
75
+ renderTarget.innerHTML = renderTemplate({ children: data.pages })
68
76
  this.items = document
69
77
  .getElementById("sitemap")
70
78
  .querySelectorAll(".sitemap_page")
71
79
  this.sitemap_wrapper = document.getElementById("sitemap-wrapper")
72
80
  this._observe()
81
+ PageSorter()
82
+ }
73
83
 
74
- if (this.options.ready) {
75
- this.options.ready()
76
- }
84
+ reRender(pageId, data) {
85
+ let pageEl = document.getElementById(`page_${pageId}`)
86
+ pageEl.outerHTML = this.list_template({ children: data.pages })
87
+ pageEl = document.getElementById(`page_${pageId}`)
88
+ const sortables = pageEl.querySelectorAll("ul.children")
89
+ createSortables(sortables)
90
+ displayPageFolders()
77
91
  }
78
92
 
79
93
  // Filters the sitemap
@@ -130,4 +144,9 @@ export default class Sitemap {
130
144
  return false
131
145
  })
132
146
  }
147
+
148
+ errorHandler(error) {
149
+ Alchemy.growl(error.message || error, "error")
150
+ console.error(error)
151
+ }
133
152
  }
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy_cms/admin",
3
- "version": "5.3.0",
3
+ "version": "5.3.1",
4
4
  "description": "AlchemyCMS",
5
5
  "browser": "package/admin.js",
6
6
  "files": [
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.0
4
+ version: 5.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2022-03-09 00:00:00.000000000 Z
16
+ date: 2022-03-11 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: active_model_serializers
@@ -633,7 +633,6 @@ files:
633
633
  - app/assets/javascripts/alchemy/alchemy.initializer.js.coffee
634
634
  - app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee
635
635
  - app/assets/javascripts/alchemy/alchemy.list_filter.js.coffee
636
- - app/assets/javascripts/alchemy/alchemy.page_sorter.js
637
636
  - app/assets/javascripts/alchemy/alchemy.preview.js.coffee
638
637
  - app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee
639
638
  - app/assets/javascripts/alchemy/alchemy.spinner.js
@@ -650,6 +649,7 @@ files:
650
649
  - app/assets/javascripts/alchemy/templates/node.hbs
651
650
  - app/assets/javascripts/alchemy/templates/node_folder.hbs
652
651
  - app/assets/javascripts/alchemy/templates/page.hbs
652
+ - app/assets/javascripts/alchemy/templates/page_folder.hbs
653
653
  - app/assets/javascripts/alchemy/templates/spinner.hbs
654
654
  - app/assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js
655
655
  - app/assets/stylesheets/alchemy/_defaults.scss
@@ -927,7 +927,6 @@ files:
927
927
  - app/views/alchemy/admin/pages/configure.html.erb
928
928
  - app/views/alchemy/admin/pages/edit.html.erb
929
929
  - app/views/alchemy/admin/pages/flush.js.erb
930
- - app/views/alchemy/admin/pages/fold.js.erb
931
930
  - app/views/alchemy/admin/pages/index.html.erb
932
931
  - app/views/alchemy/admin/pages/info.html.erb
933
932
  - app/views/alchemy/admin/pages/link.html.erb
@@ -935,7 +934,6 @@ files:
935
934
  - app/views/alchemy/admin/pages/locked.html.erb
936
935
  - app/views/alchemy/admin/pages/new.html.erb
937
936
  - app/views/alchemy/admin/pages/show.html.erb
938
- - app/views/alchemy/admin/pages/sort.html.erb
939
937
  - app/views/alchemy/admin/pages/unlock.js.erb
940
938
  - app/views/alchemy/admin/pages/update.js.erb
941
939
  - app/views/alchemy/admin/partials/_autocomplete_tag_list.html.erb
@@ -1190,6 +1188,7 @@ files:
1190
1188
  - package/src/i18n.js
1191
1189
  - package/src/node_tree.js
1192
1190
  - package/src/page_publication_fields.js
1191
+ - package/src/page_sorter.js
1193
1192
  - package/src/sitemap.js
1194
1193
  - package/src/translations.js
1195
1194
  - package/src/utils/__tests__/ajax.spec.js
@@ -1215,7 +1214,6 @@ files:
1215
1214
  - vendor/assets/javascripts/flatpickr/flatpickr.min.js
1216
1215
  - vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js
1217
1216
  - vendor/assets/javascripts/jquery_plugins/jquery.scrollTo.min.js
1218
- - vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js
1219
1217
  - vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js
1220
1218
  - vendor/assets/javascripts/jquery_plugins/select2.js
1221
1219
  - vendor/assets/javascripts/keymaster.js
@@ -1,24 +0,0 @@
1
- Alchemy.PageSorter = function () {
2
- var $sortables = $("ul#sitemap").find("ul.level_0_children")
3
-
4
- $sortables.nestedSortable({
5
- disableNesting: "no-nest",
6
- forcePlaceholderSize: true,
7
- handle: ".handle",
8
- items: "li",
9
- listType: "ul",
10
- opacity: 0.5,
11
- placeholder: "placeholder",
12
- tabSize: 16,
13
- tolerance: "pointer",
14
- toleranceElement: "> div"
15
- })
16
-
17
- $("#save_page_order").click(function (e) {
18
- e.preventDefault()
19
- Alchemy.Buttons.disable(this)
20
- $.post(Alchemy.routes.order_admin_pages_path, {
21
- set: JSON.stringify($sortables.nestedSortable("toHierarchy"))
22
- })
23
- })
24
- }
@@ -1,2 +0,0 @@
1
- $('#fold_button_<%= @page.id %>').css('background', 'none');
2
- Alchemy.currentSitemap.reload(<%= @page.id %>);
@@ -1,19 +0,0 @@
1
- <% content_for :toolbar do %>
2
- <div class="button_with_label">
3
- <%= link_to alchemy.admin_pages_path, class: 'icon_button' do %>
4
- <%= render_icon 'angle-double-left' %>
5
- <% end %>
6
- <label><%= Alchemy.t(:cancel) %></label>
7
- </div>
8
- <% end %>
9
-
10
- <div id="sort_panel">
11
- <%= render_message do %>
12
- <%= Alchemy.t(:explain_sitemap_dragndrop_sorting) %>
13
- <% end %>
14
- <div class="buttons">
15
- <%= button_tag Alchemy.t('save order'), id: 'save_page_order' %>
16
- </div>
17
- </div>
18
-
19
- <%= render 'sitemap', page_partial: 'page', full: true %>