alchemy_cms 6.0.0.pre.rc3 → 6.0.0.pre.rc6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +0 -7
  3. data/CHANGELOG.md +26 -0
  4. data/alchemy_cms.gemspec +1 -1
  5. data/app/assets/javascripts/alchemy/admin.js +0 -2
  6. data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +6 -1
  7. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +2 -2
  8. data/app/assets/stylesheets/alchemy/_extends.scss +4 -4
  9. data/app/assets/stylesheets/alchemy/flatpickr.scss +182 -232
  10. data/app/assets/stylesheets/alchemy/sitemap.scss +7 -1
  11. data/app/controllers/alchemy/admin/base_controller.rb +9 -3
  12. data/app/controllers/alchemy/admin/pages_controller.rb +1 -3
  13. data/app/models/alchemy/page.rb +9 -3
  14. data/app/services/alchemy/tag_validations.rb +21 -0
  15. data/app/views/alchemy/admin/pages/_sitemap.html.erb +8 -8
  16. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  17. data/app/views/alchemy/admin/pages/fold.js.erb +1 -1
  18. data/app/views/alchemy/admin/pages/update.js.erb +0 -5
  19. data/config/locales/alchemy.en.yml +0 -1
  20. data/config/routes.rb +0 -1
  21. data/db/migrate/20200226213334_alchemy_four_point_four.rb +30 -30
  22. data/db/migrate/20200423073425_create_alchemy_essence_nodes.rb +1 -1
  23. data/db/migrate/20200504210159_remove_site_id_from_nodes.rb +1 -1
  24. data/db/migrate/20200505215518_add_language_id_foreign_key_to_alchemy_pages.rb +1 -1
  25. data/db/migrate/20200511113603_add_menu_type_to_alchemy_nodes.rb +1 -1
  26. data/db/migrate/20200514091507_make_page_layoutpage_null_false.rb +1 -1
  27. data/db/migrate/20200519073500_remove_visible_from_alchemy_pages.rb +1 -1
  28. data/db/migrate/20200617110713_create_alchemy_picture_thumbs.rb +1 -1
  29. data/db/migrate/20200907111332_remove_tri_state_booleans.rb +1 -1
  30. data/db/migrate/20201207131309_create_page_versions.rb +1 -1
  31. data/db/migrate/20201207135820_add_page_version_id_to_alchemy_elements.rb +1 -1
  32. data/lib/alchemy/engine.rb +5 -4
  33. data/lib/alchemy/error_tracking.rb +14 -0
  34. data/lib/alchemy/version.rb +1 -1
  35. data/lib/alchemy_cms.rb +1 -0
  36. data/package/admin.js +7 -1
  37. data/package/src/datepicker.js +39 -0
  38. data/package/src/page_publication_fields.js +27 -0
  39. data/package/src/sitemap.js +133 -0
  40. data/package.json +2 -1
  41. metadata +11 -8
  42. data/app/assets/javascripts/alchemy/alchemy.datepicker.js.coffee +0 -29
  43. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +0 -119
@@ -0,0 +1,27 @@
1
+ // Handles the page publication date fields
2
+ export default function () {
3
+ document.addEventListener("DialogReady.Alchemy", function (evt) {
4
+ const dialog = evt.detail.body
5
+ const public_on_field = dialog.querySelector("#page_public_on")
6
+ const public_until_field = dialog.querySelector("#page_public_until")
7
+ const publication_date_fields = dialog.querySelector(
8
+ ".page-publication-date-fields"
9
+ )
10
+
11
+ dialog
12
+ .querySelector("#page_public")
13
+ .addEventListener("click", function (evt) {
14
+ const checkbox = evt.target
15
+ const now = new Date()
16
+
17
+ if (checkbox.checked) {
18
+ publication_date_fields.classList.remove("hidden")
19
+ public_on_field._flatpickr.setDate(now)
20
+ } else {
21
+ publication_date_fields.classList.add("hidden")
22
+ public_on_field.value = ""
23
+ }
24
+ public_until_field.value = ""
25
+ })
26
+ })
27
+ }
@@ -0,0 +1,133 @@
1
+ // The admin sitemap Alchemy class
2
+
3
+ export default class Sitemap {
4
+ // Storing some objects.
5
+ constructor(options) {
6
+ const list_template_regexp = new RegExp("/" + options.page_root_id, "g")
7
+ const list_template_html = document
8
+ .getElementById("sitemap-list")
9
+ .innerHTML.replace(list_template_regexp, "/{{id}}")
10
+ this.search_field = document.querySelector(".search_input_field")
11
+ this.filter_field_clear = document.querySelector(".search_field_clear")
12
+ this.filter_field_clear.removeAttribute("href")
13
+ this.display = document.getElementById("page_filter_result")
14
+ this.sitemap_wrapper = document.getElementById("sitemap-wrapper")
15
+ this.template = Handlebars.compile(
16
+ document.getElementById("sitemap-template").innerHTML
17
+ )
18
+ this.list_template = Handlebars.compile(list_template_html)
19
+ this.items = null
20
+ this.options = options
21
+ Handlebars.registerPartial("list", list_template_html)
22
+ this.load(options.page_root_id)
23
+ }
24
+
25
+ // Loads the sitemap
26
+ load(pageId) {
27
+ const spinner = this.options.spinner || new Alchemy.Spinner("medium")
28
+ const spinTarget = this.sitemap_wrapper
29
+ spinTarget.innerHTML = ""
30
+ 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
+ })
37
+ }
38
+
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
+ }
50
+
51
+ fetch(url) {
52
+ return fetch(url).catch((error) => console.warn(`Request failed: ${error}`))
53
+ }
54
+
55
+ // Renders the sitemap
56
+ render(data, foldingId) {
57
+ let renderTarget, renderTemplate
58
+
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
+ }
68
+ this.items = document
69
+ .getElementById("sitemap")
70
+ .querySelectorAll(".sitemap_page")
71
+ this.sitemap_wrapper = document.getElementById("sitemap-wrapper")
72
+ this._observe()
73
+
74
+ if (this.options.ready) {
75
+ this.options.ready()
76
+ }
77
+ }
78
+
79
+ // Filters the sitemap
80
+ filter(term) {
81
+ const results = []
82
+
83
+ this.items.forEach(function (item) {
84
+ if (
85
+ term !== "" &&
86
+ item.getAttribute("name").toLowerCase().indexOf(term) !== -1
87
+ ) {
88
+ item.classList.add("highlight")
89
+ item.classList.remove("no-match")
90
+ results.push(item)
91
+ } else {
92
+ item.classList.add("no-match")
93
+ item.classList.remove("highlight")
94
+ }
95
+ })
96
+ this.filter_field_clear.style.display = "inline-block"
97
+ const { length } = results
98
+
99
+ if (length === 1) {
100
+ this.display.style.display = "block"
101
+ this.display.innerText = `1 ${Alchemy.t("page_found")}`
102
+ results[0].scrollIntoView({ behavior: "smooth", block: "center" })
103
+ } else if (length > 1) {
104
+ this.display.style.display = "block"
105
+ this.display.innerText = `${length} ${Alchemy.t("pages_found")}`
106
+ } else {
107
+ this.items.forEach((item) =>
108
+ item.classList.remove("no-match", "highlight")
109
+ )
110
+ this.display.style.display = "none"
111
+ window.scrollTo({
112
+ top: 0,
113
+ left: 0,
114
+ behavior: "smooth"
115
+ })
116
+ this.filter_field_clear.style.display = "none"
117
+ }
118
+ }
119
+
120
+ // Adds onkey up observer to search field
121
+ _observe() {
122
+ this.search_field.addEventListener("keyup", (evt) => {
123
+ const term = evt.target.value
124
+ this.filter(term.toLowerCase())
125
+ })
126
+ this.search_field.addEventListener("focus", () => key.setScope("search"))
127
+ this.filter_field_clear.addEventListener("click", () => {
128
+ this.search_field.value = ""
129
+ this.filter("")
130
+ return false
131
+ })
132
+ }
133
+ }
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy_cms/admin",
3
- "version": "6.0.0-rc3",
3
+ "version": "6.0.0-rc6",
4
4
  "description": "AlchemyCMS",
5
5
  "browser": "package/admin.js",
6
6
  "files": [
@@ -24,6 +24,7 @@
24
24
  },
25
25
  "homepage": "https://github.com/AlchemyCMS/alchemy_cms#readme",
26
26
  "dependencies": {
27
+ "flatpickr": "^4.6.9",
27
28
  "lodash-es": "^4.17.21",
28
29
  "sortablejs": "^1.10.2"
29
30
  },
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: 6.0.0.pre.rc3
4
+ version: 6.0.0.pre.rc6
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: 2021-11-24 00:00:00.000000000 Z
16
+ date: 2022-03-05 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: actionmailer
@@ -408,9 +408,9 @@ dependencies:
408
408
  - - ">="
409
409
  - !ruby/object:Gem::Version
410
410
  version: '1.8'
411
- - - "<"
411
+ - - "<="
412
412
  - !ruby/object:Gem::Version
413
- version: 2.4.2
413
+ version: 2.5.0
414
414
  type: :runtime
415
415
  prerelease: false
416
416
  version_requirements: !ruby/object:Gem::Requirement
@@ -418,9 +418,9 @@ dependencies:
418
418
  - - ">="
419
419
  - !ruby/object:Gem::Version
420
420
  version: '1.8'
421
- - - "<"
421
+ - - "<="
422
422
  - !ruby/object:Gem::Version
423
- version: 2.4.2
423
+ version: 2.5.0
424
424
  - !ruby/object:Gem::Dependency
425
425
  name: request_store
426
426
  requirement: !ruby/object:Gem::Requirement
@@ -757,7 +757,6 @@ files:
757
757
  - app/assets/javascripts/alchemy/alchemy.buttons.js.coffee
758
758
  - app/assets/javascripts/alchemy/alchemy.char_counter.js.coffee
759
759
  - app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee
760
- - app/assets/javascripts/alchemy/alchemy.datepicker.js.coffee
761
760
  - app/assets/javascripts/alchemy/alchemy.dialog.js.coffee
762
761
  - app/assets/javascripts/alchemy/alchemy.dirty.js.coffee
763
762
  - app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee
@@ -776,7 +775,6 @@ files:
776
775
  - app/assets/javascripts/alchemy/alchemy.page_sorter.js
777
776
  - app/assets/javascripts/alchemy/alchemy.preview.js.coffee
778
777
  - app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee
779
- - app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee
780
778
  - app/assets/javascripts/alchemy/alchemy.spinner.js
781
779
  - app/assets/javascripts/alchemy/alchemy.string_extension.js.coffee
782
780
  - app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee
@@ -1008,6 +1006,7 @@ files:
1008
1006
  - app/serializers/alchemy/picture_serializer.rb
1009
1007
  - app/services/alchemy/delete_elements.rb
1010
1008
  - app/services/alchemy/duplicate_element.rb
1009
+ - app/services/alchemy/tag_validations.rb
1011
1010
  - app/views/alchemy/_edit_mode.html.erb
1012
1011
  - app/views/alchemy/_menubar.html.erb
1013
1012
  - app/views/alchemy/_preview_mode_code.html.erb
@@ -1308,6 +1307,7 @@ files:
1308
1307
  - lib/alchemy/element_definition.rb
1309
1308
  - lib/alchemy/elements_finder.rb
1310
1309
  - lib/alchemy/engine.rb
1310
+ - lib/alchemy/error_tracking.rb
1311
1311
  - lib/alchemy/errors.rb
1312
1312
  - lib/alchemy/essence.rb
1313
1313
  - lib/alchemy/filetypes.rb
@@ -1424,12 +1424,15 @@ files:
1424
1424
  - package.json
1425
1425
  - package/admin.js
1426
1426
  - package/src/__tests__/i18n.spec.js
1427
+ - package/src/datepicker.js
1427
1428
  - package/src/file_editors.js
1428
1429
  - package/src/i18n.js
1429
1430
  - package/src/image_cropper.js
1430
1431
  - package/src/image_loader.js
1431
1432
  - package/src/node_tree.js
1433
+ - package/src/page_publication_fields.js
1432
1434
  - package/src/picture_editors.js
1435
+ - package/src/sitemap.js
1433
1436
  - package/src/translations.js
1434
1437
  - package/src/utils/__tests__/ajax.spec.js
1435
1438
  - package/src/utils/__tests__/events.spec.js
@@ -1,29 +0,0 @@
1
- window.Alchemy = {} if typeof(window.Alchemy) is 'undefined'
2
-
3
- $.extend Alchemy,
4
-
5
- Datepicker: (scope) ->
6
- $datepicker_inputs = $('input[data-datepicker-type]', scope)
7
-
8
- # Initializes the datepickers on the text inputs and sets the proper type
9
- # to enable browsers default datepicker if the current OS is iOS.
10
- if Alchemy.isiOS
11
- $datepicker_inputs.prop "type", ->
12
- return $(this).data('datepicker-type')
13
- else
14
- $datepicker_inputs.each ->
15
- type = $(this).data('datepicker-type')
16
- options =
17
- # alchemy_i18n supports `zh_CN` etc., but flatpickr only has two-letter codes (`zh`)
18
- locale: Alchemy.locale.slice(0, 2)
19
- altInput: true
20
- altFormat: Alchemy.t("formats.#{type}")
21
- altInputClass: ""
22
- enableTime: /time/.test(type)
23
- noCalendar: type == "time"
24
- time_24hr: Alchemy.t("formats.time_24hr")
25
- onValueUpdate: (_selectedDates, _dateStr, instance) ->
26
- Alchemy.setElementDirty $(instance.element).closest(".element-editor")
27
- $(this).flatpickr(options)
28
-
29
- return
@@ -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