alchemy_cms 5.2.5 → 5.3.0
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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.rspec +1 -0
- data/CHANGELOG.md +9 -0
- data/Rakefile +18 -0
- data/alchemy_cms.gemspec +2 -2
- data/app/assets/javascripts/alchemy/admin.js +0 -1
- data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +6 -1
- data/app/assets/javascripts/alchemy/page_select.js +13 -8
- data/app/assets/stylesheets/alchemy/sitemap.scss +7 -1
- data/app/controllers/alchemy/admin/base_controller.rb +9 -3
- data/app/controllers/alchemy/admin/pages_controller.rb +2 -1
- data/app/models/alchemy/page.rb +3 -2
- data/app/models/alchemy/picture/transformations.rb +3 -3
- data/app/views/alchemy/admin/pages/_form.html.erb +19 -0
- data/app/views/alchemy/admin/pages/_new_page_form.html.erb +16 -5
- data/app/views/alchemy/admin/pages/_sitemap.html.erb +8 -8
- data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
- data/app/views/alchemy/admin/pages/fold.js.erb +1 -1
- data/app/views/alchemy/admin/pages/update.js.erb +7 -0
- data/config/initializers/dragonfly.rb +8 -0
- data/lib/alchemy/dragonfly/processors/crop_resize.rb +35 -0
- data/lib/alchemy/engine.rb +7 -0
- data/lib/alchemy/error_tracking/airbrake_handler.rb +13 -0
- data/lib/alchemy/error_tracking.rb +14 -0
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +1 -0
- data/package/admin.js +5 -1
- data/package/src/page_publication_fields.js +27 -0
- data/package/src/sitemap.js +133 -0
- data/package.json +1 -1
- metadata +10 -17
- data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +0 -119
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b27eb5b4a0bdb143597a3e2cc33d0a8bfa4dd0d8febb2fd9261f2ad692716bc8
|
4
|
+
data.tar.gz: 7c67ed54f66b70419ac653421bdf3f8d38389bd3e67acfe70675bfcc8b357abd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aad3d674038b933605c7c8b5d11d74c707ed2687f9a5350e7762d56882a8994f85ea0a7e86e60ed4ee6cddb32aa46ecde70c42bcf91ceca62ad00e071195c497
|
7
|
+
data.tar.gz: c2f4a299345aafda98c2d40465ab8e011ee5d345104d32342503197afedccc4ed7bc527cce09afe8163dad148fe2019f3a2e18b0a0770eb48d971c9df51239dc
|
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 5.2.7 (2022-03-01)
|
2
|
+
|
3
|
+
- Fix copying page with descendants to a different language ([dbwinger](https://github.com/dbwinger))
|
4
|
+
- Handle copying/pasting global pages ([dbwinger](https://github.com/dbwinger))
|
5
|
+
|
6
|
+
## 5.2.6 (2022-02-28)
|
7
|
+
|
8
|
+
- Add crop_resize Dragonfly processor ([tvdeyen](https://github.com/tvdeyen))
|
9
|
+
|
1
10
|
## 5.2.5 (2021-11-24)
|
2
11
|
|
3
12
|
- Adjust tinymce skin assets urls again ([tvdeyen](https://github.com/tvdeyen))
|
data/Rakefile
CHANGED
@@ -41,12 +41,16 @@ namespace :alchemy do
|
|
41
41
|
task :prepare do
|
42
42
|
system(
|
43
43
|
<<~BASH
|
44
|
+
yarn install && \
|
45
|
+
yarn link && \
|
44
46
|
cd spec/dummy && \
|
45
47
|
export RAILS_ENV=test && \
|
46
48
|
bin/rake db:create && \
|
47
49
|
bin/rake db:environment:set && \
|
48
50
|
bin/rake db:migrate:reset && \
|
49
51
|
bin/rails g alchemy:install --skip --skip-demo-files --auto-accept && \
|
52
|
+
yarn link @alchemy_cms/admin && \
|
53
|
+
RAILS_ENV=test bin/webpack && \
|
50
54
|
cd -
|
51
55
|
BASH
|
52
56
|
) || fail
|
@@ -72,4 +76,18 @@ namespace :alchemy do
|
|
72
76
|
File.delete(backup)
|
73
77
|
end
|
74
78
|
end
|
79
|
+
|
80
|
+
desc "Release a new Ruby gem and npm package in one command"
|
81
|
+
task :release do
|
82
|
+
require "json"
|
83
|
+
require_relative "lib/alchemy/version"
|
84
|
+
package = File.read("package.json")
|
85
|
+
unless JSON.parse(package)["version"] == Alchemy.version
|
86
|
+
abort "Ruby gem and npm package versions are out of sync! Please fix."
|
87
|
+
end
|
88
|
+
# Release the Ruby gem with bundler
|
89
|
+
Rake::Task["release"].invoke
|
90
|
+
# Publish npm package via CLI
|
91
|
+
system "npm publish"
|
92
|
+
end
|
75
93
|
end
|
data/alchemy_cms.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |gem|
|
|
23
23
|
gem.add_runtime_dependency 'awesome_nested_set', ['~> 3.1']
|
24
24
|
gem.add_runtime_dependency 'cancancan', ['>= 2.1', '< 4.0']
|
25
25
|
gem.add_runtime_dependency 'coffee-rails', ['>= 4.0', '< 6.0']
|
26
|
-
gem.add_runtime_dependency 'dragonfly', ['~> 1.
|
26
|
+
gem.add_runtime_dependency 'dragonfly', ['~> 1.4']
|
27
27
|
gem.add_runtime_dependency 'dragonfly_svg', ['~> 0.0.4']
|
28
28
|
gem.add_runtime_dependency 'gutentag', ['~> 2.2', '>= 2.2.1']
|
29
29
|
gem.add_runtime_dependency 'handlebars_assets', ['~> 0.23']
|
@@ -33,7 +33,7 @@ Gem::Specification.new do |gem|
|
|
33
33
|
gem.add_runtime_dependency 'originator', ['~> 3.1']
|
34
34
|
gem.add_runtime_dependency 'non-stupid-digest-assets', ['~> 1.0.8']
|
35
35
|
gem.add_runtime_dependency 'rails', ['>= 5.2.0', '< 6.1']
|
36
|
-
gem.add_runtime_dependency 'ransack', ['>= 1.8', '<
|
36
|
+
gem.add_runtime_dependency 'ransack', ['>= 1.8', '< 3.0']
|
37
37
|
gem.add_runtime_dependency 'request_store', ['~> 1.2']
|
38
38
|
gem.add_runtime_dependency 'responders', ['>= 2.0', '< 4.0']
|
39
39
|
gem.add_runtime_dependency 'sassc-rails', ['~> 2.1']
|
@@ -40,7 +40,6 @@
|
|
40
40
|
//= require alchemy/alchemy.page_sorter
|
41
41
|
//= require alchemy/alchemy.uploader
|
42
42
|
//= require alchemy/alchemy.preview_window
|
43
|
-
//= require alchemy/alchemy.sitemap
|
44
43
|
//= require alchemy/alchemy.spinner
|
45
44
|
//= require alchemy/alchemy.tinymce
|
46
45
|
//= require alchemy/alchemy.tooltips
|
@@ -82,7 +82,12 @@ class window.Alchemy.Dialog
|
|
82
82
|
@dialog_body.hide()
|
83
83
|
@dialog_body.html(data)
|
84
84
|
@init()
|
85
|
-
@dialog.
|
85
|
+
@dialog[0].dispatchEvent(new CustomEvent(
|
86
|
+
"DialogReady.Alchemy",
|
87
|
+
bubbles: true
|
88
|
+
detail:
|
89
|
+
body: @dialog_body[0]
|
90
|
+
))
|
86
91
|
if @options.ready?
|
87
92
|
@options.ready(@dialog_body)
|
88
93
|
@dialog_body.show('fade', 200)
|
@@ -1,9 +1,11 @@
|
|
1
|
-
$.fn.alchemyPageSelect = function(options) {
|
1
|
+
$.fn.alchemyPageSelect = function (options) {
|
2
2
|
var pageTemplate = HandlebarsTemplates.page
|
3
3
|
|
4
4
|
return this.select2({
|
5
5
|
placeholder: options.placeholder,
|
6
|
-
allowClear:
|
6
|
+
allowClear: options.hasOwnProperty("allowClear")
|
7
|
+
? options.allowClear
|
8
|
+
: true,
|
7
9
|
minimumInputLength: 3,
|
8
10
|
initSelection: function (_$el, callback) {
|
9
11
|
if (options.initialSelection) {
|
@@ -12,13 +14,16 @@ $.fn.alchemyPageSelect = function(options) {
|
|
12
14
|
},
|
13
15
|
ajax: {
|
14
16
|
url: options.url,
|
15
|
-
datatype:
|
17
|
+
datatype: "json",
|
16
18
|
quietMillis: 300,
|
17
19
|
data: function (term, page) {
|
18
20
|
return {
|
19
|
-
q: $.extend(
|
20
|
-
|
21
|
-
|
21
|
+
q: $.extend(
|
22
|
+
{
|
23
|
+
name_cont: term
|
24
|
+
},
|
25
|
+
options.query_params
|
26
|
+
),
|
22
27
|
page: page
|
23
28
|
}
|
24
29
|
},
|
@@ -34,8 +39,8 @@ $.fn.alchemyPageSelect = function(options) {
|
|
34
39
|
formatSelection: function (page) {
|
35
40
|
return page.text || page.name
|
36
41
|
},
|
37
|
-
formatResult: function(page) {
|
38
|
-
return pageTemplate({page: page})
|
42
|
+
formatResult: function (page) {
|
43
|
+
return pageTemplate({ page: page })
|
39
44
|
}
|
40
45
|
})
|
41
46
|
}
|
@@ -23,7 +23,7 @@ $sitemap-url-xlarge-width: 350px;
|
|
23
23
|
|
24
24
|
#sitemap-wrapper {
|
25
25
|
position: relative;
|
26
|
-
min-height: calc(100vh -
|
26
|
+
min-height: calc(100vh - 148px);
|
27
27
|
}
|
28
28
|
|
29
29
|
.sitemap_pagename_link {
|
@@ -154,6 +154,8 @@ $sitemap-url-xlarge-width: 350px;
|
|
154
154
|
position: absolute;
|
155
155
|
left: -23px;
|
156
156
|
top: 0;
|
157
|
+
width: 16px;
|
158
|
+
height: $sitemap-line-height;
|
157
159
|
}
|
158
160
|
|
159
161
|
.placeholder {
|
@@ -258,3 +260,7 @@ $sitemap-url-xlarge-width: 350px;
|
|
258
260
|
}
|
259
261
|
}
|
260
262
|
}
|
263
|
+
|
264
|
+
#search_field_clear {
|
265
|
+
cursor: pointer;
|
266
|
+
}
|
@@ -40,9 +40,7 @@ module Alchemy
|
|
40
40
|
def exception_handler(error)
|
41
41
|
exception_logger(error)
|
42
42
|
show_error_notice(error)
|
43
|
-
|
44
|
-
notify_airbrake(error) unless Rails.env.development? || Rails.env.test?
|
45
|
-
end
|
43
|
+
notify_error_tracker(error)
|
46
44
|
end
|
47
45
|
|
48
46
|
# Displays an error notice in the Alchemy backend.
|
@@ -148,6 +146,14 @@ module Alchemy
|
|
148
146
|
site
|
149
147
|
end
|
150
148
|
end
|
149
|
+
|
150
|
+
def notify_error_tracker(exception)
|
151
|
+
if ::Alchemy::ErrorTracking.notification_handler.respond_to?(:call)
|
152
|
+
::Alchemy::ErrorTracking.notification_handler.call(exception)
|
153
|
+
else
|
154
|
+
Rails.logger.warn("To use the Alchemy::ErrorTracking.notification_handler, it must respond to #call.")
|
155
|
+
end
|
156
|
+
end
|
151
157
|
end
|
152
158
|
end
|
153
159
|
end
|
@@ -128,6 +128,7 @@ module Alchemy
|
|
128
128
|
def update
|
129
129
|
# stores old page_layout value, because unfurtunally rails @page.changes does not work here.
|
130
130
|
@old_page_layout = @page.page_layout
|
131
|
+
@old_parent_id = @page.parent_id
|
131
132
|
if @page.update(page_params)
|
132
133
|
@notice = Alchemy.t("Page saved", name: @page.name)
|
133
134
|
@while_page_edit = request.referer.include?("edit")
|
@@ -136,7 +137,7 @@ module Alchemy
|
|
136
137
|
@tree = serialized_page_tree
|
137
138
|
end
|
138
139
|
else
|
139
|
-
configure
|
140
|
+
render :configure
|
140
141
|
end
|
141
142
|
end
|
142
143
|
|
data/app/models/alchemy/page.rb
CHANGED
@@ -217,8 +217,8 @@ module Alchemy
|
|
217
217
|
|
218
218
|
def copy_and_paste(source, new_parent, new_name)
|
219
219
|
page = copy(source, {
|
220
|
-
|
221
|
-
language: new_parent
|
220
|
+
parent: new_parent,
|
221
|
+
language: new_parent&.language,
|
222
222
|
name: new_name,
|
223
223
|
title: new_name,
|
224
224
|
})
|
@@ -415,6 +415,7 @@ module Alchemy
|
|
415
415
|
next if child == new_parent
|
416
416
|
|
417
417
|
new_child = Page.copy(child, {
|
418
|
+
parent_id: new_parent.id,
|
418
419
|
language_id: new_parent.language_id,
|
419
420
|
language_code: new_parent.language_code,
|
420
421
|
})
|
@@ -197,12 +197,12 @@ module Alchemy
|
|
197
197
|
# Use imagemagick to custom crop an image. Uses -thumbnail for better performance when resizing.
|
198
198
|
#
|
199
199
|
def xy_crop_resize(dimensions, top_left, crop_dimensions, upsample)
|
200
|
-
crop_argument =
|
200
|
+
crop_argument = dimensions_to_string(crop_dimensions)
|
201
201
|
crop_argument += "+#{top_left[:x]}+#{top_left[:y]}"
|
202
202
|
|
203
|
-
resize_argument =
|
203
|
+
resize_argument = dimensions_to_string(dimensions)
|
204
204
|
resize_argument += ">" unless upsample
|
205
|
-
image_file.
|
205
|
+
image_file.crop_resize(crop_argument, resize_argument)
|
206
206
|
end
|
207
207
|
|
208
208
|
# Used when centercropping.
|
@@ -1,4 +1,8 @@
|
|
1
1
|
<%= alchemy_form_for [:admin, @page], class: 'edit_page' do |f| %>
|
2
|
+
<% unless @page.language_root? || @page.layoutpage %>
|
3
|
+
<%= f.input :parent_id, required: true, input_html: { class: 'alchemy_selectbox' } %>
|
4
|
+
<% end %>
|
5
|
+
|
2
6
|
<%= f.input :page_layout,
|
3
7
|
collection: @page_layouts,
|
4
8
|
label: page_layout_label(@page),
|
@@ -43,3 +47,18 @@
|
|
43
47
|
|
44
48
|
<%= f.submit Alchemy.t(:save) %>
|
45
49
|
<% end %>
|
50
|
+
|
51
|
+
<script>
|
52
|
+
$('#page_parent_id').alchemyPageSelect({
|
53
|
+
placeholder: "<%= Alchemy.t(:search_page) %>",
|
54
|
+
url: "<%= alchemy.api_pages_path %>",
|
55
|
+
allowClear: false,
|
56
|
+
<% if @page.parent %>
|
57
|
+
initialSelection: {
|
58
|
+
id: <%= @page.parent.id %>,
|
59
|
+
text: "<%= @page.parent.name %>",
|
60
|
+
url_path: "<%= @page.parent.url_path %>"
|
61
|
+
}
|
62
|
+
<% end %>
|
63
|
+
})
|
64
|
+
</script>
|
@@ -3,11 +3,7 @@
|
|
3
3
|
<%= f.hidden_field(:parent_id) %>
|
4
4
|
<% else %>
|
5
5
|
<% @page.parent = @current_language.root_page %>
|
6
|
-
<%= f.input :parent_id,
|
7
|
-
collection: @current_language.pages.contentpages,
|
8
|
-
label_method: :name,
|
9
|
-
value_method: :id,
|
10
|
-
input_html: { class: "alchemy_selectbox" } %>
|
6
|
+
<%= f.input :parent_id, as: :string, input_html: { class: 'alchemy_selectbox' } %>
|
11
7
|
<% end %>
|
12
8
|
<%= f.hidden_field(:language_id) %>
|
13
9
|
<%= f.hidden_field(:layoutpage) %>
|
@@ -21,3 +17,18 @@
|
|
21
17
|
<%= f.input :name %>
|
22
18
|
<%= f.submit Alchemy.t(:create) %>
|
23
19
|
<% end %>
|
20
|
+
|
21
|
+
<script>
|
22
|
+
$('input[type="text"]#page_parent_id').alchemyPageSelect({
|
23
|
+
placeholder: "<%= Alchemy.t(:search_page) %>",
|
24
|
+
url: "<%= alchemy.api_pages_path %>",
|
25
|
+
allowClear: false,
|
26
|
+
<% if @page.parent %>
|
27
|
+
initialSelection: {
|
28
|
+
id: <%= @page.parent.id %>,
|
29
|
+
text: "<%= @page.parent.name %>",
|
30
|
+
url_path: "<%= @page.parent.url_path %>"
|
31
|
+
}
|
32
|
+
<% end %>
|
33
|
+
})
|
34
|
+
</script>
|
@@ -1,11 +1,10 @@
|
|
1
|
-
<
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
</h4>
|
1
|
+
<h4 id="sitemap_heading">
|
2
|
+
<span class="page_name"><%= Alchemy::Page.human_attribute_name(:name) %></span>
|
3
|
+
<span class="page_urlname"><%= Alchemy::Page.human_attribute_name(:urlname) %></span>
|
4
|
+
<span class="page_status"><%= Alchemy.t(:page_status) %></span>
|
5
|
+
</h4>
|
7
6
|
|
8
|
-
|
7
|
+
<div id="sitemap-wrapper">
|
9
8
|
</div>
|
10
9
|
|
11
10
|
<script id="sitemap-template" type="text/x-handlebars-template">
|
@@ -22,7 +21,7 @@
|
|
22
21
|
|
23
22
|
<script type="text/javascript">
|
24
23
|
$(function() {
|
25
|
-
Alchemy.Sitemap
|
24
|
+
Alchemy.currentSitemap = new Alchemy.Sitemap({
|
26
25
|
url: '<%= alchemy.tree_admin_pages_path %>',
|
27
26
|
page_root_id: <%= @page_root.id %>,
|
28
27
|
full: <%= full %>
|
@@ -32,5 +31,6 @@
|
|
32
31
|
}
|
33
32
|
<% end %>
|
34
33
|
});
|
34
|
+
Alchemy.PagePublicationFields();
|
35
35
|
});
|
36
36
|
</script>
|
@@ -133,7 +133,7 @@
|
|
133
133
|
if (!not_dirty) Alchemy.pleaseWaitOverlay(false);
|
134
134
|
return not_dirty;
|
135
135
|
});
|
136
|
-
Alchemy.
|
136
|
+
Alchemy.PagePublicationFields();
|
137
137
|
Alchemy.PageLeaveObserver();
|
138
138
|
Alchemy.ElementsWindow.init('<%= alchemy.admin_elements_path(page_id: @page.id) %>', {
|
139
139
|
texts: {
|
@@ -1,2 +1,2 @@
|
|
1
1
|
$('#fold_button_<%= @page.id %>').css('background', 'none');
|
2
|
-
Alchemy.
|
2
|
+
Alchemy.currentSitemap.reload(<%= @page.id %>);
|
@@ -13,6 +13,13 @@
|
|
13
13
|
Alchemy.growl("<%= j @notice %>");
|
14
14
|
Alchemy.closeCurrentDialog();
|
15
15
|
|
16
|
+
<% elsif @page.parent_id != @old_parent_id -%>
|
17
|
+
|
18
|
+
Alchemy.closeCurrentDialog(function() {
|
19
|
+
Alchemy.growl("<%= j @notice %>");
|
20
|
+
Alchemy.currentSitemap.load(<%= @page.get_language_root.id %>);
|
21
|
+
});
|
22
|
+
|
16
23
|
<% else -%>
|
17
24
|
|
18
25
|
if (page) {
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "dragonfly_svg"
|
3
|
+
require "alchemy/dragonfly/processors/crop_resize"
|
3
4
|
|
4
5
|
# Logger
|
5
6
|
Dragonfly.logger = Rails.logger
|
@@ -9,3 +10,10 @@ if defined?(ActiveRecord::Base)
|
|
9
10
|
ActiveRecord::Base.extend Dragonfly::Model
|
10
11
|
ActiveRecord::Base.extend Dragonfly::Model::Validations
|
11
12
|
end
|
13
|
+
|
14
|
+
# Dragonfly 1.4.0 only allows `quality` as argument to `encode`
|
15
|
+
Dragonfly::ImageMagick::Processors::Encode::WHITELISTED_ARGS << "flatten"
|
16
|
+
|
17
|
+
Rails.application.config.after_initialize do
|
18
|
+
Dragonfly.app(:alchemy_pictures).add_processor(:crop_resize, Alchemy::Dragonfly::Processors::CropResize.new)
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dragonfly/image_magick/commands"
|
4
|
+
|
5
|
+
module Alchemy
|
6
|
+
module Dragonfly
|
7
|
+
module Processors
|
8
|
+
class CropResize
|
9
|
+
include ::Dragonfly::ParamValidators
|
10
|
+
|
11
|
+
IS_CROP_ARGUMENT = ->(args_string) {
|
12
|
+
args_string.match?(::Dragonfly::ImageMagick::Processors::Thumb::CROP_GEOMETRY)
|
13
|
+
}
|
14
|
+
|
15
|
+
IS_RESIZE_ARGUMENT = ->(args_string) {
|
16
|
+
args_string.match?(::Dragonfly::ImageMagick::Processors::Thumb::RESIZE_GEOMETRY)
|
17
|
+
}
|
18
|
+
|
19
|
+
def call(content, crop_argument, resize_argument)
|
20
|
+
validate!(crop_argument, &IS_CROP_ARGUMENT)
|
21
|
+
validate!(resize_argument, &IS_RESIZE_ARGUMENT)
|
22
|
+
::Dragonfly::ImageMagick::Commands.convert(
|
23
|
+
content,
|
24
|
+
"-crop #{crop_argument} -resize #{resize_argument}"
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_url(attrs, _args = "", opts = {})
|
29
|
+
format = opts["format"]
|
30
|
+
attrs.ext = format if format
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/alchemy/engine.rb
CHANGED
@@ -44,5 +44,12 @@ module Alchemy
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
initializer "alchemy.error_tracking" do
|
49
|
+
if defined?(Airbrake)
|
50
|
+
require_relative "error_tracking/airbrake_handler"
|
51
|
+
Alchemy::ErrorTracking.notification_handler = Alchemy::ErrorTracking::AirbrakeHandler
|
52
|
+
end
|
53
|
+
end
|
47
54
|
end
|
48
55
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alchemy
|
4
|
+
module ErrorTracking
|
5
|
+
class BaseHandler
|
6
|
+
def self.call(exception)
|
7
|
+
# implement your own notification method
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
mattr_accessor :notification_handler
|
12
|
+
@@notification_handler = BaseHandler
|
13
|
+
end
|
14
|
+
end
|
data/lib/alchemy/version.rb
CHANGED
data/lib/alchemy_cms.rb
CHANGED
@@ -37,6 +37,7 @@ require_relative "alchemy/controller_actions"
|
|
37
37
|
require_relative "alchemy/deprecation"
|
38
38
|
require_relative "alchemy/element_definition"
|
39
39
|
require_relative "alchemy/elements_finder"
|
40
|
+
require_relative "alchemy/error_tracking"
|
40
41
|
require_relative "alchemy/errors"
|
41
42
|
require_relative "alchemy/essence"
|
42
43
|
require_relative "alchemy/filetypes"
|
data/package/admin.js
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
import translate from "./src/i18n"
|
2
2
|
import translationData from "./src/translations"
|
3
3
|
import NodeTree from "./src/node_tree"
|
4
|
+
import Sitemap from "./src/sitemap"
|
5
|
+
import PagePublicationFields from "./src/page_publication_fields.js"
|
4
6
|
|
5
7
|
// Global Alchemy object
|
6
8
|
if (typeof window.Alchemy === "undefined") {
|
@@ -12,5 +14,7 @@ Object.assign(Alchemy, {
|
|
12
14
|
// Global utility method for translating a given string
|
13
15
|
t: translate,
|
14
16
|
translations: Object.assign(Alchemy.translations || {}, translationData),
|
15
|
-
NodeTree
|
17
|
+
NodeTree,
|
18
|
+
Sitemap,
|
19
|
+
PagePublicationFields
|
16
20
|
})
|
@@ -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
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.
|
4
|
+
version: 5.3.0
|
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:
|
16
|
+
date: 2022-03-09 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: active_model_serializers
|
@@ -108,12 +108,6 @@ dependencies:
|
|
108
108
|
requirement: !ruby/object:Gem::Requirement
|
109
109
|
requirements:
|
110
110
|
- - "~>"
|
111
|
-
- !ruby/object:Gem::Version
|
112
|
-
version: '1.0'
|
113
|
-
- - ">="
|
114
|
-
- !ruby/object:Gem::Version
|
115
|
-
version: 1.0.7
|
116
|
-
- - "<"
|
117
111
|
- !ruby/object:Gem::Version
|
118
112
|
version: '1.4'
|
119
113
|
type: :runtime
|
@@ -121,12 +115,6 @@ dependencies:
|
|
121
115
|
version_requirements: !ruby/object:Gem::Requirement
|
122
116
|
requirements:
|
123
117
|
- - "~>"
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: '1.0'
|
126
|
-
- - ">="
|
127
|
-
- !ruby/object:Gem::Version
|
128
|
-
version: 1.0.7
|
129
|
-
- - "<"
|
130
118
|
- !ruby/object:Gem::Version
|
131
119
|
version: '1.4'
|
132
120
|
- !ruby/object:Gem::Dependency
|
@@ -282,7 +270,7 @@ dependencies:
|
|
282
270
|
version: '1.8'
|
283
271
|
- - "<"
|
284
272
|
- !ruby/object:Gem::Version
|
285
|
-
version:
|
273
|
+
version: '3.0'
|
286
274
|
type: :runtime
|
287
275
|
prerelease: false
|
288
276
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -292,7 +280,7 @@ dependencies:
|
|
292
280
|
version: '1.8'
|
293
281
|
- - "<"
|
294
282
|
- !ruby/object:Gem::Version
|
295
|
-
version:
|
283
|
+
version: '3.0'
|
296
284
|
- !ruby/object:Gem::Dependency
|
297
285
|
name: request_store
|
298
286
|
requirement: !ruby/object:Gem::Requirement
|
@@ -602,6 +590,7 @@ files:
|
|
602
590
|
- ".hound.yml"
|
603
591
|
- ".localeapp/config.rb"
|
604
592
|
- ".prettierrc"
|
593
|
+
- ".rspec"
|
605
594
|
- ".rubocop.yml"
|
606
595
|
- ".yardopts"
|
607
596
|
- CHANGELOG.md
|
@@ -647,7 +636,6 @@ files:
|
|
647
636
|
- app/assets/javascripts/alchemy/alchemy.page_sorter.js
|
648
637
|
- app/assets/javascripts/alchemy/alchemy.preview.js.coffee
|
649
638
|
- app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee
|
650
|
-
- app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee
|
651
639
|
- app/assets/javascripts/alchemy/alchemy.spinner.js
|
652
640
|
- app/assets/javascripts/alchemy/alchemy.string_extension.js.coffee
|
653
641
|
- app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee
|
@@ -1092,9 +1080,12 @@ files:
|
|
1092
1080
|
- lib/alchemy/configuration_methods.rb
|
1093
1081
|
- lib/alchemy/controller_actions.rb
|
1094
1082
|
- lib/alchemy/deprecation.rb
|
1083
|
+
- lib/alchemy/dragonfly/processors/crop_resize.rb
|
1095
1084
|
- lib/alchemy/element_definition.rb
|
1096
1085
|
- lib/alchemy/elements_finder.rb
|
1097
1086
|
- lib/alchemy/engine.rb
|
1087
|
+
- lib/alchemy/error_tracking.rb
|
1088
|
+
- lib/alchemy/error_tracking/airbrake_handler.rb
|
1098
1089
|
- lib/alchemy/errors.rb
|
1099
1090
|
- lib/alchemy/essence.rb
|
1100
1091
|
- lib/alchemy/filetypes.rb
|
@@ -1198,6 +1189,8 @@ files:
|
|
1198
1189
|
- package/src/__tests__/i18n.spec.js
|
1199
1190
|
- package/src/i18n.js
|
1200
1191
|
- package/src/node_tree.js
|
1192
|
+
- package/src/page_publication_fields.js
|
1193
|
+
- package/src/sitemap.js
|
1201
1194
|
- package/src/translations.js
|
1202
1195
|
- package/src/utils/__tests__/ajax.spec.js
|
1203
1196
|
- package/src/utils/__tests__/events.spec.js
|
@@ -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
|