From 59748b2ef0ec3d782d53bb828d4d77eee18766c2 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Mon, 17 Apr 2017 16:21:59 -0500 Subject: [PATCH 01/19] feat: initial stages of Paperclip-based AssetFieldType system rebuild using Shrine --- app/models/asset_field_type.rb | 120 ++++++-------------------------- app/uploaders/image_uploader.rb | 23 ++++++ config/initializers/shrine.rb | 11 +++ cortex-plugins-core.gemspec | 8 ++- 4 files changed, 64 insertions(+), 98 deletions(-) create mode 100644 app/uploaders/image_uploader.rb create mode 100644 config/initializers/shrine.rb diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index c363d6b..4668889 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -1,49 +1,45 @@ class AssetFieldType < FieldType - attr_accessor :asset_file_name, - :asset_content_type, - :asset_file_size, - :asset_updated_at, - :asset - - attr_reader :dimensions, + attr_reader :asset, :existing_data - before_save :extract_dimensions - - do_not_validate_attachment_file_type :asset - validates :asset, attachment_presence: true, if: :validate_presence? - validate :validate_asset_size, if: :validate_size? - validate :validate_asset_content_type, if: :validate_content_type? + attr_accessor :asset_data def metadata=(metadata_hash) @metadata = metadata_hash.deep_symbolize_keys @existing_data = metadata_hash[:existing_data] - Paperclip::HasAttachedFile.define_on(self.class, :asset, existing_metadata) end def data=(data_hash) - self.asset = data_hash.deep_symbolize_keys[:asset] + attacher = ImageUploader::Attacher.new self, :asset + uploader = ImageUploader.new :cache + asset_file = uploader.upload data_hash['asset'] + + attacher.set asset_file + @asset = attacher.promote action: :store, metadata: @metadata end def data { - 'asset': { - 'file_name': asset_file_name, - 'url': asset.url, - 'style_urls': style_urls, - 'dimensions': dimensions, - 'content_type': asset_content_type, - 'file_size': asset_file_size, - 'updated_at': asset_updated_at + asset: { + filename: asset.metadata['filename'], + original_filename: asset.original_filename, + extension: asset.extension, + mime_type: asset.mime_type, + url: asset.url, + file_size: asset.size, + #updated_at: asset.updated_at, # Does Shrine give this to us? + dimensions: { + width: asset.width, + height: asset.height + } }, - 'media_title': media_title, - 'asset_field_type_id': id + shrine_asset: asset.to_json } end def field_item_as_indexed_json_for_field_type(field_item, options = {}) json = {} - json[mapping_field_name] = asset_file_name + json[mapping_field_name] = field_item.data['asset']['filename'] json end @@ -54,19 +50,7 @@ def mapping private def image? - asset_content_type =~ %r{^(image|(x-)?application)/(bmp|gif|jpeg|jpg|pjpeg|png|x-png)$} - end - - def extract_dimensions - return unless image? - tempfile = asset.queued_for_write[:original] - unless tempfile.nil? - geometry = Paperclip::Geometry.from_file(tempfile) - @dimensions = { - width: geometry.width.to_i, - height: geometry.height.to_i - } - end + MimeMagic.new(asset.mime_type).mediatype == 'image' end def allowed_content_types @@ -75,65 +59,7 @@ def allowed_content_types end end - def media_title - existing_data['media_title'] || ContentItemService.form_fields[@metadata[:naming_data][:title]][:text].parameterize.underscore - end - def mapping_field_name "#{field_name.parameterize('_')}_asset_file_name" end - - def validate_presence? - validations.key? :presence - end - - def attachment_size_validator - AttachmentSizeValidator.new(validations[:size].merge(attributes: :asset)) - end - - def attachment_content_type_validator - AttachmentContentTypeValidator.new({content_type: allowed_content_types}.merge(attributes: :asset)) - end - - alias_method :valid_presence_validation?, :validate_presence? - - def validate_size? - begin - attachment_size_validator - true - rescue ArgumentError, NoMethodError - false - end - end - - def validate_content_type? - begin - attachment_content_type_validator - true - rescue ArgumentError, NoMethodError - false - end - end - - def validate_asset_size - attachment_size_validator.validate_each(self, :asset, asset) - end - - def validate_asset_content_type - attachment_content_type_validator.validate_each(self, :asset, asset) - end - - def style_urls - if existing_data.empty? - (metadata[:styles].map { |key, value| [key, asset.url(key)] }).to_h - else - existing_data.deep_symbolize_keys[:asset][:style_urls] - end - end - - def existing_metadata - metadata.except!(:existing_data) - metadata[:path].gsub!(":media_title", media_title) if metadata[:path] - metadata - end end diff --git a/app/uploaders/image_uploader.rb b/app/uploaders/image_uploader.rb new file mode 100644 index 0000000..1067352 --- /dev/null +++ b/app/uploaders/image_uploader.rb @@ -0,0 +1,23 @@ +require 'image_processing/mini_magick' + +class ImageUploader < Shrine + include ImageProcessing::MiniMagick + + plugin :determine_mime_type + plugin :remove_attachment + plugin :store_dimensions + plugin :validation_helpers + plugin :pretty_location + plugin :processing + plugin :versions + + Attacher.validate do + validate_max_size 5.megabytes, message: 'is too large (max is 5 MB)' + validate_mime_type_inclusion %w(image/jpeg image/png image/gif) + end + + process(:store) do |io, context| # can we accept an array of 'actions' for process(action)? + thumb = resize_to_limit!(io.download, 200, 200) + { original: io, thumb: thumb } + end +end diff --git a/config/initializers/shrine.rb b/config/initializers/shrine.rb new file mode 100644 index 0000000..cf0c2a3 --- /dev/null +++ b/config/initializers/shrine.rb @@ -0,0 +1,11 @@ +require 'shrine' +require 'shrine/storage/file_system' +require 'shrine/storage/s3' + +Shrine.storages = { + cache: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/cache'), + store: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/store') +} + +Shrine.plugin :logging, logger: Rails.logger +Shrine.plugin :cached_attachment_data diff --git a/cortex-plugins-core.gemspec b/cortex-plugins-core.gemspec index f826f64..db87db2 100644 --- a/cortex-plugins-core.gemspec +++ b/cortex-plugins-core.gemspec @@ -21,6 +21,12 @@ Gem::Specification.new do |s| s.add_dependency "cells", "~> 4.1" s.add_dependency "cells-rails", "~> 0.0.6" s.add_dependency "cells-haml", "~> 0.0.10" - s.add_dependency "mimemagic", "~> 0.3.2" s.add_dependency "jsonb_accessor", "~> 1.0.0.beta" + + # AssetFieldType + s.add_dependency "shrine", "~> 2.6.1" + s.add_dependency "mimemagic", "~> 0.3.2" + s.add_dependency "image_processing", "~> 0.4.1" + s.add_dependency "mini_magick", "~> 4.7.0" + s.add_dependency "fastimage", "~> 2.1.0" end From ef3a60e8291b7ad4ba2ebc1afd3cbf345ae043a2 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Mon, 17 Apr 2017 16:43:27 -0500 Subject: [PATCH 02/19] feat: versions in AssetFieldType data hash --- app/models/asset_field_type.rb | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 4668889..45f9e37 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -19,19 +19,25 @@ def data=(data_hash) end def data + versions = asset.transform_values do |version| + { + filename: version.metadata['filename'], + extension: version.extension, + mime_type: version.mime_type, + url: version.url, + file_size: version.size, + dimensions: { + width: version.width, + height: version.height + } + } + end + { asset: { - filename: asset.metadata['filename'], - original_filename: asset.original_filename, - extension: asset.extension, - mime_type: asset.mime_type, - url: asset.url, - file_size: asset.size, + original_filename: asset[:original].original_filename, #updated_at: asset.updated_at, # Does Shrine give this to us? - dimensions: { - width: asset.width, - height: asset.height - } + versions: versions }, shrine_asset: asset.to_json } @@ -39,12 +45,12 @@ def data def field_item_as_indexed_json_for_field_type(field_item, options = {}) json = {} - json[mapping_field_name] = field_item.data['asset']['filename'] + json[mapping_field_name] = field_item.data['asset']['original_filename'] json end def mapping - {name: mapping_field_name, type: :string, analyzer: :keyword} + { name: mapping_field_name, type: :string, analyzer: :keyword } end private From dc016a791267395b0c17484d5d8016fdc7378832 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 19 Apr 2017 16:49:46 -0500 Subject: [PATCH 03/19] feat: set metadata context for validation, do not destroy files upon asset deletion, misc --- app/assets/stylesheets/cortex-plugins-core/application.scss | 1 + app/cells/plugins/core/asset_cell.rb | 6 +++++- app/models/asset_field_type.rb | 6 ++++-- app/uploaders/image_uploader.rb | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 app/assets/stylesheets/cortex-plugins-core/application.scss diff --git a/app/assets/stylesheets/cortex-plugins-core/application.scss b/app/assets/stylesheets/cortex-plugins-core/application.scss new file mode 100644 index 0000000..f60d31b --- /dev/null +++ b/app/assets/stylesheets/cortex-plugins-core/application.scss @@ -0,0 +1 @@ +// Components and such can be imported here diff --git a/app/cells/plugins/core/asset_cell.rb b/app/cells/plugins/core/asset_cell.rb index c9daed1..fe8422f 100644 --- a/app/cells/plugins/core/asset_cell.rb +++ b/app/cells/plugins/core/asset_cell.rb @@ -19,6 +19,10 @@ def render_allowed_asset_extensions field.validations['allowed_extensions']&.join(', ') end + def allowed_asset_extensions_for_form + '.' + field.validations['allowed_extensions']&.join(',.') + end + def render_max_asset_size number_to_human_size(field.validations['size']&.[]('less_than')) end @@ -36,7 +40,7 @@ def render_label end def render_input - @options[:form].file_field 'data[asset]' + @options[:form].file_field 'data[asset]', accept: allowed_asset_extensions_for_form end def render_tooltip diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 45f9e37..31e6fef 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -6,21 +6,23 @@ class AssetFieldType < FieldType def metadata=(metadata_hash) @metadata = metadata_hash.deep_symbolize_keys - @existing_data = metadata_hash[:existing_data] + @existing_data = @metadata[:existing_data] end def data=(data_hash) attacher = ImageUploader::Attacher.new self, :asset + attacher.context[:metadata] = @metadata uploader = ImageUploader.new :cache asset_file = uploader.upload data_hash['asset'] attacher.set asset_file - @asset = attacher.promote action: :store, metadata: @metadata + @asset = attacher.promote action: :store end def data versions = asset.transform_values do |version| { + id: version.id, filename: version.metadata['filename'], extension: version.extension, mime_type: version.mime_type, diff --git a/app/uploaders/image_uploader.rb b/app/uploaders/image_uploader.rb index 1067352..60d6612 100644 --- a/app/uploaders/image_uploader.rb +++ b/app/uploaders/image_uploader.rb @@ -4,19 +4,19 @@ class ImageUploader < Shrine include ImageProcessing::MiniMagick plugin :determine_mime_type - plugin :remove_attachment plugin :store_dimensions plugin :validation_helpers plugin :pretty_location plugin :processing plugin :versions + plugin :keep_files, destroyed: true Attacher.validate do validate_max_size 5.megabytes, message: 'is too large (max is 5 MB)' validate_mime_type_inclusion %w(image/jpeg image/png image/gif) end - process(:store) do |io, context| # can we accept an array of 'actions' for process(action)? + process(:store) do |io, context| thumb = resize_to_limit!(io.download, 200, 200) { original: io, thumb: thumb } end From 0b84fa50d136f5f1755db524c38bd4c5c9dd82cf Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 19 Apr 2017 17:03:49 -0500 Subject: [PATCH 04/19] refactor: extract versions_data from AssetFieldType data method --- app/models/asset_field_type.rb | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 31e6fef..26e9462 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -20,26 +20,11 @@ def data=(data_hash) end def data - versions = asset.transform_values do |version| - { - id: version.id, - filename: version.metadata['filename'], - extension: version.extension, - mime_type: version.mime_type, - url: version.url, - file_size: version.size, - dimensions: { - width: version.width, - height: version.height - } - } - end - { asset: { original_filename: asset[:original].original_filename, #updated_at: asset.updated_at, # Does Shrine give this to us? - versions: versions + versions: versions_data }, shrine_asset: asset.to_json } @@ -70,4 +55,21 @@ def allowed_content_types def mapping_field_name "#{field_name.parameterize('_')}_asset_file_name" end + + def versions_data + asset.transform_values do |version| + { + id: version.id, + filename: version.metadata['filename'], + extension: version.extension, + mime_type: version.mime_type, + url: version.url, + file_size: version.size, + dimensions: { + width: version.width, + height: version.height + } + } + end + end end From 69e399a5b27cbcd06ee884b764f65033a5658e9e Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 19 Apr 2017 17:28:18 -0500 Subject: [PATCH 05/19] feat: index, edit partials for new AssetFieldType backend --- app/cells/plugins/core/asset_info/index.haml | 2 +- app/cells/plugins/core/asset_info/show.haml | 14 +++++++------- app/cells/plugins/core/asset_info_cell.rb | 6 +++--- app/models/asset_field_type.rb | 4 ++++ app/uploaders/image_uploader.rb | 3 ++- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/cells/plugins/core/asset_info/index.haml b/app/cells/plugins/core/asset_info/index.haml index e24b916..5bddbfc 100644 --- a/app/cells/plugins/core/asset_info/index.haml +++ b/app/cells/plugins/core/asset_info/index.haml @@ -1,2 +1,2 @@ - if asset - = image_tag(asset['style_urls'][config[:thumbnail_style]], height: '50px') + = image_tag(asset['versions'][config[:thumbnail_style]]['url'], height: '50px') diff --git a/app/cells/plugins/core/asset_info/show.haml b/app/cells/plugins/core/asset_info/show.haml index 450a352..1922252 100644 --- a/app/cells/plugins/core/asset_info/show.haml +++ b/app/cells/plugins/core/asset_info/show.haml @@ -7,19 +7,19 @@ .asset-info.mdl-card.mdl-shadow--2dp .mdl-card__title %h2.mdl-card__title-text - = image_tag(asset['url'], style: 'max-height: 200px;') + = image_tag(asset['versions']['original']['url'], style: 'max-height: 200px;') .mdl-card__supporting-text %dl %dt Original Filename %dd - = asset['file_name'] - %dt File Type + = asset['original_filename'] + %dt Original File Type %dd - = asset['content_type'] - %dt File Size + = asset['versions']['original']['mime_type'] + %dt File Size (Original Dimensions) %dd - = number_to_human_size(asset['file_size']) - %dt Dimensions + = number_to_human_size(asset['versions']['original']['file_size']) + %dt Original Dimensions %dd = dimensions %dt Creator diff --git a/app/cells/plugins/core/asset_info_cell.rb b/app/cells/plugins/core/asset_info_cell.rb index ab06633..870be3b 100644 --- a/app/cells/plugins/core/asset_info_cell.rb +++ b/app/cells/plugins/core/asset_info_cell.rb @@ -25,7 +25,7 @@ def asset end def dimensions - "#{asset['dimensions']['width']} x #{asset['dimensions']['width']}" + "#{asset['versions']['original']['dimensions']['width']} x #{asset['versions']['original']['dimensions']['width']}" end def creator @@ -37,11 +37,11 @@ def created_at end def updated_at - DateTime.parse(asset['updated_at']).to_formatted_s(:long_ordinal) + content_item.updated_at.to_formatted_s(:long_ordinal) end def link_to_asset - link_to asset['url'], asset['url'], target: '_blank' + link_to asset['versions']['original']['url'], asset['versions']['original']['url'], target: '_blank' end end end diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 26e9462..9579fa7 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -56,6 +56,10 @@ def mapping_field_name "#{field_name.parameterize('_')}_asset_file_name" end + def media_title + existing_data['media_title'] || ContentItemService.form_fields[@metadata[:naming_data][:title]][:text].parameterize.underscore + end + def versions_data asset.transform_values do |version| { diff --git a/app/uploaders/image_uploader.rb b/app/uploaders/image_uploader.rb index 60d6612..dec3cb0 100644 --- a/app/uploaders/image_uploader.rb +++ b/app/uploaders/image_uploader.rb @@ -18,6 +18,7 @@ class ImageUploader < Shrine process(:store) do |io, context| thumb = resize_to_limit!(io.download, 200, 200) - { original: io, thumb: thumb } + mini = resize_to_limit!(io.download, 100, 100) + { original: io, thumb: thumb, mini: mini } end end From 2043085fb35dbfa04e0a14d922ed849ff1b879bd Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 19 Apr 2017 17:33:04 -0500 Subject: [PATCH 06/19] fix: asset URL copy functionality --- app/cells/plugins/core/asset_info/show.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cells/plugins/core/asset_info/show.haml b/app/cells/plugins/core/asset_info/show.haml index 1922252..f59f0a3 100644 --- a/app/cells/plugins/core/asset_info/show.haml +++ b/app/cells/plugins/core/asset_info/show.haml @@ -35,7 +35,7 @@ %dd = link_to_asset .mdl-card__menu - .mdl-button.mdl-button--icon.mdl-js-button.mdl-js-ripple-effect#copy-asset-url{data: {'clipboard-text': asset['url']}} + .mdl-button.mdl-button--icon.mdl-js-button.mdl-js-ripple-effect#copy-asset-url{data: {'clipboard-text': asset['versions']['original']['url']}} %i.material-icons content_copy .mdl-tooltip{for: 'copy-asset-url'} Copy Asset URL From 15c03242b3deb503fc1ef079d695c586cceacdbb Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 19 Apr 2017 17:54:34 -0500 Subject: [PATCH 07/19] refactor: extract logic from AssetFieldType data setter, prepare for generic FileUploader implementation --- app/cells/plugins/core/asset_cell.rb | 1 + app/models/asset_field_type.rb | 26 ++++++++++++------- .../{image_uploader.rb => asset_uploader.rb} | 5 +++- 3 files changed, 22 insertions(+), 10 deletions(-) rename app/uploaders/{image_uploader.rb => asset_uploader.rb} (86%) diff --git a/app/cells/plugins/core/asset_cell.rb b/app/cells/plugins/core/asset_cell.rb index fe8422f..206ffb8 100644 --- a/app/cells/plugins/core/asset_cell.rb +++ b/app/cells/plugins/core/asset_cell.rb @@ -48,6 +48,7 @@ def render_tooltip end def associated_content_item_thumb_url + # TODO: The thumb version needs to be configurable data['asset']['style_urls']['mini'] end diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 9579fa7..553f619 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -6,16 +6,12 @@ class AssetFieldType < FieldType def metadata=(metadata_hash) @metadata = metadata_hash.deep_symbolize_keys - @existing_data = @metadata[:existing_data] + @existing_data = metadata[:existing_data] end def data=(data_hash) - attacher = ImageUploader::Attacher.new self, :asset - attacher.context[:metadata] = @metadata - uploader = ImageUploader.new :cache - asset_file = uploader.upload data_hash['asset'] - - attacher.set asset_file + cached_file = cache_uploader.upload data_hash['asset'] + attacher.set cached_file @asset = attacher.promote action: :store end @@ -23,7 +19,7 @@ def data { asset: { original_filename: asset[:original].original_filename, - #updated_at: asset.updated_at, # Does Shrine give this to us? + # TODO: updated_at: asset.updated_at, -- Does Shrine give this to us? versions: versions_data }, shrine_asset: asset.to_json @@ -57,7 +53,19 @@ def mapping_field_name end def media_title - existing_data['media_title'] || ContentItemService.form_fields[@metadata[:naming_data][:title]][:text].parameterize.underscore + # TODO: Abstract this somehow + existing_data['media_title'] || ContentItemService.form_fields[metadata[:naming_data][:title]][:text].parameterize.underscore + end + + def attacher + attacher = AssetUploader::Attacher.new self, :asset + attacher.context[:metadata] = metadata + attacher.context[:validations] = validations + attacher + end + + def cache_uploader + AssetUploader.new :cache end def versions_data diff --git a/app/uploaders/image_uploader.rb b/app/uploaders/asset_uploader.rb similarity index 86% rename from app/uploaders/image_uploader.rb rename to app/uploaders/asset_uploader.rb index dec3cb0..43e0ff8 100644 --- a/app/uploaders/image_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -1,6 +1,6 @@ require 'image_processing/mini_magick' -class ImageUploader < Shrine +class AssetUploader < Shrine include ImageProcessing::MiniMagick plugin :determine_mime_type @@ -20,5 +20,8 @@ class ImageUploader < Shrine thumb = resize_to_limit!(io.download, 200, 200) mini = resize_to_limit!(io.download, 100, 100) { original: io, thumb: thumb, mini: mini } + + # Detect if image, then process versions + # Perform optimizations end end From e786706d05e6c3ea554e5850bb70d24df03e756f Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Fri, 21 Apr 2017 17:06:57 -0500 Subject: [PATCH 08/19] feat: configurable AssetFieldType storage (S3 + Filesystem), non-image asset support, styling refactors, misc --- .../cortex-plugins-core/application.scss | 2 +- .../components/thumbnail-placeholder.scss | 12 +++++++++++ app/cells/plugins/core/asset_info/index.haml | 6 +++++- app/cells/plugins/core/asset_info/show.haml | 18 +++++++++------- app/cells/plugins/core/asset_info_cell.rb | 8 +++++++ app/models/asset_field_type.rb | 15 ++++++++++++- app/uploaders/asset_uploader.rb | 21 +++++++++++++------ lib/tasks/cortex/core/media.rake | 19 +++++++++++++---- 8 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss diff --git a/app/assets/stylesheets/cortex-plugins-core/application.scss b/app/assets/stylesheets/cortex-plugins-core/application.scss index f60d31b..1b6aae3 100644 --- a/app/assets/stylesheets/cortex-plugins-core/application.scss +++ b/app/assets/stylesheets/cortex-plugins-core/application.scss @@ -1 +1 @@ -// Components and such can be imported here +@import 'components/thumbnail-placeholder'; diff --git a/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss b/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss new file mode 100644 index 0000000..e73d03e --- /dev/null +++ b/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss @@ -0,0 +1,12 @@ +.thumbnail-placeholder { + background-color: $jumbo-button-hover-background; + display: flex; + height: 50px; + width: 50px; + + .h4 { + align-self: center; + text-align: center; + width: 100%; + } +} diff --git a/app/cells/plugins/core/asset_info/index.haml b/app/cells/plugins/core/asset_info/index.haml index 5bddbfc..27e86e5 100644 --- a/app/cells/plugins/core/asset_info/index.haml +++ b/app/cells/plugins/core/asset_info/index.haml @@ -1,2 +1,6 @@ -- if asset +- if asset && asset['versions'][config[:thumbnail_style]] = image_tag(asset['versions'][config[:thumbnail_style]]['url'], height: '50px') +- else + .thumbnail-placeholder + .h4 + = asset['versions']['original']['extension'] diff --git a/app/cells/plugins/core/asset_info/show.haml b/app/cells/plugins/core/asset_info/show.haml index f59f0a3..ad1eae0 100644 --- a/app/cells/plugins/core/asset_info/show.haml +++ b/app/cells/plugins/core/asset_info/show.haml @@ -4,10 +4,11 @@ new Clipboard('#copy-asset-url'); }); - .asset-info.mdl-card.mdl-shadow--2dp - .mdl-card__title - %h2.mdl-card__title-text - = image_tag(asset['versions']['original']['url'], style: 'max-height: 200px;') + .asset-info.mdl-card + - if asset_is_image? + .mdl-card__title + %h2.mdl-card__title-text + = image_tag(asset['versions']['original']['url'], style: 'max-height: 200px;') .mdl-card__supporting-text %dl %dt Original Filename @@ -16,12 +17,13 @@ %dt Original File Type %dd = asset['versions']['original']['mime_type'] - %dt File Size (Original Dimensions) + %dt Original File Size %dd = number_to_human_size(asset['versions']['original']['file_size']) - %dt Original Dimensions - %dd - = dimensions + - if asset_is_image? + %dt Original Dimensions + %dd + = dimensions %dt Creator %dd = creator.fullname diff --git a/app/cells/plugins/core/asset_info_cell.rb b/app/cells/plugins/core/asset_info_cell.rb index 870be3b..e5bacc9 100644 --- a/app/cells/plugins/core/asset_info_cell.rb +++ b/app/cells/plugins/core/asset_info_cell.rb @@ -43,6 +43,14 @@ def updated_at def link_to_asset link_to asset['versions']['original']['url'], asset['versions']['original']['url'], target: '_blank' end + + def asset_type + MimeMagic.new(asset['versions']['original']['mime_type']).mediatype + end + + def asset_is_image? + asset_type == 'image' + end end end end diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 553f619..751766f 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -57,8 +57,21 @@ def media_title existing_data['media_title'] || ContentItemService.form_fields[metadata[:naming_data][:title]][:text].parameterize.underscore end + def store + case metadata[:storage][:type] + when 's3' + Shrine::Storage::S3.new(metadata[:storage][:config]) + when 'file_system' + Shrine::Storage::FileSystem.new(metadata[:storage][:config]) + else + AssetUploader.storages[:store] + end + end + def attacher - attacher = AssetUploader::Attacher.new self, :asset + AssetUploader.storages[:store_copy] = store # this may not be thread safe, but no other way to do this right now + AssetUploader.opts[:keep_files] = metadata[:keep_files] # this may not be thread safe, but no other way to do this right now + attacher = AssetUploader::Attacher.new self, :asset, store: :store_copy attacher.context[:metadata] = metadata attacher.context[:validations] = validations attacher diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index 43e0ff8..7fc47fe 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -9,7 +9,7 @@ class AssetUploader < Shrine plugin :pretty_location plugin :processing plugin :versions - plugin :keep_files, destroyed: true + plugin :keep_files, destroyed: true, replaced: true Attacher.validate do validate_max_size 5.megabytes, message: 'is too large (max is 5 MB)' @@ -17,11 +17,20 @@ class AssetUploader < Shrine end process(:store) do |io, context| - thumb = resize_to_limit!(io.download, 200, 200) - mini = resize_to_limit!(io.download, 100, 100) - { original: io, thumb: thumb, mini: mini } + if image?(io) + thumb = resize_to_limit!(io.download, 200, 200) + mini = resize_to_limit!(io.download, 100, 100) + { original: io, thumb: thumb, mini: mini } - # Detect if image, then process versions - # Perform optimizations + # Perform optimizations + else + { original: io } + end + end + + private + + def image?(io) + MimeMagic.new(io.data['metadata']['mime_type']).mediatype == 'image' end end diff --git a/lib/tasks/cortex/core/media.rake b/lib/tasks/cortex/core/media.rake index 225d378..3b695cb 100644 --- a/lib/tasks/cortex/core/media.rake +++ b/lib/tasks/cortex/core/media.rake @@ -34,7 +34,7 @@ namespace :cortex do naming_data: { title: fieldTitle.id }, - styles: { + versions: { large: {geometry: '1800x1800>', format: :jpg}, medium: {geometry: '800x800>', format: :jpg}, default: {geometry: '300x300>', format: :jpg}, @@ -42,10 +42,21 @@ namespace :cortex do micro: {geometry: '50x50>', format: :jpg}, post_tile: {geometry: '1140x', format: :jpg} }, - processors: [:thumbnail, :paperclip_optimizer], - preserve_files: true, + keep_files: [:destroyed, :replaced], path: ':class/:attachment/:media_title-:style.:extension', - s3_headers: {'Cache-Control': 'public, max-age=315576000'} + storage: { + type: 's3', + config: { + access_key_id: ENV['S3_ACCESS_KEY_ID'], + secret_access_key: ENV['S3_SECRET_ACCESS_KEY'], + region: ENV['S3_REGION'], + bucket: ENV['S3_BUCKET_NAME'], + upload_options: {cache_control: 'public, max-age=315576000'} + #:url => ':s3_alias_url', + #:s3_host_alias => ENV['S3_HOST_ALIAS'], + #:s3_protocol => ENV['S3_PROTOCOL'] + } + } }) media.fields.new(name: 'Description', field_type: 'text_field_type', validations: {presence: true}) media.fields.new(name: 'Tags', field_type: 'tag_field_type') From 7ccd5a6a6e9052a43be500180120a39b7e418aae Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Fri, 21 Apr 2017 17:56:11 -0500 Subject: [PATCH 09/19] feat: AssetFieldType CDN support, S3 access control support, styling error fix --- .../components/thumbnail-placeholder.scss | 2 +- app/models/asset_field_type.rb | 6 +++++- app/uploaders/asset_uploader.rb | 6 ++++-- lib/tasks/cortex/core/media.rake | 9 +++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss b/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss index e73d03e..82bfdef 100644 --- a/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss +++ b/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss @@ -1,5 +1,5 @@ .thumbnail-placeholder { - background-color: $jumbo-button-hover-background; + background-color: #E4E4E4; // TODO: determine how to inherit variables from Cortex. This should be $color-grey-evenlighter display: flex; height: 50px; width: 50px; diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 751766f..08f70ce 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -81,6 +81,10 @@ def cache_uploader AssetUploader.new :cache end + def host_alias + metadata[:storage][:host_alias] unless metadata[:storage][:host_alias].empty? + end + def versions_data asset.transform_values do |version| { @@ -88,7 +92,7 @@ def versions_data filename: version.metadata['filename'], extension: version.extension, mime_type: version.mime_type, - url: version.url, + url: version.url(public: true, host: host_alias), file_size: version.size, dimensions: { width: version.width, diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index 7fc47fe..dba9358 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -20,9 +20,11 @@ class AssetUploader < Shrine if image?(io) thumb = resize_to_limit!(io.download, 200, 200) mini = resize_to_limit!(io.download, 100, 100) - { original: io, thumb: thumb, mini: mini } + #convert!(image, format, page = nil, &block) + #resize_to_fit!(image, width, height) - # Perform optimizations + # TODO: Perform optimizations + { original: io, thumb: thumb, mini: mini } else { original: io } end diff --git a/lib/tasks/cortex/core/media.rake b/lib/tasks/cortex/core/media.rake index 3b695cb..497fa9e 100644 --- a/lib/tasks/cortex/core/media.rake +++ b/lib/tasks/cortex/core/media.rake @@ -46,15 +46,16 @@ namespace :cortex do path: ':class/:attachment/:media_title-:style.:extension', storage: { type: 's3', + host_alias: ENV['HOST_ALIAS'], config: { access_key_id: ENV['S3_ACCESS_KEY_ID'], secret_access_key: ENV['S3_SECRET_ACCESS_KEY'], region: ENV['S3_REGION'], bucket: ENV['S3_BUCKET_NAME'], - upload_options: {cache_control: 'public, max-age=315576000'} - #:url => ':s3_alias_url', - #:s3_host_alias => ENV['S3_HOST_ALIAS'], - #:s3_protocol => ENV['S3_PROTOCOL'] + upload_options: { + acl: 'public-read', + cache_control: 'public, max-age=315576000' + } } } }) From bd48fb63d17cbe48469467dd8637d33b66080ef5 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Sun, 23 Apr 2017 15:16:39 -0500 Subject: [PATCH 10/19] feat: dynamic AssetFieldType versions --- app/models/asset_field_type.rb | 4 ++-- app/uploaders/asset_uploader.rb | 16 +++++++--------- lib/tasks/cortex/core/media.rake | 13 ++++++------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 08f70ce..856d844 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -19,7 +19,7 @@ def data { asset: { original_filename: asset[:original].original_filename, - # TODO: updated_at: asset.updated_at, -- Does Shrine give this to us? + # TODO: updated_at: asset.updated_at, -- Does Shrine give this to us? Potentially distinct from record's updated_at versions: versions_data }, shrine_asset: asset.to_json @@ -60,7 +60,7 @@ def media_title def store case metadata[:storage][:type] when 's3' - Shrine::Storage::S3.new(metadata[:storage][:config]) + Shrine::Storage::S3.new(metadata[:storage][:config]) # TODO: Encrypt credentials? when 'file_system' Shrine::Storage::FileSystem.new(metadata[:storage][:config]) else diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index dba9358..436ae0d 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -17,16 +17,14 @@ class AssetUploader < Shrine end process(:store) do |io, context| - if image?(io) - thumb = resize_to_limit!(io.download, 200, 200) - mini = resize_to_limit!(io.download, 100, 100) - #convert!(image, format, page = nil, &block) - #resize_to_fit!(image, width, height) + # TODO: Perform image optimizations, support versions without processors or formatters + versions = { original: io } - # TODO: Perform optimizations - { original: io, thumb: thumb, mini: mini } - else - { original: io } + if image?(io) + versions.merge(context[:metadata][:versions].transform_values do |version| + processed_version = send("#{version[:process][:method]}!", io.download, *version[:process][:config].values) + convert!(processed_version, version[:format]) + end) end end diff --git a/lib/tasks/cortex/core/media.rake b/lib/tasks/cortex/core/media.rake index 497fa9e..8f0dc1b 100644 --- a/lib/tasks/cortex/core/media.rake +++ b/lib/tasks/cortex/core/media.rake @@ -18,7 +18,7 @@ namespace :cortex do puts "Creating Fields..." allowed_asset_content_types = %w(txt css js pdf doc docx ppt pptx csv xls xlsx svg ico png jpg gif bmp) - fieldTitle = media.fields.new(name: 'Title', field_type: 'text_field_type', validations: {presence: true, uniqueness: true}) + fieldTitle = media.fields.new(name: 'Title', field_type: 'text_field_type', validations: { presence: true, uniqueness: true }) fieldTitle.save media.fields.new(name: 'Asset', field_type: 'asset_field_type', validations: @@ -35,12 +35,11 @@ namespace :cortex do title: fieldTitle.id }, versions: { - large: {geometry: '1800x1800>', format: :jpg}, - medium: {geometry: '800x800>', format: :jpg}, - default: {geometry: '300x300>', format: :jpg}, - mini: {geometry: '100x100>', format: :jpg}, - micro: {geometry: '50x50>', format: :jpg}, - post_tile: {geometry: '1140x', format: :jpg} + large: { process: { method: 'resize_to_limit', config: { width: '1800', height: '1800' } }, format: :jpg }, + medium: { process: { method: 'resize_to_limit', config: { width: '800', height: '800' } }, format: :jpg }, + default: { process: { method: 'resize_to_limit', config: { width: '300', height: '300' } }, format: :jpg }, + mini: { process: { method: 'resize_to_limit', config: { width: '100', height: '100' } }, format: :jpg }, + micro: { process: { method: 'resize_to_limit', config: { width: '50', height: '50' } }, format: :jpg }, }, keep_files: [:destroyed, :replaced], path: ':class/:attachment/:media_title-:style.:extension', From aa2dd70099b0308760a15b91819546067bda424d Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Mon, 24 Apr 2017 02:12:01 -0500 Subject: [PATCH 11/19] feat: AssetFieldType dynamic validations --- app/models/asset_field_type.rb | 55 +++++++++++++------ app/models/text_field_type.rb | 5 +- app/uploaders/asset_uploader.rb | 20 +++++-- config/initializers/shrine.rb | 1 - .../plugins/cortex_validation_helpers.rb | 23 ++++++++ lib/tasks/cortex/core/media.rake | 4 +- 6 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 lib/shrine/plugins/cortex_validation_helpers.rb diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 856d844..110fd3b 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -1,21 +1,29 @@ +require 'shrine/storage/s3' + class AssetFieldType < FieldType attr_reader :asset, :existing_data attr_accessor :asset_data + before_save :promote + + validate :asset_presence, if: :validate_presence? + validate :asset_errors + def metadata=(metadata_hash) @metadata = metadata_hash.deep_symbolize_keys @existing_data = metadata[:existing_data] end def data=(data_hash) - cached_file = cache_uploader.upload data_hash['asset'] - attacher.set cached_file - @asset = attacher.promote action: :store + attacher.assign data_hash['asset'].open if data_hash['asset'] + @asset = attacher.get end def data + return {} if errors.any? || attacher.errors.any? + { asset: { original_filename: asset[:original].original_filename, @@ -42,16 +50,14 @@ def image? MimeMagic.new(asset.mime_type).mediatype == 'image' end - def allowed_content_types - validations[:allowed_extensions].collect do |allowed_content_type| - MimeMagic.by_extension(allowed_content_type).type - end - end - def mapping_field_name "#{field_name.parameterize('_')}_asset_file_name" end + def promote + @asset = attacher.promote action: :store unless asset.is_a?(Hash) + end + def media_title # TODO: Abstract this somehow existing_data['media_title'] || ContentItemService.form_fields[metadata[:naming_data][:title]][:text].parameterize.underscore @@ -69,16 +75,15 @@ def store end def attacher - AssetUploader.storages[:store_copy] = store # this may not be thread safe, but no other way to do this right now - AssetUploader.opts[:keep_files] = metadata[:keep_files] # this may not be thread safe, but no other way to do this right now - attacher = AssetUploader::Attacher.new self, :asset, store: :store_copy - attacher.context[:metadata] = metadata - attacher.context[:validations] = validations - attacher - end + unless @attacher + AssetUploader.storages[:store_copy] = store # this may not be thread safe, but no other way to do this right now + AssetUploader.opts[:keep_files] = metadata[:keep_files] # this may not be thread safe, but no other way to do this right now + @attacher = AssetUploader::Attacher.new self, :asset, store: :store_copy + @attacher.context[:metadata] = metadata + @attacher.context[:validations] = validations + end - def cache_uploader - AssetUploader.new :cache + @attacher end def host_alias @@ -101,4 +106,18 @@ def versions_data } end end + + def validate_presence? + validations.key? :presence + end + + def asset_presence + errors.add(:asset, 'must be present') unless asset + end + + def asset_errors + attacher.errors.each do |message| + errors.add(:asset, message) + end + end end diff --git a/app/models/text_field_type.rb b/app/models/text_field_type.rb index 0bbcbdc..64364a8 100644 --- a/app/models/text_field_type.rb +++ b/app/models/text_field_type.rb @@ -26,16 +26,13 @@ def mapping_field_name "#{field_name.parameterize('_')}_text" end - def text_present - errors.add(:text, 'must be present') if @text.empty? - end - def text_length validator = LengthValidator.new(validations[:length].merge(attributes: [:text])) validator.validate_each(self, :text, text) end def text_unique + # TODO: This breaks when you try to update existing text unless metadata[:existing_data][:text] == text || field.field_items.jsonb_contains(:data, text: text).empty? errors.add(:text, "#{field.name} Must be unique") end diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index 436ae0d..826d4e8 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -6,19 +6,29 @@ class AssetUploader < Shrine plugin :determine_mime_type plugin :store_dimensions plugin :validation_helpers + plugin :cortex_validation_helpers plugin :pretty_location plugin :processing plugin :versions plugin :keep_files, destroyed: true, replaced: true Attacher.validate do - validate_max_size 5.megabytes, message: 'is too large (max is 5 MB)' - validate_mime_type_inclusion %w(image/jpeg image/png image/gif) + # TODO: DRY this via metaprogramming + validate_mime_type_inclusion allowed_content_types if validate? :allowed_extensions + validate_max_size validations[:max_size] if validate? :max_size + validate_min_size validations[:min_size] if validate? :min_size + + if store.image?(get) + validate_max_width validations[:max_width] if validate? :max_width + validate_max_height validations[:max_height] if validate? :max_height + validate_min_width validations[:min_width] if validate? :min_width + validate_min_height validations[:min_height] if validate? :min_height + end end process(:store) do |io, context| - # TODO: Perform image optimizations, support versions without processors or formatters - versions = { original: io } + # TODO: Perform image optimizations (build plugin), support versions without processors or formatters + versions = { original: io.download } if image?(io) versions.merge(context[:metadata][:versions].transform_values do |version| @@ -28,8 +38,6 @@ class AssetUploader < Shrine end end - private - def image?(io) MimeMagic.new(io.data['metadata']['mime_type']).mediatype == 'image' end diff --git a/config/initializers/shrine.rb b/config/initializers/shrine.rb index cf0c2a3..7bc5084 100644 --- a/config/initializers/shrine.rb +++ b/config/initializers/shrine.rb @@ -1,6 +1,5 @@ require 'shrine' require 'shrine/storage/file_system' -require 'shrine/storage/s3' Shrine.storages = { cache: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/cache'), diff --git a/lib/shrine/plugins/cortex_validation_helpers.rb b/lib/shrine/plugins/cortex_validation_helpers.rb new file mode 100644 index 0000000..dfe4bf7 --- /dev/null +++ b/lib/shrine/plugins/cortex_validation_helpers.rb @@ -0,0 +1,23 @@ +class Shrine + module Plugins + module CortexValidationHelpers + module AttacherMethods + def validations + context[:validations] + end + + def validate?(validation) + validations.key? validation + end + + def allowed_content_types + validations[:allowed_extensions].collect do |allowed_content_type| + MimeMagic.by_extension(allowed_content_type).type + end + end + end + end + + register_plugin(:cortex_validation_helpers, CortexValidationHelpers) + end +end diff --git a/lib/tasks/cortex/core/media.rake b/lib/tasks/cortex/core/media.rake index 8f0dc1b..feba86f 100644 --- a/lib/tasks/cortex/core/media.rake +++ b/lib/tasks/cortex/core/media.rake @@ -25,9 +25,7 @@ namespace :cortex do { presence: true, allowed_extensions: allowed_asset_content_types, - size: { - less_than: 50.megabytes - } + max_size: 50.megabytes }, metadata: { From 91df5ea0cf386ba23ffb88cd888154a24905e3a2 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Mon, 24 Apr 2017 02:54:32 -0500 Subject: [PATCH 12/19] feat: preliminary AssetFieldType dynamic path generation --- app/uploaders/asset_uploader.rb | 11 ++++++++++- config/initializers/shrine.rb | 1 - lib/tasks/cortex/core/media.rake | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index 826d4e8..452886e 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -7,7 +7,6 @@ class AssetUploader < Shrine plugin :store_dimensions plugin :validation_helpers plugin :cortex_validation_helpers - plugin :pretty_location plugin :processing plugin :versions plugin :keep_files, destroyed: true, replaced: true @@ -38,6 +37,16 @@ class AssetUploader < Shrine end end + def _generate_location(io, context) + # TODO: This is broken + attachment = :asset + media_title = '' + style = context[:version] || :original + name = super + + ERB.new(context[:metadata][:path]).result # TODO: Shrine is overwriting metadata.. + end + def image?(io) MimeMagic.new(io.data['metadata']['mime_type']).mediatype == 'image' end diff --git a/config/initializers/shrine.rb b/config/initializers/shrine.rb index 7bc5084..9ad84af 100644 --- a/config/initializers/shrine.rb +++ b/config/initializers/shrine.rb @@ -7,4 +7,3 @@ } Shrine.plugin :logging, logger: Rails.logger -Shrine.plugin :cached_attachment_data diff --git a/lib/tasks/cortex/core/media.rake b/lib/tasks/cortex/core/media.rake index feba86f..2dbabc7 100644 --- a/lib/tasks/cortex/core/media.rake +++ b/lib/tasks/cortex/core/media.rake @@ -40,7 +40,7 @@ namespace :cortex do micro: { process: { method: 'resize_to_limit', config: { width: '50', height: '50' } }, format: :jpg }, }, keep_files: [:destroyed, :replaced], - path: ':class/:attachment/:media_title-:style.:extension', + path: '<%= attachment %>/<%= media_title %>-<%= style %>', storage: { type: 's3', host_alias: ENV['HOST_ALIAS'], From fc79a1587462db4631a484e5a4904fcae29349ff Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Tue, 25 Apr 2017 17:59:03 -0500 Subject: [PATCH 13/19] feat: dynamic AssetFieldType paths --- app/models/asset_field_type.rb | 33 ++++++++++--------- app/uploaders/asset_uploader.rb | 14 ++++---- .../plugins/cortex_validation_helpers.rb | 2 +- lib/tasks/cortex/core/media.rake | 2 +- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/app/models/asset_field_type.rb b/app/models/asset_field_type.rb index 110fd3b..0de03a1 100644 --- a/app/models/asset_field_type.rb +++ b/app/models/asset_field_type.rb @@ -1,9 +1,7 @@ require 'shrine/storage/s3' class AssetFieldType < FieldType - attr_reader :asset, - :existing_data - + attr_reader :asset attr_accessor :asset_data before_save :promote @@ -11,22 +9,16 @@ class AssetFieldType < FieldType validate :asset_presence, if: :validate_presence? validate :asset_errors - def metadata=(metadata_hash) - @metadata = metadata_hash.deep_symbolize_keys - @existing_data = metadata[:existing_data] - end - def data=(data_hash) - attacher.assign data_hash['asset'].open if data_hash['asset'] + assign data_hash['asset'] if data_hash['asset'] @asset = attacher.get end def data return {} if errors.any? || attacher.errors.any? - { asset: { - original_filename: asset[:original].original_filename, + original_filename: @original_filename, # TODO: updated_at: asset.updated_at, -- Does Shrine give this to us? Potentially distinct from record's updated_at versions: versions_data }, @@ -58,9 +50,15 @@ def promote @asset = attacher.promote action: :store unless asset.is_a?(Hash) end - def media_title - # TODO: Abstract this somehow - existing_data['media_title'] || ContentItemService.form_fields[metadata[:naming_data][:title]][:text].parameterize.underscore + def assign(attachment) + @original_filename = attachment.original_filename + + attachment.open + begin + attacher.assign attachment + ensure + attachment.close + end end def store @@ -79,8 +77,11 @@ def attacher AssetUploader.storages[:store_copy] = store # this may not be thread safe, but no other way to do this right now AssetUploader.opts[:keep_files] = metadata[:keep_files] # this may not be thread safe, but no other way to do this right now @attacher = AssetUploader::Attacher.new self, :asset, store: :store_copy - @attacher.context[:metadata] = metadata - @attacher.context[:validations] = validations + @attacher.context[:config] = { + original_filename: @original_filename, + metadata: metadata, + validations: validations + } end @attacher diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index 452886e..dceee24 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -12,7 +12,6 @@ class AssetUploader < Shrine plugin :keep_files, destroyed: true, replaced: true Attacher.validate do - # TODO: DRY this via metaprogramming validate_mime_type_inclusion allowed_content_types if validate? :allowed_extensions validate_max_size validations[:max_size] if validate? :max_size validate_min_size validations[:min_size] if validate? :min_size @@ -27,24 +26,25 @@ class AssetUploader < Shrine process(:store) do |io, context| # TODO: Perform image optimizations (build plugin), support versions without processors or formatters + context[:generated_hex] = SecureRandom.hex(8) versions = { original: io.download } if image?(io) - versions.merge(context[:metadata][:versions].transform_values do |version| + versions.merge(context[:config][:metadata][:versions].transform_values do |version| processed_version = send("#{version[:process][:method]}!", io.download, *version[:process][:config].values) convert!(processed_version, version[:format]) end) end end - def _generate_location(io, context) - # TODO: This is broken + def generate_location(io, context) attachment = :asset - media_title = '' style = context[:version] || :original - name = super + original_name, _dot, original_extension = context[:config][:original_filename].rpartition('.') + generated_name, _dot, extension = super.rpartition('.') + generated_hex = context[:generated_hex] - ERB.new(context[:metadata][:path]).result # TODO: Shrine is overwriting metadata.. + ERB.new(context[:config][:metadata][:path]).result(binding) end def image?(io) diff --git a/lib/shrine/plugins/cortex_validation_helpers.rb b/lib/shrine/plugins/cortex_validation_helpers.rb index dfe4bf7..a8d6137 100644 --- a/lib/shrine/plugins/cortex_validation_helpers.rb +++ b/lib/shrine/plugins/cortex_validation_helpers.rb @@ -3,7 +3,7 @@ module Plugins module CortexValidationHelpers module AttacherMethods def validations - context[:validations] + context[:config][:validations] end def validate?(validation) diff --git a/lib/tasks/cortex/core/media.rake b/lib/tasks/cortex/core/media.rake index 2dbabc7..8316fe6 100644 --- a/lib/tasks/cortex/core/media.rake +++ b/lib/tasks/cortex/core/media.rake @@ -40,7 +40,7 @@ namespace :cortex do micro: { process: { method: 'resize_to_limit', config: { width: '50', height: '50' } }, format: :jpg }, }, keep_files: [:destroyed, :replaced], - path: '<%= attachment %>/<%= media_title %>-<%= style %>', + path: '<%= attachment %>/<%= original_name %>-<%= style %>-<%= generated_hex %>.<%= extension %>', storage: { type: 's3', host_alias: ENV['HOST_ALIAS'], From cc5f33441c2d229536ab0776d86aa197e37c9654 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Tue, 25 Apr 2017 18:04:48 -0500 Subject: [PATCH 14/19] fix: correctly generate hash of versions during non-image AssetFieldType processing --- app/uploaders/asset_uploader.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index dceee24..e287cbc 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -30,11 +30,13 @@ class AssetUploader < Shrine versions = { original: io.download } if image?(io) - versions.merge(context[:config][:metadata][:versions].transform_values do |version| + versions.merge!(context[:config][:metadata][:versions].transform_values do |version| processed_version = send("#{version[:process][:method]}!", io.download, *version[:process][:config].values) convert!(processed_version, version[:format]) end) end + + versions end def generate_location(io, context) From 233f7faa8fb97ebaa445153868fece219b4f4dc7 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 26 Apr 2017 02:41:16 -0500 Subject: [PATCH 15/19] fix: associated ContentItem issues with new AssetFieldType --- app/cells/plugins/core/asset_cell.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/cells/plugins/core/asset_cell.rb b/app/cells/plugins/core/asset_cell.rb index 206ffb8..a15101a 100644 --- a/app/cells/plugins/core/asset_cell.rb +++ b/app/cells/plugins/core/asset_cell.rb @@ -24,7 +24,7 @@ def allowed_asset_extensions_for_form end def render_max_asset_size - number_to_human_size(field.validations['size']&.[]('less_than')) + number_to_human_size(field.validations['max_size']) end def input_classes @@ -49,7 +49,7 @@ def render_tooltip def associated_content_item_thumb_url # TODO: The thumb version needs to be configurable - data['asset']['style_urls']['mini'] + data['asset']['versions']['mini']['url'] end def render_associated_content_item_thumb From dc232962965e9c6a0cefa5c7e889be6986baf2c7 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 26 Apr 2017 10:36:59 -0500 Subject: [PATCH 16/19] chore: update a todo comment to reflect latest plans --- .../cortex-plugins-core/components/thumbnail-placeholder.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss b/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss index 82bfdef..403665b 100644 --- a/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss +++ b/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss @@ -1,5 +1,5 @@ .thumbnail-placeholder { - background-color: #E4E4E4; // TODO: determine how to inherit variables from Cortex. This should be $color-grey-evenlighter + background-color: #E4E4E4; // TODO: Abstract to cortex-style-base library. This should be $color-grey-evenlighter display: flex; height: 50px; width: 50px; From 34c9ad2698d3e1b2db56e82102cc1f9b6039ace5 Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 26 Apr 2017 11:00:08 -0500 Subject: [PATCH 17/19] feat: drop in variables from cortex (to eventually be abstracted to style-base), uppercase thumbnail placeholder text --- .../cortex-plugins-core/application.scss | 4 + .../components/thumbnail-placeholder.scss | 3 +- .../variables/_colors.scss | 47 ++++++++ .../variables/_typography.scss | 114 ++++++++++++++++++ 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/cortex-plugins-core/variables/_colors.scss create mode 100644 app/assets/stylesheets/cortex-plugins-core/variables/_typography.scss diff --git a/app/assets/stylesheets/cortex-plugins-core/application.scss b/app/assets/stylesheets/cortex-plugins-core/application.scss index 1b6aae3..a7749a2 100644 --- a/app/assets/stylesheets/cortex-plugins-core/application.scss +++ b/app/assets/stylesheets/cortex-plugins-core/application.scss @@ -1 +1,5 @@ +// TODO: These two files should be removed once we abstract Cortex styles to a cortex-style-base lib +@import 'variables/colors'; +@import 'variables/typography'; + @import 'components/thumbnail-placeholder'; diff --git a/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss b/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss index 403665b..4c1debe 100644 --- a/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss +++ b/app/assets/stylesheets/cortex-plugins-core/components/thumbnail-placeholder.scss @@ -1,10 +1,11 @@ .thumbnail-placeholder { - background-color: #E4E4E4; // TODO: Abstract to cortex-style-base library. This should be $color-grey-evenlighter + background-color: $color-grey-evenlighter; display: flex; height: 50px; width: 50px; .h4 { + text-transform: uppercase; align-self: center; text-align: center; width: 100%; diff --git a/app/assets/stylesheets/cortex-plugins-core/variables/_colors.scss b/app/assets/stylesheets/cortex-plugins-core/variables/_colors.scss new file mode 100644 index 0000000..f83e5d0 --- /dev/null +++ b/app/assets/stylesheets/cortex-plugins-core/variables/_colors.scss @@ -0,0 +1,47 @@ +// Color Definitions + +$color-teal: #63C0B9; +$color-teal-dark: #54A1A1; +$color-teal-light: #A1D9D5; + +$color-orange: #F79C25; +$color-orange-dark: #E78523; +$color-orange-light: #FED473; + +$color-anchor-blue-light: #747D8E; + +$color-green: #009b74; + +$color-slate-grey: #6E788F; +$color-grey: #BBB; // Type +$color-grey-dark: #333; // Type, Sidebar background, Login page background +$color-grey-light: #D4D4D4; // Content background color +$color-grey-lightest: #DDD; // Disabled and flat button background color +$color-grey-evenlighter: #E4E4E4; +$color-grey-reallylight: #F2F2F2; +$color-grey-extralight: #F4F4F4; // Wizard Instruction Panel background color + +$color-red: #d85252; + +$color-white: white; // Header +$color-black: #000000; + + +// Color Semantics + +$employer-color: $color-teal; +$employer-color-dark: $color-teal-dark; +$employer-color-light: $color-teal-light; + +$flash-error-background: $color-red; +$flash-success-background: $color-green; + +$ar-color: $color-orange; +$ar-color-dark: $color-orange-dark; +$ar-color-light: $color-orange-light; + +$notes-text: #6E788F; + +$jumbo-button-text: #6E788F; +$jumbo-button-hover-background: $color-grey-evenlighter; +$jumbo-button-active-background: $color-grey-reallylight; diff --git a/app/assets/stylesheets/cortex-plugins-core/variables/_typography.scss b/app/assets/stylesheets/cortex-plugins-core/variables/_typography.scss new file mode 100644 index 0000000..948f884 --- /dev/null +++ b/app/assets/stylesheets/cortex-plugins-core/variables/_typography.scss @@ -0,0 +1,114 @@ +@font-face { + font-family: Montserrat; + font-style: normal; + font-weight: normal; + src: url(asset_path('Montserrat-Regular.otf')) format("opentype"); +} + +@font-face { + font-family: Montserrat; + font-style: normal; + font-weight: bold; + src: url(asset_path('Montserrat-Medium.otf')) format("opentype"); +} + +@font-face { + font-family: Montserrat; + font-style: normal; + font-weight: bolder; + src: url(asset_path('Montserrat-SemiBold.otf')) format("opentype"); +} + +@font-face { + font-family: Montserrat; + font-style: normal; + font-weight: lighter; + src: url(asset_path('Montserrat-Light.otf')) format("opentype"); +} + +$cortex-font-stack: Montserrat, sans-serif; +$base-font-size: 1rem; + +%display-text { // Cortex Logo + color: $color-grey-dark; + font-family: $cortex-font-stack; + font-weight: normal; + font-size: 2.1775rem; +} + +h1, h2, h3, h4, h5, h6 { + line-height: 1.5; +} + +h1, +.text-style-1 { // Bread Crumbs and Page Headers + color: $color-teal; + font-family: $cortex-font-stack; + font-weight: bold; + font-size: 1.17rem; + text-decoration: none; +} + +h2, +.text-style-2 { + color: $color-grey; + font-family: $cortex-font-stack; + font-weight: lighter; + font-size: 1.17rem; +} + +h3, +.text-style-3 { + color: $color-grey; + font-family: $cortex-font-stack; + font-weight: bold; + font-size: 1.17rem; +} + +h4, +.text-style-4 { // Section Headers, Dropdowns, Button Text + color: $color-grey-dark; + font-family: $cortex-font-stack; + font-size: 0.83rem; +} + +h5, +.text-style-5 { // Field Text + color: $color-grey; + font-family: $cortex-font-stack; + font-weight: lighter; + font-size: $base-font-size; +} + +h6, +.text-style-6 { // Field Text Filled + color: $color-anchor-blue-light; + font-family: $cortex-font-stack; + font-size: $base-font-size; +} + +.text-style-7 { // Help Notes + color: $color-anchor-blue-light; + font-family: $cortex-font-stack; + font-weight: lighter; + font-size: 0.67rem; +} + +%sidebar-nav { // Side Nav + color: $color-grey; + font-family: $cortex-font-stack; + font-weight: lighter; + font-size: 0.875rem; + text-transform: uppercase; + + &.active { // Side Nav Selected + font-weight: bold; + color: $color-teal; + } +} + +p { + color: $color-anchor-blue-light; + font-family: $cortex-font-stack; + font-size: $base-font-size; +} From 81d6919a4df6503c80b59fe889abf89a58b6d58a Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 26 Apr 2017 11:00:59 -0500 Subject: [PATCH 18/19] chore: bump to v0.12.0 --- lib/cortex/plugins/core/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cortex/plugins/core/version.rb b/lib/cortex/plugins/core/version.rb index 6e9de1d..da48cb2 100644 --- a/lib/cortex/plugins/core/version.rb +++ b/lib/cortex/plugins/core/version.rb @@ -1,7 +1,7 @@ module Cortex module Plugins module Core - VERSION = '0.11.2' + VERSION = '0.12.0' end end end From 52f03cc6f608b5553352bf994d1c3789cabbf9fc Mon Sep 17 00:00:00 2001 From: Alex Tharp Date: Wed, 26 Apr 2017 11:09:17 -0500 Subject: [PATCH 19/19] refactor: loosen gemspec version requirements, bump to v0.12.1 --- cortex-plugins-core.gemspec | 14 +++++++------- lib/cortex/plugins/core/version.rb | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cortex-plugins-core.gemspec b/cortex-plugins-core.gemspec index db87db2..4a2040d 100644 --- a/cortex-plugins-core.gemspec +++ b/cortex-plugins-core.gemspec @@ -19,14 +19,14 @@ Gem::Specification.new do |s| s.add_dependency "rails", ">= 4" s.add_dependency "react_on_rails", "~> 6" s.add_dependency "cells", "~> 4.1" - s.add_dependency "cells-rails", "~> 0.0.6" - s.add_dependency "cells-haml", "~> 0.0.10" + s.add_dependency "cells-rails", "~> 0.0" + s.add_dependency "cells-haml", "~> 0.0" s.add_dependency "jsonb_accessor", "~> 1.0.0.beta" # AssetFieldType - s.add_dependency "shrine", "~> 2.6.1" - s.add_dependency "mimemagic", "~> 0.3.2" - s.add_dependency "image_processing", "~> 0.4.1" - s.add_dependency "mini_magick", "~> 4.7.0" - s.add_dependency "fastimage", "~> 2.1.0" + s.add_dependency "shrine", "~> 2.6" + s.add_dependency "mimemagic", "~> 0.3" + s.add_dependency "image_processing", "~> 0.4" + s.add_dependency "mini_magick", "~> 4.7" + s.add_dependency "fastimage", "~> 2.1" end diff --git a/lib/cortex/plugins/core/version.rb b/lib/cortex/plugins/core/version.rb index da48cb2..21c0511 100644 --- a/lib/cortex/plugins/core/version.rb +++ b/lib/cortex/plugins/core/version.rb @@ -1,7 +1,7 @@ module Cortex module Plugins module Core - VERSION = '0.12.0' + VERSION = '0.12.1' end end end