rails-uploader 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 88c10bdc5c1c64828bf15e99962dee2c0e547eea
4
- data.tar.gz: 00ad13f2236fa9751d588bfac7275578dab4bd99
3
+ metadata.gz: 78c6ab34d67ec73bf7b3d802e186d3a179d7a25e
4
+ data.tar.gz: e76947d90bd1f5c0ea024a3939a0565ab7088477
5
5
  SHA512:
6
- metadata.gz: 637a0ecdea91d3edabb08bb572a7a7739ae97ea6a2d8a8cbe76c724c64836c39fef17f9d6b94e96148a8f7a72bd685ea9c7615e48270e715a0e38d4e29bff136
7
- data.tar.gz: 6cda451daaf4e3d7a5728dcfe49c2cf5e9b8fa07e78e76128fd3d50bc82ad45f5178ae1a15f6a57dc703d7907e321a19a8e4c8cb07d3c6a3d85fafd2e26fe0d6
6
+ metadata.gz: 34b3d48312fd023264a053d7c7c669dd6da137701b33c288c8e18f90bc70218d1e467f48c84673f60738f84aaec8f50a3490ff5d665587428a781ea23a73c327
7
+ data.tar.gz: 25737d84ebc664c361195c97b380a694297a96c9c7d5d299187cf898404ceaf8f74080f6c03cb93b77e711755b43352c5d6b9b79b66cf4a0d9127e7825d932dc
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2012 Fodojo http://fodojo.com/
1
+ Copyright 2016 Fodojo LLC http://fodojo.com/
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -2,16 +2,12 @@
2
2
 
3
3
  This gem use https://github.com/blueimp/jQuery-File-Upload for upload files.
4
4
 
5
- Preview:
6
-
7
- ![Uploader in use](http://img39.imageshack.us/img39/2206/railsuploader.png)
8
-
9
5
  ## Install
10
6
 
11
7
  In Gemfile:
12
8
 
13
9
  ```
14
- gem "rails-uploader"
10
+ gem 'rails-uploader'
15
11
  ```
16
12
 
17
13
  In routes:
@@ -33,44 +29,14 @@ Architecture to store uploaded files (cancan integration):
33
29
  ``` ruby
34
30
  class Asset < ActiveRecord::Base
35
31
  include Uploader::Asset
36
-
37
- def uploader_create(params, request = nil)
38
- ability = Ability.new(request.env['warden'].user)
39
-
40
- if ability.can? :create, self
41
- self.user = request.env['warden'].user
42
- super
43
- else
44
- errors.add(:id, :access_denied)
45
- end
46
- end
47
-
48
- def uploader_destroy(params, request = nil)
49
- ability = Ability.new(request.env['warden'].user)
50
-
51
- if ability.can? :delete, self
52
- super
53
- else
54
- errors.add(:id, :access_denied)
55
- end
56
- end
57
32
  end
58
33
 
59
34
  class Picture < Asset
60
- mount_uploader :data, PictureUploader
61
-
62
- validates_integrity_of :data
63
- validates_filesize_of :data, :maximum => 2.megabytes.to_i
35
+ mount_uploader :data, PictureUploader, mount_on: :data_file_name
36
+ validates :data, file_size: { maximum: 5.megabytes.to_i }
64
37
 
65
- # structure of returned json array of files. (used in Hash.to_json operation)
66
- def serializable_hash(options=nil)
67
- {
68
- "id" => id.to_s,
69
- "filename" => File.basename(data.path),
70
- "url" => data.url,
71
- "thumb_url" => data.url(:thumb),
72
- "public_token" => public_token
73
- }
38
+ def thumb_url
39
+ url(:thumb)
74
40
  end
75
41
  end
76
42
  ```
@@ -79,12 +45,9 @@ For example user has one picture:
79
45
 
80
46
  ``` ruby
81
47
  class User < ActiveRecord::Base
82
- has_one :picture, :as => :assetable, :dependent => :destroy
48
+ has_one :picture, as: :assetable, dependent: :destroy
83
49
 
84
50
  fileuploads :picture
85
-
86
- # If your don't use strong_parameters, uncomment next line
87
- # attr_accessible :fileupload_guid
88
51
  end
89
52
  ```
90
53
 
@@ -94,45 +57,6 @@ Find asset by foreign key or guid:
94
57
  @user.fileupload_asset(:picture)
95
58
  ```
96
59
 
97
- ### Mongoid
98
-
99
- No parent asset model is required, one only has to `include Uploader::Asset::Mongoid` into the
100
- model that should act like an asset:
101
-
102
- ``` ruby
103
- class Picture
104
- include Mongoid::Document
105
- include Uploader::Asset::Mongoid
106
-
107
- belongs_to :user
108
- end
109
-
110
- class User
111
- include Mongoid::Document
112
- include Uploader::Fileuploads
113
-
114
- has_one :picture, :as => :assetable
115
-
116
- fileuploads :picture
117
- end
118
- ```
119
-
120
- ### Notice
121
-
122
- User method fileuploads only once pre model. So if you have many attached files, use this:
123
-
124
- ``` ruby
125
- class User
126
- include Uploader::Fileuploads
127
-
128
- has_one :picture, :as => :assetable
129
- has_one :avatar, :as => :assetable
130
-
131
- fileuploads :picture, :avatar
132
- end
133
- ```
134
-
135
-
136
60
  ### Include assets
137
61
 
138
62
  Javascripts:
@@ -156,19 +80,19 @@ Stylesheets:
156
80
  or FormBuilder:
157
81
 
158
82
  ```erb
159
- <%= form.uploader_field :photo, :sortable => true %>
83
+ <%= form.uploader_field :photo, sortable: true %>
160
84
  ```
161
85
 
162
86
  ### Formtastic
163
87
 
164
88
  ```erb
165
- <%= f.input :pictures, :as => :uploader %>
89
+ <%= f.input :pictures, as: :uploader %>
166
90
  ```
167
91
 
168
92
  ### SimpleForm
169
93
 
170
94
  ```erb
171
- <%= f.input :pictures, :as => :uploader, :input_html => {:sortable => true} %>
95
+ <%= f.input :pictures, as: :uploader, input_html: { sortable: true } %>
172
96
  ```
173
97
 
174
98
  #### Confirming deletions
@@ -181,6 +105,158 @@ This is only working in Formtastic and FormBuilder:
181
105
  # the i18n lookup key would be en.formtastic.delete_confirmations.picture
182
106
  ```
183
107
 
108
+ ## Authorization
109
+
110
+ Setup custom authorization adapter and current user:
111
+
112
+ ``` ruby
113
+ # config/initializers/uploader.rb
114
+ Uploader.setup do |config|
115
+ config.authorization_adapter = CanCanUploaderAdapter
116
+ config.current_user_proc = -> (request) { request.env['warden'].user }
117
+ end
118
+ ```
119
+
120
+ CanCanUploaderAdapter class just create cancan ability object and call can? method with same args:
121
+
122
+ ``` ruby
123
+ class CanCanUploaderAdapter < Uploader::AuthorizationAdapter
124
+ def authorized?(action, subject = nil)
125
+ cancan_ability.can?(action, subject)
126
+ end
127
+
128
+ def scope_collection(collection, action = :index)
129
+ collection.accessible_by(cancan_ability, action)
130
+ end
131
+
132
+ protected
133
+
134
+ def cancan_ability
135
+ @cancan_ability ||= Ability.new(user)
136
+ end
137
+ end
138
+ ```
139
+
140
+ ## JSON Response
141
+
142
+ https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#using-jquery-file-upload-ui-version-with-a-custom-server-side-upload-handler
143
+
144
+ Extend your custom server-side upload handler to return a JSON response akin to the following output:
145
+
146
+ ``` json
147
+ {"files": [
148
+ {
149
+ "name": "picture1.jpg",
150
+ "size": 902604,
151
+ "url": "http:\/\/example.org\/files\/picture1.jpg",
152
+ "thumb_url": "http:\/\/example.org\/files\/thumbnail\/picture1.jpg",
153
+ "id": 1,
154
+ "content_type": "image/jpg"
155
+ },
156
+ {
157
+ "name": "picture2.jpg",
158
+ "size": 841946,
159
+ "url": "http:\/\/example.org\/files\/picture2.jpg",
160
+ "thumb_url": "http:\/\/example.org\/files\/thumbnail\/picture2.jpg",
161
+ "id": 2,
162
+ "content_type": "image/jpg"
163
+ }
164
+ ]}
165
+ ```
166
+
167
+ To return errors to the UI, just add an error property to the individual file objects:
168
+
169
+ ``` json
170
+ {"files": [
171
+ {
172
+ "name": "picture1.jpg",
173
+ "size": 902604,
174
+ "error": "Filetype not allowed"
175
+ },
176
+ {
177
+ "name": "picture2.jpg",
178
+ "size": 841946,
179
+ "error": "Filetype not allowed"
180
+ }
181
+ ]}
182
+ ```
183
+
184
+ When removing files using the delete button, the response should be like this:
185
+
186
+ ``` json
187
+ {"files": [
188
+ {
189
+ "picture1.jpg": true
190
+ },
191
+ {
192
+ "picture2.jpg": true
193
+ }
194
+ ]}
195
+ ```
196
+
197
+ Note that the response should always be a JSON object containing a files array even if only one file is uploaded.
198
+
199
+ ### Customize JSON response and views
200
+
201
+ To customize JSON response just overwrite to_fileupload method:
202
+
203
+ ``` ruby
204
+ class Asset
205
+ include Uploader::Asset
206
+
207
+ def to_fileupload
208
+ {
209
+ id: id,
210
+ name: filename,
211
+ content_type: content_type,
212
+ size: size,
213
+ url: url,
214
+ thumb_url: thumb_url
215
+ }
216
+ end
217
+ end
218
+ ```
219
+
220
+ For exsample let's overwrite id method to public_token method:
221
+
222
+ ``` ruby
223
+ class Asset
224
+ include Uploader::Asset
225
+
226
+ def to_fileupload
227
+ super.merge(id: public_token)
228
+ end
229
+
230
+ def self.fileupload_find_asset(params)
231
+ where(public_token: params[:id]).first
232
+ end
233
+ end
234
+ ```
235
+
236
+ To customize views just create new theme. For example create avatar theme:
237
+
238
+ app/views/uploader/avatar/_container.html.erb
239
+ app/views/uploader/avatar/_download.html.erb
240
+ app/views/uploader/avatar/_upload.html.erb
241
+
242
+ And pass theme to input field:
243
+
244
+ ``` slim
245
+ = form.uploader_field :photo, theme: 'avatar'
246
+ ```
247
+
248
+ ## Chunked file uploads
249
+
250
+ Chunked file uploads are only supported by browsers with support for XHR file uploads and the Blob API, which includes Google Chrome and Mozilla Firefox 4+.
251
+
252
+ To upload large files in smaller chunks, set the max_chunk_size option to a preferred maximum chunk size in Bytes:
253
+
254
+ ``` slim
255
+ = f.uploader_field :video, class: 'button', theme: 'media', data: { max_chunk_size: 10_000_000 }
256
+ ```
257
+
258
+ That's it!
259
+
184
260
  ## Contributing
185
261
 
186
262
  1. Fork it
@@ -189,4 +265,4 @@ This is only working in Formtastic and FormBuilder:
189
265
  4. Push to the branch (`git push origin my-new-feature`)
190
266
  5. Create new Pull Request
191
267
 
192
- Copyright (c) 2013 Fodojo, released under the MIT license
268
+ Copyright (c) 2016 Fodojo LLC, released under the MIT license
@@ -1,8 +1,10 @@
1
- //= require uploader/jquery.ui.widget
2
- //= require uploader/locales/en
3
- //= require uploader/tmpl.min
4
- //= require uploader/load-image.min
1
+ //= require uploader/vendor/jquery.ui.widget
2
+ //= require uploader/vendor/tmpl.min
3
+
5
4
  //= require uploader/jquery.iframe-transport
6
5
  //= require uploader/jquery.fileupload
6
+ //= require uploader/jquery.fileupload-process
7
+ //= require uploader/jquery.fileupload-validate
7
8
  //= require uploader/jquery.fileupload-ui
8
9
 
10
+ //= require uploader/jquery.uploader
@@ -0,0 +1,58 @@
1
+ $ = jQuery
2
+
3
+ $.fn.uploaderWidget = (options = {}) ->
4
+ @each ->
5
+ $this = $(this)
6
+ data = $this.data('uploaderWidget')
7
+ if (!data)
8
+ $this.data('uploaderWidget', new UploaderWidget(this, options))
9
+ if (typeof options is 'string')
10
+ data[options]()
11
+
12
+ class UploaderWidget
13
+ constructor: (@dom_id, options = {}) ->
14
+ defaults =
15
+ dataType: 'json'
16
+ autoUpload: true
17
+ paramName: 'asset[data]'
18
+ formData: (form) -> return []
19
+ namespace: 'uploader'
20
+ uploadTemplateId: 'template-upload-'
21
+ downloadTemplateId: 'template-download-'
22
+
23
+ @options = $.extend defaults, options
24
+
25
+ this._setup()
26
+
27
+ _setup: ->
28
+ @element = $(@dom_id)
29
+ @container = @element.find('div.uploader-files')
30
+ @template = @element.data('tpml')
31
+ @input = @element.find('input[type="file"]:eq(0)')
32
+
33
+ @options['dropZone'] = @element
34
+ @options['filesContainer'] = @container
35
+ @options['uploadTemplateId'] += @template
36
+ @options['downloadTemplateId'] += @template
37
+
38
+ this._initFileupload()
39
+
40
+ _initFileupload: ->
41
+ @input.fileupload(@options)
42
+
43
+ @uploader = (@input.data('blueimp-fileupload') || @input.data('fileupload'))
44
+
45
+ this._load() if @element.data('exists')
46
+
47
+ _load: ->
48
+ $.ajax(
49
+ url: @input.data('url')
50
+ dataType: 'json'
51
+ method: 'GET'
52
+ success: (data) =>
53
+ if data['files']?
54
+ this.render(data['files'])
55
+ )
56
+
57
+ render: (files) ->
58
+ @uploader._renderDownload(files).appendTo(@container)
@@ -1,56 +1,80 @@
1
1
  module Uploader
2
2
  class AttachmentsController < ActionController::Metal
3
3
  include AbstractController::Callbacks
4
-
5
- before_filter :find_klass
6
- before_filter :find_asset, :only => [:destroy]
7
-
4
+ include Uploader::Authorization
5
+ include Uploader::ChunkedUploads
6
+
7
+ before_action :find_klass
8
+ before_action :build_asset, only: [:create]
9
+ before_action :find_asset, only: [:destroy]
10
+
11
+ def index
12
+ authorize!(:index, @klass)
13
+
14
+ @assets = @klass.fileupload_find_assets(params)
15
+ @assets = uploader_authorization.scope_collection(@assets)
16
+
17
+ render_json(files: @assets.map(&:to_fileupload))
18
+ end
19
+
8
20
  def create
9
- @asset = @klass.new(params[:asset])
10
- @asset.uploader_create(params, request)
21
+ authorize!(:create, @asset)
22
+
23
+ with_chunked_upload(asset_params[:data]) do |file|
24
+ @asset.data = file
25
+ @asset.fileupload_create(params, request)
26
+ end
27
+
11
28
  render_resourse(@asset, 201)
12
29
  end
13
-
14
- def update
15
- @assets = Array.wrap(params[:assets] || [])
16
30
 
17
- @assets.each_with_index do |id, index|
18
- @klass.where(:id => id).update_all(:sort_order => index)
19
- end
31
+ def update
32
+ authorize!(:update, @klass)
20
33
 
21
- render_json(:files => [])
34
+ @klass.fileupload_update_ordering(params)
35
+ render_json(files: [])
22
36
  end
23
37
 
24
38
  def destroy
25
- @asset.uploader_destroy(params, request)
26
- render_resourse(@asset, 200)
39
+ authorize!(:destroy, @asset)
40
+
41
+ @asset.fileupload_destroy(params, request)
42
+ render_resourse(@asset)
27
43
  end
28
-
44
+
29
45
  protected
30
-
31
- def find_klass
32
- @klass = Uploader.constantize(params[:klass])
33
- raise ActionController::RoutingError.new("Class not found #{params[:klass]}") if @klass.nil?
34
- end
35
46
 
36
- def find_asset
37
- @asset = @klass.where(:public_token => params[:id]).first
38
- raise ActionController::RoutingError.new("Asset not found by guid #{params[:id]}") if @asset.nil?
39
- end
40
-
41
- def render_resourse(record, status = 200)
42
- if record.errors.empty?
43
- render_json({:files => [record]}, status)
44
- else
45
- render_json(record.errors, 422)
46
- end
47
- end
48
-
49
- def render_json(hash_or_object, status = 200)
50
- self.status = status
51
- self.content_type = Uploader.content_type(env["HTTP_USER_AGENT"])
52
- self.response_body = hash_or_object.to_json(:root => false)
47
+ def find_klass
48
+ @klass = Uploader.constantize(params[:klass])
49
+ raise ActionController::RoutingError, "Class not found #{params[:klass]}" if @klass.nil?
50
+ end
51
+
52
+ def build_asset
53
+ @asset = @klass.new(asset_params)
54
+ end
55
+
56
+ def find_asset
57
+ @asset = @klass.fileupload_find_asset(params)
58
+ raise ActionController::RoutingError, "Asset not found by #{params[:id]}" if @asset.nil?
59
+ end
60
+
61
+ def render_resourse(record, status = 200)
62
+ if record.errors.empty?
63
+ render_json({ files: [record.to_fileupload] }, status)
64
+ else
65
+ hash = { name: record.filename, error: record.errors.full_messages.first }
66
+ render_json({ files: [hash] }, 422)
53
67
  end
54
-
68
+ end
69
+
70
+ def render_json(hash_or_object, status = 200)
71
+ self.status = status
72
+ self.content_type = request.format
73
+ self.response_body = hash_or_object.to_json(root: false)
74
+ end
75
+
76
+ def asset_params
77
+ ActionController::Parameters.new(params).require(:asset).permit(:data)
78
+ end
55
79
  end
56
80
  end