glebtv-rails-uploader 0.0.8
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 +241 -0
- data/Rakefile +26 -0
- data/app/assets/javascripts/uploader/application.js +9 -0
- data/app/assets/javascripts/uploader/rails_admin.js +25 -0
- data/app/assets/stylesheets/uploader/application.css +7 -0
- data/app/controllers/uploader/attachments_controller.rb +67 -0
- data/app/views/rails_admin/main/_form_rails_uploader.haml +10 -0
- data/app/views/uploader/default/_container.html.erb +67 -0
- data/app/views/uploader/default/_download.html.erb +17 -0
- data/app/views/uploader/default/_upload.html.erb +14 -0
- data/config/locales/en.yml +5 -0
- data/config/locales/ru.yml +5 -0
- data/config/locales/uk.yml +5 -0
- data/config/routes.rb +7 -0
- data/lib/file_size_validator.rb +66 -0
- data/lib/glebtv-rails-uploader.rb +3 -0
- data/lib/uploader.rb +35 -0
- data/lib/uploader/asset.rb +47 -0
- data/lib/uploader/engine.rb +25 -0
- data/lib/uploader/fileuploads.rb +109 -0
- data/lib/uploader/helpers/field_tag.rb +100 -0
- data/lib/uploader/helpers/form_builder.rb +16 -0
- data/lib/uploader/helpers/form_tag_helper.rb +16 -0
- data/lib/uploader/hooks/active_record.rb +5 -0
- data/lib/uploader/hooks/formtastic.rb +14 -0
- data/lib/uploader/hooks/simple_form.rb +9 -0
- data/lib/uploader/rails_admin/field.rb +28 -0
- data/lib/uploader/version.rb +3 -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/models/article.rb +7 -0
- data/spec/dummy/app/models/asset.rb +27 -0
- data/spec/dummy/app/models/picture.rb +5 -0
- data/spec/dummy/app/uploaders/picture_uploader.rb +55 -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 +58 -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 +3 -0
- data/spec/dummy/db/migrate/20120508093416_create_assets.rb +19 -0
- data/spec/dummy/db/migrate/20120508093830_create_articles.rb +10 -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/factories/articles.rb +8 -0
- data/spec/factories/assets.rb +11 -0
- data/spec/factories/files/rails.png +0 -0
- data/spec/fileuploads_spec.rb +57 -0
- data/spec/requests/attachments_controller_spec.rb +38 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/uploader_spec.rb +18 -0
- data/vendor/assets/images/uploader/but_del_tag2.png +0 -0
- data/vendor/assets/images/uploader/ico_attach.png +0 -0
- data/vendor/assets/images/uploader/preloader.gif +0 -0
- data/vendor/assets/images/uploader/progressBarFillBg.png +0 -0
- data/vendor/assets/javascripts/uploader/jquery.fileupload-ip.js +160 -0
- data/vendor/assets/javascripts/uploader/jquery.fileupload-ui.js +637 -0
- data/vendor/assets/javascripts/uploader/jquery.fileupload.js +904 -0
- data/vendor/assets/javascripts/uploader/jquery.iframe-transport.js +171 -0
- data/vendor/assets/javascripts/uploader/jquery.ui.widget.js +282 -0
- data/vendor/assets/javascripts/uploader/load-image.min.js +1 -0
- data/vendor/assets/javascripts/uploader/locales/en.js +27 -0
- data/vendor/assets/javascripts/uploader/locales/ru.js +27 -0
- data/vendor/assets/javascripts/uploader/locales/uk.js +27 -0
- data/vendor/assets/javascripts/uploader/tmpl.min.js +1 -0
- data/vendor/assets/stylesheets/uploader/default.css +214 -0
- data/vendor/assets/stylesheets/uploader/jquery.fileupload-ui.css +30 -0
- metadata +317 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
<!-- The template to display files available for download -->
|
2
|
+
<script id="template-download-<%= field.klass %>" type="text/x-tmpl">
|
3
|
+
{% for (var i=0, file; file=o.files[i]; i++) { %}
|
4
|
+
<div class="attach_item template-download" data-id="{%=file.id%}">
|
5
|
+
<div class="delete">
|
6
|
+
<a href="/" class="del_btn" data-type="DELETE" data-url="/uploader/attachments/{%=file.id%}?klass=<%= field.klass %>"></a>
|
7
|
+
</div>
|
8
|
+
<div class="thumbnail preview">
|
9
|
+
<a href="{%=file.url%}"><img src="{%=file.thumb_url%}" title="{%=file.filename%}" rel="gallery"></a>
|
10
|
+
</div>
|
11
|
+
<div class="infoHolder">
|
12
|
+
<div class="fileName">{%=file.filename%}</div>
|
13
|
+
<div class="fileWeight">{%=o.formatFileSize(file.size)%}</div>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
{% } %}
|
17
|
+
</script>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!-- The template to display files available for upload -->
|
2
|
+
<script id="template-upload-<%= field.klass %>" type="text/x-tmpl">
|
3
|
+
{% for (var i=0, file; file=o.files[i]; i++) { %}
|
4
|
+
<div class="attach_item loading template-upload">
|
5
|
+
<div class="cancel"><a href="#" class="del_btn"></a></div>
|
6
|
+
<div class="thumbnail preview"><img class="preloader" src="/assets/uploader/preloader.gif" alt=""></div>
|
7
|
+
<div class="infoHolder">
|
8
|
+
<div class="fileName">{%=file.name%}</div>
|
9
|
+
<div class="fileWeight">{%=o.formatFileSize(file.size)%}</div>
|
10
|
+
<div class="progressBar progress"><div class="bar fill" style="width: 0%;"></div></div>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
{% } %}
|
14
|
+
</script>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# lib/file_size_validator.rb
|
2
|
+
# Based on: https://gist.github.com/795665
|
3
|
+
|
4
|
+
class FileSizeValidator < ActiveModel::EachValidator
|
5
|
+
MESSAGES = { :is => :wrong_size, :minimum => :size_too_small, :maximum => :size_too_big }.freeze
|
6
|
+
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
|
7
|
+
|
8
|
+
DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
|
9
|
+
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
if range = (options.delete(:in) || options.delete(:within))
|
13
|
+
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
|
14
|
+
options[:minimum], options[:maximum] = range.begin, range.end
|
15
|
+
options[:maximum] -= 1 if range.exclude_end?
|
16
|
+
end
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def check_validity!
|
22
|
+
keys = CHECKS.keys & options.keys
|
23
|
+
|
24
|
+
if keys.empty?
|
25
|
+
raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
|
26
|
+
end
|
27
|
+
|
28
|
+
keys.each do |key|
|
29
|
+
value = options[key]
|
30
|
+
|
31
|
+
unless value.is_a?(Integer) && value >= 0
|
32
|
+
raise ArgumentError, ":#{key} must be a nonnegative Integer"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_each(record, attribute, value)
|
38
|
+
raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.kind_of? CarrierWave::Uploader::Base
|
39
|
+
value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String)
|
40
|
+
|
41
|
+
CHECKS.each do |key, validity_check|
|
42
|
+
next unless check_value = options[key]
|
43
|
+
|
44
|
+
value ||= [] if key == :maximum
|
45
|
+
|
46
|
+
value_size = value.size
|
47
|
+
next if value_size.send(validity_check, check_value)
|
48
|
+
|
49
|
+
errors_options = options.except(*RESERVED_OPTIONS)
|
50
|
+
errors_options[:file_size] = help.number_to_human_size check_value
|
51
|
+
|
52
|
+
default_message = options[MESSAGES[key]]
|
53
|
+
errors_options[:message] ||= default_message if default_message
|
54
|
+
|
55
|
+
record.errors.add(attribute, MESSAGES[key], errors_options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
def help
|
59
|
+
Helper.instance
|
60
|
+
end
|
61
|
+
|
62
|
+
class Helper
|
63
|
+
include Singleton
|
64
|
+
include ActionView::Helpers::NumberHelper
|
65
|
+
end
|
66
|
+
end
|
data/lib/uploader.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module Uploader
|
5
|
+
autoload :Fileuploads, 'uploader/fileuploads'
|
6
|
+
autoload :Asset, 'uploader/asset'
|
7
|
+
|
8
|
+
module Helpers
|
9
|
+
autoload :FormTagHelper, 'uploader/helpers/form_tag_helper'
|
10
|
+
autoload :FormBuilder, 'uploader/helpers/form_builder'
|
11
|
+
autoload :FieldTag, 'uploader/helpers/field_tag'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.guid
|
15
|
+
SecureRandom.base64(15).tr('+/=', 'xyz').slice(0, 10)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.root_path
|
19
|
+
@root_path ||= Pathname.new( File.dirname(File.expand_path('../', __FILE__)) )
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.assets
|
23
|
+
['uploader/application.css', 'uploader/application.js', 'uploader/rails_admin.js'] +
|
24
|
+
Dir[root_path.join('vendor/assets/javascripts/uploader/**', '*.{js,css}')].inject([]) do |list, path|
|
25
|
+
list << Pathname.new(path).relative_path_from(root_path.join('vendor/assets/javascripts')).to_s
|
26
|
+
list
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'uploader/engine'
|
32
|
+
|
33
|
+
if Object.const_defined?("RailsAdmin")
|
34
|
+
require "uploader/rails_admin/field"
|
35
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Uploader
|
2
|
+
module Asset
|
3
|
+
# Save asset
|
4
|
+
# Usage:
|
5
|
+
#
|
6
|
+
# class Asset < ActiveRecord::Base
|
7
|
+
# include Uploader::Asset
|
8
|
+
#
|
9
|
+
# def uploader_create(params, request = nil)
|
10
|
+
# self.user = request.env['warden'].user
|
11
|
+
# super
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
def uploader_create(params, request = nil)
|
16
|
+
self.guid = params[:guid]
|
17
|
+
self.assetable_type = params[:assetable_type]
|
18
|
+
if self.class.respond_to?(:collection)
|
19
|
+
self.assetable_id = Moped::BSON::ObjectId.from_string(params[:assetable_id])
|
20
|
+
else
|
21
|
+
self.assetable_id = params[:assetable_id]
|
22
|
+
end
|
23
|
+
|
24
|
+
save
|
25
|
+
end
|
26
|
+
|
27
|
+
# Destroy asset
|
28
|
+
# Usage (cancan example):
|
29
|
+
#
|
30
|
+
# class Asset < ActiveRecord::Base
|
31
|
+
# include Uploader::Asset
|
32
|
+
#
|
33
|
+
# def uploader_destroy(params, request = nil)
|
34
|
+
# ability = Ability.new(request.env['warden'].user)
|
35
|
+
# if ability.can? :delete, self
|
36
|
+
# super
|
37
|
+
# else
|
38
|
+
# errors.add(:id, :access_denied)
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
def uploader_destroy(params, request)
|
44
|
+
destroy
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'uploader'
|
3
|
+
|
4
|
+
module Uploader
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
isolate_namespace Uploader
|
7
|
+
|
8
|
+
initializer "uploader.assets_precompile" do |app|
|
9
|
+
app.config.assets.precompile += Uploader.assets
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer "uploader.helpers" do
|
13
|
+
ActiveSupport.on_load :action_view do
|
14
|
+
ActionView::Base.send(:include, Uploader::Helpers::FormTagHelper)
|
15
|
+
ActionView::Helpers::FormBuilder.send(:include, Uploader::Helpers::FormBuilder)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer "uploader.hooks" do
|
20
|
+
require "uploader/hooks/active_record" if Object.const_defined?("ActiveRecord")
|
21
|
+
require "uploader/hooks/formtastic" if Object.const_defined?("Formtastic")
|
22
|
+
require "uploader/hooks/simple_form" if Object.const_defined?("SimpleForm")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Uploader
|
2
|
+
module Fileuploads
|
3
|
+
def self.included(base)
|
4
|
+
base.send :extend, SingletonMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module SingletonMethods
|
8
|
+
# Join ActiveRecord object with uploaded file
|
9
|
+
# Usage:
|
10
|
+
#
|
11
|
+
# class Article < ActiveRecord::Base
|
12
|
+
# has_one :picture, :as => :assetable, :dependent => :destroy
|
13
|
+
#
|
14
|
+
# fileuploads :picture
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
#
|
18
|
+
def fileuploads(*args)
|
19
|
+
options = args.extract_options!
|
20
|
+
|
21
|
+
class_attribute :fileuploads_options, :instance_writer => false
|
22
|
+
self.fileuploads_options = options
|
23
|
+
|
24
|
+
class_attribute :fileuploads_columns, :instance_writer => false
|
25
|
+
self.fileuploads_columns = args.map(&:to_sym)
|
26
|
+
|
27
|
+
unless self.is_a?(ClassMethods)
|
28
|
+
include InstanceMethods
|
29
|
+
extend ClassMethods
|
30
|
+
|
31
|
+
after_save :fileuploads_update, :if => :fileupload_changed?
|
32
|
+
|
33
|
+
fileuploads_columns.each { |asset| accepts_nested_attributes_for asset, :allow_destroy => true }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
# Update reflection klass by guid
|
40
|
+
def fileupload_update(record_id, guid, method)
|
41
|
+
query = fileupload_klass(method).where(:guid => guid, :assetable_type => base_class.name.to_s)
|
42
|
+
query.update_all(:assetable_id => record_id, :guid => nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Find asset by guid
|
46
|
+
def fileupload_find(method, guid)
|
47
|
+
klass = fileupload_klass(method)
|
48
|
+
klass.where(:guid => guid).first
|
49
|
+
end
|
50
|
+
|
51
|
+
# Find class by reflection
|
52
|
+
def fileupload_klass(method)
|
53
|
+
reflect_on_association(method.to_sym).klass
|
54
|
+
end
|
55
|
+
|
56
|
+
unless respond_to?(:base_class)
|
57
|
+
def base_class
|
58
|
+
self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
module InstanceMethods
|
64
|
+
# Generate unique key
|
65
|
+
def fileupload_guid
|
66
|
+
@fileupload_guid ||= Uploader.guid
|
67
|
+
end
|
68
|
+
|
69
|
+
def fileupload_guid=(value)
|
70
|
+
@fileupload_changed = true unless value.blank?
|
71
|
+
@fileupload_guid = value.blank? ? nil : value
|
72
|
+
end
|
73
|
+
|
74
|
+
def fileupload_changed?
|
75
|
+
@fileupload_changed === true
|
76
|
+
end
|
77
|
+
|
78
|
+
def fileupload_multiple?(method)
|
79
|
+
association = self.class.reflect_on_association(method.to_sym)
|
80
|
+
if association.respond_to?(:collection?)
|
81
|
+
!!(association && association.collection?)
|
82
|
+
else
|
83
|
+
association.macro == :has_many
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Find or build new asset object
|
88
|
+
def fileupload_asset(method)
|
89
|
+
if fileuploads_columns.include?(method.to_sym)
|
90
|
+
asset = new_record? ? self.class.fileupload_find(method, fileupload_guid) : send(method)
|
91
|
+
asset ||= send("build_#{method}") if respond_to?("build_#{method}")
|
92
|
+
asset
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def fileuploads_columns
|
97
|
+
self.class.fileuploads_columns
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
def fileuploads_update
|
103
|
+
fileuploads_columns.each do |method|
|
104
|
+
self.class.fileupload_update(id, fileupload_guid, method)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Uploader
|
2
|
+
module Helpers
|
3
|
+
class FieldTag
|
4
|
+
attr_reader :template, :object, :theme
|
5
|
+
|
6
|
+
delegate :uploader, :to => :template
|
7
|
+
|
8
|
+
# Wrapper for render uploader field
|
9
|
+
# Usage:
|
10
|
+
#
|
11
|
+
# uploader = FieldTag.new(object_name, method_name, template, options)
|
12
|
+
# uploader.to_s
|
13
|
+
#
|
14
|
+
def initialize(object_name, method_name, template, options = {}) #:nodoc:
|
15
|
+
options = { :object_name => object_name, :method_name => method_name }.merge(options)
|
16
|
+
|
17
|
+
@template, @options = template, options.dup
|
18
|
+
|
19
|
+
@theme = (@options.delete(:theme) || "default")
|
20
|
+
@value = @options.delete(:value) if @options.key?(:value)
|
21
|
+
|
22
|
+
@object = @options.delete(:object) if @options.key?(:object)
|
23
|
+
@object ||= @template.instance_variable_get("@#{object_name}")
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s(locals = {}) #:nodoc:
|
27
|
+
locals = { :field => self }.merge(locals)
|
28
|
+
@template.render :partial => "uploader/#{@theme}/container", :locals => locals
|
29
|
+
end
|
30
|
+
|
31
|
+
def id
|
32
|
+
@id ||= @template.dom_id(@object, [method_name, 'uploader', @object.fileupload_guid].join('_'))
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_name
|
36
|
+
@options[:method_name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def object_name
|
40
|
+
@options[:object_name]
|
41
|
+
end
|
42
|
+
|
43
|
+
def multiple?
|
44
|
+
@object.fileupload_multiple?(method_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def value
|
48
|
+
@value ||= @object.fileupload_asset(method_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def values
|
52
|
+
if value.first.respond_to?(:sort)
|
53
|
+
Array.wrap(value).sort_by(&:sort)
|
54
|
+
else
|
55
|
+
Array.wrap(value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def exists?
|
60
|
+
values.map(&:persisted?).any?
|
61
|
+
end
|
62
|
+
|
63
|
+
def klass
|
64
|
+
@klass ||= @object.class.fileupload_klass(method_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def attachments_path(options = {})
|
68
|
+
options = {
|
69
|
+
:guid => @object.fileupload_guid,
|
70
|
+
:assetable_type => @object.class.base_class.name.to_s,
|
71
|
+
:klass => klass.to_s
|
72
|
+
}.merge(options)
|
73
|
+
|
74
|
+
options[:assetable_id] = @object.id if @object.persisted?
|
75
|
+
|
76
|
+
uploader.attachments_path(options)
|
77
|
+
end
|
78
|
+
|
79
|
+
def sort_path(options = {})
|
80
|
+
options = {
|
81
|
+
:guid => @object.fileupload_guid,
|
82
|
+
:assetable_type => @object.class.base_class.name.to_s,
|
83
|
+
:klass => klass.to_s
|
84
|
+
}.merge(options)
|
85
|
+
|
86
|
+
options[:assetable_id] = @object.id if @object.persisted?
|
87
|
+
|
88
|
+
uploader.sort_attachments_path(options)
|
89
|
+
end
|
90
|
+
|
91
|
+
def input_html
|
92
|
+
@input_html ||= {
|
93
|
+
:"data-url" => attachments_path,
|
94
|
+
:multiple => multiple?,
|
95
|
+
:class => "uploader"
|
96
|
+
}.merge(@options[:input_html] || {})
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|