pail 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/Guardfile +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +218 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/pail.rb +194 -0
- data/lib/pail/engine.rb +4 -0
- data/lib/pail/version.rb +3 -0
- data/pail.gemspec +31 -0
- data/vendor/assets/javascripts/pail.js +1 -0
- data/vendor/assets/javascripts/plupload/Moxie.swf +0 -0
- data/vendor/assets/javascripts/plupload/Moxie.xap +0 -0
- data/vendor/assets/javascripts/plupload/plupload.full.min.js +2341 -0
- data/vendor/assets/stylesheets/pail.css +11 -0
- data/vendor/assets/stylesheets/twitter/bootstrap.css +366 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 54c0ae0fb36a073adbf4ac0f4231991841572362
|
4
|
+
data.tar.gz: 0d75a69b5b42b896fc6d746c3f537389edc76b08
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5ac65e006f9ffc929175457c409b62c78156d5aa0d09faf07a52f31e810ccd9a65f428519769a41652d80316935cb3ada13e2c2c3fd0b9606790177fcaf39299
|
7
|
+
data.tar.gz: 24fb2095bb633a60dd922e217b4223e8515acdd915bdce0cc837471ab0f3154e02b12ac6fc1646f41e33e09352d9fcf9e6698f40c55c5042d2ec265654d4aea6
|
data/.coveralls.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features)
|
6
|
+
|
7
|
+
## Uncomment to clear the screen before every task
|
8
|
+
# clearing :on
|
9
|
+
|
10
|
+
## Guard internally checks for changes in the Guardfile and exits.
|
11
|
+
## If you want Guard to automatically start up again, run guard in a
|
12
|
+
## shell loop, e.g.:
|
13
|
+
##
|
14
|
+
## $ while bundle exec guard; do echo "Restarting Guard..."; done
|
15
|
+
##
|
16
|
+
## Note: if you are using the `directories` clause above and you are not
|
17
|
+
## watching the project directory ('.'), then you will want to move
|
18
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
19
|
+
#
|
20
|
+
# $ mkdir config
|
21
|
+
# $ mv Guardfile config/
|
22
|
+
# $ ln -s config/Guardfile .
|
23
|
+
#
|
24
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
25
|
+
|
26
|
+
guard 'rspec', cmd: "bundle exec rspec" do
|
27
|
+
# watch /lib/ files
|
28
|
+
watch(%r{^lib/(.+).rb$}) do |m|
|
29
|
+
"spec/#{m[1]}_spec.rb"
|
30
|
+
end
|
31
|
+
|
32
|
+
# watch /spec/ files
|
33
|
+
watch(%r{^spec/(.+).rb$}) do |m|
|
34
|
+
"spec/#{m[1]}.rb"
|
35
|
+
end
|
36
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Eddie Johnston
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
# Pail
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/eddiej/pail.png?branch=master)](https://travis-ci.org/eddiej/pail)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/eddiej/pail/badge.png)](https://coveralls.io/r/eddiej/pail)
|
5
|
+
|
6
|
+
|
7
|
+
Pail is a Ruby gem that makes it easy to upload files directly from your views to an Amazon S3 Bucket, bypassing your application server.
|
8
|
+
|
9
|
+
![Alt Image Text](https://raw.githubusercontent.com/eddiej/pail/gh-pages/in_progress.png "Optional Title")
|
10
|
+
|
11
|
+
It uses the Plupload JavaScript uploader originally written for PHP, bundling the required Javascript libraries with convenience methods for generating the policy document and SHA1 signature needed to upload to S3.
|
12
|
+
Pail has the option of applying Twitter Boostrap progress-bar styling to the uploader, but it is unstyled by default.
|
13
|
+
|
14
|
+
|
15
|
+
## Why Do I Need It?
|
16
|
+
|
17
|
+
To avoid blocking your server during file uploads. There are two approaches to processing and storing file uploads from your Rails app to S3: pass-through uploading and direct uploading.
|
18
|
+
|
19
|
+
**Pass-through uploading**, which sends files to your Rails application before uploading them from there to S3 storage, is easy to implement, but your application's server threads will be tied up during the upload process which can have a blocking effect on your application.
|
20
|
+
|
21
|
+
If all of the available threads of your server are occupied by slow uploads, your application will become unavailable until one of the uploads finishes and frees up a thread. Some hosting providers like Heroku terminate requests that last longer than 30 seconds, so uploads that take any longer won't complete.
|
22
|
+
|
23
|
+
**Direct uploading** makes a direct connection between the end-user's browser and S3, bypassing your application entirely. This will greatly reduce the processing required by your application and keep your threads free for other requests, but it can be complicated to implement. See [Heroku's Guide for direct uploading to S3 with Rails](https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails) for an example implementation.
|
24
|
+
|
25
|
+
Pail takes the complication out of direct uploading, allowing users to create a direct uploader with just one line of code.
|
26
|
+
|
27
|
+
|
28
|
+
## Requirements
|
29
|
+
|
30
|
+
Before you can upload to S3, you'll need to:
|
31
|
+
|
32
|
+
- create a bucket
|
33
|
+
- configure the bucket to allow uploads
|
34
|
+
- generate a Base64 encoded Policy document
|
35
|
+
- generate a Base64 encoded SHA1 encrypted signature
|
36
|
+
|
37
|
+
Your S3 bucket needs to be created and configured manually, but Pail looks after the the policy document and signature generation. Pail needs access to three variables; the name of the bucket you'll be uploading to, an AWS access key id and secret access key. These should be made available as environrment variables in your Rails app. There are various wasy of doing this, you could use an initializer or a gem like [dotenv](https://github.com/bkeepers/dotenv).
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
ENV['S3_BUCKET']=assets.mydomain.com
|
41
|
+
ENV['AWS_ACCESS_KEY_ID']=AKIAJLAP2FSKJEBDFLWWA
|
42
|
+
ENV['AWS_SECRET_ACCESS_KEY']=KKdkahb4kdfheb4KGcII8iRzpFymlUanYGszLf1U
|
43
|
+
```
|
44
|
+
|
45
|
+
### Configuring Your S3 Bucket
|
46
|
+
|
47
|
+
Plupload provides a number of different runtimes including HTML5, Flash and Sliverlight. Depending on the runtime you wish to use, the configuration settings are slightly different. Configuring your S3 Bucket for the three main runtimes is documented below, see the [Plupload documentation](http://www.plupload.com/docs/Upload-to-Amazon-S3) for a more detailed explanation.
|
48
|
+
|
49
|
+
#### HTML5 Runtime
|
50
|
+
|
51
|
+
To use the HTML5 Runtime, you must add a CORS configuration similar to the one below in the `Permissions` section of the S3 bucket options. This is accessed from your S3 dashboard.
|
52
|
+
|
53
|
+
```xml
|
54
|
+
<CORSConfiguration>
|
55
|
+
<CORSRule>
|
56
|
+
<AllowedOrigin>*</AllowedOrigin>
|
57
|
+
<AllowedHeader>*</AllowedHeader>
|
58
|
+
<AllowedMethod>GET</AllowedMethod>
|
59
|
+
<AllowedMethod>POST</AllowedMethod>
|
60
|
+
<MaxAgeSeconds>3000</MaxAgeSeconds>
|
61
|
+
</CORSRule>
|
62
|
+
</CORSConfiguration>
|
63
|
+
```
|
64
|
+
|
65
|
+
|
66
|
+
#### Flash Runtime
|
67
|
+
|
68
|
+
Flash requires a `crossdomain.xml` policy file to be present at the root of your bucket to support cross-origin requests:
|
69
|
+
|
70
|
+
```xml
|
71
|
+
<?xml version="1.0"?>
|
72
|
+
<!DOCTYPE cross-domain-policy SYSTEM
|
73
|
+
"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
|
74
|
+
<cross-domain-policy>
|
75
|
+
<allow-access-from domain="*" secure="false" />
|
76
|
+
</cross-domain-policy>
|
77
|
+
```
|
78
|
+
|
79
|
+
#### Silverlight Runtime
|
80
|
+
|
81
|
+
Silverlight uses its own Security Policy File - `clientaccesspolicy.xml`, but when all domains are allowed, it can fallback to `crossdomain.xml`.
|
82
|
+
|
83
|
+
See the [Plupload documentation](http://www.plupload.com/docs/Upload-to-Amazon-S3#for-silverlight-runtime) for more information on generating `clientaccesspolicy.xml` for allowing only specific domains.
|
84
|
+
|
85
|
+
|
86
|
+
### Policy Generation
|
87
|
+
|
88
|
+
In order to upload to S3, each request musy be accompanied with a Base64 encoded policy, a set of rules your request should conform to. Amazon will reject requests with missing or invalid policy documents and respond with a 403 Forbidden error.
|
89
|
+
|
90
|
+
When you call the `pail()` function, the gem will generate the policy using your private key and secret and inject the encrypted values into the configuration hash on your page. This means that your sensitive information doesn't get exposed on the front-end.
|
91
|
+
|
92
|
+
### Signature Generation
|
93
|
+
In addition to sending a Policy document, each request must also be signed with a HMAC-SHA1 encrypted and Base64 encoded signature which is created from you AWS Secret Access Key. Pail will generate the signature for you and insert the encryoted values into the configuration hash on the front-end.
|
94
|
+
|
95
|
+
## Creating an Uploader
|
96
|
+
|
97
|
+
To use the Pail, add the Gem to your Gemfile and include the Pail Javascript in your `application.js` file. Note that jQuery is a pre-requisite for Pail.
|
98
|
+
|
99
|
+
<pre>
|
100
|
+
//= require jquery
|
101
|
+
//= require jquery_ujs
|
102
|
+
//= require turbolinks
|
103
|
+
//= require_tree .
|
104
|
+
<b>//= require pail</b>
|
105
|
+
</pre>
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
Inserting an uploader onto a page is as simple as including a small amount of HTML markup and calling the `pail()` function in a view template. In an ERB file, this would look like:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
<%= pail() %>
|
113
|
+
```
|
114
|
+
|
115
|
+
Your HTML markup must provide:
|
116
|
+
|
117
|
+
- a file select button
|
118
|
+
- a cancel button
|
119
|
+
- a placeholder element that will display information about the file being uploaded
|
120
|
+
|
121
|
+
The default IDs for each of these elements is `#selectfile`, `#resetupload`, and `#uploadfile` but these can be overridden via the options hash. The markup below contains the required elements:
|
122
|
+
|
123
|
+
```html
|
124
|
+
<div id='uploadcontainer'>
|
125
|
+
<h3>Upload A File</h3>
|
126
|
+
<div id='uploadfile'>
|
127
|
+
<div class="progress"></div>
|
128
|
+
</div>
|
129
|
+
<button type="button" class="btn btn-default btn btn-sm" id="selectfile">Select File</button>
|
130
|
+
<button type="button" class="btn btn-danger btn btn-sm disabled" id="resetupload">Reset</button>
|
131
|
+
</div>
|
132
|
+
```
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
Any placeholder content can be placed within the `#uploadfile` element as long as it is wrapped in a containing element. By default, an empty div with the class 'progress' is inserted which displays an empty progress bar well. This will be replaced with a progress bar once a file is selected and put back again if the upload is reset.
|
137
|
+
|
138
|
+
### Parameters
|
139
|
+
|
140
|
+
Custom options can be sent in a hash parameter of the `pail()` function. The options currently configurable in Pail and their default values are:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
options[:key] ||= 'test'
|
144
|
+
options[:acl] ||= 'public-read'
|
145
|
+
options[:expiration_date] ||= 10.hours.from_now.utc.iso8601
|
146
|
+
options[:max_filesize] ||= 500.kilabytes
|
147
|
+
options[:content_type] ||= 'image/'
|
148
|
+
options[:filter_title] ||= 'Images'
|
149
|
+
options[:filter_extentions] ||= 'jpg,jpeg,gif,png,bmp'
|
150
|
+
options[:runtimes] ||= 'html5'
|
151
|
+
options[:selectid] ||= 'selectfile'
|
152
|
+
options[:cancelid] ||= 'resetupload'
|
153
|
+
options[:progress_bar_class] ||= 'progress-bar progress-bar-striped active'
|
154
|
+
```
|
155
|
+
|
156
|
+
The [Plupload Options documentation](http://www.plupload.com/docs/Options) should be referred to for explanations of each value. The `selectid`, `cancelid` and `progress_bar_class` values are relevant only to Pail and not Plupload.
|
157
|
+
|
158
|
+
### Upload Progress
|
159
|
+
|
160
|
+
The upload progress bar uses the same markup as the [Twitter Bootstap progress bar](http://getbootstrap.com/components/#progress).
|
161
|
+
|
162
|
+
```html
|
163
|
+
<div class="progress">
|
164
|
+
<div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 60%;">
|
165
|
+
<span class="file-info">Moonshine.jpg (453 kb) 60% </span>
|
166
|
+
</div>
|
167
|
+
</div>
|
168
|
+
```
|
169
|
+
|
170
|
+
The control buttons and progress bar will be unstyled by default, but you can apply the Boostrap styles by including the Pail stylesheet from `application.css`:
|
171
|
+
|
172
|
+
<pre>
|
173
|
+
*
|
174
|
+
*= require_tree .
|
175
|
+
*= require_self
|
176
|
+
<b>*= require pail</b>
|
177
|
+
*/
|
178
|
+
</pre>
|
179
|
+
|
180
|
+
If you already use Twitter Bootstrap in your application, the styles from your theme will automatically be applied.
|
181
|
+
|
182
|
+
## Upload Events
|
183
|
+
|
184
|
+
Plupload defines a number of events that can be bound to, a full list of which can be found in the [Plupload documentation](http://www.plupload.com/docs/Uploader#events). By default, Pail uses event binding to change the style of the progress bar according to the state of the uploader.
|
185
|
+
|
186
|
+
Additional functionality would generally be bound to the FileUploadaed event to perform additional actions once a file has been uploaded. Examples might include:
|
187
|
+
|
188
|
+
- Insert the S3 url of the uploaded file into a hidden field and submit a form.
|
189
|
+
- Send the S3 url to a controller via a remote AJAX call
|
190
|
+
- Redirect to another page that displays the uploaded asset
|
191
|
+
|
192
|
+
To bind additional events, use the jQuery `bind` function after `<%= pail %>` has been called.
|
193
|
+
|
194
|
+
```javascript
|
195
|
+
<script>
|
196
|
+
window.uploader.bind('FileUploaded', function(up, file, info) { alert (info); })
|
197
|
+
</script>
|
198
|
+
```
|
199
|
+
|
200
|
+
Note that the uploader object created by the Plupload Javascript is added as a property of the window object and can be accessed by `window.uploader`.
|
201
|
+
|
202
|
+
The default Pail events can be removed using the `unbind` function, e.g:
|
203
|
+
|
204
|
+
```javascript
|
205
|
+
<script>
|
206
|
+
window.uploader.unbind('UploadProgress')
|
207
|
+
window.uploader.unbind('FileUploaded')
|
208
|
+
...
|
209
|
+
</script>
|
210
|
+
```
|
211
|
+
|
212
|
+
## Testing
|
213
|
+
Tests are written using RSpec. To start the test suite, run:
|
214
|
+
|
215
|
+
```rails
|
216
|
+
bundle exec rspec
|
217
|
+
```
|
218
|
+
or view the tests and results output on [Travis CI](https://travis-ci.org/eddiej/pail).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "pail"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/pail.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
require "pail/version"
|
2
|
+
require "pail/engine"
|
3
|
+
|
4
|
+
module Pail
|
5
|
+
class Generate
|
6
|
+
def self.signature(secret_access_key, policy)
|
7
|
+
Base64.encode64(
|
8
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), secret_access_key, policy)
|
9
|
+
).gsub("\n","")
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.policy(bucket, expiration_date, acl, max_filesize)
|
13
|
+
Base64.encode64(
|
14
|
+
"{'expiration': '#{expiration_date}',
|
15
|
+
'conditions': [
|
16
|
+
{'bucket': '#{bucket}'},
|
17
|
+
{'acl': '#{acl}'},
|
18
|
+
{'success_action_status': '201'},
|
19
|
+
['content-length-range', 0, #{max_filesize}],
|
20
|
+
['starts-with', '$key', ''],
|
21
|
+
['starts-with', '$Content-Type', ''],
|
22
|
+
['starts-with', '$name', ''],
|
23
|
+
['starts-with', '$Filename', '']
|
24
|
+
]
|
25
|
+
}"
|
26
|
+
).gsub(/\n|\r/, '')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module PailHelper
|
31
|
+
|
32
|
+
def pail(options = {})
|
33
|
+
# Read in the default bucket, AWS key and secret from environment variables.
|
34
|
+
|
35
|
+
@bucket = ENV['S3_BUCKET']
|
36
|
+
@access_key_id = ENV['AWS_ACCESS_KEY_ID']
|
37
|
+
@secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
|
38
|
+
@options = options.dup
|
39
|
+
|
40
|
+
raise ArgumentError.new('a bucket, access key id and secret access key must be set') unless @bucket && @access_key_id && @secret_access_key
|
41
|
+
|
42
|
+
@options[:key] ||= 'uploads' # folder on AWS to store file in
|
43
|
+
@options[:acl] ||= 'public-read'
|
44
|
+
@options[:expiration_date] ||= 10.hours.from_now.utc.iso8601
|
45
|
+
@options[:max_filesize] ||= 104857600 # 100.megabytes
|
46
|
+
@options[:content_type] ||= 'image/' # Videos would be binary/octet-stream
|
47
|
+
@options[:filter_title] ||= 'Images'
|
48
|
+
@options[:filter_extensions] ||= 'jpg,jpeg,gif,png,bmp'
|
49
|
+
@options[:runtimes] ||= 'html5'
|
50
|
+
|
51
|
+
@options[:selectid] ||= 'selectfile'
|
52
|
+
@options[:cancelid] ||= 'resetupload'
|
53
|
+
@options[:progress_bar_class] ||= 'progress-bar progress-bar-striped active'
|
54
|
+
|
55
|
+
id = @options[:id] ? "_#{@options[:id]}" : ''
|
56
|
+
|
57
|
+
policy = Pail::Generate::policy(
|
58
|
+
@bucket,
|
59
|
+
@options[:expiration_date],
|
60
|
+
@options[:acl],
|
61
|
+
@options[:max_filesize]
|
62
|
+
)
|
63
|
+
|
64
|
+
signature = Pail::Generate::signature(@secret_access_key, policy)
|
65
|
+
|
66
|
+
out = ""
|
67
|
+
filters = "filters : [
|
68
|
+
{title : '#{@options[:filter_title]}', extensions : '#{@options[:filter_extensions]}'}
|
69
|
+
],"
|
70
|
+
if @options[:filters]
|
71
|
+
filters = 'filters : ['
|
72
|
+
filters = filters + @options[:filters].join(',')
|
73
|
+
filters = filters + "],"
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
out << javascript_tag(<<JAVASCRIPT
|
78
|
+
|
79
|
+
uploader = new plupload.Uploader({
|
80
|
+
browse_button : '#{@options[:selectid]}',
|
81
|
+
container : 'uploadcontainer',
|
82
|
+
runtimes : '#{@options[:runtimes]}',
|
83
|
+
url : 'https://s3.amazonaws.com/#{@bucket}/',
|
84
|
+
max_file_size : '#{number_to_human_size(@options[:max_filesize]).gsub(/ /,'').downcase}',
|
85
|
+
multipart: true,
|
86
|
+
multipart_params: {
|
87
|
+
'key': '#{@options[:key]}/${filename}',
|
88
|
+
'Filename': '${filename}', // adding this to keep consistency across the runtimes
|
89
|
+
'acl': '#{@options[:acl]}',
|
90
|
+
'Content-Type': '#{@options[:content_type]}',
|
91
|
+
'success_action_status': '201',
|
92
|
+
'AWSAccessKeyId' : '#{@access_key_id}',
|
93
|
+
'policy': '#{policy}',
|
94
|
+
'signature': '#{signature}'
|
95
|
+
},
|
96
|
+
// optional, but better be specified directly
|
97
|
+
file_data_name: 'file',
|
98
|
+
// re-use widget (not related to S3, but to Plupload UI Widget)
|
99
|
+
multiple_queues: true,
|
100
|
+
// Specify what files to browse for
|
101
|
+
#{filters}
|
102
|
+
// Flash settings
|
103
|
+
flash_swf_url : '/assets/plupload/Moxie.swf',
|
104
|
+
// Silverlight settings
|
105
|
+
silverlight_xap_url : '/assets/plupload/Moxie.xap',
|
106
|
+
});
|
107
|
+
|
108
|
+
placeholder = $('#uploadfile').children().first();
|
109
|
+
uploader.init()
|
110
|
+
var queueMaxima = 1;
|
111
|
+
|
112
|
+
// 1. Files Added
|
113
|
+
uploader.bind('FilesAdded', function(up, files) {
|
114
|
+
$.each(files, function(i, file) {
|
115
|
+
$('#uploadfile').empty().append(' \
|
116
|
+
<div id="' + file.id + '" class="progress"> \
|
117
|
+
<div class="#{@options[:progress_bar_class]}" role="progressbar" aria-valuenow="'+file.percent+'" aria-valuemin="0" aria-valuemax="100"> \
|
118
|
+
<span class="file-info">' + file.name + ' (' + plupload.formatSize(file.size) + ')</span>' +
|
119
|
+
'</div> \
|
120
|
+
</div>');
|
121
|
+
});
|
122
|
+
$('##{@options[:cancelid]}').removeClass('disabled')
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
if(uploader.files.length > queueMaxima){
|
127
|
+
while(uploader.files.length > queueMaxima){
|
128
|
+
if(uploader.files.length > queueMaxima){
|
129
|
+
x = uploader.files[queueMaxima-1]
|
130
|
+
|
131
|
+
$('#'+x.id).remove();
|
132
|
+
uploader.removeFile(uploader.files[queueMaxima-1]);
|
133
|
+
uploader.stop()
|
134
|
+
}
|
135
|
+
}
|
136
|
+
if(typeof(plupload_hook_removedExcessFromQueue) == "function"){plupload_hook_removedExcessFromQueue()}
|
137
|
+
}
|
138
|
+
up.start();
|
139
|
+
up.refresh(); // Reposition Flash/Silverlight
|
140
|
+
});
|
141
|
+
|
142
|
+
// 2. Right before Upload Starts
|
143
|
+
uploader.bind('BeforeUpload', function(up, file) {
|
144
|
+
$('#' + file.id + " .progress-bar")
|
145
|
+
.addClass('active')
|
146
|
+
});
|
147
|
+
|
148
|
+
// 2. Upload Progresses
|
149
|
+
uploader.bind('UploadProgress', function(up, file) {
|
150
|
+
$('#' + file.id + " .progress-bar")
|
151
|
+
.width(file.percent + "%")
|
152
|
+
.html('<span class="file-info">' + file.name + ' (' + plupload.formatSize(file.size) + ') ' + file.percent + "%</span>");
|
153
|
+
});
|
154
|
+
|
155
|
+
// 3. Error Occurs
|
156
|
+
uploader.bind('Error', function(up, err) {
|
157
|
+
if($('.file-info').length > 0){
|
158
|
+
error_element = $('.file-info')
|
159
|
+
}
|
160
|
+
else{
|
161
|
+
error_element = $('.progress')
|
162
|
+
}
|
163
|
+
error_element.html("Error: " + err.code + ", " + err.message + (err.file ? " File: " + err.file.name : ""));
|
164
|
+
$('#' + err.file.id + " .progress-bar")
|
165
|
+
.addClass('progress-bar-danger')
|
166
|
+
.removeClass('active')
|
167
|
+
});
|
168
|
+
|
169
|
+
// 4. File is Uploaded
|
170
|
+
uploader.bind('FileUploaded', function(up, file) {
|
171
|
+
$('#' + file.id + " .progress-bar")
|
172
|
+
.addClass('progress-bar-success')
|
173
|
+
.removeClass('active')
|
174
|
+
});
|
175
|
+
|
176
|
+
// 5. Stop button is clicked.
|
177
|
+
$('#uploadcontainer').on('click', '##{@options[:cancelid]}', function(){
|
178
|
+
uploader.stop();
|
179
|
+
$('#uploadfile').html(placeholder);
|
180
|
+
$('##{@options[:cancelid]}').addClass('disabled')
|
181
|
+
$('#' + file.id + " .progress-bar")
|
182
|
+
.removeClass('active')
|
183
|
+
uploader.refresh(); // Reposition Flash/Silverlight
|
184
|
+
});
|
185
|
+
|
186
|
+
window.uploader = uploader;
|
187
|
+
JAVASCRIPT
|
188
|
+
)
|
189
|
+
|
190
|
+
raw(out);
|
191
|
+
end
|
192
|
+
end
|
193
|
+
ActionView::Base.send :include, PailHelper
|
194
|
+
end
|