alchemy_cms 5.2.6 → 5.3.1

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.

Potentially problematic release.


This version of alchemy_cms might be problematic. Click here for more details.

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 %>