alchemy_cms 5.0.3 → 5.1.1
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/CHANGELOG.md +66 -2
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/alchemy_cms.gemspec +3 -3
- data/app/assets/images/alchemy/missing-image.svg +1 -0
- data/app/assets/javascripts/alchemy/admin.js +0 -1
- 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 +8 -0
- data/app/assets/stylesheets/alchemy/admin.scss +0 -1
- data/app/assets/stylesheets/alchemy/archive.scss +23 -17
- data/app/assets/stylesheets/alchemy/buttons.scss +26 -15
- data/app/assets/stylesheets/alchemy/elements.scss +58 -19
- data/app/assets/stylesheets/alchemy/errors.scss +1 -1
- data/app/assets/stylesheets/alchemy/frame.scss +0 -1
- data/app/assets/stylesheets/alchemy/hints.scss +2 -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 +13 -3
- data/app/assets/stylesheets/alchemy/selects.scss +26 -20
- data/app/assets/stylesheets/alchemy/tables.scss +38 -9
- data/app/assets/stylesheets/alchemy/tags.scss +19 -31
- data/app/controllers/alchemy/admin/pages_controller.rb +58 -8
- 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/decorators/alchemy/element_editor.rb +1 -0
- data/app/helpers/alchemy/admin/base_helper.rb +0 -44
- data/app/helpers/alchemy/admin/navigation_helper.rb +2 -1
- data/app/models/alchemy/attachment.rb +20 -3
- data/app/models/alchemy/attachment/url.rb +40 -0
- data/app/models/alchemy/essence_picture.rb +3 -3
- data/app/models/alchemy/essence_picture_view.rb +5 -3
- data/app/models/alchemy/legacy_page_url.rb +1 -1
- data/app/models/alchemy/page.rb +24 -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 -77
- 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/models/alchemy/site/layout.rb +30 -2
- 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_footer.html.erb +1 -1
- data/app/views/alchemy/admin/elements/publish.js.erb +1 -0
- 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/_create_language_form.html.erb +19 -29
- data/app/views/alchemy/admin/pages/_form.html.erb +4 -6
- data/app/views/alchemy/admin/pages/_new_page_form.html.erb +12 -2
- data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +29 -0
- data/app/views/alchemy/admin/pages/_table.html.erb +27 -0
- data/app/views/alchemy/admin/pages/_table_row.html.erb +107 -0
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +77 -0
- data/app/views/alchemy/admin/pages/edit.html.erb +9 -1
- data/app/views/alchemy/admin/pages/index.html.erb +41 -74
- data/app/views/alchemy/admin/pages/list/_table.html.erb +31 -0
- data/app/views/alchemy/admin/pages/unlock.js.erb +2 -2
- data/app/views/alchemy/admin/pages/update.js.erb +19 -10
- 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/_filter_bar.html.erb +13 -11
- 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/config/alchemy/config.yml +15 -11
- data/config/alchemy/modules.yml +12 -12
- data/config/locales/alchemy.en.yml +6 -4
- 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/permissions.rb +1 -0
- 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/test_support/integration_helpers.rb +0 -7
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +2 -4
- 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
- data/vendor/assets/javascripts/jquery_plugins/select2.js +3729 -0
- data/vendor/assets/stylesheets/alchemy_admin/select2.scss +740 -0
- metadata +41 -31
- 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
|
@@ -1,94 +1,45 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Alchemy
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
class Picture < BaseRecord
|
|
5
|
+
class Url
|
|
6
|
+
attr_reader :variant
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
:flatten,
|
|
12
|
-
:format,
|
|
13
|
-
:quality,
|
|
14
|
-
:size,
|
|
15
|
-
:upsample,
|
|
16
|
-
]
|
|
8
|
+
# @param [Alchemy::PictureVariant]
|
|
9
|
+
#
|
|
10
|
+
def initialize(variant)
|
|
11
|
+
raise ArgumentError, "Variant missing!" if variant.nil?
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
#
|
|
20
|
-
# Any additional options are passed to the url_helper, so you can add arguments to your url.
|
|
21
|
-
#
|
|
22
|
-
# Example:
|
|
23
|
-
#
|
|
24
|
-
# <%= image_tag picture.url(size: '320x200', format: 'png') %>
|
|
25
|
-
#
|
|
26
|
-
def url(options = {})
|
|
27
|
-
image = image_file
|
|
28
|
-
|
|
29
|
-
raise MissingImageFileError, "Missing image file for #{inspect}" if image.nil?
|
|
30
|
-
|
|
31
|
-
image = processed_image(image, options)
|
|
32
|
-
image = encoded_image(image, options)
|
|
33
|
-
|
|
34
|
-
image.url(options.except(*TRANSFORMATION_OPTIONS).merge(name: name))
|
|
35
|
-
rescue MissingImageFileError, WrongImageFormatError => e
|
|
36
|
-
log_warning e.message
|
|
37
|
-
nil
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
# Returns the processed image dependent of size and cropping parameters
|
|
43
|
-
def processed_image(image, options = {})
|
|
44
|
-
size = options[:size]
|
|
45
|
-
upsample = !!options[:upsample]
|
|
46
|
-
|
|
47
|
-
return image unless size.present? && has_convertible_format?
|
|
48
|
-
|
|
49
|
-
if options[:crop]
|
|
50
|
-
crop(size, options[:crop_from], options[:crop_size], upsample)
|
|
51
|
-
else
|
|
52
|
-
resize(size, upsample)
|
|
13
|
+
@variant = variant
|
|
53
14
|
end
|
|
54
|
-
end
|
|
55
15
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
target_format = options[:format] || default_render_format
|
|
16
|
+
# The URL to a variant of a picture
|
|
17
|
+
#
|
|
18
|
+
# @return [String]
|
|
19
|
+
#
|
|
20
|
+
def call(params = {})
|
|
21
|
+
return variant.image.url(params) unless processible_image?
|
|
63
22
|
|
|
64
|
-
|
|
65
|
-
raise WrongImageFormatError.new(self, target_format)
|
|
23
|
+
"/#{uid}"
|
|
66
24
|
end
|
|
67
25
|
|
|
68
|
-
|
|
69
|
-
flatten: target_format != "gif" && image_file_format == "gif",
|
|
70
|
-
}.with_indifferent_access.merge(options)
|
|
71
|
-
|
|
72
|
-
encoding_options = []
|
|
26
|
+
private
|
|
73
27
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if target_format =~ /jpe?g/ && convert_format
|
|
77
|
-
quality = options[:quality] || Config.get(:output_image_jpg_quality)
|
|
78
|
-
encoding_options << "-quality #{quality}"
|
|
28
|
+
def processible_image?
|
|
29
|
+
variant.image.is_a?(::Dragonfly::Job)
|
|
79
30
|
end
|
|
80
31
|
|
|
81
|
-
|
|
82
|
-
|
|
32
|
+
def uid
|
|
33
|
+
signature = PictureThumb::Signature.call(variant)
|
|
34
|
+
thumb = variant.picture.thumbs.detect { |t| t.signature == signature }
|
|
35
|
+
if thumb
|
|
36
|
+
uid = thumb.uid
|
|
37
|
+
else
|
|
38
|
+
uid = PictureThumb::Uid.call(signature, variant)
|
|
39
|
+
PictureThumb.generator_class.call(variant, signature, uid)
|
|
40
|
+
end
|
|
41
|
+
uid
|
|
83
42
|
end
|
|
84
|
-
|
|
85
|
-
convertion_needed = convert_format || encoding_options.present?
|
|
86
|
-
|
|
87
|
-
if has_convertible_format? && convertion_needed
|
|
88
|
-
image = image.encode(target_format, encoding_options.join(" "))
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
image
|
|
92
43
|
end
|
|
93
44
|
end
|
|
94
45
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
# The persisted version of a rendered picture variant
|
|
5
|
+
#
|
|
6
|
+
# You can configure the generator class to implement a
|
|
7
|
+
# different thumbnail store (ie. a remote file storage).
|
|
8
|
+
#
|
|
9
|
+
# config/initializers/alchemy.rb
|
|
10
|
+
# Alchemy::PictureThumb.generator_class = My::ThumbnailGenerator
|
|
11
|
+
#
|
|
12
|
+
class PictureThumb < BaseRecord
|
|
13
|
+
belongs_to :picture, class_name: "Alchemy::Picture"
|
|
14
|
+
|
|
15
|
+
validates :signature, presence: true
|
|
16
|
+
validates :uid, presence: true
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Thumbnail generator class
|
|
20
|
+
#
|
|
21
|
+
# @see Alchemy::PictureThumb::Create
|
|
22
|
+
def generator_class
|
|
23
|
+
@_generator_class ||= Alchemy::PictureThumb::Create
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Set a thumbnail generator class
|
|
27
|
+
#
|
|
28
|
+
# @see Alchemy::PictureThumb::Create
|
|
29
|
+
def generator_class=(klass)
|
|
30
|
+
@_generator_class = klass
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Upfront generation of picture thumbnails
|
|
34
|
+
#
|
|
35
|
+
# Called after a Alchemy::Picture has been created (after an image has been uploaded)
|
|
36
|
+
#
|
|
37
|
+
# Generates three types of thumbnails that are used by Alchemys picture archive and
|
|
38
|
+
# persists them in the configures file store (Default Dragonfly::FileDataStore).
|
|
39
|
+
#
|
|
40
|
+
# @see Picture::THUMBNAIL_SIZES
|
|
41
|
+
def generate_thumbs!(picture)
|
|
42
|
+
Alchemy::Picture::THUMBNAIL_SIZES.values.each do |size|
|
|
43
|
+
variant = Alchemy::PictureVariant.new(picture, {
|
|
44
|
+
size: size,
|
|
45
|
+
flatten: true,
|
|
46
|
+
})
|
|
47
|
+
signature = Alchemy::PictureThumb::Signature.call(variant)
|
|
48
|
+
thumb = find_by(signature: signature)
|
|
49
|
+
next if thumb
|
|
50
|
+
|
|
51
|
+
uid = Alchemy::PictureThumb::Uid.call(signature, variant)
|
|
52
|
+
generator_class.call(variant, signature, uid)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class PictureThumb < BaseRecord
|
|
5
|
+
# Stores the render result of a Alchemy::PictureVariant
|
|
6
|
+
# in the configured Dragonfly datastore
|
|
7
|
+
# (Default: Dragonfly::FileDataStore)
|
|
8
|
+
#
|
|
9
|
+
class Create
|
|
10
|
+
class << self
|
|
11
|
+
# @param [Alchemy::PictureVariant] variant the to be rendered image
|
|
12
|
+
# @param [String] signature A unique hashed version of the rendering options
|
|
13
|
+
# @param [String] uid The Unique Image Identifier the image is stored at
|
|
14
|
+
#
|
|
15
|
+
# @return [Alchemy::PictureThumb] The persisted thumbnail record
|
|
16
|
+
#
|
|
17
|
+
def call(variant, signature, uid)
|
|
18
|
+
image = variant.image
|
|
19
|
+
image.to_file(server_path(uid)).close
|
|
20
|
+
variant.picture.thumbs.create!(
|
|
21
|
+
picture: variant.picture,
|
|
22
|
+
signature: signature,
|
|
23
|
+
uid: uid,
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# Alchemys dragonfly datastore config seperates the storage path from the public server
|
|
30
|
+
# path for security reasons. The Dragonfly FileDataStorage does not support that,
|
|
31
|
+
# so we need to build the path on our own.
|
|
32
|
+
def server_path(uid)
|
|
33
|
+
dragonfly_app = ::Dragonfly.app(:alchemy_pictures)
|
|
34
|
+
"#{dragonfly_app.datastore.server_root}/#{uid}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class PictureThumb < BaseRecord
|
|
5
|
+
class Signature
|
|
6
|
+
# Returns a unique image process signature
|
|
7
|
+
#
|
|
8
|
+
# @param [Alchemy::PictureVariant]
|
|
9
|
+
#
|
|
10
|
+
# @return [String]
|
|
11
|
+
def self.call(variant)
|
|
12
|
+
steps_without_fetch = variant.image.steps.reject do |step|
|
|
13
|
+
step.is_a?(::Dragonfly::Job::Fetch)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
steps_with_id = [[variant.picture.id]] + steps_without_fetch
|
|
17
|
+
job_string = steps_with_id.map(&:to_a).to_dragonfly_unique_s
|
|
18
|
+
|
|
19
|
+
Digest::SHA1.hexdigest(job_string)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class PictureThumb < BaseRecord
|
|
5
|
+
class Uid
|
|
6
|
+
# Returns a image variant uid for storage
|
|
7
|
+
#
|
|
8
|
+
# @param [String]
|
|
9
|
+
# @param [Alchemy::PictureVariant]
|
|
10
|
+
#
|
|
11
|
+
# @return [String]
|
|
12
|
+
def self.call(signature, variant)
|
|
13
|
+
picture = variant.picture
|
|
14
|
+
filename = variant.image_file_name || "image"
|
|
15
|
+
name = File.basename(filename, ".*").gsub(/[^\w.]+/, "_")
|
|
16
|
+
ext = variant.render_format
|
|
17
|
+
|
|
18
|
+
"pictures/#{picture.id}/#{signature}/#{name}.#{ext}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module Alchemy
|
|
6
|
+
# Represents a rendered picture
|
|
7
|
+
#
|
|
8
|
+
# Resizes, crops and encodes the image with imagemagick
|
|
9
|
+
#
|
|
10
|
+
class PictureVariant
|
|
11
|
+
extend Forwardable
|
|
12
|
+
|
|
13
|
+
include Alchemy::Logger
|
|
14
|
+
include Alchemy::Picture::Transformations
|
|
15
|
+
|
|
16
|
+
attr_reader :picture, :render_format
|
|
17
|
+
|
|
18
|
+
def_delegators :@picture,
|
|
19
|
+
:image_file,
|
|
20
|
+
:image_file_width,
|
|
21
|
+
:image_file_height,
|
|
22
|
+
:image_file_name,
|
|
23
|
+
:image_file_size
|
|
24
|
+
|
|
25
|
+
# @param [Alchemy::Picture]
|
|
26
|
+
#
|
|
27
|
+
# @param [Hash] options passed to the image processor
|
|
28
|
+
# @option options [Boolean] :crop Pass true to enable cropping
|
|
29
|
+
# @option options [String] :crop_from Coordinates to start cropping from
|
|
30
|
+
# @option options [String] :crop_size Size of the cropping area
|
|
31
|
+
# @option options [Boolean] :flatten Pass true to flatten GIFs
|
|
32
|
+
# @option options [String|Symbol] :format Image format to encode the image in
|
|
33
|
+
# @option options [Integer] :quality JPEG compress quality
|
|
34
|
+
# @option options [String] :size Size of resulting image in WxH
|
|
35
|
+
# @option options [Boolean] :upsample Pass true to upsample (grow) an image if the original size is lower than the resulting size
|
|
36
|
+
#
|
|
37
|
+
def initialize(picture, options = {})
|
|
38
|
+
raise ArgumentError, "Picture missing!" if picture.nil?
|
|
39
|
+
|
|
40
|
+
@picture = picture
|
|
41
|
+
@options = options
|
|
42
|
+
@render_format = options[:format] || picture.default_render_format
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Process a variant of picture
|
|
46
|
+
#
|
|
47
|
+
# @return [Dragonfly::Attachment|Dragonfly::Job] The processed image variant
|
|
48
|
+
#
|
|
49
|
+
def image
|
|
50
|
+
image = image_file
|
|
51
|
+
|
|
52
|
+
raise MissingImageFileError, "Missing image file for #{picture.inspect}" if image.nil?
|
|
53
|
+
|
|
54
|
+
image = processed_image(image, @options)
|
|
55
|
+
image = encoded_image(image, @options)
|
|
56
|
+
image
|
|
57
|
+
rescue MissingImageFileError, WrongImageFormatError => e
|
|
58
|
+
log_warning(e.message)
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# Returns the processed image dependent of size and cropping parameters
|
|
65
|
+
def processed_image(image, options = {})
|
|
66
|
+
size = options[:size]
|
|
67
|
+
upsample = !!options[:upsample]
|
|
68
|
+
|
|
69
|
+
return image unless size.present? && picture.has_convertible_format?
|
|
70
|
+
|
|
71
|
+
if options[:crop]
|
|
72
|
+
crop(size, options[:crop_from], options[:crop_size], upsample)
|
|
73
|
+
else
|
|
74
|
+
resize(size, upsample)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns the encoded image
|
|
79
|
+
#
|
|
80
|
+
# Flatten animated gifs, only if converting to a different format.
|
|
81
|
+
# Can be overwritten via +options[:flatten]+.
|
|
82
|
+
#
|
|
83
|
+
def encoded_image(image, options = {})
|
|
84
|
+
unless render_format.in?(Alchemy::Picture.allowed_filetypes)
|
|
85
|
+
raise WrongImageFormatError.new(picture, @render_format)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
options = {
|
|
89
|
+
flatten: render_format != "gif" && picture.image_file_format == "gif",
|
|
90
|
+
}.with_indifferent_access.merge(options)
|
|
91
|
+
|
|
92
|
+
encoding_options = []
|
|
93
|
+
|
|
94
|
+
convert_format = render_format.sub("jpeg", "jpg") != picture.image_file_format.sub("jpeg", "jpg")
|
|
95
|
+
|
|
96
|
+
if render_format =~ /jpe?g/ && convert_format
|
|
97
|
+
quality = options[:quality] || Config.get(:output_image_jpg_quality)
|
|
98
|
+
encoding_options << "-quality #{quality}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if options[:flatten]
|
|
102
|
+
encoding_options << "-flatten"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
convertion_needed = convert_format || encoding_options.present?
|
|
106
|
+
|
|
107
|
+
if picture.has_convertible_format? && convertion_needed
|
|
108
|
+
image = image.encode(render_format, encoding_options.join(" "))
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
image
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -25,10 +25,38 @@ module Alchemy
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# Returns
|
|
28
|
+
# Returns sites layout definition
|
|
29
29
|
#
|
|
30
30
|
def definition
|
|
31
|
-
self.class.definitions.detect { |l| l["name"] == partial_name }
|
|
31
|
+
self.class.definitions.detect { |l| l["name"] == partial_name } || {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns sites page layout names
|
|
35
|
+
#
|
|
36
|
+
# If no site layout file is defined all page layouts are returned
|
|
37
|
+
#
|
|
38
|
+
# @param [Boolean] layoutpages Return layout pages only (default false)
|
|
39
|
+
#
|
|
40
|
+
# @return [Array<String>] Array of page layout names
|
|
41
|
+
#
|
|
42
|
+
def page_layout_names(layoutpages: false)
|
|
43
|
+
page_layout_definitions.select do |layout|
|
|
44
|
+
!!layout["layoutpage"] && layoutpages || !layout["layoutpage"] && !layoutpages
|
|
45
|
+
end.collect { |layout| layout["name"] }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns sites page layout definitions
|
|
49
|
+
#
|
|
50
|
+
# If no site layout file is defined all page layouts are returned
|
|
51
|
+
#
|
|
52
|
+
def page_layout_definitions
|
|
53
|
+
if definition["page_layouts"].presence
|
|
54
|
+
Alchemy::PageLayout.all.select do |layout|
|
|
55
|
+
layout["name"].in?(definition["page_layouts"])
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
Alchemy::PageLayout.all
|
|
59
|
+
end
|
|
32
60
|
end
|
|
33
61
|
|
|
34
62
|
# Returns the name for the layout partial
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
</div>
|
|
8
8
|
<div class="value with-icon">
|
|
9
9
|
<label><%= Alchemy::Attachment.human_attribute_name(:url) %></label>
|
|
10
|
-
<p><%=
|
|
11
|
-
<a data-clipboard-text="<%=
|
|
10
|
+
<p><%= @attachment.url %></p>
|
|
11
|
+
<a data-clipboard-text="<%= @attachment.url %>" class="icon_button--right">
|
|
12
12
|
<%= render_icon(:clipboard, style: 'regular') %>
|
|
13
13
|
</a>
|
|
14
14
|
</div>
|
|
15
15
|
<div class="value with-icon">
|
|
16
16
|
<label><%= Alchemy::Attachment.human_attribute_name(:download_url) %></label>
|
|
17
|
-
<p><%=
|
|
18
|
-
<a data-clipboard-text="<%=
|
|
17
|
+
<p><%= @attachment.url(download: true) %></p>
|
|
18
|
+
<a data-clipboard-text="<%= @attachment.url(download: true) %>" class="icon_button--right">
|
|
19
19
|
<%= render_icon(:clipboard, style: 'regular') %>
|
|
20
20
|
</a>
|
|
21
21
|
</div>
|
|
@@ -24,18 +24,18 @@
|
|
|
24
24
|
<% case @attachment.icon_css_class %>
|
|
25
25
|
<% when "file-image" %>
|
|
26
26
|
<div class="attachment_preview_container image-preview">
|
|
27
|
-
<%= image_tag(
|
|
27
|
+
<%= image_tag(@attachment.url, class: "full_width") %>
|
|
28
28
|
</div>
|
|
29
29
|
<% when "file-audio" %>
|
|
30
30
|
<div class="attachment_preview_container player-preview">
|
|
31
|
-
<%= audio_tag(
|
|
31
|
+
<%= audio_tag(@attachment.url, preload: "none", controls: true, class: "full_width") %>
|
|
32
32
|
</div>
|
|
33
33
|
<% when "file-video" %>
|
|
34
34
|
<div class="attachment_preview_container player-preview">
|
|
35
|
-
<%= video_tag(
|
|
35
|
+
<%= video_tag(@attachment.url, preload: "metadata", controls: true, class: "full_width") %>
|
|
36
36
|
</div>
|
|
37
37
|
<% when "file-pdf" %>
|
|
38
|
-
<iframe src="<%=
|
|
38
|
+
<iframe src="<%= @attachment.url %>" frameborder=0 class="full-iframe">
|
|
39
39
|
Your browser does not support frames.
|
|
40
40
|
</iframe>
|
|
41
41
|
<% end %>
|