alchemy_cms 7.3.3 → 7.3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +9 -0
- data/README.md +25 -4
- data/Rakefile +22 -0
- data/app/assets/builds/alchemy/admin.css +1 -1
- data/app/assets/builds/alchemy/admin.css.map +1 -1
- data/app/assets/builds/alchemy/welcome.css +1 -17
- data/app/assets/builds/alchemy/welcome.css.map +1 -1
- data/app/assets/stylesheets/alchemy/admin/elements.scss +1 -1
- data/app/assets/stylesheets/alchemy/admin/image_library.scss +40 -26
- data/app/controllers/alchemy/admin/base_controller.rb +27 -3
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +1 -5
- data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
- data/app/models/concerns/alchemy/picture_thumbnails.rb +4 -5
- data/app/views/alchemy/_menubar.html.erb +1 -1
- data/lib/alchemy/resource.rb +14 -4
- data/lib/alchemy/version.rb +1 -1
- data/vendor/javascript/tinymce.min.js +1 -1
- metadata +3 -5
@@ -1,8 +1,3 @@
|
|
1
|
-
$picture-overlay-handle-width: 24px;
|
2
|
-
$image-overlay-form-width: 350px - $picture-overlay-handle-width;
|
3
|
-
$image-overlay-transition-duration: $transition-duration;
|
4
|
-
$image-overlay-transition-easing: ease-in;
|
5
|
-
|
6
1
|
.alchemy-image-overlay {
|
7
2
|
&.open {
|
8
3
|
background-color: rgba(0, 0, 0, 0.6);
|
@@ -17,10 +12,21 @@ $image-overlay-transition-easing: ease-in;
|
|
17
12
|
max-width: 100%;
|
18
13
|
}
|
19
14
|
|
15
|
+
.alchemy-image-overlay-container {
|
16
|
+
--picture-overlay-handle-width: 24px;
|
17
|
+
--image-overlay-form-width: calc(350px - var(--picture-overlay-handle-width));
|
18
|
+
--image-overlay-transition-duration: 250ms;
|
19
|
+
--image-overlay-transition-easing: ease-in;
|
20
|
+
|
21
|
+
&.open {
|
22
|
+
overflow: hidden;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
20
26
|
.alchemy-image-overlay-dialog {
|
21
27
|
&.hide-form {
|
22
28
|
.picture-details-overlay {
|
23
|
-
right:
|
29
|
+
right: calc(-1 * var(--image-overlay-form-width));
|
24
30
|
}
|
25
31
|
|
26
32
|
.zoomed-picture-background {
|
@@ -28,11 +34,11 @@ $image-overlay-transition-easing: ease-in;
|
|
28
34
|
}
|
29
35
|
|
30
36
|
.alchemy-image-overlay-close {
|
31
|
-
right:
|
37
|
+
right: calc(var(--picture-overlay-handle-width) + var(--spacing-2));
|
32
38
|
}
|
33
39
|
|
34
40
|
.next-picture {
|
35
|
-
right:
|
41
|
+
right: var(--picture-overlay-handle-width);
|
36
42
|
}
|
37
43
|
|
38
44
|
.picture-overlay-handle {
|
@@ -64,17 +70,18 @@ $image-overlay-transition-easing: ease-in;
|
|
64
70
|
height: 32px;
|
65
71
|
top: var(--spacing-2);
|
66
72
|
right: calc(
|
67
|
-
var(--spacing-2) +
|
73
|
+
var(--spacing-2) + var(--picture-overlay-handle-width) +
|
74
|
+
var(--image-overlay-form-width)
|
68
75
|
);
|
69
76
|
cursor: pointer;
|
70
|
-
transition: right
|
71
|
-
|
77
|
+
transition: right var(--image-overlay-transition-duration)
|
78
|
+
var(--image-overlay-transition-easing);
|
72
79
|
|
73
80
|
.icon {
|
74
81
|
font-size: 2em;
|
75
82
|
color: var(--color-grey_light);
|
76
83
|
text-shadow: 0 0 4px var(--color-text);
|
77
|
-
transition: color
|
84
|
+
transition: color var(--image-overlay-transition-duration) linear;
|
78
85
|
|
79
86
|
&:hover {
|
80
87
|
color: var(--color-white);
|
@@ -88,13 +95,13 @@ $image-overlay-transition-easing: ease-in;
|
|
88
95
|
top: 0;
|
89
96
|
background-color: var(--color-grey_light);
|
90
97
|
box-shadow: -2px 0 4px -2px var(--color-text);
|
91
|
-
transition: right
|
92
|
-
|
98
|
+
transition: right var(--image-overlay-transition-duration)
|
99
|
+
var(--image-overlay-transition-easing);
|
93
100
|
}
|
94
101
|
|
95
102
|
.picture-details-overlay {
|
96
103
|
right: 0;
|
97
|
-
width:
|
104
|
+
width: var(--image-overlay-form-width);
|
98
105
|
height: 100%;
|
99
106
|
padding: var(--spacing-2) var(--spacing-4) var(--spacing-2) var(--spacing-1);
|
100
107
|
overflow: auto;
|
@@ -117,9 +124,9 @@ $image-overlay-transition-easing: ease-in;
|
|
117
124
|
}
|
118
125
|
|
119
126
|
.picture-overlay-handle {
|
120
|
-
width:
|
127
|
+
width: var(--picture-overlay-handle-width);
|
121
128
|
height: 100%;
|
122
|
-
right:
|
129
|
+
right: var(--image-overlay-form-width);
|
123
130
|
cursor: pointer;
|
124
131
|
|
125
132
|
.icon {
|
@@ -129,8 +136,8 @@ $image-overlay-transition-easing: ease-in;
|
|
129
136
|
transform: translate(-50%, -50%);
|
130
137
|
font-size: 1.2em;
|
131
138
|
color: var(--color-icon);
|
132
|
-
transition: transform
|
133
|
-
|
139
|
+
transition: transform var(--image-overlay-transition-duration)
|
140
|
+
var(--image-overlay-transition-easing);
|
134
141
|
}
|
135
142
|
|
136
143
|
&:hover {
|
@@ -143,15 +150,16 @@ $image-overlay-transition-easing: ease-in;
|
|
143
150
|
height: 100%;
|
144
151
|
padding-top: var(--spacing-2);
|
145
152
|
padding-right: calc(
|
146
|
-
|
153
|
+
var(--image-overlay-form-width) + var(--spacing-2) +
|
154
|
+
var(--picture-overlay-handle-width)
|
147
155
|
);
|
148
156
|
padding-bottom: var(--spacing-2);
|
149
157
|
padding-left: var(--spacing-2);
|
150
158
|
margin: 0 auto;
|
151
159
|
text-align: center;
|
152
160
|
cursor: pointer;
|
153
|
-
transition: padding-right
|
154
|
-
|
161
|
+
transition: padding-right var(--image-overlay-transition-duration)
|
162
|
+
var(--image-overlay-transition-easing);
|
155
163
|
|
156
164
|
&:before {
|
157
165
|
content: "";
|
@@ -233,7 +241,7 @@ $image-overlay-transition-easing: ease-in;
|
|
233
241
|
justify-content: center;
|
234
242
|
align-items: center;
|
235
243
|
text-decoration: none;
|
236
|
-
transition: background-color
|
244
|
+
transition: background-color var(--image-overlay-transition-duration) linear;
|
237
245
|
|
238
246
|
.icon {
|
239
247
|
width: 32px;
|
@@ -246,16 +254,20 @@ $image-overlay-transition-easing: ease-in;
|
|
246
254
|
background-color: rgba(0, 0, 0, 0.3);
|
247
255
|
|
248
256
|
.icon {
|
249
|
-
transition: fill
|
257
|
+
transition: fill var(--image-overlay-transition-duration) linear;
|
250
258
|
}
|
251
259
|
}
|
252
260
|
}
|
253
261
|
|
262
|
+
.next-picture {
|
263
|
+
transition-property: background-color, right;
|
264
|
+
}
|
265
|
+
|
254
266
|
.icon {
|
255
267
|
--icon-size: 4em;
|
256
268
|
fill: var(--color-grey_light);
|
257
269
|
filter: drop-shadow(0 0 4px var(--color-icon));
|
258
|
-
transition: all
|
270
|
+
transition: all var(--image-overlay-transition-duration) linear;
|
259
271
|
vertical-align: middle;
|
260
272
|
}
|
261
273
|
|
@@ -265,7 +277,9 @@ $image-overlay-transition-easing: ease-in;
|
|
265
277
|
}
|
266
278
|
|
267
279
|
.next-picture {
|
268
|
-
right:
|
280
|
+
right: calc(
|
281
|
+
var(--image-overlay-form-width) + var(--picture-overlay-handle-width)
|
282
|
+
);
|
269
283
|
@include border-left-radius($default-border-radius);
|
270
284
|
}
|
271
285
|
}
|
@@ -31,6 +31,27 @@ module Alchemy
|
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
+
def safe_redirect_path(path = params[:redirect_to], fallback: admin_path)
|
35
|
+
if is_safe_redirect_path?(path)
|
36
|
+
path
|
37
|
+
elsif is_safe_redirect_path?(fallback)
|
38
|
+
fallback
|
39
|
+
else
|
40
|
+
admin_path
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def is_safe_redirect_path?(path)
|
45
|
+
mount_path = alchemy.root_path
|
46
|
+
path.to_s.match? %r{^#{mount_path}admin/}
|
47
|
+
end
|
48
|
+
|
49
|
+
def relative_referer_path(referer = request.referer)
|
50
|
+
return unless referer
|
51
|
+
|
52
|
+
URI(referer).path
|
53
|
+
end
|
54
|
+
|
34
55
|
# Disable layout rendering for xhr requests.
|
35
56
|
def set_layout
|
36
57
|
(request.xhr? || turbo_frame_request?) ? false : "alchemy/admin"
|
@@ -105,15 +126,18 @@ module Alchemy
|
|
105
126
|
end
|
106
127
|
end
|
107
128
|
|
108
|
-
# Does redirects for html and js requests
|
129
|
+
# Does redirects for html, turbo_stream and js requests
|
130
|
+
#
|
131
|
+
# Makes sure that the redirect path is safe.
|
109
132
|
#
|
110
133
|
def do_redirect_to(url_or_path)
|
134
|
+
redirect_path = safe_redirect_path(url_or_path)
|
111
135
|
respond_to do |format|
|
112
136
|
format.js {
|
113
|
-
@redirect_url =
|
137
|
+
@redirect_url = redirect_path
|
114
138
|
render :redirect
|
115
139
|
}
|
116
|
-
format.html { redirect_to
|
140
|
+
format.html { redirect_to redirect_path }
|
117
141
|
end
|
118
142
|
end
|
119
143
|
|
@@ -40,7 +40,7 @@ module Alchemy
|
|
40
40
|
def switch
|
41
41
|
@language = set_alchemy_language(params[:language_id])
|
42
42
|
session[:alchemy_language_id] = @language.id
|
43
|
-
do_redirect_to
|
43
|
+
do_redirect_to relative_referer_path || alchemy.admin_dashboard_path
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|
@@ -189,11 +189,7 @@ module Alchemy
|
|
189
189
|
end
|
190
190
|
|
191
191
|
def unlock_redirect_path
|
192
|
-
|
193
|
-
params[:redirect_to]
|
194
|
-
else
|
195
|
-
admin_pages_path
|
196
|
-
end
|
192
|
+
safe_redirect_path(fallback: admin_pages_path)
|
197
193
|
end
|
198
194
|
|
199
195
|
# Sets the page public and updates the published_at attribute that is used as cache_key
|
@@ -78,7 +78,7 @@ module Alchemy
|
|
78
78
|
flash[:error] = resource_instance_variable.errors.full_messages.join(", ")
|
79
79
|
end
|
80
80
|
flash_notice_for_resource_action
|
81
|
-
do_redirect_to resource_url_proxy.url_for(search_filter_params.merge(action: "index"))
|
81
|
+
do_redirect_to resource_url_proxy.url_for(search_filter_params.merge(action: "index", only_path: true))
|
82
82
|
end
|
83
83
|
|
84
84
|
def resource_handler
|
@@ -102,11 +102,10 @@ module Alchemy
|
|
102
102
|
|
103
103
|
# Show image cropping link for ingredient
|
104
104
|
def allow_image_cropping?
|
105
|
-
settings[:crop] && picture
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
) && !!picture.image_file
|
105
|
+
settings[:crop] && picture&.can_be_cropped_to?(
|
106
|
+
settings[:size],
|
107
|
+
settings[:upsample]
|
108
|
+
) && !!picture.image_file
|
110
109
|
end
|
111
110
|
|
112
111
|
private
|
data/lib/alchemy/resource.rb
CHANGED
@@ -188,11 +188,21 @@ module Alchemy
|
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
191
|
+
# Returns a sorted array of attributes.
|
192
|
+
#
|
193
|
+
# Attribute called "name" comes first.
|
194
|
+
# Attribute called "updated_at" comes last.
|
195
|
+
# Boolean type attributes come after non-boolean attributes but before "updated_at".
|
196
|
+
#
|
191
197
|
def sorted_attributes
|
192
|
-
@_sorted_attributes ||= attributes
|
193
|
-
|
194
|
-
|
195
|
-
|
198
|
+
@_sorted_attributes ||= attributes.sort_by! do |attr|
|
199
|
+
[
|
200
|
+
(attr[:name] == "name") ? 0 : 1,
|
201
|
+
(attr[:name] == "updated_at") ? 3 : 2,
|
202
|
+
(attr[:type] == :boolean) ? 2 : 1,
|
203
|
+
attr[:name]
|
204
|
+
]
|
205
|
+
end
|
196
206
|
end
|
197
207
|
|
198
208
|
def editable_attributes
|