Migration to Shrine attachment handling
This commit is contained in:
parent
2d53397dda
commit
c4f5741b0c
13 changed files with 175 additions and 181 deletions
|
|
@ -2,38 +2,21 @@ class Screenshot < ApplicationRecord
|
|||
belongs_to :package, inverse_of: :screenshots
|
||||
belongs_to :user, inverse_of: :screenshots
|
||||
|
||||
# Use paperclip gem to handle image files related to screenshots
|
||||
# After migration from Paperclip to ActiveStorage:
|
||||
has_one_attached :image
|
||||
|
||||
# Validate using 'active_storage_validations' gem
|
||||
validates :image, attached: true, content_type: { in: 'image/png', message: 'is not a valid PNG image file' }
|
||||
|
||||
# Before migration from Paperclip to ActiveStorage:
|
||||
# has_attached_file :image,
|
||||
# styles: { :large => '800x600>', :thumb => '160x120>' },
|
||||
# default_url: '/images/dummy/no-screenshots-available.svg',
|
||||
# path: ':rails_root/public/screenshots/:id_partition/:style.png',
|
||||
# url: '/screenshots/:id_partition/:style.png'
|
||||
# Paperclip
|
||||
has_attached_file :image,
|
||||
styles: { :large => '800x600>', :thumb => '160x120>' },
|
||||
default_url: '/images/dummy/no-screenshots-available.svg',
|
||||
path: ':rails_root/public/screenshots/:id_partition/:style.png',
|
||||
url: '/screenshots/:id_partition/:style.png'
|
||||
# validates_attachment_content_type :image, :content_type => 'image/png'
|
||||
# validates_with AttachmentSizeValidator, :attributes => :image, :less_than => 5.megabytes
|
||||
# # validate :validate_image_is_unique
|
||||
# validate :validate_image_is_unique
|
||||
# validates :delete_reason, length: { in: 5..100 }, allow_nil: true
|
||||
|
||||
# Validator that checks if the image has already been uploaded.
|
||||
# A generic uniqueness validator does not work because it would attribute
|
||||
# the error to the :image_fingerprint field and not the actual :image field.
|
||||
# Currently this check makes sure that the same screenshot is not uploaded
|
||||
# twice for the same package. However it allows the screenshot to be uploaded
|
||||
# for two different packages. Restricting that further may happen later.
|
||||
def validate_image_is_unique
|
||||
# Look for images with the same checksum / image_fingerprint
|
||||
if screenshot=Screenshot.find_by(image_fingerprint: image_fingerprint, package_id: self.package.id)
|
||||
unless screenshot.id == self.id
|
||||
errors.add(:image, "has already been uploaded for this package")
|
||||
end
|
||||
end
|
||||
end
|
||||
# Shrine
|
||||
# include PaperclipShrineSynchronization # needs to be after `has_attached_file`
|
||||
include ImageUploader::Attachment(:simage) # adds an `simage` virtual attribute
|
||||
|
||||
|
||||
# Calculate how many days ago this screenshot has been uploaded
|
||||
def age_days
|
||||
|
|
@ -180,4 +163,32 @@ class Screenshot < ApplicationRecord
|
|||
).processed
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Validator that checks if the image has already been uploaded.
|
||||
# A generic uniqueness validator does not work because it would attribute
|
||||
# the error to the :image_fingerprint field and not the actual :image field.
|
||||
# Currently this check makes sure that the same screenshot is not uploaded
|
||||
# twice for the same package. However it allows the screenshot to be uploaded
|
||||
# for two different packages. Restricting that further may happen later.
|
||||
def validate_image_is_unique
|
||||
# Look for images with the same checksum / image_fingerprint
|
||||
if screenshot=Screenshot.find_by(image_fingerprint: image_fingerprint, package_id: self.package.id)
|
||||
unless screenshot.id == self.id
|
||||
errors.add(:image, "has already been uploaded for this package")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Check that the uploaded file can be handled by ImageMagick and is not broken
|
||||
# def image_intact?
|
||||
# begin
|
||||
# #MiniMagick::Image.open(self.image.blob.open)
|
||||
# MiniMagick::Image.read(self.image.download)
|
||||
# rescue MiniMagick::Invalid => exc
|
||||
# error.add(:image, 'appears to be a broken image file')
|
||||
# end
|
||||
# end
|
||||
|
||||
end
|
||||
|
|
|
|||
49
app/uploaders/image_uploader.rb
Normal file
49
app/uploaders/image_uploader.rb
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# This is a subclass of Shrine base that will be further configured for it's requirements.
|
||||
# This will be included in the model to manage the file.
|
||||
|
||||
class ImageUploader < Shrine
|
||||
ALLOWED_TYPES = %w[image/jpeg image/png image/webp]
|
||||
MAX_SIZE = 5*1024*1024 # 5 MB
|
||||
MAX_DIMENSIONS = [3000, 3000] # 3000x3000
|
||||
|
||||
plugin :remove_attachment
|
||||
plugin :pretty_location
|
||||
plugin :validation_helpers
|
||||
plugin :store_dimensions, log_subscriber: nil
|
||||
# plugin :derivation_endpoint, prefix: "derivations/image"
|
||||
|
||||
# File validations (requires `validation_helpers` plugin)
|
||||
Attacher.validate do
|
||||
validate_size 0..MAX_SIZE
|
||||
|
||||
if validate_mime_type ALLOWED_TYPES
|
||||
validate_max_dimensions MAX_DIMENSIONS
|
||||
end
|
||||
end
|
||||
|
||||
# Thumbnails processor (requires `derivatives` plugin)
|
||||
Attacher.derivatives do |original|
|
||||
puts "original=#{original}"
|
||||
magick = ImageProcessing::MiniMagick.source(original)
|
||||
{
|
||||
small: magick.resize_to_limit!(160, 120),
|
||||
medium: magick.resize_to_limit!(800, 600),
|
||||
large: magick.composite!('public/logo/watermark.png', gravity: 'east')
|
||||
}
|
||||
# GenerateThumbnail.call(original, width, height) # lib/generate_thumbnail.rb
|
||||
|
||||
# THUMBNAILS.transform_values do |(width, height)|
|
||||
# GenerateThumbnail.call(original, width, height) # lib/generate_thumbnail.rb
|
||||
# end
|
||||
end
|
||||
|
||||
# Default to dynamic thumbnail URL (requires `default_url` plugin)
|
||||
Attacher.default_url do |derivative: nil, **|
|
||||
file&.derivation_url(:thumbnail, *THUMBNAILS.fetch(derivative)) if derivative
|
||||
end
|
||||
|
||||
# Dynamic thumbnail definition (requires `derivation_endpoint` plugin)
|
||||
# derivation :thumbnail do |file, width, height|
|
||||
# GenerateThumbnail.call(file, width.to_i, height.to_i) # lib/generate_thumbnail.rb
|
||||
# end
|
||||
end
|
||||
|
|
@ -4,7 +4,8 @@ a.black href=package_path(name: pkg.name)
|
|||
- if pkg.screenshots_approved.any?
|
||||
// TODO: smarter selection of the most useful screenshot instead of taking the first one
|
||||
- screenshot = pkg.screenshots.first
|
||||
= image_tag(screenshot.image.variant(resize_to_limit: [160,120]), alt: screenshot.caption, class: 'thumbnail')
|
||||
/= image_tag(screenshot.image.variant(resize_to_limit: [160,120]), alt: screenshot.caption, class: 'thumbnail')
|
||||
= image_tag(screenshot.simage_url(:small), alt: screenshot.caption, class: 'thumbnail')
|
||||
- else
|
||||
img.screenshot.thumbnail src="/images/dummy/no-screenshots-upload-one.svg"
|
||||
div
|
||||
|
|
|
|||
14
app/views/packages/_medium_image.slim
Normal file
14
app/views/packages/_medium_image.slim
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/ Show image in medium size with zoom option and caption below
|
||||
a href =url_for(screenshot.simage_url(:large)) rel='fancybox-thumb' title=screenshot.caption data-fancybox='gallery' data-caption=screenshot_caption(screenshot)
|
||||
.image-with-zoom-icon
|
||||
= image_tag(url_for(screenshot.simage_url(:medium)), alt: screenshot.caption, class: 'thumbnail')
|
||||
|
||||
.magnifying-glass-icon
|
||||
i.fa.fa-search
|
||||
|
||||
.imgcaption
|
||||
= screenshot.description
|
||||
|
||||
/ Display management buttons for admins only
|
||||
- if user_signed_in? and current_user.is_admin?
|
||||
= render(partial: 'admin_dropdown', locals: {screenshot: screenshot})
|
||||
Loading…
Add table
Add a link
Reference in a new issue