alchemy_cms 5.0.0.rc1 → 5.1.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
- data/.github/workflows/stale.yml +1 -1
- data/.gitignore +1 -0
- data/.travis.yml +48 -0
- data/CHANGELOG.md +63 -2
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +3 -3
- data/README.md +2 -2
- data/alchemy_cms.gemspec +2 -2
- data/app/assets/images/alchemy/missing-image.svg +1 -0
- data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +1 -4
- data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +0 -3
- data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +29 -4
- data/app/assets/stylesheets/alchemy/_variables.scss +3 -0
- data/app/assets/stylesheets/alchemy/archive.scss +23 -17
- data/app/assets/stylesheets/alchemy/errors.scss +1 -1
- data/app/assets/stylesheets/alchemy/navigation.scss +7 -9
- data/app/assets/stylesheets/alchemy/pagination.scss +1 -1
- data/app/assets/stylesheets/alchemy/search.scss +12 -2
- data/app/assets/stylesheets/alchemy/selects.scss +4 -2
- data/app/assets/stylesheets/alchemy/tags.scss +19 -31
- data/app/controllers/alchemy/admin/pages_controller.rb +11 -2
- data/app/controllers/alchemy/admin/pictures_controller.rb +13 -6
- data/app/controllers/alchemy/admin/resources_controller.rb +3 -3
- data/app/controllers/alchemy/pages_controller.rb +49 -14
- data/app/helpers/alchemy/admin/base_helper.rb +0 -42
- data/app/helpers/alchemy/admin/navigation_helper.rb +2 -1
- data/app/helpers/alchemy/url_helper.rb +2 -2
- data/app/models/alchemy/attachment.rb +21 -1
- data/app/models/alchemy/attachment/url.rb +40 -0
- data/app/models/alchemy/essence_file.rb +1 -1
- data/app/models/alchemy/essence_picture.rb +4 -4
- data/app/models/alchemy/essence_picture_view.rb +10 -4
- data/app/models/alchemy/page.rb +16 -1
- data/app/models/alchemy/page/page_natures.rb +2 -0
- data/app/models/alchemy/page/url_path.rb +8 -6
- data/app/models/alchemy/picture.rb +58 -2
- data/app/models/alchemy/picture/calculations.rb +55 -0
- data/app/models/alchemy/picture/transformations.rb +5 -49
- data/app/models/alchemy/picture/url.rb +28 -75
- data/app/models/alchemy/picture_thumb.rb +57 -0
- data/app/models/alchemy/picture_thumb/create.rb +39 -0
- data/app/models/alchemy/picture_thumb/signature.rb +23 -0
- data/app/models/alchemy/picture_thumb/uid.rb +22 -0
- data/app/models/alchemy/picture_variant.rb +114 -0
- data/app/views/alchemy/admin/attachments/show.html.erb +8 -8
- data/app/views/alchemy/admin/dashboard/index.html.erb +13 -16
- data/app/views/alchemy/admin/essence_pictures/crop.html.erb +1 -1
- data/app/views/alchemy/admin/essence_pictures/edit.html.erb +2 -2
- data/app/views/alchemy/admin/layoutpages/edit.html.erb +4 -6
- data/app/views/alchemy/admin/pages/_form.html.erb +4 -6
- data/app/views/alchemy/admin/pages/_new_page_form.html.erb +2 -1
- data/app/views/alchemy/admin/pages/edit.html.erb +9 -1
- data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +14 -13
- data/app/views/alchemy/admin/partials/_search_form.html.erb +8 -8
- data/app/views/alchemy/admin/pictures/_archive.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_form.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_picture.html.erb +3 -3
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/index.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/show.html.erb +3 -3
- data/app/views/alchemy/admin/resources/_per_page_select.html.erb +3 -3
- data/app/views/alchemy/admin/resources/index.html.erb +24 -22
- data/app/views/alchemy/admin/sites/_form.html.erb +2 -2
- data/app/views/alchemy/admin/tags/index.html.erb +14 -15
- data/app/views/alchemy/base/500.html.erb +11 -13
- data/app/views/alchemy/essences/_essence_file_view.html.erb +4 -4
- data/config/alchemy/config.yml +15 -11
- data/config/alchemy/modules.yml +12 -12
- data/config/locales/alchemy.en.yml +2 -0
- data/config/routes.rb +1 -1
- data/db/migrate/20200617110713_create_alchemy_picture_thumbs.rb +22 -0
- data/db/migrate/20200907111332_remove_tri_state_booleans.rb +33 -0
- data/lib/alchemy.rb +66 -0
- data/lib/alchemy/admin/preview_url.rb +2 -0
- data/lib/alchemy/auth_accessors.rb +12 -5
- data/lib/alchemy/config.rb +1 -3
- data/lib/alchemy/engine.rb +7 -6
- data/lib/alchemy/modules.rb +11 -1
- data/lib/alchemy/resource.rb +2 -2
- data/lib/alchemy/test_support/factories/picture_factory.rb +0 -1
- data/lib/alchemy/test_support/factories/picture_thumb_factory.rb +12 -0
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +2 -3
- data/lib/generators/alchemy/install/files/alchemy.en.yml +2 -2
- data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +5 -5
- data/lib/tasks/alchemy/thumbnails.rake +37 -0
- metadata +24 -15
- data/.github/workflows/ci.yml +0 -134
- data/.github/workflows/greetings.yml +0 -13
- data/app/controllers/concerns/alchemy/locale_redirects.rb +0 -40
- data/app/controllers/concerns/alchemy/page_redirects.rb +0 -68
- data/lib/alchemy/userstamp.rb +0 -12
@@ -256,7 +256,7 @@
|
|
256
256
|
> a {
|
257
257
|
display: flex;
|
258
258
|
cursor: pointer;
|
259
|
-
padding:
|
259
|
+
padding-left: 2 * $default-padding;
|
260
260
|
|
261
261
|
&:focus {
|
262
262
|
@include default-focus-style($box-shadow: inset 0 0 0 2px $focus-color);
|
@@ -273,18 +273,16 @@
|
|
273
273
|
text-shadow: $text-shadow-light;
|
274
274
|
}
|
275
275
|
|
276
|
-
.page_language
|
276
|
+
.page_language,
|
277
|
+
.page_site {
|
277
278
|
display: inline-block;
|
278
279
|
color: $muted-text-color;
|
279
|
-
margin-right: 2px;
|
280
280
|
font-size: $small-font-size;
|
281
|
-
|
281
|
+
margin-right: $default-margin;
|
282
|
+
line-height: 31px;
|
282
283
|
}
|
283
284
|
|
284
|
-
.
|
285
|
-
|
286
|
-
color: $muted-text-color;
|
287
|
-
margin-right: $default-margin;
|
288
|
-
font-size: $small-font-size;
|
285
|
+
.page_language {
|
286
|
+
text-transform: uppercase;
|
289
287
|
}
|
290
288
|
}
|
@@ -11,9 +11,19 @@
|
|
11
11
|
top: 9px;
|
12
12
|
}
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
button {
|
15
|
+
position: absolute;
|
16
|
+
top: 0;
|
17
|
+
left: 0;
|
18
|
+
width: 30px;
|
16
19
|
height: inherit;
|
20
|
+
appearance: none;
|
21
|
+
background-color: transparent;
|
22
|
+
border: 0 none;
|
23
|
+
border-radius: 0;
|
24
|
+
box-shadow: none;
|
25
|
+
margin: 0;
|
26
|
+
padding: 0;
|
17
27
|
}
|
18
28
|
}
|
19
29
|
|
@@ -1,5 +1,3 @@
|
|
1
|
-
$medium-select-box-width: 90px;
|
2
|
-
|
3
1
|
select {
|
4
2
|
@include button-defaults(
|
5
3
|
$background-color: $form-field-background-color,
|
@@ -76,6 +74,10 @@ select {
|
|
76
74
|
width: $medium-select-box-width;
|
77
75
|
}
|
78
76
|
|
77
|
+
&.large {
|
78
|
+
width: $large-select-box-width;
|
79
|
+
}
|
80
|
+
|
79
81
|
&.select2-container-active {
|
80
82
|
|
81
83
|
.select2-choice, .select2-choices {
|
@@ -1,26 +1,10 @@
|
|
1
1
|
.tag-list {
|
2
|
+
display: flex;
|
3
|
+
flex-direction: column;
|
2
4
|
height: 100%;
|
3
|
-
padding-bottom: 138px;
|
4
|
-
|
5
|
-
&.filtered {
|
6
|
-
padding-bottom: 164px;
|
7
|
-
}
|
8
|
-
|
9
|
-
&.with_filter_bar {
|
10
|
-
padding-bottom: 218px;
|
11
|
-
|
12
|
-
&.filtered {
|
13
|
-
padding-bottom: 244px;
|
14
|
-
}
|
15
|
-
}
|
16
5
|
|
17
6
|
.js_filter_field_box {
|
18
|
-
|
19
|
-
right: auto;
|
20
|
-
top: auto;
|
21
|
-
width: 200px;
|
22
|
-
z-index: 1100;
|
23
|
-
transition: background-color $transition-duration;
|
7
|
+
margin: 0;
|
24
8
|
|
25
9
|
input {
|
26
10
|
background-color: transparentize($white, 0.25);
|
@@ -29,23 +13,22 @@
|
|
29
13
|
background-color: $white;
|
30
14
|
}
|
31
15
|
}
|
32
|
-
|
33
|
-
label { display: none }
|
34
16
|
}
|
35
17
|
|
36
18
|
ul {
|
37
19
|
list-style-type: none;
|
38
20
|
padding: 0;
|
39
|
-
margin:
|
21
|
+
margin: 0;
|
40
22
|
height: 100%;
|
41
|
-
width: 200px;
|
42
23
|
overflow-x: hidden;
|
43
24
|
overflow-y: auto;
|
44
25
|
|
45
26
|
li {
|
46
27
|
display: block;
|
47
28
|
|
48
|
-
&:first-child {
|
29
|
+
&:first-child {
|
30
|
+
margin-top: 0;
|
31
|
+
}
|
49
32
|
|
50
33
|
a {
|
51
34
|
@include tag-base;
|
@@ -53,13 +36,14 @@
|
|
53
36
|
display: block;
|
54
37
|
}
|
55
38
|
|
56
|
-
&:hover {
|
39
|
+
&:hover {
|
40
|
+
background-color: $very-light-blue;
|
41
|
+
}
|
57
42
|
|
58
43
|
&.active {
|
59
|
-
|
60
44
|
a {
|
61
45
|
background-color: $dark-gray;
|
62
|
-
color: $light-gray
|
46
|
+
color: $light-gray;
|
63
47
|
}
|
64
48
|
}
|
65
49
|
}
|
@@ -69,7 +53,7 @@
|
|
69
53
|
.tag {
|
70
54
|
@include tag-base(
|
71
55
|
$margin: $default-margin/2 0,
|
72
|
-
$padding: $default-padding 2
|
56
|
+
$padding: $default-padding 2 * $default-padding $default-padding
|
73
57
|
);
|
74
58
|
pointer-events: none;
|
75
59
|
font-size: $small-font-size;
|
@@ -124,7 +108,9 @@
|
|
124
108
|
position: relative;
|
125
109
|
border-radius: $default-border-radius;
|
126
110
|
|
127
|
-
&.odd {
|
111
|
+
&.odd {
|
112
|
+
background-color: #eaf3f9;
|
113
|
+
}
|
128
114
|
|
129
115
|
input {
|
130
116
|
position: absolute;
|
@@ -141,14 +127,16 @@
|
|
141
127
|
}
|
142
128
|
}
|
143
129
|
|
144
|
-
.tag_list,
|
130
|
+
.tag_list,
|
131
|
+
.autocomplete_tag_list {
|
145
132
|
.select2-container.select2-container-multi {
|
146
133
|
.select2-search-choice {
|
147
134
|
padding: 0;
|
148
135
|
|
149
136
|
div {
|
150
137
|
@include tag-base(
|
151
|
-
$padding: $default-padding 6
|
138
|
+
$padding: $default-padding 6 * $default-padding $default-padding 3 *
|
139
|
+
$default-padding,
|
152
140
|
$margin: 0
|
153
141
|
);
|
154
142
|
}
|
@@ -85,7 +85,12 @@ module Alchemy
|
|
85
85
|
elsif page_needs_lock?
|
86
86
|
@page.lock_to!(current_alchemy_user)
|
87
87
|
end
|
88
|
-
@
|
88
|
+
@preview_urls = Alchemy.preview_sources.map do |klass|
|
89
|
+
[
|
90
|
+
klass.model_name.human,
|
91
|
+
klass.new(routes: Alchemy::Engine.routes).url_for(@page),
|
92
|
+
]
|
93
|
+
end
|
89
94
|
@layoutpage = @page.layoutpage?
|
90
95
|
end
|
91
96
|
|
@@ -138,7 +143,7 @@ module Alchemy
|
|
138
143
|
|
139
144
|
def link
|
140
145
|
@attachments = Attachment.all.collect { |f|
|
141
|
-
[f.name, download_attachment_path(id: f.id, name: f.
|
146
|
+
[f.name, download_attachment_path(id: f.id, name: f.slug)]
|
142
147
|
}
|
143
148
|
end
|
144
149
|
|
@@ -178,6 +183,10 @@ module Alchemy
|
|
178
183
|
def publish
|
179
184
|
# fetching page via before filter
|
180
185
|
@page.publish!
|
186
|
+
|
187
|
+
# Send publish notification to all registered publish targets
|
188
|
+
Alchemy.publish_targets.each { |p| p.perform_later(@page) }
|
189
|
+
|
181
190
|
flash[:notice] = Alchemy.t(:page_published, name: @page.name)
|
182
191
|
redirect_back(fallback_location: admin_pages_path)
|
183
192
|
end
|
@@ -11,16 +11,18 @@ module Alchemy
|
|
11
11
|
before_action :load_resource,
|
12
12
|
only: [:show, :edit, :update, :destroy, :info]
|
13
13
|
|
14
|
+
before_action :set_size, only: [:index, :show, :edit_multiple]
|
15
|
+
|
14
16
|
authorize_resource class: Alchemy::Picture
|
15
17
|
|
16
18
|
def index
|
17
|
-
@size = params[:size].present? ? params[:size] : "medium"
|
18
19
|
@query = Picture.ransack(search_filter_params[:q])
|
19
20
|
@pictures = Picture.search_by(
|
20
21
|
search_filter_params,
|
21
22
|
@query,
|
22
23
|
items_per_page,
|
23
24
|
)
|
25
|
+
@pictures = @pictures.includes(:thumbs)
|
24
26
|
|
25
27
|
if in_overlay?
|
26
28
|
archive_overlay
|
@@ -115,7 +117,7 @@ module Alchemy
|
|
115
117
|
|
116
118
|
def items_per_page
|
117
119
|
if in_overlay?
|
118
|
-
case
|
120
|
+
case @size
|
119
121
|
when "small" then 25
|
120
122
|
when "large" then 4
|
121
123
|
else
|
@@ -124,19 +126,24 @@ module Alchemy
|
|
124
126
|
else
|
125
127
|
cookies[:alchemy_pictures_per_page] = params[:per_page] ||
|
126
128
|
cookies[:alchemy_pictures_per_page] ||
|
127
|
-
pictures_per_page_for_size
|
129
|
+
pictures_per_page_for_size
|
128
130
|
end
|
129
131
|
end
|
130
132
|
|
131
133
|
def items_per_page_options
|
132
|
-
per_page = pictures_per_page_for_size
|
134
|
+
per_page = pictures_per_page_for_size
|
133
135
|
[per_page, per_page * 2, per_page * 4]
|
134
136
|
end
|
135
137
|
|
136
138
|
private
|
137
139
|
|
138
|
-
def
|
139
|
-
|
140
|
+
def set_size
|
141
|
+
@size = params[:size] || session[:alchemy_pictures_size] || "medium"
|
142
|
+
session[:alchemy_pictures_size] = @size
|
143
|
+
end
|
144
|
+
|
145
|
+
def pictures_per_page_for_size
|
146
|
+
case @size
|
140
147
|
when "small" then 60
|
141
148
|
when "large" then 12
|
142
149
|
else
|
@@ -148,15 +148,15 @@ module Alchemy
|
|
148
148
|
|
149
149
|
def common_search_filter_includes
|
150
150
|
[
|
151
|
-
{q: [
|
151
|
+
{ q: [
|
152
152
|
resource_handler.search_field_name,
|
153
153
|
:s,
|
154
|
-
]},
|
154
|
+
] },
|
155
155
|
:tagged_with,
|
156
156
|
:filter,
|
157
157
|
:page,
|
158
158
|
:per_page,
|
159
|
-
]
|
159
|
+
]
|
160
160
|
end
|
161
161
|
|
162
162
|
def items_per_page
|
@@ -13,7 +13,10 @@ module Alchemy
|
|
13
13
|
|
14
14
|
# Redirecting concerns. Order is important here!
|
15
15
|
include SiteRedirects
|
16
|
-
|
16
|
+
|
17
|
+
before_action :enforce_no_locale,
|
18
|
+
if: :locale_prefix_not_allowed?,
|
19
|
+
only: [:index, :show]
|
17
20
|
|
18
21
|
before_action :load_index_page, only: [:index]
|
19
22
|
before_action :load_page, only: [:show]
|
@@ -21,11 +24,13 @@ module Alchemy
|
|
21
24
|
# Legacy page redirects need to run after the page was loaded and before we render 404.
|
22
25
|
include LegacyPageRedirects
|
23
26
|
|
24
|
-
# From here on, we need a +@page+ to work with!
|
25
|
-
before_action :page_not_found!,
|
27
|
+
# From here on, we need a published +@page+ to work with!
|
28
|
+
before_action :page_not_found!, unless: -> { @page&.public? }, only: [:index, :show]
|
26
29
|
|
27
|
-
# Page redirects need to run after the page was loaded and we're sure to have a +@page+ set.
|
28
|
-
|
30
|
+
# Page redirects need to run after the page was loaded and we're sure to have a public +@page+ set.
|
31
|
+
before_action :enforce_locale,
|
32
|
+
if: :locale_prefix_missing?,
|
33
|
+
only: [:index, :show]
|
29
34
|
|
30
35
|
# We only need to set the +@root_page+ if we are sure that no more redirects happen.
|
31
36
|
before_action :set_root_page, only: [:index, :show]
|
@@ -66,12 +71,8 @@ module Alchemy
|
|
66
71
|
# descendant it finds. If no public page can be found it renders a 404 error.
|
67
72
|
#
|
68
73
|
def show
|
69
|
-
|
70
|
-
|
71
|
-
else
|
72
|
-
authorize! :show, @page
|
73
|
-
render_page if render_fresh_page?
|
74
|
-
end
|
74
|
+
authorize! :show, @page
|
75
|
+
render_page if render_fresh_page?
|
75
76
|
end
|
76
77
|
|
77
78
|
# Renders a search engine compatible xml sitemap.
|
@@ -84,13 +85,25 @@ module Alchemy
|
|
84
85
|
|
85
86
|
private
|
86
87
|
|
88
|
+
# Redirects to requested action without locale prefixed
|
89
|
+
def enforce_no_locale
|
90
|
+
redirect_permanently_to additional_params.merge(locale: nil)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Is the requested locale allowed?
|
94
|
+
#
|
95
|
+
# If Alchemy is not in multi language mode or the requested locale is the default locale,
|
96
|
+
# then we want to redirect to a non prefixed url.
|
97
|
+
#
|
98
|
+
def locale_prefix_not_allowed?
|
99
|
+
params[:locale].present? && !multi_language? ||
|
100
|
+
params[:locale].presence == ::I18n.default_locale.to_s
|
101
|
+
end
|
102
|
+
|
87
103
|
# == Loads index page
|
88
104
|
#
|
89
105
|
# Loads the current public language root page.
|
90
106
|
#
|
91
|
-
# If the root page is not public it redirects to the first published child.
|
92
|
-
# This can be configured via +redirect_to_public_child+ [default: true]
|
93
|
-
#
|
94
107
|
# If no index page and no admin users are present we show the "Welcome to Alchemy" page.
|
95
108
|
#
|
96
109
|
def load_index_page
|
@@ -116,6 +129,28 @@ module Alchemy
|
|
116
129
|
)
|
117
130
|
end
|
118
131
|
|
132
|
+
def enforce_locale
|
133
|
+
redirect_permanently_to page_locale_redirect_url(locale: Language.current.code)
|
134
|
+
end
|
135
|
+
|
136
|
+
def locale_prefix_missing?
|
137
|
+
multi_language? && params[:locale].blank? && !default_locale?
|
138
|
+
end
|
139
|
+
|
140
|
+
def default_locale?
|
141
|
+
Language.current.code.to_sym == ::I18n.default_locale.to_sym
|
142
|
+
end
|
143
|
+
|
144
|
+
# Page url with or without locale while keeping all additional params
|
145
|
+
def page_locale_redirect_url(options = {})
|
146
|
+
options = {
|
147
|
+
locale: prefix_locale? ? @page.language_code : nil,
|
148
|
+
urlname: @page.urlname,
|
149
|
+
}.merge(options)
|
150
|
+
|
151
|
+
alchemy.show_page_path additional_params.merge(options)
|
152
|
+
end
|
153
|
+
|
119
154
|
# Redirects to given url with 301 status
|
120
155
|
def redirect_permanently_to(url)
|
121
156
|
redirect_to url, status: :moved_permanently
|
@@ -272,48 +272,6 @@ module Alchemy
|
|
272
272
|
end
|
273
273
|
end
|
274
274
|
|
275
|
-
# Renders the toolbar shown on top of the records.
|
276
|
-
#
|
277
|
-
# == Example
|
278
|
-
#
|
279
|
-
# <% label_title = Alchemy.t("Create #{resource_name}", default: Alchemy.t('Create')) %>
|
280
|
-
# <% toolbar(
|
281
|
-
# buttons: [
|
282
|
-
# {
|
283
|
-
# icon: :plus,
|
284
|
-
# label: label_title,
|
285
|
-
# url: new_resource_path,
|
286
|
-
# title: label_title,
|
287
|
-
# hotkey: 'alt+n',
|
288
|
-
# dialog_options: {
|
289
|
-
# title: label_title,
|
290
|
-
# size: "430x400"
|
291
|
-
# },
|
292
|
-
# if_permitted_to: [:create, resource_model]
|
293
|
-
# }
|
294
|
-
# ]
|
295
|
-
# ) %>
|
296
|
-
#
|
297
|
-
# @option options [Array] :buttons ([])
|
298
|
-
# Pass an Array with button options. They will be passed to {#toolbar_button} helper.
|
299
|
-
# @option options [Boolean] :search (true)
|
300
|
-
# Show searchfield.
|
301
|
-
#
|
302
|
-
def toolbar(options = {})
|
303
|
-
defaults = {
|
304
|
-
buttons: [],
|
305
|
-
search: true,
|
306
|
-
}
|
307
|
-
options = defaults.merge(options)
|
308
|
-
content_for(:toolbar) do
|
309
|
-
content = <<-CONTENT.strip_heredoc
|
310
|
-
#{options[:buttons].map { |button_options| toolbar_button(button_options) }.join}
|
311
|
-
#{render("alchemy/admin/partials/search_form", url: options[:search_url]) if options[:search]}
|
312
|
-
CONTENT
|
313
|
-
content.html_safe
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
275
|
# (internal) Used by upload form
|
318
276
|
def new_asset_path_with_session_information(asset_type)
|
319
277
|
session_key = Rails.application.config.session_options[:key]
|