alchemy_cms 5.2.6 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.rspec +1 -0
  4. data/CHANGELOG.md +19 -0
  5. data/Rakefile +18 -0
  6. data/alchemy_cms.gemspec +1 -1
  7. data/app/assets/javascripts/alchemy/admin.js +0 -2
  8. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +6 -1
  9. data/app/assets/javascripts/alchemy/page_select.js +13 -8
  10. data/app/assets/javascripts/alchemy/templates/index.js +1 -0
  11. data/app/assets/javascripts/alchemy/templates/page.hbs +17 -7
  12. data/app/assets/javascripts/alchemy/templates/page_folder.hbs +3 -0
  13. data/app/assets/stylesheets/alchemy/page-select.scss +29 -4
  14. data/app/assets/stylesheets/alchemy/sitemap.scss +9 -7
  15. data/app/controllers/alchemy/admin/pages_controller.rb +10 -13
  16. data/app/controllers/alchemy/api/pages_controller.rb +14 -4
  17. data/app/models/alchemy/page.rb +3 -2
  18. data/app/serializers/alchemy/page_serializer.rb +7 -1
  19. data/app/serializers/alchemy/page_tree_serializer.rb +3 -3
  20. data/app/views/alchemy/admin/pages/_form.html.erb +19 -0
  21. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +16 -5
  22. data/app/views/alchemy/admin/pages/_page.html.erb +111 -133
  23. data/app/views/alchemy/admin/pages/_sitemap.html.erb +10 -16
  24. data/app/views/alchemy/admin/pages/_toolbar.html.erb +0 -12
  25. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  26. data/app/views/alchemy/admin/pages/index.html.erb +1 -1
  27. data/app/views/alchemy/admin/pages/update.js.erb +7 -0
  28. data/app/views/alchemy/admin/partials/_routes.html.erb +8 -1
  29. data/app/views/alchemy/essences/_essence_page_editor.html.erb +1 -1
  30. data/config/locales/alchemy.en.yml +0 -3
  31. data/config/routes.rb +4 -2
  32. data/lib/alchemy/permissions.rb +0 -1
  33. data/lib/alchemy/version.rb +1 -1
  34. data/package/admin.js +5 -1
  35. data/package/src/page_publication_fields.js +27 -0
  36. data/package/src/page_sorter.js +68 -0
  37. data/package/src/sitemap.js +152 -0
  38. data/package.json +1 -1
  39. metadata +9 -9
  40. data/app/assets/javascripts/alchemy/alchemy.page_sorter.js +0 -24
  41. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +0 -119
  42. data/app/views/alchemy/admin/pages/fold.js.erb +0 -2
  43. data/app/views/alchemy/admin/pages/sort.html.erb +0 -19
  44. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +0 -434
@@ -0,0 +1,152 @@
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"
5
+
6
+ export default class Sitemap {
7
+ // Storing some objects.
8
+ constructor(options) {
9
+ const list_template_html = document
10
+ .getElementById("sitemap-list")
11
+ .innerHTML.replace(/__ID__/g, "{{id}}")
12
+ this.search_field = document.querySelector(".search_input_field")
13
+ this.filter_field_clear = document.querySelector(".search_field_clear")
14
+ this.filter_field_clear.removeAttribute("href")
15
+ this.display = document.getElementById("page_filter_result")
16
+ this.sitemap_wrapper = document.getElementById("sitemap-wrapper")
17
+ this.template = Handlebars.compile(
18
+ document.getElementById("sitemap-template").innerHTML
19
+ )
20
+ this.list_template = Handlebars.compile(list_template_html)
21
+ this.items = null
22
+ this.options = options
23
+ Handlebars.registerPartial("list", list_template_html)
24
+ this.load(options.page_root_id)
25
+ }
26
+
27
+ // Loads the sitemap
28
+ load(pageId) {
29
+ const spinner = new Alchemy.Spinner("medium")
30
+ const spinTarget = this.sitemap_wrapper
31
+ spinTarget.innerHTML = ""
32
+ spinner.spin(spinTarget)
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)
40
+ }
41
+
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)
54
+
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
+ )
68
+ }
69
+
70
+ // Renders the sitemap
71
+ render(data) {
72
+ const renderTarget = this.sitemap_wrapper
73
+ const renderTemplate = this.template
74
+
75
+ renderTarget.innerHTML = renderTemplate({ children: data.pages })
76
+ this.items = document
77
+ .getElementById("sitemap")
78
+ .querySelectorAll(".sitemap_page")
79
+ this.sitemap_wrapper = document.getElementById("sitemap-wrapper")
80
+ this._observe()
81
+ PageSorter()
82
+ }
83
+
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()
91
+ }
92
+
93
+ // Filters the sitemap
94
+ filter(term) {
95
+ const results = []
96
+
97
+ this.items.forEach(function (item) {
98
+ if (
99
+ term !== "" &&
100
+ item.getAttribute("name").toLowerCase().indexOf(term) !== -1
101
+ ) {
102
+ item.classList.add("highlight")
103
+ item.classList.remove("no-match")
104
+ results.push(item)
105
+ } else {
106
+ item.classList.add("no-match")
107
+ item.classList.remove("highlight")
108
+ }
109
+ })
110
+ this.filter_field_clear.style.display = "inline-block"
111
+ const { length } = results
112
+
113
+ if (length === 1) {
114
+ this.display.style.display = "block"
115
+ this.display.innerText = `1 ${Alchemy.t("page_found")}`
116
+ results[0].scrollIntoView({ behavior: "smooth", block: "center" })
117
+ } else if (length > 1) {
118
+ this.display.style.display = "block"
119
+ this.display.innerText = `${length} ${Alchemy.t("pages_found")}`
120
+ } else {
121
+ this.items.forEach((item) =>
122
+ item.classList.remove("no-match", "highlight")
123
+ )
124
+ this.display.style.display = "none"
125
+ window.scrollTo({
126
+ top: 0,
127
+ left: 0,
128
+ behavior: "smooth"
129
+ })
130
+ this.filter_field_clear.style.display = "none"
131
+ }
132
+ }
133
+
134
+ // Adds onkey up observer to search field
135
+ _observe() {
136
+ this.search_field.addEventListener("keyup", (evt) => {
137
+ const term = evt.target.value
138
+ this.filter(term.toLowerCase())
139
+ })
140
+ this.search_field.addEventListener("focus", () => key.setScope("search"))
141
+ this.filter_field_clear.addEventListener("click", () => {
142
+ this.search_field.value = ""
143
+ this.filter("")
144
+ return false
145
+ })
146
+ }
147
+
148
+ errorHandler(error) {
149
+ Alchemy.growl(error.message || error, "error")
150
+ console.error(error)
151
+ }
152
+ }
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy_cms/admin",
3
- "version": "5.2.6",
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.2.6
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-02-28 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
@@ -270,7 +270,7 @@ dependencies:
270
270
  version: '1.8'
271
271
  - - "<"
272
272
  - !ruby/object:Gem::Version
273
- version: 2.4.2
273
+ version: '3.0'
274
274
  type: :runtime
275
275
  prerelease: false
276
276
  version_requirements: !ruby/object:Gem::Requirement
@@ -280,7 +280,7 @@ dependencies:
280
280
  version: '1.8'
281
281
  - - "<"
282
282
  - !ruby/object:Gem::Version
283
- version: 2.4.2
283
+ version: '3.0'
284
284
  - !ruby/object:Gem::Dependency
285
285
  name: request_store
286
286
  requirement: !ruby/object:Gem::Requirement
@@ -590,6 +590,7 @@ files:
590
590
  - ".hound.yml"
591
591
  - ".localeapp/config.rb"
592
592
  - ".prettierrc"
593
+ - ".rspec"
593
594
  - ".rubocop.yml"
594
595
  - ".yardopts"
595
596
  - CHANGELOG.md
@@ -632,10 +633,8 @@ files:
632
633
  - app/assets/javascripts/alchemy/alchemy.initializer.js.coffee
633
634
  - app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee
634
635
  - app/assets/javascripts/alchemy/alchemy.list_filter.js.coffee
635
- - app/assets/javascripts/alchemy/alchemy.page_sorter.js
636
636
  - app/assets/javascripts/alchemy/alchemy.preview.js.coffee
637
637
  - app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee
638
- - app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee
639
638
  - app/assets/javascripts/alchemy/alchemy.spinner.js
640
639
  - app/assets/javascripts/alchemy/alchemy.string_extension.js.coffee
641
640
  - app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee
@@ -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
@@ -1189,6 +1187,9 @@ files:
1189
1187
  - package/src/__tests__/i18n.spec.js
1190
1188
  - package/src/i18n.js
1191
1189
  - package/src/node_tree.js
1190
+ - package/src/page_publication_fields.js
1191
+ - package/src/page_sorter.js
1192
+ - package/src/sitemap.js
1192
1193
  - package/src/translations.js
1193
1194
  - package/src/utils/__tests__/ajax.spec.js
1194
1195
  - package/src/utils/__tests__/events.spec.js
@@ -1213,7 +1214,6 @@ files:
1213
1214
  - vendor/assets/javascripts/flatpickr/flatpickr.min.js
1214
1215
  - vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js
1215
1216
  - vendor/assets/javascripts/jquery_plugins/jquery.scrollTo.min.js
1216
- - vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js
1217
1217
  - vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js
1218
1218
  - vendor/assets/javascripts/jquery_plugins/select2.js
1219
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,119 +0,0 @@
1
- window.Alchemy = {} if typeof(window.Alchemy) is 'undefined'
2
-
3
- # The admin sitemap Alchemy module
4
- Alchemy.Sitemap =
5
-
6
- # Storing some objects.
7
- init: (options) ->
8
- @search_field = $(".search_input_field")
9
- @filter_field_clear = $('.search_field_clear')
10
- @display = $('#page_filter_result')
11
- @sitemap_wrapper = $('#sitemap-wrapper p.loading')
12
- @template = Handlebars.compile($('#sitemap-template').html())
13
- list_template_regexp = new RegExp '\/' + options.page_root_id, 'g'
14
- list_template_html = $('#sitemap-list').html().replace(list_template_regexp, '/{{id}}')
15
- @list_template = Handlebars.compile(list_template_html)
16
- @items = null
17
- @options = options
18
- @watchPagePublicationState()
19
- true
20
-
21
- Handlebars.registerPartial('list', list_template_html)
22
-
23
- @fetch()
24
-
25
- # Fetches the sitemap from JSON
26
- fetch: (foldingId) ->
27
- self = Alchemy.Sitemap
28
-
29
- if foldingId
30
- spinner = new Alchemy.Spinner('small')
31
- spinTarget = $('#fold_button_' + foldingId)
32
- renderTarget = $('#page_' + foldingId)
33
- renderTemplate = @list_template
34
- pageId = foldingId
35
- else
36
- spinner = @options.spinner || new Alchemy.Spinner('medium')
37
- spinTarget = @sitemap_wrapper
38
- renderTarget = @sitemap_wrapper
39
- renderTemplate = @template
40
- pageId = @options.page_root_id
41
-
42
- spinner.spin(spinTarget[0])
43
-
44
- request = $.ajax url: @options.url, data:
45
- id: pageId
46
- full: @options.full
47
-
48
- request.done (data) ->
49
- # This will also remove the spinner
50
- renderTarget.replaceWith(renderTemplate({children: data.pages}))
51
- self.items = $(".sitemap_page", '#sitemap')
52
- self._observe()
53
-
54
- if self.options.ready
55
- self.options.ready()
56
-
57
- request.fail (jqXHR, status) ->
58
- console.warn("Request failed: " + status)
59
-
60
- # Filters the sitemap
61
- filter: (term) ->
62
- results = []
63
- self = Alchemy.Sitemap
64
- self.items.map ->
65
- item = $(this)
66
- if term != '' && item.attr('name').toLowerCase().indexOf(term) != -1
67
- item.addClass('highlight')
68
- item.removeClass('no-match')
69
- results.push item
70
- else
71
- item.addClass('no-match')
72
- item.removeClass('highlight')
73
- self.filter_field_clear.show()
74
- length = results.length
75
- if length == 1
76
- self.display.show().text("1 #{Alchemy.t('page_found')}")
77
- $.scrollTo(results[0], {duration: 400, offset: -80})
78
- else if length > 1
79
- self.display.show().text("#{length} #{Alchemy.t('pages_found')}")
80
- else
81
- self.items.removeClass('no-match highlight')
82
- self.display.hide()
83
- $.scrollTo('0', 400)
84
- self.filter_field_clear.hide()
85
-
86
- # Adds onkey up observer to search field
87
- _observe: ->
88
- filter = @filter
89
- @search_field.on 'keyup', ->
90
- term = $(this).val()
91
- filter(term.toLowerCase())
92
- @search_field.on 'focus', ->
93
- key.setScope('search')
94
- @filter_field_clear.click =>
95
- @search_field.val('')
96
- filter('')
97
- false
98
-
99
- # Handles the page publication date fields
100
- watchPagePublicationState: ->
101
- $(document).on 'DialogReady.Alchemy', (e, $dialog) ->
102
- $public_on_field = $('#page_public_on', $dialog)
103
- $public_until_field = $('#page_public_until', $dialog)
104
- $publication_date_fields = $('.page-publication-date-fields', $dialog)
105
-
106
- $('#page_public', $dialog).click ->
107
- $checkbox = $(this)
108
- format = $checkbox.data('date-format')
109
- now = new Date()
110
- if $checkbox.is(':checked')
111
- $publication_date_fields.removeClass('hidden')
112
- $public_on_field[0]._flatpickr.setDate(now)
113
- else
114
- $publication_date_fields.addClass('hidden')
115
- $public_on_field.val('')
116
- $public_until_field.val('')
117
- true
118
-
119
- return
@@ -1,2 +0,0 @@
1
- $('#fold_button_<%= @page.id %>').css('background', 'none');
2
- Alchemy.Sitemap.fetch(<%= @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 %>