rails-uploader 0.2.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +162 -86
  4. data/app/assets/javascripts/uploader/application.js +6 -4
  5. data/app/assets/javascripts/uploader/jquery.uploader.js.coffee +58 -0
  6. data/app/controllers/uploader/attachments_controller.rb +63 -39
  7. data/app/views/uploader/default/_container.html.erb +19 -45
  8. data/app/views/uploader/default/_download.html.erb +4 -5
  9. data/app/views/uploader/default/_upload.html.erb +0 -1
  10. data/config/locales/en.yml +2 -0
  11. data/config/locales/ru.yml +3 -0
  12. data/config/locales/uk.yml +3 -0
  13. data/config/routes.rb +1 -1
  14. data/lib/uploader/asset.rb +63 -84
  15. data/lib/uploader/authorization.rb +52 -0
  16. data/lib/uploader/authorization_adapter.rb +24 -0
  17. data/lib/uploader/chunked_uploads.rb +15 -0
  18. data/lib/uploader/engine.rb +6 -0
  19. data/lib/uploader/file_part.rb +18 -0
  20. data/lib/uploader/fileuploads.rb +42 -65
  21. data/lib/uploader/helpers/field_tag.rb +10 -5
  22. data/lib/uploader/hooks/formtastic.rb +0 -13
  23. data/lib/uploader/upload_request.rb +72 -0
  24. data/lib/uploader/version.rb +1 -1
  25. data/lib/uploader.rb +41 -8
  26. data/spec/dummy/app/models/asset.rb +12 -0
  27. data/spec/dummy/app/models/picture.rb +6 -0
  28. data/spec/dummy/db/test.sqlite3 +0 -0
  29. data/spec/dummy/log/test.log +325 -0
  30. data/spec/dummy/public/uploads/picture/data/1/thumb_rails.png +0 -0
  31. data/spec/dummy/public/uploads/picture/data/3/thumb_rails.png +0 -0
  32. data/spec/fileuploads_spec.rb +4 -4
  33. data/spec/requests/attachments_controller_spec.rb +11 -12
  34. data/vendor/assets/javascripts/uploader/jquery.fileupload-process.js +175 -0
  35. data/vendor/assets/javascripts/uploader/jquery.fileupload-ui.js +164 -261
  36. data/vendor/assets/javascripts/uploader/jquery.fileupload-validate.js +122 -0
  37. data/vendor/assets/javascripts/uploader/jquery.fileupload.js +335 -101
  38. data/vendor/assets/javascripts/uploader/jquery.iframe-transport.js +47 -15
  39. data/vendor/assets/javascripts/uploader/vendor/jquery.ui.widget.js +572 -0
  40. data/vendor/assets/javascripts/uploader/vendor/tmpl.min.js +1 -0
  41. data/vendor/assets/stylesheets/uploader/default.css +26 -19
  42. metadata +12 -9
  43. data/vendor/assets/javascripts/uploader/jquery.fileupload-fp.js +0 -227
  44. data/vendor/assets/javascripts/uploader/jquery.ui.widget.js +0 -530
  45. data/vendor/assets/javascripts/uploader/load-image.min.js +0 -1
  46. data/vendor/assets/javascripts/uploader/locales/en.js +0 -27
  47. data/vendor/assets/javascripts/uploader/locales/ru.js +0 -27
  48. data/vendor/assets/javascripts/uploader/locales/uk.js +0 -27
  49. data/vendor/assets/javascripts/uploader/tmpl.min.js +0 -1
@@ -1,52 +1,26 @@
1
- <%= content_tag(:div, :id => field.id, :class => "uploader-dnd-area") do -%>
2
- <%= hidden_field(field.object_name, :fileupload_guid, :object => field.object) if field.object.new_record? %>
3
-
1
+ <%= content_tag(:div, id: field.id, class: 'uploader-dnd-area', data: { tpml: field.klass.to_s, exists: field.exists? }) do -%>
2
+ <%= hidden_field(field.object_name, :fileupload_guid, object: field.object) if field.object.new_record? %>
3
+
4
4
  <div class="uploader-files"></div>
5
-
6
- <div class="uploader-dnd-hints">
7
- <span class="uploader-button gray fileinput-button">
5
+
6
+ <div class="uploader-dnd-hints">
7
+ <span class="uploader-button gray fileinput-button">
8
8
  <span><%= I18n.t('uploader.button') %></span>
9
- <%= fields_for field.object do |f| -%>
10
- <%= f.fields_for field.method_name, field.klass.new do |m| -%>
11
- <%= m.file_field(:data, field.input_html) %>
12
- <% end -%>
13
- <% end -%>
9
+ <%= file_field field.method_name, :data, field.input_html %>
14
10
  </span>
15
-
16
- <div class="uploader-dnd-hint">
17
- <%= I18n.t('uploader.or') %><span><%= I18n.t('uploader.drop') %></span>
18
- </div>
19
- </div>
20
-
21
- <%= render :partial => "uploader/#{field.theme}/upload", :locals => {:field => field} %>
22
- <%= render :partial => "uploader/#{field.theme}/download", :locals => {:field => field} %>
23
- <%= render :partial => "uploader/#{field.theme}/sortable", :locals => {:field => field} if field.sortable? %>
24
-
25
- <script type="text/javascript">
11
+
12
+ <div class="uploader-dnd-hint">
13
+ <%= I18n.t('uploader.or') %><span><%= I18n.t('uploader.drop') %></span>
14
+ </div>
15
+ </div>
16
+
17
+ <%= render partial: "uploader/#{field.theme}/upload", locals: { field: field } %>
18
+ <%= render partial: "uploader/#{field.theme}/download", locals: { field: field } %>
19
+ <%= render partial: "uploader/#{field.theme}/sortable", locals: { field: field } if field.sortable? %>
20
+
21
+ <script type="text/javascript">
26
22
  $(function() {
27
- var uploader, container;
28
- container = $("#<%= field.id %> div.uploader-files");
29
-
30
- $('#<%= field.id %> input[type="file"]').each(function(){
31
- $(this).fileupload({
32
- dataType: 'json',
33
- dropZone: $("#<%= field.id %>"),
34
- autoUpload: true,
35
- paramName: "asset[data]",
36
- formData: function(form){ return []; },
37
- filesContainer: container,
38
- namespace: 'uploader',
39
- uploadTemplateId: 'template-upload-<%= field.klass %>',
40
- downloadTemplateId: 'template-download-<%= field.klass %>'
41
- });
42
-
43
- <% if field.exists? -%>
44
- uploader = ($(this).data('blueimp-fileupload') || $(this).data('fileupload'));
45
- uploader
46
- ._renderDownload(<%=raw field.values.to_json(:root => false) %>)
47
- .appendTo(container);
48
- <% end -%>
49
- });
23
+ $("#<%= field.id %>").uploaderWidget();
50
24
  });
51
25
  </script>
52
26
  <% end -%>
@@ -1,18 +1,17 @@
1
- <!-- The template to display files available for download -->
2
1
  <script id="template-download-<%= field.klass %>" type="text/x-tmpl">
3
2
  {% for (var i=0, file; file=o.files[i]; i++) { %}
4
3
  <div id="asset_{%=file.id%}" class="attach_item template-download">
5
4
  <div class="buttons-panel">
6
- <a href="#" class="del_btn delete" data-type="DELETE" data-url="/uploader/attachments/{%=file.public_token%}?klass=<%= field.klass %>"></a>
5
+ <a href="#" class="del_btn delete" data-type="DELETE" data-url="/uploader/attachments/{%=file.id%}?klass=<%= field.klass %>"></a>
7
6
  </div>
8
7
  <div class="thumbnail preview">
9
- <a href="{%=file.url%}" download="{%=file.filename%}">
10
- <img src="{%=file.thumb_url%}" title="{%=file.filename%}" rel="gallery" />
8
+ <a href="{%=file.url%}" download="{%=file.name%}">
9
+ <img src="{%=file.thumb_url%}" title="{%=file.name%}" rel="gallery" />
11
10
  </a>
12
11
  </div>
13
12
  <div class="infoHolder">
14
13
  <div class="fileName">
15
- <a href="{%=file.url%}" download="{%=file.filename%}">{%=file.filename%}</a>
14
+ <a href="{%=file.url%}" download="{%=file.name%}">{%=file.name%}</a>
16
15
  </div>
17
16
  <div class="fileWeight">{%=o.formatFileSize(file.size)%}</div>
18
17
  </div>
@@ -1,4 +1,3 @@
1
- <!-- The template to display files available for upload -->
2
1
  <script id="template-upload-<%= field.klass %>" type="text/x-tmpl">
3
2
  {% for (var i=0, file; file=o.files[i]; i++) { %}
4
3
  <div class="attach_item loading template-upload">
@@ -4,3 +4,5 @@ en:
4
4
  drop: "Drag&Drop files here"
5
5
  or: "or"
6
6
  confirm_delete: "Are you sure?"
7
+ access_denied:
8
+ message: 'Access denied'
@@ -3,3 +3,6 @@ ru:
3
3
  button: "Выберите файл на компьютере"
4
4
  drop: "Перетащите файл сюда"
5
5
  or: "или"
6
+ confirm_delete: "Are you sure?"
7
+ access_denied:
8
+ message: 'Access denied'
@@ -3,3 +3,6 @@ uk:
3
3
  button: "Виберіть файл на комп'ютері"
4
4
  drop: "Перетягніть файл сюди"
5
5
  or: "або"
6
+ confirm_delete: "Are you sure?"
7
+ access_denied:
8
+ message: 'Access denied'
data/config/routes.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  Uploader::Engine.routes.draw do
2
- resources :attachments, :only => [:create, :update, :destroy]
2
+ resources :attachments, only: [:index, :create, :update, :destroy]
3
3
  end
@@ -1,104 +1,83 @@
1
+ require 'active_support/concern'
2
+
1
3
  module Uploader
2
4
  module Asset
3
- def self.included(base)
4
- base.send(:extend, Uploader::Asset::ClassMethods)
5
- base.send(:include, Uploader::Asset::AssetProcessor)
6
-
7
- base.instance_eval do
8
- before_create :generate_public_token
9
- end
10
- end
11
-
12
- module Mongoid
13
- def self.included(base)
14
- base.send(:extend, Uploader::Asset::ClassMethods)
15
- base.send(:include, Uploader::Asset::AssetProcessor)
5
+ extend ActiveSupport::Concern
16
6
 
17
- base.instance_eval do
18
- field :guid, type: String
19
- field :public_token, type: String
20
-
21
- before_create :generate_public_token
22
- end
7
+ module ClassMethods
8
+ def fileupload_find_asset(params)
9
+ where(id: params[:id]).first
23
10
  end
24
11
 
25
- def as_json(options = {})
26
- json_data = super
27
- json_data['filename'] = File.basename(data.path)
28
- json_data['size'] = data.file.size
29
- json_data['id'] = json_data['_id']
30
- json_data['thumb_url'] = data.thumb.url if data.respond_to?(:thumb)
31
-
32
- json_data
12
+ def fileupload_find_assets(params)
13
+ where(assetable_type: params[:assetable_type], assetable_id: params[:assetable_id])
33
14
  end
34
15
 
35
- def assetable_id_format(assetable_id)
36
- Moped::BSON::ObjectId.from_string(assetable_id)
37
- end
16
+ def fileupload_update_ordering(params)
17
+ assets = Array.wrap(params[:assets] || [])
38
18
 
39
- class << self
40
- def include_root_in_json
41
- false
19
+ assets.each_with_index do |id, index|
20
+ where(id: id).update_all(sort_order: index)
42
21
  end
43
22
  end
44
23
  end
45
24
 
46
- module ClassMethods
47
- def generate_token(column)
48
- loop do
49
- token = Uploader.guid
50
- break token unless where({ column => token }).exists?
51
- end
52
- end
25
+ # Save asset
26
+ # Usage:
27
+ #
28
+ # class Asset < ActiveRecord::Base
29
+ # include Uploader::Asset
30
+ #
31
+ # def fileupload_create(params, request = nil)
32
+ # self.user = request.env['warden'].user
33
+ # super
34
+ # end
35
+ # end
36
+ #
37
+ def fileupload_create(params, _request = nil)
38
+ self[Uploader.guid_column] = params[:guid]
39
+ fileupload_set_assetable(params)
40
+ save
53
41
  end
54
42
 
55
- module AssetProcessor
56
- # Save asset
57
- # Usage:
58
- #
59
- # class Asset < ActiveRecord::Base
60
- # include Uploader::Asset
61
- #
62
- # def uploader_create(params, request = nil)
63
- # self.user = request.env['warden'].user
64
- # super
65
- # end
66
- # end
67
- #
68
- def uploader_create(params, request = nil)
69
- self.guid = params[:guid]
70
- self.assetable_type = params[:assetable_type]
71
- self.assetable_id = assetable_id_format(params[:assetable_id])
72
- save
73
- end
74
-
75
- # Destroy asset
76
- # Usage (cancan example):
77
- #
78
- # class Asset < ActiveRecord::Base
79
- # include Uploader::Asset
80
- #
81
- # def uploader_destroy(params, request = nil)
82
- # ability = Ability.new(request.env['warden'].user)
83
- # if ability.can? :delete, self
84
- # super
85
- # else
86
- # errors.add(:id, :access_denied)
87
- # end
88
- # end
89
- # end
90
- #
91
- def uploader_destroy(params, request)
92
- destroy
93
- end
43
+ # Destroy asset
44
+ # Usage (cancan example):
45
+ #
46
+ # class Asset < ActiveRecord::Base
47
+ # include Uploader::Asset
48
+ #
49
+ # def fileupload_destroy(params, request = nil)
50
+ # ability = Ability.new(request.env['warden'].user)
51
+ # if ability.can? :delete, self
52
+ # super
53
+ # else
54
+ # errors.add(:id, :access_denied)
55
+ # end
56
+ # end
57
+ # end
58
+ #
59
+ def fileupload_destroy(_params, _request = nil)
60
+ destroy
61
+ end
94
62
 
95
- def generate_public_token
96
- self.public_token = self.class.generate_token(:public_token)
97
- end
63
+ # Serialize asset to fileupload JSON format
64
+ #
65
+ def to_fileupload
66
+ {
67
+ id: id,
68
+ name: filename,
69
+ content_type: content_type,
70
+ size: size,
71
+ url: url,
72
+ thumb_url: thumb_url
73
+ }
98
74
  end
99
75
 
100
- def assetable_id_format(assetable_id)
101
- assetable_id || 0
76
+ protected
77
+
78
+ def fileupload_set_assetable(params)
79
+ self.assetable_type = params[:assetable_type]
80
+ self.assetable_id = params[:assetable_id]
102
81
  end
103
82
  end
104
83
  end
@@ -0,0 +1,52 @@
1
+ require 'active_support/concern'
2
+
3
+ module Uploader
4
+ module Authorization
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include ActiveSupport::Rescuable
9
+
10
+ rescue_from Uploader::AccessDenied, with: :dispatch_uploader_access_denied
11
+ end
12
+
13
+ protected
14
+
15
+ # Authorize the action and subject. Available in the controller
16
+ def authorized?(action, subject = nil)
17
+ uploader_authorization.authorized?(action, subject)
18
+ end
19
+
20
+ # Authorize the action and subject. Available in the controller.
21
+ # If the action is not allowd, it raises an Uploader::AccessDenied exception.
22
+ def authorize!(action, subject = nil)
23
+ return if authorized?(action, subject)
24
+ raise Uploader::AccessDenied.new(current_uploader_user, action, subject)
25
+ end
26
+
27
+ # Retrieve or instantiate the authorization instance for this resource
28
+ def uploader_authorization
29
+ @uploader_authorization ||= uploader_authorization_adapter.new(current_uploader_user)
30
+ end
31
+
32
+ # Returns the class to be used as the authorization adapter
33
+ def uploader_authorization_adapter
34
+ adapter = Uploader.authorization_adapter
35
+
36
+ if adapter.is_a? String
37
+ ActiveSupport::Dependencies.constantize(adapter)
38
+ else
39
+ adapter
40
+ end
41
+ end
42
+
43
+ def dispatch_uploader_access_denied(exception)
44
+ render json: { message: exception.message }, status: 403
45
+ end
46
+
47
+ def current_uploader_user
48
+ return if Uploader.current_user_proc.nil?
49
+ @current_uploader_user ||= Uploader.current_user_proc.call(request)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,24 @@
1
+ module Uploader
2
+ class AuthorizationAdapter
3
+ attr_reader :user
4
+
5
+ # Initialize a new authorization adapter. This happens on each and
6
+ # every request to a controller.
7
+ def initialize(user)
8
+ @user = user
9
+ end
10
+
11
+ # Returns true of false depending on if the user is authorized to perform
12
+ # the action on the subject.
13
+ def authorized?(action, subject = nil)
14
+ true
15
+ end
16
+
17
+ # A hook method for authorization libraries to scope the collection. By
18
+ # default, we just return the same collection. The returned scope is used
19
+ # as the starting point for all queries to the db in the controller.
20
+ def scope_collection(collection, action = :index)
21
+ collection
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module Uploader
2
+ module ChunkedUploads
3
+ extend ActiveSupport::Concern
4
+
5
+ protected
6
+
7
+ def with_chunked_upload(file_or_part)
8
+ uploader = UploadRequest.new(env, file_or_part)
9
+ return unless uploader.completed?
10
+
11
+ yield uploader.file
12
+ uploader.cleanup
13
+ end
14
+ end
15
+ end
@@ -2,6 +2,12 @@ require 'rails'
2
2
  require 'uploader'
3
3
 
4
4
  module Uploader
5
+ # Usage (config/routes.rb):
6
+ #
7
+ # Rails.application.routes.draw do
8
+ # mount Uploader::Engine => '/uploader'
9
+ # end
10
+ #
5
11
  class Engine < ::Rails::Engine
6
12
  isolate_namespace Uploader
7
13
 
@@ -0,0 +1,18 @@
1
+ module Uploader
2
+ class FilePart < File
3
+ def initialize(path, filename)
4
+ @filename = filename
5
+ super(path, 'a')
6
+ end
7
+
8
+ def original_filename
9
+ @filename
10
+ end
11
+
12
+ def concat(other_file)
13
+ binmode
14
+ write(other_file.read)
15
+ other_file.close
16
+ end
17
+ end
18
+ end
@@ -1,25 +1,21 @@
1
+ require 'active_support/concern'
2
+
1
3
  module Uploader
2
4
  module Fileuploads
3
- def self.included(base)
4
- base.send :extend, SingletonMethods
5
- end
5
+ extend ActiveSupport::Concern
6
6
 
7
- module Mongoid
8
- def self.included(base)
9
- base.send :include, Uploader::Fileuploads
10
- end
7
+ included do
8
+ class_attribute :fileupload_options, instance_writer: false
11
9
 
12
- def self.include_root_in_json
13
- false
14
- end
10
+ after_create :fileupload_update, if: :fileupload_changed?
15
11
  end
16
12
 
17
- module SingletonMethods
13
+ module ClassMethods
18
14
  # Join ActiveRecord object with uploaded file
19
15
  # Usage:
20
16
  #
21
17
  # class Article < ActiveRecord::Base
22
- # has_one :picture, :as => :assetable, :dependent => :destroy
18
+ # has_one :picture, as: :assetable, dependent: :destroy
23
19
  #
24
20
  # fileuploads :picture
25
21
  # end
@@ -28,27 +24,16 @@ module Uploader
28
24
  def fileuploads(*args)
29
25
  options = args.extract_options!
30
26
 
31
- unless respond_to?(:fileuploads_options)
32
- class_attribute(:fileuploads_options, instance_writer: false)
33
- self.fileuploads_options ||= {}
34
-
35
- include InstanceMethods
36
- extend ClassMethods
37
-
38
- after_save :fileuploads_update, if: :fileupload_changed?
39
- end
27
+ self.fileupload_options ||= {}
40
28
 
41
- args.each do |name|
42
- fileuploads_options[name] = options
43
- accepts_nested_attributes_for(name, allow_destroy: true)
29
+ args.each do |column|
30
+ self.fileupload_options[column] = options
44
31
  end
45
32
  end
46
- end
47
33
 
48
- module ClassMethods
49
34
  # Update reflection klass by guid
50
35
  def fileupload_update(record_id, guid, method)
51
- fileupload_scope(method, guid).update_all(assetable_id: record_id, guid: nil)
36
+ fileupload_scope(method, guid).update_all(assetable_id: record_id, Uploader.guid_column => nil)
52
37
  end
53
38
 
54
39
  # Find asset(s) by guid
@@ -58,7 +43,7 @@ module Uploader
58
43
  end
59
44
 
60
45
  def fileupload_scope(method, guid)
61
- fileupload_klass(method).where(guid: guid, assetable_type: base_class.name.to_s)
46
+ fileupload_klass(method).where(assetable_type: base_class.name.to_s, Uploader.guid_column => guid)
62
47
  end
63
48
 
64
49
  # Find class by reflection
@@ -72,54 +57,46 @@ module Uploader
72
57
  # many? for Mongoid and :collection? for AR
73
58
  method_name = association.respond_to?(:many?) ? :many? : :collection?
74
59
 
75
- !!(association && association.send(method_name))
76
- end
77
-
78
- unless respond_to?(:base_class)
79
- def base_class
80
- self
81
- end
60
+ association && association.send(method_name)
82
61
  end
83
62
  end
84
63
 
85
- module InstanceMethods
86
- # Generate unique key
87
- def fileupload_guid
88
- @fileupload_guid ||= Uploader.guid
89
- end
64
+ # Generate unique key per form
65
+ def fileupload_guid
66
+ @fileupload_guid ||= Uploader.guid
67
+ end
90
68
 
91
- def fileupload_guid=(value)
92
- @fileupload_changed = true unless value.blank?
93
- @fileupload_guid = value.blank? ? nil : value
94
- end
69
+ def fileupload_guid=(value)
70
+ @fileupload_changed = (@fileupload_guid != value)
71
+ @fileupload_guid = value.blank? ? nil : value
72
+ end
95
73
 
96
- def fileupload_changed?
97
- @fileupload_changed == true
98
- end
74
+ def fileupload_changed?
75
+ @fileupload_changed == true
76
+ end
99
77
 
100
- def fileupload_multiple?(method)
101
- self.class.fileupload_multiple?(method)
102
- end
78
+ def fileupload_multiple?(method)
79
+ self.class.fileupload_multiple?(method)
80
+ end
103
81
 
104
- # Find or build new asset object
105
- def fileupload_asset(method)
106
- if fileuploads_columns.include?(method.to_sym)
107
- asset = new_record? ? self.class.fileupload_find(method, fileupload_guid) : send(method)
108
- asset ||= send("build_#{method}") if respond_to?("build_#{method}")
109
- asset
110
- end
82
+ # Find or build new asset object
83
+ def fileupload_asset(method)
84
+ if fileupload_associations.include?(method.to_sym)
85
+ asset = new_record? ? self.class.fileupload_find(method, fileupload_guid) : send(method)
86
+ asset ||= send("build_#{method}") if respond_to?("build_#{method}")
87
+ asset
111
88
  end
89
+ end
112
90
 
113
- def fileuploads_columns
114
- self.class.fileuploads_options.keys
115
- end
91
+ def fileupload_associations
92
+ self.class.fileupload_options.keys
93
+ end
116
94
 
117
- protected
95
+ protected
118
96
 
119
- def fileuploads_update
120
- fileuploads_options.each do |method, _options|
121
- self.class.fileupload_update(id, fileupload_guid, method)
122
- end
97
+ def fileupload_update
98
+ fileupload_options.each do |method, _options|
99
+ self.class.fileupload_update(id, fileupload_guid, method)
123
100
  end
124
101
  end
125
102
  end