require'trusty_cms/geometry'classAsset<ActiveRecord::Basehas_many:page_attachments,dependent: :destroyhas_many:pages,through: :page_attachmentshas_siteifrespond_to?:has_sitebelongs_to:created_by,class_name: 'User'belongs_to:updated_by,class_name: 'User'default_scope{order('created_at DESC')}scope:latest,lambda{|limit|order('created_at DESC').limit(limit)}scope:of_types,lambda{|types|mimes=AssetType.slice(*types).map(&:mime_types).flattenreturnnoneifmimes.empty?joins(asset_attachment: :blob).where(active_storage_blobs: {content_type: mimes})}scope:matching,lambda{|term|joins(asset_attachment: :blob).where(['LOWER(active_storage_blobs.filename) LIKE (:term) OR LOWER(title) LIKE (:term) OR LOWER(caption) LIKE (:term)',{term: "%#{term.downcase}%"}])}scope:excepting,lambda{|assets|ifassets.any?assets=assets.split(',')ifassets.is_a?(String)asset_ids=assets.first.is_a?(Asset)?assets.map(&:id):assetswhere(["assets.id NOT IN(#{asset_ids.map{'?'}.join(',')})",*asset_ids])else{}end}defself.approved_content_typesAssetType.known_mimetypesendhas_one_attached:assetvalidates:asset,presence: truevalidate:asset_within_configured_size,if: ->{asset.attached?}validate:approved_content_type,if: ->{asset.attached?}before_validation:sync_attachment_metadatabefore_save:assign_titlebefore_save:assign_uuiddefasset_within_configured_sizelimit_mb=ifvideo_content_type?TrustyCms.config['assets.max_video_size'].to_ielseTrustyCms.config['assets.max_asset_size'].to_iendlimit_bytes=limit_mb.megabytesreturnifasset.blob.byte_size.between?(1,limit_bytes)errors.add(:asset,:wrong_size_error,limit_mb: limit_mb)enddefasset_typeAssetType.for(asset)enddeffilenamereturnasset.filename.to_sifasset.attached?self[:asset_file_name]enddefcontent_typereturnasset.content_typeifasset.attached?self[:asset_content_type]enddefbyte_sizereturnasset.blob.byte_sizeifasset.attached?self[:asset_file_size]enddelegate:active_storage_styles,:style_dimensions,:style_format,to: :asset_typedefpdf?content_type=='application/pdf'enddefthumbnail(style_name='normal')ifasset.attached?&&pdf?returnstyle_name.to_s=='thumbnail'?asset_type.icon('thumbnail'):rewrite_cloud_url(asset.url)endvariant=asset_variant(style_name.to_s)returnrewrite_cloud_url(variant.processed.url)ifvariantasset_type.icon(style_name.to_s)enddefpublic_url(style_name='normal')ifstyle_name.to_s=='original'||render_original(style_name)returnrewrite_cloud_url(asset.url)endvariant=asset_variant(style_name.to_s)returnrewrite_cloud_url(variant.processed.url)ifvariantrewrite_cloud_url(asset.url)enddefself.ransackable_attributes(auth_object=nil)%w[asset_content_type asset_file_name asset_file_size caption created_at created_by_id id original_extension original_height original_width title updated_at updated_by_id uuid]enddefrender_original(_style_name)returnfalseunlessasset.attached?prefix=TrustyCms::Config['assets.storage.prefix'].presenceprefix?asset.key.start_with?(prefix):asset.key.include?('/')enddefasset_variant(style_name)style=active_storage_styles[style_name.to_sym]returnunlessstyletransformations=active_storage_transformations(style[:geometry])transformations[:format]=style[:format]ifstyle[:format]ifasset.variable?asset.variant(transformations)elsifasset.previewable?asset.preview(transformations)endenddefstyle?(style_name='original')style_name=='original'||active_storage_styles.keys.include?(style_name.to_sym)enddefbasenameFile.basename(filename,'.*')iffilenameenddefextension(style_name='original')ifstyle_name=='original'original_extensionelsifstyle=active_storage_styles[style_name.to_sym]style[:format]elseoriginal_extensionendenddeforiginal_extensionreturnfilename.split('.').last.downcaseiffilenameenddefattached_to?(page)pages.include?(page)enddeforiginal_geometry@original_geometry||=TrustyCms::Geometry.new(*original_dimensions)enddefgeometry(style_name='original')unlessstyle?(style_name)raiseTrustyCms::StyleError,"Requested style #{style_name} is not defined for this asset."end@geometry||={}begin@geometry[style_name]||=ifstyle_name.to_s=='original'original_geometryelsestyle=active_storage_styles[style_name.to_sym]original_geometry.transformed_by(style[:geometry])# this can return dimensions for fully specified style sizes but not for relative sizes when there are no original dimensionsendrescueTrustyCms::TransformationError=>eRails.logger.warn"geometry transformation error: #{e}"original_geometry# returns a blank geometry if the real geometry cannot be calculatedendenddefaspect(style_name='original')geometry(style_name).aspectenddeforientation(style_name='original')a=aspect(style_name)ifa==nil?'unknown'elsifa<1.0'vertical'elsifa>1.0'horizontal'else'square'endenddefwidth(style_name='original')geometry(style_name).width.to_ienddefheight(style_name='original')geometry(style_name).height.to_ienddefsquare?(style_name='original')geometry(style_name).square?enddefvertical?(style_name='original')geometry(style_name).vertical?enddefhorizontal?(style_name='original')geometry(style_name).horizontal?enddefdimensions_known?original_dimensions.all?(&:positive?)endprivatedeforiginal_dimensionswidth=original_width.presenceheight=original_height.presenceifasset.attached?width||=asset.metadata[:width]||asset.metadata['width']height||=asset.metadata[:height]||asset.metadata['height']end[width.to_i,height.to_i]enddefactive_storage_transformations(geometry)return{}unlessgeometry.present?parsed=TrustyCms::Geometry.parse(geometry)width=parsed.width.positive??parsed.width:nilheight=parsed.height.positive??parsed.height:nilreturn{}unlesswidth&&heightcaseparsed.modifierwhen'#','^','!'{resize_to_fill: [width,height].compact}when'>','<'{resize_to_limit: [width,height].compact}else{resize_to_limit: [width,height].compact}endenddefvideo_content_type?AssetType.known?(:video)&&AssetType.find(:video).mime_types.include?(content_type)enddefapproved_content_typereturnifAsset.approved_content_types.include?(asset.content_type)errors.add(:asset,:content_type,filename: asset.filename.to_s)enddefsync_attachment_metadatareturnunlessasset.attached?self.asset_file_name=asset.filename.to_sself.asset_content_type=asset.content_typeself.asset_file_size=asset.blob.byte_sizeself.original_extension=asset.filename.extension&.downcaseifasset.metadata[:width].present?&&asset.metadata[:height].present?self.original_width=asset.metadata[:width]self.original_height=asset.metadata[:height]endenddefassign_titlereturnunlessasset.attached?self.title=asset.filename.baseenddefassign_uuidself.uuid=SecureRandom.uuidunlessuuid?enddefrewrite_cloud_url(url)returnurlunlessdefined?(TrustyCmsClippedExtension::Cloud)TrustyCmsClippedExtension::Cloud.rewrite_url(url)endclass<<selfdefknown_typesAssetType.known_typesend# searching and pagination moved to the controllerdeffind_all_by_asset_types(asset_types,*args)with_asset_types(asset_types){where*args}enddefcount_with_asset_types(asset_types,*args)with_asset_types(asset_types){where(*args).count}enddefwith_asset_types(asset_types,&block)w_asset_types=AssetType.conditions_for(asset_types)with_scope(where(conditions: ["#{w_asset_types} = ?",block]))endend# called from AssetType to set type_condition? methods on Assetdefself.define_class_method(name,&block)eigenclass.send:define_method,name,&blockend# returns the return value of class << self block, which is self (as defined within that block)defself.eigenclassclass<<selfself;endend# for backwards compatibilitydefself.thumbnail_sizesAssetType.find(:image).active_storage_stylesenddefself.thumbnail_namesthumbnail_sizes.keysend# this is a convenience for image-pickersdefself.thumbnail_optionsasset_sizes=thumbnail_sizes.mapdo|k,v|size_id=ksize_description="#{k}: "size_description<<(v.is_a?(Array)?v.join(' as '):v)[size_description,size_id]end.sort_by{|pair|pair.last.to_s}asset_sizes.unshift['Original (as uploaded)','original']asset_sizesendend