alchemy_cms 5.0.10 → 5.1.0.beta1
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.
- 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 +50 -40
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +2 -2
- data/README.md +2 -2
- data/alchemy_cms.gemspec +4 -4
- data/app/assets/images/alchemy/missing-image.svg +1 -0
- data/app/assets/stylesheets/alchemy/_variables.scss +1 -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 -10
- data/app/assets/stylesheets/alchemy/pagination.scss +1 -1
- data/app/assets/stylesheets/alchemy/search.scss +12 -2
- data/app/assets/stylesheets/alchemy/tags.scss +19 -31
- data/app/assets/stylesheets/tinymce/skins/alchemy/content.min.css.scss +3 -3
- data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +7 -7
- data/app/controllers/alchemy/admin/base_controller.rb +3 -9
- 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 -44
- data/app/helpers/alchemy/admin/navigation_helper.rb +2 -1
- data/app/helpers/alchemy/pages_helper.rb +1 -1
- data/app/models/alchemy/attachment/url.rb +40 -0
- data/app/models/alchemy/attachment.rb +21 -4
- data/app/models/alchemy/element.rb +1 -1
- data/app/models/alchemy/essence_picture.rb +3 -3
- data/app/models/alchemy/essence_picture_view.rb +5 -3
- data/app/models/alchemy/node.rb +1 -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/page.rb +17 -2
- data/app/models/alchemy/picture/calculations.rb +55 -0
- data/app/models/alchemy/picture/transformations.rb +8 -52
- data/app/models/alchemy/picture/url.rb +28 -77
- data/app/models/alchemy/picture.rb +59 -3
- 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_thumb.rb +57 -0
- data/app/models/alchemy/picture_variant.rb +114 -0
- data/app/serializers/alchemy/page_tree_serializer.rb +4 -4
- 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/elements/_element_toolbar.html.erb +1 -1
- 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/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 +4 -1
- 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 +3 -3
- data/app/views/alchemy/essences/_essence_picture_view.html.erb +3 -3
- data/config/alchemy/config.yml +15 -11
- data/config/alchemy/modules.yml +12 -12
- data/config/initializers/dragonfly.rb +0 -8
- 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/auth_accessors.rb +12 -5
- data/lib/alchemy/config.rb +1 -3
- data/lib/alchemy/engine.rb +6 -8
- data/lib/alchemy/modules.rb +11 -1
- data/lib/alchemy/resource.rb +3 -5
- 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/upgrader/five_point_zero.rb +0 -32
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +0 -1
- data/lib/generators/alchemy/install/files/alchemy.en.yml +2 -2
- data/lib/generators/alchemy/install/install_generator.rb +1 -2
- data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +5 -5
- data/lib/tasks/alchemy/thumbnails.rake +37 -0
- data/lib/tasks/alchemy/upgrade.rake +0 -20
- data/package/admin.js +0 -2
- data/package/src/__tests__/i18n.spec.js +0 -23
- data/package/src/i18n.js +3 -1
- data/package.json +1 -1
- metadata +34 -23
- data/.github/workflows/ci.yml +0 -126
- 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/dragonfly/processors/crop_resize.rb +0 -35
- data/lib/alchemy/error_tracking/airbrake_handler.rb +0 -13
- data/lib/alchemy/error_tracking.rb +0 -14
- data/lib/alchemy/userstamp.rb +0 -12
|
@@ -1561,23 +1561,23 @@ i.mce-i-resize {
|
|
|
1561
1561
|
opacity: 0.6;
|
|
1562
1562
|
filter: alpha(opacity=60);
|
|
1563
1563
|
zoom: 1;
|
|
1564
|
-
background: #fff url('
|
|
1564
|
+
background: #fff url('img/loader.gif') no-repeat center center;
|
|
1565
1565
|
}
|
|
1566
1566
|
|
|
1567
1567
|
@font-face {
|
|
1568
1568
|
font-family: 'tinymce';
|
|
1569
|
-
src: url('
|
|
1570
|
-
url('
|
|
1571
|
-
url('
|
|
1569
|
+
src: url('fonts/tinymce.woff') format('woff'),
|
|
1570
|
+
url('fonts/tinymce.ttf') format('truetype'),
|
|
1571
|
+
url('fonts/tinymce.svg#tinymce') format('svg');
|
|
1572
1572
|
font-weight: normal;
|
|
1573
1573
|
font-style: normal;
|
|
1574
1574
|
}
|
|
1575
1575
|
|
|
1576
1576
|
@font-face {
|
|
1577
1577
|
font-family: 'tinymce-small';
|
|
1578
|
-
src: url('
|
|
1579
|
-
url('
|
|
1580
|
-
url('
|
|
1578
|
+
src: url('fonts/tinymce-small.woff') format('woff'),
|
|
1579
|
+
url('fonts/tinymce-small.ttf') format('truetype'),
|
|
1580
|
+
url('fonts/tinymce-small.svg#tinymce') format('svg');
|
|
1581
1581
|
font-weight: normal;
|
|
1582
1582
|
font-style: normal;
|
|
1583
1583
|
}
|
|
@@ -40,7 +40,9 @@ module Alchemy
|
|
|
40
40
|
def exception_handler(error)
|
|
41
41
|
exception_logger(error)
|
|
42
42
|
show_error_notice(error)
|
|
43
|
-
|
|
43
|
+
if defined?(Airbrake)
|
|
44
|
+
notify_airbrake(error) unless Rails.env.development? || Rails.env.test?
|
|
45
|
+
end
|
|
44
46
|
end
|
|
45
47
|
|
|
46
48
|
# Displays an error notice in the Alchemy backend.
|
|
@@ -145,14 +147,6 @@ module Alchemy
|
|
|
145
147
|
site
|
|
146
148
|
end
|
|
147
149
|
end
|
|
148
|
-
|
|
149
|
-
def notify_error_tracker(exception)
|
|
150
|
-
if ::Alchemy::ErrorTracking.notification_handler.respond_to?(:call)
|
|
151
|
-
::Alchemy::ErrorTracking.notification_handler.call(exception)
|
|
152
|
-
else
|
|
153
|
-
Rails.logger.warn("To use the Alchemy::ErrorTracking.notification_handler, it must respond to #call.")
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
150
|
end
|
|
157
151
|
end
|
|
158
152
|
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,50 +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
|
-
deprecate toolbar: "Please use `content_for(:toolbar)` instead", deprecator: Alchemy::Deprecation
|
|
318
|
-
|
|
319
275
|
# (internal) Used by upload form
|
|
320
276
|
def new_asset_path_with_session_information(asset_type)
|
|
321
277
|
session_key = Rails.application.config.session_options[:key]
|
|
@@ -175,7 +175,8 @@ module Alchemy
|
|
|
175
175
|
# Returns true if the given entry's controller is current controller
|
|
176
176
|
#
|
|
177
177
|
def is_entry_controller_active?(entry)
|
|
178
|
-
entry["controller"].gsub(/\A\//, "") == params[:controller]
|
|
178
|
+
entry["controller"].gsub(/\A\//, "") == params[:controller] ||
|
|
179
|
+
entry.fetch("nested_controllers", []).include?(params[:controller])
|
|
179
180
|
end
|
|
180
181
|
|
|
181
182
|
# Returns true if the given entry's action is current controllers action
|
|
@@ -100,7 +100,7 @@ module Alchemy
|
|
|
100
100
|
|
|
101
101
|
# Returns true if page is in the active branch
|
|
102
102
|
def page_active?(page)
|
|
103
|
-
@_page_ancestors ||= @page
|
|
103
|
+
@_page_ancestors ||= Page.ancestors_for(@page)
|
|
104
104
|
@_page_ancestors.include?(page)
|
|
105
105
|
end
|
|
106
106
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class Attachment < BaseRecord
|
|
5
|
+
# The class representing an URL to an attachment
|
|
6
|
+
#
|
|
7
|
+
# Set a different one
|
|
8
|
+
#
|
|
9
|
+
# Alchemy::Attachment.url_class = MyRemoteUrlClass
|
|
10
|
+
#
|
|
11
|
+
class Url
|
|
12
|
+
def initialize(attachment)
|
|
13
|
+
@attachment = attachment
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The attachment url
|
|
17
|
+
#
|
|
18
|
+
# @param [Hash] options
|
|
19
|
+
# @option options [Symbol] :download return a URL for downloading the attachment
|
|
20
|
+
# @option options [Symbol] :name The filename
|
|
21
|
+
# @option options [Symbol] :format The file extension
|
|
22
|
+
#
|
|
23
|
+
# @return [String]
|
|
24
|
+
#
|
|
25
|
+
def call(options = {})
|
|
26
|
+
if options.delete(:download)
|
|
27
|
+
routes.download_attachment_path(@attachment, options)
|
|
28
|
+
else
|
|
29
|
+
routes.show_attachment_path(@attachment, options)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def routes
|
|
36
|
+
Alchemy::Engine.routes.url_helpers
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -28,7 +28,7 @@ module Alchemy
|
|
|
28
28
|
after_assign { |f| write_attribute(:file_mime_type, f.mime_type) }
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
stampable stamper_class_name: Alchemy.
|
|
31
|
+
stampable stamper_class_name: Alchemy.user_class_name
|
|
32
32
|
|
|
33
33
|
has_many :essence_files, class_name: "Alchemy::EssenceFile", foreign_key: "attachment_id"
|
|
34
34
|
has_many :contents, through: :essence_files
|
|
@@ -37,6 +37,20 @@ module Alchemy
|
|
|
37
37
|
|
|
38
38
|
# We need to define this method here to have it available in the validations below.
|
|
39
39
|
class << self
|
|
40
|
+
# The class used to generate URLs for attachments
|
|
41
|
+
#
|
|
42
|
+
# @see Alchemy::Attachment::Url
|
|
43
|
+
def url_class
|
|
44
|
+
@_url_class ||= Alchemy::Attachment::Url
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Set a different attachment url class
|
|
48
|
+
#
|
|
49
|
+
# @see Alchemy::Attachment::Url
|
|
50
|
+
def url_class=(klass)
|
|
51
|
+
@_url_class = klass
|
|
52
|
+
end
|
|
53
|
+
|
|
40
54
|
def searchable_alchemy_resource_attributes
|
|
41
55
|
%w(name file_name)
|
|
42
56
|
end
|
|
@@ -76,14 +90,17 @@ module Alchemy
|
|
|
76
90
|
}
|
|
77
91
|
end
|
|
78
92
|
|
|
93
|
+
def url(options = {})
|
|
94
|
+
if file
|
|
95
|
+
self.class.url_class.new(self).call(options)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
79
99
|
# An url save filename without format suffix
|
|
80
100
|
def slug
|
|
81
101
|
CGI.escape(file_name.gsub(/\.#{extension}$/, "").tr(".", " "))
|
|
82
102
|
end
|
|
83
103
|
|
|
84
|
-
alias_method :urlname, :slug
|
|
85
|
-
deprecate urlname: :slug, deprecator: Alchemy::Deprecation
|
|
86
|
-
|
|
87
104
|
# Checks if the attachment is restricted, because it is attached on restricted pages only
|
|
88
105
|
def restricted?
|
|
89
106
|
pages.any? && pages.not_restricted.blank?
|
|
@@ -60,7 +60,7 @@ module Alchemy
|
|
|
60
60
|
#
|
|
61
61
|
acts_as_list scope: [:page_id, :fixed, :parent_element_id]
|
|
62
62
|
|
|
63
|
-
stampable stamper_class_name: Alchemy.
|
|
63
|
+
stampable stamper_class_name: Alchemy.user_class_name
|
|
64
64
|
|
|
65
65
|
has_many :contents, dependent: :destroy, inverse_of: :element
|
|
66
66
|
|
|
@@ -62,7 +62,7 @@ module Alchemy
|
|
|
62
62
|
def picture_url(options = {})
|
|
63
63
|
return if picture.nil?
|
|
64
64
|
|
|
65
|
-
picture.url
|
|
65
|
+
picture.url(picture_url_options.merge(options)) || "missing-image.png"
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
# Picture rendering options
|
|
@@ -103,7 +103,7 @@ module Alchemy
|
|
|
103
103
|
format: picture.image_file_format,
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
picture.url(options)
|
|
106
|
+
picture.url(options) || "alchemy/missing-image.svg"
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
# The name of the picture used as preview text in element editor views.
|
|
@@ -140,7 +140,7 @@ module Alchemy
|
|
|
140
140
|
# Show image cropping link for content
|
|
141
141
|
def allow_image_cropping?
|
|
142
142
|
content && content.settings[:crop] && picture &&
|
|
143
|
-
picture.can_be_cropped_to(
|
|
143
|
+
picture.can_be_cropped_to?(
|
|
144
144
|
content.settings[:size],
|
|
145
145
|
content.settings[:upsample],
|
|
146
146
|
) && !!picture.image_file
|
|
@@ -44,17 +44,19 @@ module Alchemy
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
private
|
|
48
|
-
|
|
49
47
|
def caption
|
|
50
48
|
return unless show_caption?
|
|
51
49
|
|
|
52
50
|
@_caption ||= content_tag(:figcaption, essence.caption)
|
|
53
51
|
end
|
|
54
52
|
|
|
53
|
+
def src
|
|
54
|
+
essence.picture_url(options.except(*DEFAULT_OPTIONS.keys))
|
|
55
|
+
end
|
|
56
|
+
|
|
55
57
|
def img_tag
|
|
56
58
|
@_img_tag ||= image_tag(
|
|
57
|
-
|
|
59
|
+
src, {
|
|
58
60
|
alt: alt_text,
|
|
59
61
|
title: essence.title.presence,
|
|
60
62
|
class: caption ? nil : essence.css_class.presence,
|
data/app/models/alchemy/node.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Alchemy
|
|
|
7
7
|
before_destroy :check_if_related_essence_nodes_present
|
|
8
8
|
|
|
9
9
|
acts_as_nested_set scope: "language_id", touch: true
|
|
10
|
-
stampable stamper_class_name: Alchemy.
|
|
10
|
+
stampable stamper_class_name: Alchemy.user_class_name
|
|
11
11
|
|
|
12
12
|
belongs_to :language, class_name: "Alchemy::Language"
|
|
13
13
|
belongs_to :page, class_name: "Alchemy::Page", optional: true, inverse_of: :nodes
|
|
@@ -20,8 +20,6 @@ module Alchemy
|
|
|
20
20
|
# link_to page.url
|
|
21
21
|
#
|
|
22
22
|
class UrlPath
|
|
23
|
-
ROOT_PATH = "/"
|
|
24
|
-
|
|
25
23
|
def initialize(page)
|
|
26
24
|
@page = page
|
|
27
25
|
@language = @page.language
|
|
@@ -41,7 +39,7 @@ module Alchemy
|
|
|
41
39
|
private
|
|
42
40
|
|
|
43
41
|
def language_root_path
|
|
44
|
-
@language.default? ?
|
|
42
|
+
@language.default? ? root_path : language_path
|
|
45
43
|
end
|
|
46
44
|
|
|
47
45
|
def page_path_with_language_prefix
|
|
@@ -49,15 +47,19 @@ module Alchemy
|
|
|
49
47
|
end
|
|
50
48
|
|
|
51
49
|
def page_path_with_leading_slash
|
|
52
|
-
@page.language_root? ?
|
|
50
|
+
@page.language_root? ? root_path : page_path
|
|
53
51
|
end
|
|
54
52
|
|
|
55
53
|
def language_path
|
|
56
|
-
"
|
|
54
|
+
"#{root_path}#{@page.language_code}"
|
|
57
55
|
end
|
|
58
56
|
|
|
59
57
|
def page_path
|
|
60
|
-
"
|
|
58
|
+
"#{root_path}#{@page.urlname}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def root_path
|
|
62
|
+
Engine.routes.url_helpers.root_path
|
|
61
63
|
end
|
|
62
64
|
end
|
|
63
65
|
end
|
data/app/models/alchemy/page.rb
CHANGED
|
@@ -82,7 +82,7 @@ module Alchemy
|
|
|
82
82
|
|
|
83
83
|
acts_as_nested_set(dependent: :destroy, scope: [:layoutpage, :language_id])
|
|
84
84
|
|
|
85
|
-
stampable stamper_class_name: Alchemy.
|
|
85
|
+
stampable stamper_class_name: Alchemy.user_class_name
|
|
86
86
|
|
|
87
87
|
belongs_to :language
|
|
88
88
|
|
|
@@ -149,6 +149,21 @@ module Alchemy
|
|
|
149
149
|
# Class methods
|
|
150
150
|
#
|
|
151
151
|
class << self
|
|
152
|
+
# The url_path class
|
|
153
|
+
# @see Alchemy::Page::UrlPath
|
|
154
|
+
def url_path_class
|
|
155
|
+
@_url_path_class ||= Alchemy::Page::UrlPath
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Set a custom url path class
|
|
159
|
+
#
|
|
160
|
+
# # config/initializers/alchemy.rb
|
|
161
|
+
# Alchemy::Page.url_path_class = MyPageUrlPathClass
|
|
162
|
+
#
|
|
163
|
+
def url_path_class=(klass)
|
|
164
|
+
@_url_path_class = klass
|
|
165
|
+
end
|
|
166
|
+
|
|
152
167
|
# Used to store the current page previewed in the edit page template.
|
|
153
168
|
#
|
|
154
169
|
def current_preview=(page)
|
|
@@ -298,7 +313,7 @@ module Alchemy
|
|
|
298
313
|
#
|
|
299
314
|
# @see Alchemy::Page::UrlPath#call
|
|
300
315
|
def url_path
|
|
301
|
-
|
|
316
|
+
self.class.url_path_class.new(self).call
|
|
302
317
|
end
|
|
303
318
|
|
|
304
319
|
# The page's view partial is dependent from its page layout
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class Picture < BaseRecord
|
|
5
|
+
module Calculations
|
|
6
|
+
# An Image smaller than dimensions
|
|
7
|
+
# can not be cropped to given size - unless upsample is true.
|
|
8
|
+
#
|
|
9
|
+
def can_be_cropped_to?(string, upsample = false)
|
|
10
|
+
return true if upsample
|
|
11
|
+
|
|
12
|
+
is_bigger_than? sizes_from_string(string)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns true if both dimensions of the base image are bigger than the dimensions hash.
|
|
16
|
+
#
|
|
17
|
+
def is_bigger_than?(dimensions)
|
|
18
|
+
image_file_width > dimensions[:width] && image_file_height > dimensions[:height]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns true is one dimension of the base image is smaller than the dimensions hash.
|
|
22
|
+
#
|
|
23
|
+
def is_smaller_than?(dimensions)
|
|
24
|
+
!is_bigger_than?(dimensions)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Given a string with an x, this function returns a Hash with point
|
|
28
|
+
# :width and :height.
|
|
29
|
+
#
|
|
30
|
+
def sizes_from_string(string = "0x0")
|
|
31
|
+
string = "0x0" if string.nil? || string.empty?
|
|
32
|
+
|
|
33
|
+
raise ArgumentError unless string =~ /(\d*x\d*)/
|
|
34
|
+
|
|
35
|
+
width, height = string.scan(/(\d*)x(\d*)/)[0].map(&:to_i)
|
|
36
|
+
|
|
37
|
+
width = 0 if width.nil?
|
|
38
|
+
height = 0 if height.nil?
|
|
39
|
+
{
|
|
40
|
+
width: width,
|
|
41
|
+
height: height,
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# This function returns the :width and :height of the image file
|
|
46
|
+
# as a Hash
|
|
47
|
+
def image_size
|
|
48
|
+
{
|
|
49
|
+
width: image_file_width,
|
|
50
|
+
height: image_file_height,
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|