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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  3. data/.github/workflows/stale.yml +1 -1
  4. data/.gitignore +1 -0
  5. data/CHANGELOG.md +66 -2
  6. data/CONTRIBUTING.md +2 -2
  7. data/Gemfile +1 -1
  8. data/README.md +1 -1
  9. data/alchemy_cms.gemspec +3 -3
  10. data/app/assets/images/alchemy/missing-image.svg +1 -0
  11. data/app/assets/javascripts/alchemy/admin.js +0 -1
  12. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +1 -4
  13. data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +0 -3
  14. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +29 -4
  15. data/app/assets/stylesheets/alchemy/_variables.scss +8 -0
  16. data/app/assets/stylesheets/alchemy/admin.scss +0 -1
  17. data/app/assets/stylesheets/alchemy/archive.scss +23 -17
  18. data/app/assets/stylesheets/alchemy/buttons.scss +26 -15
  19. data/app/assets/stylesheets/alchemy/elements.scss +58 -19
  20. data/app/assets/stylesheets/alchemy/errors.scss +1 -1
  21. data/app/assets/stylesheets/alchemy/frame.scss +0 -1
  22. data/app/assets/stylesheets/alchemy/hints.scss +2 -1
  23. data/app/assets/stylesheets/alchemy/navigation.scss +7 -10
  24. data/app/assets/stylesheets/alchemy/pagination.scss +1 -1
  25. data/app/assets/stylesheets/alchemy/search.scss +13 -3
  26. data/app/assets/stylesheets/alchemy/selects.scss +26 -20
  27. data/app/assets/stylesheets/alchemy/tables.scss +38 -9
  28. data/app/assets/stylesheets/alchemy/tags.scss +19 -31
  29. data/app/controllers/alchemy/admin/pages_controller.rb +58 -8
  30. data/app/controllers/alchemy/admin/pictures_controller.rb +13 -6
  31. data/app/controllers/alchemy/admin/resources_controller.rb +3 -3
  32. data/app/controllers/alchemy/pages_controller.rb +49 -14
  33. data/app/decorators/alchemy/element_editor.rb +1 -0
  34. data/app/helpers/alchemy/admin/base_helper.rb +0 -44
  35. data/app/helpers/alchemy/admin/navigation_helper.rb +2 -1
  36. data/app/models/alchemy/attachment.rb +20 -3
  37. data/app/models/alchemy/attachment/url.rb +40 -0
  38. data/app/models/alchemy/essence_picture.rb +3 -3
  39. data/app/models/alchemy/essence_picture_view.rb +5 -3
  40. data/app/models/alchemy/legacy_page_url.rb +1 -1
  41. data/app/models/alchemy/page.rb +24 -1
  42. data/app/models/alchemy/page/page_natures.rb +2 -0
  43. data/app/models/alchemy/page/url_path.rb +8 -6
  44. data/app/models/alchemy/picture.rb +58 -2
  45. data/app/models/alchemy/picture/calculations.rb +55 -0
  46. data/app/models/alchemy/picture/transformations.rb +5 -49
  47. data/app/models/alchemy/picture/url.rb +28 -77
  48. data/app/models/alchemy/picture_thumb.rb +57 -0
  49. data/app/models/alchemy/picture_thumb/create.rb +39 -0
  50. data/app/models/alchemy/picture_thumb/signature.rb +23 -0
  51. data/app/models/alchemy/picture_thumb/uid.rb +22 -0
  52. data/app/models/alchemy/picture_variant.rb +114 -0
  53. data/app/models/alchemy/site/layout.rb +30 -2
  54. data/app/views/alchemy/admin/attachments/show.html.erb +8 -8
  55. data/app/views/alchemy/admin/dashboard/index.html.erb +13 -16
  56. data/app/views/alchemy/admin/elements/_element_footer.html.erb +1 -1
  57. data/app/views/alchemy/admin/elements/publish.js.erb +1 -0
  58. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +1 -1
  59. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +2 -2
  60. data/app/views/alchemy/admin/layoutpages/edit.html.erb +4 -6
  61. data/app/views/alchemy/admin/pages/_create_language_form.html.erb +19 -29
  62. data/app/views/alchemy/admin/pages/_form.html.erb +4 -6
  63. data/app/views/alchemy/admin/pages/_new_page_form.html.erb +12 -2
  64. data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +29 -0
  65. data/app/views/alchemy/admin/pages/_table.html.erb +27 -0
  66. data/app/views/alchemy/admin/pages/_table_row.html.erb +107 -0
  67. data/app/views/alchemy/admin/pages/_toolbar.html.erb +77 -0
  68. data/app/views/alchemy/admin/pages/edit.html.erb +9 -1
  69. data/app/views/alchemy/admin/pages/index.html.erb +41 -74
  70. data/app/views/alchemy/admin/pages/list/_table.html.erb +31 -0
  71. data/app/views/alchemy/admin/pages/unlock.js.erb +2 -2
  72. data/app/views/alchemy/admin/pages/update.js.erb +19 -10
  73. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +14 -13
  74. data/app/views/alchemy/admin/partials/_search_form.html.erb +8 -8
  75. data/app/views/alchemy/admin/pictures/_archive.html.erb +1 -1
  76. data/app/views/alchemy/admin/pictures/_form.html.erb +1 -1
  77. data/app/views/alchemy/admin/pictures/_picture.html.erb +3 -3
  78. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +1 -1
  79. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +1 -1
  80. data/app/views/alchemy/admin/pictures/index.html.erb +1 -1
  81. data/app/views/alchemy/admin/pictures/show.html.erb +3 -3
  82. data/app/views/alchemy/admin/resources/_filter_bar.html.erb +13 -11
  83. data/app/views/alchemy/admin/resources/_per_page_select.html.erb +3 -3
  84. data/app/views/alchemy/admin/resources/index.html.erb +4 -1
  85. data/app/views/alchemy/admin/tags/index.html.erb +14 -15
  86. data/app/views/alchemy/base/500.html.erb +11 -13
  87. data/app/views/alchemy/essences/_essence_file_view.html.erb +3 -3
  88. data/config/alchemy/config.yml +15 -11
  89. data/config/alchemy/modules.yml +12 -12
  90. data/config/locales/alchemy.en.yml +6 -4
  91. data/config/routes.rb +1 -1
  92. data/db/migrate/20200617110713_create_alchemy_picture_thumbs.rb +22 -0
  93. data/db/migrate/20200907111332_remove_tri_state_booleans.rb +33 -0
  94. data/lib/alchemy.rb +66 -0
  95. data/lib/alchemy/admin/preview_url.rb +2 -0
  96. data/lib/alchemy/auth_accessors.rb +12 -5
  97. data/lib/alchemy/config.rb +1 -3
  98. data/lib/alchemy/engine.rb +7 -6
  99. data/lib/alchemy/modules.rb +11 -1
  100. data/lib/alchemy/permissions.rb +1 -0
  101. data/lib/alchemy/test_support/factories/picture_factory.rb +0 -1
  102. data/lib/alchemy/test_support/factories/picture_thumb_factory.rb +12 -0
  103. data/lib/alchemy/test_support/integration_helpers.rb +0 -7
  104. data/lib/alchemy/version.rb +1 -1
  105. data/lib/alchemy_cms.rb +2 -4
  106. data/lib/generators/alchemy/install/files/alchemy.en.yml +2 -2
  107. data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +5 -5
  108. data/lib/tasks/alchemy/thumbnails.rake +37 -0
  109. data/vendor/assets/javascripts/jquery_plugins/select2.js +3729 -0
  110. data/vendor/assets/stylesheets/alchemy_admin/select2.scss +740 -0
  111. metadata +41 -31
  112. data/.github/workflows/greetings.yml +0 -13
  113. data/app/controllers/concerns/alchemy/locale_redirects.rb +0 -40
  114. data/app/controllers/concerns/alchemy/page_redirects.rb +0 -68
  115. data/lib/alchemy/userstamp.rb +0 -12
@@ -1,94 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- module Picture::Url
5
- include Alchemy::Logger
4
+ class Picture < BaseRecord
5
+ class Url
6
+ attr_reader :variant
6
7
 
7
- TRANSFORMATION_OPTIONS = [
8
- :crop,
9
- :crop_from,
10
- :crop_size,
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
- # Returns a path to picture for use inside a image_tag helper.
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
- # Returns the encoded image
57
- #
58
- # Flatten animated gifs, only if converting to a different format.
59
- # Can be overwritten via +options[:flatten]+.
60
- #
61
- def encoded_image(image, options = {})
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
- unless target_format.in?(Alchemy::Picture.allowed_filetypes)
65
- raise WrongImageFormatError.new(self, target_format)
23
+ "/#{uid}"
66
24
  end
67
25
 
68
- options = {
69
- flatten: target_format != "gif" && image_file_format == "gif",
70
- }.with_indifferent_access.merge(options)
71
-
72
- encoding_options = []
26
+ private
73
27
 
74
- convert_format = target_format != image_file_format.sub("jpeg", "jpg")
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
- if options[:flatten]
82
- encoding_options << "-flatten"
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 site's layout definition
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><%= alchemy.show_attachment_url(@attachment) %></p>
11
- <a data-clipboard-text="<%= alchemy.show_attachment_url(@attachment) %>" class="icon_button--right">
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><%= alchemy.download_attachment_url(@attachment) %></p>
18
- <a data-clipboard-text="<%= alchemy.download_attachment_url(@attachment) %>" class="icon_button--right">
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(alchemy.show_attachment_path(@attachment), class: "full_width") %>
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(alchemy.show_attachment_path(@attachment), preload: "none", controls: true, class: "full_width") %>
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(alchemy.show_attachment_path(@attachment), preload: "metadata", controls: true, class: "full_width") %>
35
+ <%= video_tag(@attachment.url, preload: "metadata", controls: true, class: "full_width") %>
36
36
  </div>
37
37
  <% when "file-pdf" %>
38
- <iframe src="<%= alchemy.show_attachment_path(@attachment) %>" frameborder=0 class="full-iframe">
38
+ <iframe src="<%= @attachment.url %>" frameborder=0 class="full-iframe">
39
39
  Your browser does not support frames.
40
40
  </iframe>
41
41
  <% end %>