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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +162 -86
- data/app/assets/javascripts/uploader/application.js +6 -4
- data/app/assets/javascripts/uploader/jquery.uploader.js.coffee +58 -0
- data/app/controllers/uploader/attachments_controller.rb +63 -39
- data/app/views/uploader/default/_container.html.erb +19 -45
- data/app/views/uploader/default/_download.html.erb +4 -5
- data/app/views/uploader/default/_upload.html.erb +0 -1
- data/config/locales/en.yml +2 -0
- data/config/locales/ru.yml +3 -0
- data/config/locales/uk.yml +3 -0
- data/config/routes.rb +1 -1
- data/lib/uploader/asset.rb +63 -84
- data/lib/uploader/authorization.rb +52 -0
- data/lib/uploader/authorization_adapter.rb +24 -0
- data/lib/uploader/chunked_uploads.rb +15 -0
- data/lib/uploader/engine.rb +6 -0
- data/lib/uploader/file_part.rb +18 -0
- data/lib/uploader/fileuploads.rb +42 -65
- data/lib/uploader/helpers/field_tag.rb +10 -5
- data/lib/uploader/hooks/formtastic.rb +0 -13
- data/lib/uploader/upload_request.rb +72 -0
- data/lib/uploader/version.rb +1 -1
- data/lib/uploader.rb +41 -8
- data/spec/dummy/app/models/asset.rb +12 -0
- data/spec/dummy/app/models/picture.rb +6 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/test.log +325 -0
- data/spec/dummy/public/uploads/picture/data/1/thumb_rails.png +0 -0
- data/spec/dummy/public/uploads/picture/data/3/thumb_rails.png +0 -0
- data/spec/fileuploads_spec.rb +4 -4
- data/spec/requests/attachments_controller_spec.rb +11 -12
- data/vendor/assets/javascripts/uploader/jquery.fileupload-process.js +175 -0
- data/vendor/assets/javascripts/uploader/jquery.fileupload-ui.js +164 -261
- data/vendor/assets/javascripts/uploader/jquery.fileupload-validate.js +122 -0
- data/vendor/assets/javascripts/uploader/jquery.fileupload.js +335 -101
- data/vendor/assets/javascripts/uploader/jquery.iframe-transport.js +47 -15
- data/vendor/assets/javascripts/uploader/vendor/jquery.ui.widget.js +572 -0
- data/vendor/assets/javascripts/uploader/vendor/tmpl.min.js +1 -0
- data/vendor/assets/stylesheets/uploader/default.css +26 -19
- metadata +12 -9
- data/vendor/assets/javascripts/uploader/jquery.fileupload-fp.js +0 -227
- data/vendor/assets/javascripts/uploader/jquery.ui.widget.js +0 -530
- data/vendor/assets/javascripts/uploader/load-image.min.js +0 -1
- data/vendor/assets/javascripts/uploader/locales/en.js +0 -27
- data/vendor/assets/javascripts/uploader/locales/ru.js +0 -27
- data/vendor/assets/javascripts/uploader/locales/uk.js +0 -27
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78c6ab34d67ec73bf7b3d802e186d3a179d7a25e
|
4
|
+
data.tar.gz: e76947d90bd1f5c0ea024a3939a0565ab7088477
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34b3d48312fd023264a053d7c7c669dd6da137701b33c288c8e18f90bc70218d1e467f48c84673f60738f84aaec8f50a3490ff5d665587428a781ea23a73c327
|
7
|
+
data.tar.gz: 25737d84ebc664c361195c97b380a694297a96c9c7d5d299187cf898404ceaf8f74080f6c03cb93b77e711755b43352c5d6b9b79b66cf4a0d9127e7825d932dc
|
data/MIT-LICENSE
CHANGED
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
|
-

|
8
|
-
|
9
5
|
## Install
|
10
6
|
|
11
7
|
In Gemfile:
|
12
8
|
|
13
9
|
```
|
14
|
-
gem
|
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
|
-
|
66
|
-
|
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, :
|
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, :
|
83
|
+
<%= form.uploader_field :photo, sortable: true %>
|
160
84
|
```
|
161
85
|
|
162
86
|
### Formtastic
|
163
87
|
|
164
88
|
```erb
|
165
|
-
<%= f.input :pictures, :
|
89
|
+
<%= f.input :pictures, as: :uploader %>
|
166
90
|
```
|
167
91
|
|
168
92
|
### SimpleForm
|
169
93
|
|
170
94
|
```erb
|
171
|
-
<%= f.input :pictures, :
|
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)
|
268
|
+
Copyright (c) 2016 Fodojo LLC, released under the MIT license
|
@@ -1,8 +1,10 @@
|
|
1
|
-
//= require uploader/jquery.ui.widget
|
2
|
-
//= require uploader/
|
3
|
-
|
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
|
-
|
6
|
-
|
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
|
-
|
10
|
-
|
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
|
-
|
18
|
-
|
19
|
-
end
|
31
|
+
def update
|
32
|
+
authorize!(:update, @klass)
|
20
33
|
|
21
|
-
|
34
|
+
@klass.fileupload_update_ordering(params)
|
35
|
+
render_json(files: [])
|
22
36
|
end
|
23
37
|
|
24
38
|
def destroy
|
25
|
-
|
26
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|