effective_assets 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +228 -0
- data/Rakefile +23 -0
- data/app/assets/images/effective_assets/icon_close.png +0 -0
- data/app/assets/images/effective_assets/s3_down_button.gif +0 -0
- data/app/assets/images/effective_assets/s3_over_button.gif +0 -0
- data/app/assets/images/effective_assets/s3_up_button.gif +0 -0
- data/app/assets/images/effective_assets/s3_upload.swf +0 -0
- data/app/assets/images/effective_assets/spinner.gif +0 -0
- data/app/assets/images/mime-types/excel.jpg +0 -0
- data/app/assets/images/mime-types/file.png +0 -0
- data/app/assets/images/mime-types/mp3.png +0 -0
- data/app/assets/images/mime-types/pdf.png +0 -0
- data/app/assets/images/mime-types/video.png +0 -0
- data/app/assets/images/mime-types/word.jpg +0 -0
- data/app/assets/images/mime-types/zip.png +0 -0
- data/app/assets/javascripts/effective_assets.js +1 -0
- data/app/assets/javascripts/effective_assets/asset_box_input.js.coffee +71 -0
- data/app/assets/javascripts/effective_assets/asset_box_uploader.js +122 -0
- data/app/assets/javascripts/effective_assets/asset_box_uploader_customization.js +166 -0
- data/app/assets/javascripts/effective_assets/jquery_ui_sortable.js +2106 -0
- data/app/assets/stylesheets/effective_assets.css.scss +1 -0
- data/app/assets/stylesheets/effective_assets/_asset_box_input.scss +206 -0
- data/app/controllers/effective/attachments_controller.rb +14 -0
- data/app/controllers/effective/s3_uploads_controller.rb +98 -0
- data/app/helpers/effective_assets_helper.rb +55 -0
- data/app/models/concerns/acts_as_asset_box.rb +97 -0
- data/app/models/effective/asset.rb +224 -0
- data/app/models/effective/attachment.rb +25 -0
- data/app/models/effective/delayed_job.rb +99 -0
- data/app/models/inputs/asset_box_input.rb +87 -0
- data/app/models/validators/asset_box_length_validator.rb +11 -0
- data/app/models/validators/asset_box_presence_validator.rb +6 -0
- data/app/uploaders/asset_uploader.rb +26 -0
- data/app/uploaders/effective_assets_uploader.rb +63 -0
- data/app/views/active_admin/effective_assets/_edit.html.haml +35 -0
- data/app/views/active_admin/effective_assets/_form.html.haml +4 -0
- data/app/views/active_admin/effective_assets/_new.html.haml +3 -0
- data/app/views/asset_box_input/_attachment_fields.html.haml +14 -0
- data/app/views/asset_box_input/_uploader.html.haml +119 -0
- data/app/views/assets/_video.html.erb +4 -0
- data/config/routes.rb +6 -0
- data/db/migrate/01_create_effective_assets.rb.erb +42 -0
- data/lib/effective_assets.rb +31 -0
- data/lib/effective_assets/engine.rb +38 -0
- data/lib/effective_assets/version.rb +3 -0
- data/lib/generators/effective_assets/install_generator.rb +41 -0
- data/lib/generators/templates/README +1 -0
- data/lib/generators/templates/asset_uploader.rb +25 -0
- data/lib/generators/templates/effective_assets.rb +19 -0
- data/lib/tasks/effective_assets_tasks.rake +4 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +65 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +16 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +71 -0
- data/spec/dummy/log/test.log +33 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec_link +3 -0
- data/spec/models/asset_spec.rb +46 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/factories.rb +28 -0
- metadata +431 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
module Effective
|
2
|
+
class Asset < ActiveRecord::Base
|
3
|
+
self.table_name = EffectiveAssets.assets_table_name.to_s
|
4
|
+
|
5
|
+
mount_uploader :data, EffectiveAssets.uploader
|
6
|
+
|
7
|
+
belongs_to :user if defined?(User)
|
8
|
+
# This is the user that uploaded the asset.
|
9
|
+
# We are using belongs_to because it makes all the permissions 'just work',
|
10
|
+
# in letting people create assets.
|
11
|
+
#
|
12
|
+
# But this doesn't exactly belong to a user.
|
13
|
+
# Assets belong to acts_as_asset_box enabled classes through attachments.
|
14
|
+
#
|
15
|
+
# 1. The S3 uploader makes a post to s3_controller#create
|
16
|
+
# 2. The asset is created by the current_user
|
17
|
+
# 3. But it doesn't belong to anything yet. It might be attached to a user's asset_boxes, or a page, or a post through attachments
|
18
|
+
#
|
19
|
+
|
20
|
+
has_many :attachments, :dependent => :delete_all
|
21
|
+
|
22
|
+
structure do
|
23
|
+
title :string
|
24
|
+
description :text
|
25
|
+
tags :string
|
26
|
+
|
27
|
+
content_type :string, :validates => [:presence]
|
28
|
+
upload_file :string # The full url of the file, as originally uploaded
|
29
|
+
processed :boolean, :default => false
|
30
|
+
|
31
|
+
data :string
|
32
|
+
|
33
|
+
data_size :integer
|
34
|
+
height :integer, :validates => [:numericality => { :allow_nil => true }]
|
35
|
+
width :integer, :validates => [:numericality => { :allow_nil => true }]
|
36
|
+
|
37
|
+
versions_info :text # We store a hash of {:thumb => 34567, :medium => 3343434} data sizes
|
38
|
+
|
39
|
+
timestamps
|
40
|
+
end
|
41
|
+
|
42
|
+
serialize :versions_info, Hash
|
43
|
+
|
44
|
+
#attr_accessible :title, :description, :tags, :content_type, :data_size, :upload_file, :user, :user_id, :id
|
45
|
+
#validates_presence_of :user_id
|
46
|
+
|
47
|
+
before_save :update_asset_dimensions
|
48
|
+
|
49
|
+
default_scope order('created_at DESC')
|
50
|
+
|
51
|
+
scope :images, -> { where('content_type LIKE ?', '%image%') }
|
52
|
+
scope :videos, -> { where('content_type LIKE ?', '%video%') }
|
53
|
+
scope :audio, -> { where('content_type LIKE ?', '%audio%') }
|
54
|
+
scope :files, -> { where('(content_type NOT LIKE ?) AND (content_type NOT LIKE ?) AND (content_type NOT LIKE ?)', '%image%', '%video%', '%audio%') }
|
55
|
+
|
56
|
+
scope :today, -> { where("created_at >= ?", Date.today.beginning_of_day) }
|
57
|
+
scope :this_week, -> { where("created_at >= ?", Date.today.beginning_of_week) }
|
58
|
+
scope :this_month, -> { where("created_at >= ?", Date.today.beginning_of_month) }
|
59
|
+
|
60
|
+
class << self
|
61
|
+
def s3_base_path
|
62
|
+
"http://#{EffectiveAssets.aws_bucket}.s3.amazonaws.com"
|
63
|
+
end
|
64
|
+
|
65
|
+
def string_base_path
|
66
|
+
"string://"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Just call me with Asset.create_from_url('http://somewhere.com/somthing.jpg')
|
70
|
+
def create_from_url(url, options = {})
|
71
|
+
opts = {:upload_file => url, :user_id => 1}.merge(options)
|
72
|
+
|
73
|
+
if (asset = Asset.create(opts))
|
74
|
+
Effective::DelayedJob.new.process_asset(asset)
|
75
|
+
asset
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# We have just uploaded an asset via our s3 uploader
|
82
|
+
# We want this image to be immediately available.
|
83
|
+
def create_from_s3_uploader(url, options = {})
|
84
|
+
opts = {:upload_file => "#{Asset.s3_base_path}/#{url}", :user_id => 1}.merge(options)
|
85
|
+
|
86
|
+
asset = false
|
87
|
+
|
88
|
+
Asset.transaction do
|
89
|
+
begin
|
90
|
+
asset = Asset.create!(opts)
|
91
|
+
|
92
|
+
Rails.logger.info "Copying s3 uploaded file to final resting place..."
|
93
|
+
storage = Fog::Storage.new(:provider => 'AWS', :aws_access_key_id => EffectiveAssets.aws_access_key_id, :aws_secret_access_key => EffectiveAssets.aws_secret_access_key)
|
94
|
+
storage.copy_object(EffectiveAssets.aws_bucket, url, EffectiveAssets.aws_bucket, "#{EffectiveAssets.aws_final_path.chomp('/')}/#{asset.id}/#{asset.file_name}")
|
95
|
+
storage.put_object_acl(EffectiveAssets.aws_bucket, "#{EffectiveAssets.aws_final_path.chomp('/')}/#{asset.id}/#{asset.file_name}", EffectiveAssets.aws_acl)
|
96
|
+
|
97
|
+
Rails.logger.info "Deleting original..."
|
98
|
+
directory = storage.directories.get(EffectiveAssets.aws_bucket)
|
99
|
+
directory.files.new(:key => url).destroy
|
100
|
+
|
101
|
+
asset.update_column(:upload_file, asset.url) # This is our upload file as far as CarrierWave is now concerned
|
102
|
+
|
103
|
+
Effective::DelayedJob.new.process_asset(asset)
|
104
|
+
rescue => e
|
105
|
+
Rails.logger.info e.message
|
106
|
+
Rails.logger.info e.backtrace.join('\n')
|
107
|
+
asset = false
|
108
|
+
end
|
109
|
+
|
110
|
+
raise ActiveRecord::Rollback unless asset
|
111
|
+
end
|
112
|
+
|
113
|
+
asset
|
114
|
+
end
|
115
|
+
|
116
|
+
# This loads the raw contents of a string into a file and uploads that file to s3
|
117
|
+
# Expect to be passed something like
|
118
|
+
# Asset.create_from_string('some binary stuff from a string', :filename => 'icon_close.png', :content_type => 'image/png')
|
119
|
+
def create_from_string(str, options = {})
|
120
|
+
filename = options.delete(:filename) || "file-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}.txt"
|
121
|
+
|
122
|
+
opts = {:upload_file => "#{Asset.string_base_path}#{filename}", :user_id => 1}.merge(options)
|
123
|
+
|
124
|
+
asset = Asset.new(opts)
|
125
|
+
asset.data = AssetStringIO.new(filename, str)
|
126
|
+
|
127
|
+
if asset.save
|
128
|
+
Effective::DelayedJob.new.process_asset(asset)
|
129
|
+
asset
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
before_validation do
|
135
|
+
if !content_type.present? or content_type == 'null' or content_type == 'unknown' or content_type == 'application/octet-stream'
|
136
|
+
self.content_type = case url.to_s[-4, 4].downcase
|
137
|
+
when '.mp3' ; 'audio/mp3'
|
138
|
+
when '.mp4' ; 'video/mp4'
|
139
|
+
when '.mov' ; 'video/mov'
|
140
|
+
when '.m2v' ; 'video/m2v'
|
141
|
+
when '.m4v' ; 'video/m4v'
|
142
|
+
when '.3gp' ; 'video/3gp'
|
143
|
+
when '.3g2' ; 'video/3g2'
|
144
|
+
when '.avi' ; 'video/avi'
|
145
|
+
when '.jpg' ; 'image/jpg'
|
146
|
+
when '.gif' ; 'image/gif'
|
147
|
+
when '.png' ; 'image/png'
|
148
|
+
when '.bmp' ; 'image/bmp'
|
149
|
+
when '.ico' ; 'image/x-icon'
|
150
|
+
else ; 'unknown'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def title
|
156
|
+
self[:title].present? ? self[:title] : file_name
|
157
|
+
end
|
158
|
+
|
159
|
+
# Return the final location of this asset
|
160
|
+
def url
|
161
|
+
"#{Asset.s3_base_path}/#{EffectiveAssets.aws_final_path.chomp('/')}/#{self.id.to_i}/#{upload_file.to_s.split('/').last}"
|
162
|
+
end
|
163
|
+
|
164
|
+
def file_name
|
165
|
+
url.split('/').last rescue url
|
166
|
+
end
|
167
|
+
|
168
|
+
def video?
|
169
|
+
content_type.include? 'video'
|
170
|
+
end
|
171
|
+
|
172
|
+
def image?
|
173
|
+
content_type.include? 'image'
|
174
|
+
end
|
175
|
+
|
176
|
+
def icon?
|
177
|
+
content_type.include? 'image/x-icon'
|
178
|
+
end
|
179
|
+
|
180
|
+
def audio?
|
181
|
+
content_type.include? 'audio'
|
182
|
+
end
|
183
|
+
|
184
|
+
def as_json(options={})
|
185
|
+
{:thumbnail => image_tag(:thumb).html_safe}.merge super
|
186
|
+
end
|
187
|
+
|
188
|
+
def still_processing?
|
189
|
+
!processed
|
190
|
+
end
|
191
|
+
|
192
|
+
def versions_info
|
193
|
+
self[:versions_info] || {}
|
194
|
+
end
|
195
|
+
|
196
|
+
protected
|
197
|
+
# Called in the DelayedJob
|
198
|
+
def update_asset_dimensions
|
199
|
+
if data.present? and data_changed? and image?
|
200
|
+
begin
|
201
|
+
image = MiniMagick::Image.open(self.data.path)
|
202
|
+
self.width = image[:width]
|
203
|
+
self.height = image[:height]
|
204
|
+
rescue => e
|
205
|
+
end
|
206
|
+
end
|
207
|
+
true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class AssetStringIO < StringIO
|
212
|
+
attr_accessor :filepath
|
213
|
+
|
214
|
+
def initialize(*args)
|
215
|
+
super(*args[1..-1])
|
216
|
+
@filepath = args[0]
|
217
|
+
end
|
218
|
+
|
219
|
+
def original_filename
|
220
|
+
File.basename(filepath)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This is a join table for assets and acts_as_attachable
|
2
|
+
|
3
|
+
module Effective
|
4
|
+
class Attachment < ActiveRecord::Base
|
5
|
+
self.table_name = EffectiveAssets.attachments_table_name.to_s
|
6
|
+
|
7
|
+
belongs_to :asset
|
8
|
+
belongs_to :attachable, :polymorphic => true
|
9
|
+
|
10
|
+
structure do
|
11
|
+
position :integer, :validates => [:presence, :numericality]
|
12
|
+
box :string, :default => 'assets', :validates => [:presence] # This is essentially a category
|
13
|
+
end
|
14
|
+
|
15
|
+
validates_presence_of :asset_id
|
16
|
+
|
17
|
+
#attr_accessible :box, :position, :asset_id, :attachable_type, :attachable_id
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def default_scope
|
21
|
+
includes(:asset).order(:position)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#require 'uri'
|
2
|
+
# Call with DelayedJob.new.process_asset_images(...)
|
3
|
+
# Run jobs locally with "rake jobs:work"
|
4
|
+
|
5
|
+
module Effective
|
6
|
+
class DelayedJob
|
7
|
+
def process_asset(asset)
|
8
|
+
DelayedJob.configure_carrierwave
|
9
|
+
|
10
|
+
if asset and !asset.processed? and asset.upload_file.present?
|
11
|
+
begin
|
12
|
+
puts "Processing an asset ID ##{asset.id}..."
|
13
|
+
|
14
|
+
if asset.upload_file.include?("#{Asset.s3_base_path}")
|
15
|
+
if asset.image?
|
16
|
+
puts "Asset is an image in our S3 assets directory. Downloading and processing..."
|
17
|
+
|
18
|
+
# Carrierwave must download the file, process it, then re-upload it to S3
|
19
|
+
asset.remote_data_url = asset.upload_file
|
20
|
+
asset.processed = true
|
21
|
+
asset.save!
|
22
|
+
else
|
23
|
+
puts "Asset is a non-image in our S3 uploads directory. Copying to final location..."
|
24
|
+
|
25
|
+
# We have uploaded a video, or something non-image to our S3 bucket.
|
26
|
+
# We do not currently process anything.
|
27
|
+
|
28
|
+
puts "Marking local asset as processed..."
|
29
|
+
asset.update_column(:data, asset.file_name)
|
30
|
+
asset.processed = true
|
31
|
+
asset.save!
|
32
|
+
end
|
33
|
+
elsif asset.upload_file.include?(Asset.string_base_path)
|
34
|
+
puts "Asset is a string-based asset. Processing..."
|
35
|
+
|
36
|
+
asset.data.cache_stored_file!
|
37
|
+
asset.data.retrieve_from_cache!(asset.data.cache_name)
|
38
|
+
asset.data.recreate_versions!
|
39
|
+
asset.processed = true
|
40
|
+
asset.save!
|
41
|
+
else
|
42
|
+
puts "Asset is not an s3 uploaded asset. Downloading and processing..."
|
43
|
+
|
44
|
+
# Carrierwave must download the file, process it, then re-upload it to S3
|
45
|
+
asset.remote_data_url = asset.upload_file
|
46
|
+
asset.processed = true
|
47
|
+
asset.save!
|
48
|
+
end
|
49
|
+
|
50
|
+
puts "Successfully processed the asset."
|
51
|
+
rescue => e
|
52
|
+
puts "An error occurred while processing an asset:"
|
53
|
+
puts e.message
|
54
|
+
puts e.backtrace.inspect
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
handle_asynchronously :process_asset
|
59
|
+
|
60
|
+
def reprocess_all_assets
|
61
|
+
DelayedJob.configure_carrierwave
|
62
|
+
|
63
|
+
Asset.all.each do |asset|
|
64
|
+
begin
|
65
|
+
puts "Processing Asset ID=#{asset.id}..."
|
66
|
+
asset.data.cache_stored_file!
|
67
|
+
asset.data.retrieve_from_cache!(asset.data.cache_name)
|
68
|
+
asset.data.recreate_versions!
|
69
|
+
asset.save!
|
70
|
+
puts "Successfully processed #{asset.inspect}"
|
71
|
+
rescue => e
|
72
|
+
puts "ERROR: #{asset.id} -> #{e.to_s}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
handle_asynchronously :reprocess_all_assets
|
77
|
+
|
78
|
+
def self.configure_carrierwave
|
79
|
+
return if defined? @@configured_carrierwave
|
80
|
+
|
81
|
+
CarrierWave.configure do |config|
|
82
|
+
config.fog_credentials = {
|
83
|
+
:provider => 'AWS',
|
84
|
+
:aws_access_key_id => EffectiveAssets.aws_access_key_id,
|
85
|
+
:aws_secret_access_key => EffectiveAssets.aws_secret_access_key
|
86
|
+
}
|
87
|
+
config.fog_directory = EffectiveAssets.aws_bucket
|
88
|
+
config.fog_public = EffectiveAssets.aws_acl.to_s.include?('public')
|
89
|
+
config.fog_attributes = {'Cache-Control'=>'max-age=315576000'}
|
90
|
+
config.cache_dir = "#{Rails.root}/tmp/uploads" # For heroku
|
91
|
+
end
|
92
|
+
|
93
|
+
Rails.logger.info "CONFIGURED CARRIERWAVE"
|
94
|
+
|
95
|
+
@@configured_carrierwave = true
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "formtastic"
|
2
|
+
|
3
|
+
# With formtastic, just use
|
4
|
+
#
|
5
|
+
# = f.input :pictures, :as => :asset_box
|
6
|
+
# = f.input :fav_icon, :as => :asset_box, :limit => 4, :file_types => [:jpg, :gif, :png]
|
7
|
+
# = f.input :logo, :as => :asset_box, :uploader => false, :dialog => true
|
8
|
+
# = f.input :logo, :as => :asset_box, :uploader => true, :uploader_visible => true
|
9
|
+
|
10
|
+
class AssetBoxInput
|
11
|
+
include ::Formtastic::Inputs::Base
|
12
|
+
|
13
|
+
def to_html
|
14
|
+
@@uid = (@@uid ||= 0) + 1 # We just need a unique number to pass along, incase we have multiple SWF Uploaders per form
|
15
|
+
|
16
|
+
input_wrapping do
|
17
|
+
output = label_html
|
18
|
+
output += header_html
|
19
|
+
output += "<div class='attachments'>".html_safe
|
20
|
+
output += build_values_html
|
21
|
+
output += "</div>".html_safe
|
22
|
+
|
23
|
+
if options[:uploader]
|
24
|
+
output += insert_uploader_html
|
25
|
+
output += uploader_html
|
26
|
+
end
|
27
|
+
|
28
|
+
if options[:dialog]
|
29
|
+
output += insert_dialog_html
|
30
|
+
end
|
31
|
+
|
32
|
+
output += "</div>".html_safe
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def header_html
|
37
|
+
"<div class='asset_box_input #{method.to_s.pluralize}' data-box='#{method.to_s.pluralize}' data-swf='s3_swf_#{@@uid}' data-limit='#{limit}' data-attachable-id='#{attachable_id}' data-attachable-type='#{attachable_type}'>".html_safe
|
38
|
+
end
|
39
|
+
|
40
|
+
def insert_uploader_html
|
41
|
+
"<a href='#' class='asset-box-upload'>Upload...</a>".html_safe
|
42
|
+
end
|
43
|
+
|
44
|
+
def uploader_html
|
45
|
+
template.render(:partial => 'asset_box_input/uploader', :locals => {:attachable_id => attachable_id, :attachable_type => attachable_type, :box => method.to_s.pluralize, :uid => @@uid, :file_types => options[:file_types], :limit => limit, :uploader_visible => options[:uploader_visible]}).html_safe
|
46
|
+
end
|
47
|
+
|
48
|
+
def insert_dialog_html
|
49
|
+
"<a href='#' class='asset-box-dialog'>Attach...</a>".html_safe
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_values_html
|
53
|
+
count = 0
|
54
|
+
attachments_limit = limit
|
55
|
+
|
56
|
+
attachments.map do |attachment|
|
57
|
+
count += 1 unless attachment.marked_for_destruction?
|
58
|
+
|
59
|
+
template.render(
|
60
|
+
:partial => 'asset_box_input/attachment_fields',
|
61
|
+
:locals => {:attachment => attachment, :attachable_type => attachable_type, :hidden => (count > attachments_limit) }
|
62
|
+
)
|
63
|
+
end.join.html_safe
|
64
|
+
end
|
65
|
+
|
66
|
+
def attachments
|
67
|
+
method_name = method.to_s.pluralize
|
68
|
+
object.attachments.select { |attachment| attachment.box == method_name }
|
69
|
+
end
|
70
|
+
|
71
|
+
def attachable_type
|
72
|
+
options[:attachable_type] || object.class.name.titleize.gsub(" ", "_").gsub('/', '_').downcase
|
73
|
+
end
|
74
|
+
|
75
|
+
def attachable_id
|
76
|
+
options[:attachable_id] || (object.try(:id) rescue nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
def limit
|
80
|
+
method.to_s == method.to_s.pluralize ? (options[:limit] || 10000) : 1
|
81
|
+
end
|
82
|
+
|
83
|
+
def options
|
84
|
+
{:uploader => false, :dialog => false, :uploader_visible => false}.merge(super)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class AssetBoxLengthValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(record, attribute, value)
|
3
|
+
num_in_box = (record.attachments || []).select { |attachment| attachment.box == attribute.to_s.pluralize && attachment.marked_for_destruction? == false }.size
|
4
|
+
|
5
|
+
if options[:with]
|
6
|
+
record.errors[attribute] << "requires at least #{options[:with]} #{attribute.to_s.pluralize}" if num_in_box < options[:with]
|
7
|
+
elsif options[:in]
|
8
|
+
record.errors[attribute] << "requires #{options[:in].min} to #{options[:in].max} #{attribute.to_s.pluralize}" if options[:in].include?(num_in_box) == false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|