s3_cors_fileupload 0.1.1
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.
- data/.document +5 -0
 - data/.gitignore +51 -0
 - data/.rspec +1 -0
 - data/Gemfile +14 -0
 - data/Gemfile.lock +39 -0
 - data/LICENSE.txt +20 -0
 - data/README.md +104 -0
 - data/Rakefile +58 -0
 - data/lib/generators/s3_cors_fileupload/install/USAGE +17 -0
 - data/lib/generators/s3_cors_fileupload/install/install_generator.rb +51 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/amazon_s3.yml +17 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/create_source_files.rb +14 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads.js +94 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads_controller.rb +90 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/source_file.rb +53 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/views/_template_download.html.erb +29 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/views/_template_upload.html.erb +31 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/views/_template_uploaded.html.erb +25 -0
 - data/lib/generators/s3_cors_fileupload/install/templates/views/index.html.erb +43 -0
 - data/lib/s3_cors_fileupload.rb +2 -0
 - data/lib/s3_cors_fileupload/rails.rb +8 -0
 - data/lib/s3_cors_fileupload/rails/config.rb +27 -0
 - data/lib/s3_cors_fileupload/rails/engine.rb +6 -0
 - data/lib/s3_cors_fileupload/rails/form_helper.rb +91 -0
 - data/lib/s3_cors_fileupload/rails/policy_helper.rb +48 -0
 - data/lib/s3_cors_fileupload/version.rb +5 -0
 - data/s3_cors_fileupload.gemspec +35 -0
 - data/spec/s3_cors_fileupload/version_spec.rb +17 -0
 - data/spec/s3_cors_fileupload_spec.rb +9 -0
 - data/spec/spec_helper.rb +16 -0
 - data/vendor/assets/images/loading.gif +0 -0
 - data/vendor/assets/images/progressbar.gif +0 -0
 - data/vendor/assets/javascripts/s3_cors_fileupload/index.js +6 -0
 - data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload-ui.js +732 -0
 - data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload.js +1106 -0
 - data/vendor/assets/javascripts/s3_cors_fileupload/jquery.iframe-transport.js +172 -0
 - data/vendor/assets/javascripts/s3_cors_fileupload/vendor/jquery.ui.widget.js +511 -0
 - data/vendor/assets/javascripts/s3_cors_fileupload/vendor/load-image.js +122 -0
 - data/vendor/assets/javascripts/s3_cors_fileupload/vendor/tmpl.js +87 -0
 - data/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb +85 -0
 - metadata +205 -0
 
| 
         @@ -0,0 +1,90 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'base64'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'openssl'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'digest/sha1'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class S3UploadsController < ApplicationController
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              helper_method :s3_upload_policy_document, :s3_upload_signature
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              # GET /source_files
         
     | 
| 
      
 10 
     | 
    
         
            +
              # GET /source_files.json
         
     | 
| 
      
 11 
     | 
    
         
            +
              def index
         
     | 
| 
      
 12 
     | 
    
         
            +
                @source_files = SourceFile.all
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                respond_to do |format|
         
     | 
| 
      
 15 
     | 
    
         
            +
                  format.html # index.html.erb
         
     | 
| 
      
 16 
     | 
    
         
            +
                  format.json { render json: @source_files.map{|sf| sf.to_jq_upload } }
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              # POST /source_files
         
     | 
| 
      
 21 
     | 
    
         
            +
              # POST /source_files.json
         
     | 
| 
      
 22 
     | 
    
         
            +
              def create
         
     | 
| 
      
 23 
     | 
    
         
            +
                @source_file = SourceFile.new(params[:source_file])
         
     | 
| 
      
 24 
     | 
    
         
            +
                respond_to do |format|
         
     | 
| 
      
 25 
     | 
    
         
            +
                  if @source_file.save
         
     | 
| 
      
 26 
     | 
    
         
            +
                    format.html {
         
     | 
| 
      
 27 
     | 
    
         
            +
                      render :json => @source_file.to_jq_upload,
         
     | 
| 
      
 28 
     | 
    
         
            +
                      :content_type => 'text/html',
         
     | 
| 
      
 29 
     | 
    
         
            +
                      :layout => false
         
     | 
| 
      
 30 
     | 
    
         
            +
                    }
         
     | 
| 
      
 31 
     | 
    
         
            +
                    format.json { render json: @source_file.to_jq_upload, status: :created }
         
     | 
| 
      
 32 
     | 
    
         
            +
                  else
         
     | 
| 
      
 33 
     | 
    
         
            +
                    format.html { render action: "new" }
         
     | 
| 
      
 34 
     | 
    
         
            +
                    format.json { render json: @source_file.errors, status: :unprocessable_entity }
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              # DELETE /source_files/1
         
     | 
| 
      
 40 
     | 
    
         
            +
              # DELETE /source_files/1.json
         
     | 
| 
      
 41 
     | 
    
         
            +
              def destroy
         
     | 
| 
      
 42 
     | 
    
         
            +
                @source_file = SourceFile.find(params[:id])
         
     | 
| 
      
 43 
     | 
    
         
            +
                @source_file.destroy
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                respond_to do |format|
         
     | 
| 
      
 46 
     | 
    
         
            +
                  format.html { redirect_to source_files_url }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  format.json { head :no_content }
         
     | 
| 
      
 48 
     | 
    
         
            +
                  format.xml { head :no_content }
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              # used for s3_uploader
         
     | 
| 
      
 53 
     | 
    
         
            +
              def generate_key
         
     | 
| 
      
 54 
     | 
    
         
            +
                uid = SecureRandom.uuid.gsub(/-/,'')
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                render json: {
         
     | 
| 
      
 57 
     | 
    
         
            +
                  key: "uploads/#{uid}/#{params[:filename]}",
         
     | 
| 
      
 58 
     | 
    
         
            +
                  success_action_redirect: "/"
         
     | 
| 
      
 59 
     | 
    
         
            +
                }
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              # ---- Helpers ----
         
     | 
| 
      
 63 
     | 
    
         
            +
              # generate the policy document that amazon is expecting.
         
     | 
| 
      
 64 
     | 
    
         
            +
              def s3_upload_policy_document
         
     | 
| 
      
 65 
     | 
    
         
            +
                Base64.encode64(
         
     | 
| 
      
 66 
     | 
    
         
            +
                  {
         
     | 
| 
      
 67 
     | 
    
         
            +
                    expiration: 1.hour.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
         
     | 
| 
      
 68 
     | 
    
         
            +
                    conditions: [
         
     | 
| 
      
 69 
     | 
    
         
            +
                      { bucket: S3CorsFileupload::Config.bucket },
         
     | 
| 
      
 70 
     | 
    
         
            +
                      { acl: 'public-read' },
         
     | 
| 
      
 71 
     | 
    
         
            +
                      { success_action_status: '201' },
         
     | 
| 
      
 72 
     | 
    
         
            +
                      ["starts-with", "$key", ""],
         
     | 
| 
      
 73 
     | 
    
         
            +
                      ["starts-with", "$Content-Type", ""]
         
     | 
| 
      
 74 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 75 
     | 
    
         
            +
                  }.to_json
         
     | 
| 
      
 76 
     | 
    
         
            +
                ).gsub(/\n|\r/, '')
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
              # sign our request by Base64 encoding the policy document.
         
     | 
| 
      
 80 
     | 
    
         
            +
              def s3_upload_signature
         
     | 
| 
      
 81 
     | 
    
         
            +
                Base64.encode64(
         
     | 
| 
      
 82 
     | 
    
         
            +
                  OpenSSL::HMAC.digest(
         
     | 
| 
      
 83 
     | 
    
         
            +
                    OpenSSL::Digest::Digest.new('sha1'),
         
     | 
| 
      
 84 
     | 
    
         
            +
                    S3CorsFileupload::Config.secret_access_key,
         
     | 
| 
      
 85 
     | 
    
         
            +
                    s3_upload_policy_document
         
     | 
| 
      
 86 
     | 
    
         
            +
                  )
         
     | 
| 
      
 87 
     | 
    
         
            +
                ).gsub(/\n/, '')
         
     | 
| 
      
 88 
     | 
    
         
            +
              end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'aws/s3'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class SourceFile < ActiveRecord::Base
         
     | 
| 
      
 4 
     | 
    
         
            +
              attr_accessible :url, :bucket, :key
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              validates_presence_of :file_name, :file_content_type, :file_size, :key, :bucket
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              before_validation(:on => :create) do
         
     | 
| 
      
 9 
     | 
    
         
            +
                self.file_name = key.split('/').last if key
         
     | 
| 
      
 10 
     | 
    
         
            +
                # for some reason, the response from AWS seems to escape the slashes in the keys, this line will unescape the slash
         
     | 
| 
      
 11 
     | 
    
         
            +
                self.url = url.gsub(/%2F/, '/') if url
         
     | 
| 
      
 12 
     | 
    
         
            +
                self.file_size ||= s3_object.try(:size)
         
     | 
| 
      
 13 
     | 
    
         
            +
                self.file_content_type ||= s3_object.try(:content_type)
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
              # make all attributes readonly after creating the record (not sure we need this?)
         
     | 
| 
      
 16 
     | 
    
         
            +
              after_create { readonly! }
         
     | 
| 
      
 17 
     | 
    
         
            +
              # cleanup; destroy corresponding file on S3
         
     | 
| 
      
 18 
     | 
    
         
            +
              after_destroy { s3_object.try(:delete) }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              def to_jq_upload
         
     | 
| 
      
 21 
     | 
    
         
            +
                { 
         
     | 
| 
      
 22 
     | 
    
         
            +
                  'id' => id,
         
     | 
| 
      
 23 
     | 
    
         
            +
                  'name' => file_name,
         
     | 
| 
      
 24 
     | 
    
         
            +
                  'size' => file_size,
         
     | 
| 
      
 25 
     | 
    
         
            +
                  'url' => url,
         
     | 
| 
      
 26 
     | 
    
         
            +
                  'image' => self.is_image?,
         
     | 
| 
      
 27 
     | 
    
         
            +
                  'delete_url' => Rails.application.routes.url_helpers.source_file_path(self)
         
     | 
| 
      
 28 
     | 
    
         
            +
                }
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def is_image?
         
     | 
| 
      
 32 
     | 
    
         
            +
                !!file_content_type.try(:match, /image/)
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              #---- start S3 related methods -----
         
     | 
| 
      
 36 
     | 
    
         
            +
              def s3_object
         
     | 
| 
      
 37 
     | 
    
         
            +
                @s3_object ||= AWS::S3::S3Object.find(key, bucket) if self.class.open_aws && key
         
     | 
| 
      
 38 
     | 
    
         
            +
              rescue
         
     | 
| 
      
 39 
     | 
    
         
            +
                nil
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def self.open_aws
         
     | 
| 
      
 43 
     | 
    
         
            +
                unless AWS::S3::Base.connected?
         
     | 
| 
      
 44 
     | 
    
         
            +
                  AWS::S3::Base.establish_connection!(
         
     | 
| 
      
 45 
     | 
    
         
            +
                    :access_key_id     => S3CorsFileupload::Config.access_key_id,
         
     | 
| 
      
 46 
     | 
    
         
            +
                    :secret_access_key => S3CorsFileupload::Config.secret_access_key
         
     | 
| 
      
 47 
     | 
    
         
            +
                  )
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                return AWS::S3::Base.connected?
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
              #---- end S3 related methods -----
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!-- The template to display files available for download -->
         
     | 
| 
      
 2 
     | 
    
         
            +
            <script id="template-download" type="text/x-tmpl">
         
     | 
| 
      
 3 
     | 
    
         
            +
              {% for (var i=0, file; file=o.files[i]; i++) { %}
         
     | 
| 
      
 4 
     | 
    
         
            +
                <tr class="template-download fade">
         
     | 
| 
      
 5 
     | 
    
         
            +
                  {% if (file.error) { %}
         
     | 
| 
      
 6 
     | 
    
         
            +
                    <td></td>
         
     | 
| 
      
 7 
     | 
    
         
            +
                    <td class="name"><span>{%=file.name%}</span></td>
         
     | 
| 
      
 8 
     | 
    
         
            +
                    <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
         
     | 
| 
      
 9 
     | 
    
         
            +
                    <td class="error" colspan="2"><span class="label label-important">Error</span> {%=file.error%}</td>
         
     | 
| 
      
 10 
     | 
    
         
            +
                    {% } else { %}
         
     | 
| 
      
 11 
     | 
    
         
            +
                    <td class="preview">{% if (file.thumbnail_url) { %}
         
     | 
| 
      
 12 
     | 
    
         
            +
                      <a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
         
     | 
| 
      
 13 
     | 
    
         
            +
                      {% } %}</td>
         
     | 
| 
      
 14 
     | 
    
         
            +
                    <td class="name">
         
     | 
| 
      
 15 
     | 
    
         
            +
                      <a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
         
     | 
| 
      
 16 
     | 
    
         
            +
                    </td>
         
     | 
| 
      
 17 
     | 
    
         
            +
                    <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
         
     | 
| 
      
 18 
     | 
    
         
            +
                    <td colspan="2"></td>
         
     | 
| 
      
 19 
     | 
    
         
            +
                    {% } %}
         
     | 
| 
      
 20 
     | 
    
         
            +
                  <td class="delete">
         
     | 
| 
      
 21 
     | 
    
         
            +
                    <button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
         
     | 
| 
      
 22 
     | 
    
         
            +
                      <i class="icon-trash icon-white"></i>
         
     | 
| 
      
 23 
     | 
    
         
            +
                      <span>Delete</span>
         
     | 
| 
      
 24 
     | 
    
         
            +
                    </button>
         
     | 
| 
      
 25 
     | 
    
         
            +
                    <input type="checkbox" name="delete" value="1">
         
     | 
| 
      
 26 
     | 
    
         
            +
                  </td>
         
     | 
| 
      
 27 
     | 
    
         
            +
                </tr>
         
     | 
| 
      
 28 
     | 
    
         
            +
                {% } %}
         
     | 
| 
      
 29 
     | 
    
         
            +
            </script>
         
     | 
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!-- The template to display files available for upload -->
         
     | 
| 
      
 2 
     | 
    
         
            +
            <script id="template-upload" type="text/x-tmpl">
         
     | 
| 
      
 3 
     | 
    
         
            +
              {% for (var i=0, file; file=o.files[i]; i++) { %}
         
     | 
| 
      
 4 
     | 
    
         
            +
              <tr class="template-upload fade">
         
     | 
| 
      
 5 
     | 
    
         
            +
                <td class="preview"><span class="fade"></span></td>
         
     | 
| 
      
 6 
     | 
    
         
            +
                <td class="name"><span>{%=file.name%}</span></td>
         
     | 
| 
      
 7 
     | 
    
         
            +
                <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
         
     | 
| 
      
 8 
     | 
    
         
            +
                {% if (file.error) { %}
         
     | 
| 
      
 9 
     | 
    
         
            +
                <td class="error" colspan="2"><span class="label label-important">Error</span> {%=file.error%}</td>
         
     | 
| 
      
 10 
     | 
    
         
            +
                {% } else if (o.files.valid && !i) { %}
         
     | 
| 
      
 11 
     | 
    
         
            +
                <td>
         
     | 
| 
      
 12 
     | 
    
         
            +
                  <div class="progress progress-success progress-striped active"><div class="bar" style="width:0%;"></div></div>
         
     | 
| 
      
 13 
     | 
    
         
            +
                </td>
         
     | 
| 
      
 14 
     | 
    
         
            +
                <td class="start">{% if (!o.options.autoUpload) { %}
         
     | 
| 
      
 15 
     | 
    
         
            +
                  <button class="btn btn-primary">
         
     | 
| 
      
 16 
     | 
    
         
            +
                    <i class="icon-upload icon-white"></i>
         
     | 
| 
      
 17 
     | 
    
         
            +
                    <span>Start</span>
         
     | 
| 
      
 18 
     | 
    
         
            +
                  </button>
         
     | 
| 
      
 19 
     | 
    
         
            +
                  {% } %}</td>
         
     | 
| 
      
 20 
     | 
    
         
            +
                {% } else { %}
         
     | 
| 
      
 21 
     | 
    
         
            +
                <td colspan="2"></td>
         
     | 
| 
      
 22 
     | 
    
         
            +
                {% } %}
         
     | 
| 
      
 23 
     | 
    
         
            +
                <td class="cancel">{% if (!i) { %}
         
     | 
| 
      
 24 
     | 
    
         
            +
                  <button class="btn btn-warning">
         
     | 
| 
      
 25 
     | 
    
         
            +
                    <i class="icon-ban-circle icon-white"></i>
         
     | 
| 
      
 26 
     | 
    
         
            +
                    <span>Cancel</span>
         
     | 
| 
      
 27 
     | 
    
         
            +
                  </button>
         
     | 
| 
      
 28 
     | 
    
         
            +
                  {% } %}</td>
         
     | 
| 
      
 29 
     | 
    
         
            +
              </tr>
         
     | 
| 
      
 30 
     | 
    
         
            +
              {% } %}
         
     | 
| 
      
 31 
     | 
    
         
            +
            </script>
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!-- The template to display files that have been uploaded to S3 already -->
         
     | 
| 
      
 2 
     | 
    
         
            +
            <!-- This essentially is a stand-in for the template_download partial for the s3_cors_fileupload gem's purposes -->
         
     | 
| 
      
 3 
     | 
    
         
            +
            <script id="template-uploaded" type="text/x-tmpl">
         
     | 
| 
      
 4 
     | 
    
         
            +
              <tr class="template-uploaded" id="source_file_{%=o.id%}">
         
     | 
| 
      
 5 
     | 
    
         
            +
                <td class="preview">
         
     | 
| 
      
 6 
     | 
    
         
            +
                  {% if (o.image == true) { %}
         
     | 
| 
      
 7 
     | 
    
         
            +
                    <a href="{%=o.url%}" title="{%=o.name%}" rel="gallery" download="{%=o.name%}">
         
     | 
| 
      
 8 
     | 
    
         
            +
                      <image src="{%=o.url%}", style='width:80px; height:56px;'></image>
         
     | 
| 
      
 9 
     | 
    
         
            +
                    </a>
         
     | 
| 
      
 10 
     | 
    
         
            +
                  {% } %}
         
     | 
| 
      
 11 
     | 
    
         
            +
                </td>
         
     | 
| 
      
 12 
     | 
    
         
            +
                <td class="name">
         
     | 
| 
      
 13 
     | 
    
         
            +
                  <a href="{%=o.url%}" title="{%=o.name%}" rel="{%=o.thumbnail_url&&'gallery'%}" download="{%=o.name%}">{%=o.name%}</a>
         
     | 
| 
      
 14 
     | 
    
         
            +
                </td>
         
     | 
| 
      
 15 
     | 
    
         
            +
                <td class="size"><span>{%=formatFileSize(o.size)%}</span></td>
         
     | 
| 
      
 16 
     | 
    
         
            +
                <td colspan="2"></td>
         
     | 
| 
      
 17 
     | 
    
         
            +
                <td class="delete">
         
     | 
| 
      
 18 
     | 
    
         
            +
                  <button class="btn btn-danger" data-type="DELETE" data-url="{%=o.delete_url%}">
         
     | 
| 
      
 19 
     | 
    
         
            +
                    <i class="icon-trash icon-white"></i>
         
     | 
| 
      
 20 
     | 
    
         
            +
                    <span>Delete</span>
         
     | 
| 
      
 21 
     | 
    
         
            +
                  </button>
         
     | 
| 
      
 22 
     | 
    
         
            +
                  <input type="checkbox" name="delete" value="1">
         
     | 
| 
      
 23 
     | 
    
         
            +
                </td>
         
     | 
| 
      
 24 
     | 
    
         
            +
              </tr>
         
     | 
| 
      
 25 
     | 
    
         
            +
            </script>
         
     | 
| 
         @@ -0,0 +1,43 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <%= stylesheet_link_tag '//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap-combined.min.css' %>
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            <div style="padding: 10px;">
         
     | 
| 
      
 4 
     | 
    
         
            +
              <h2>Upload file</h2>
         
     | 
| 
      
 5 
     | 
    
         
            +
              <%= s3_cors_fileupload_form :id => 'fileupload' %>
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              <div class="javascript-templates">
         
     | 
| 
      
 8 
     | 
    
         
            +
                <%= render 'template_upload' %>
         
     | 
| 
      
 9 
     | 
    
         
            +
                <%= render 'template_uploaded' %>
         
     | 
| 
      
 10 
     | 
    
         
            +
                <%# render 'template_download' %>
         
     | 
| 
      
 11 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 12 
     | 
    
         
            +
            </div>
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            <script>
         
     | 
| 
      
 15 
     | 
    
         
            +
              //<![CDATA[
         
     | 
| 
      
 16 
     | 
    
         
            +
                $(function () {
         
     | 
| 
      
 17 
     | 
    
         
            +
                  // Initialize the jQuery File Upload widget:
         
     | 
| 
      
 18 
     | 
    
         
            +
                  $('#fileupload').fileupload({
         
     | 
| 
      
 19 
     | 
    
         
            +
                    dataType: 'xml',
         
     | 
| 
      
 20 
     | 
    
         
            +
                    sequentialUploads: true,
         
     | 
| 
      
 21 
     | 
    
         
            +
                    downloadTemplateId: null,
         
     | 
| 
      
 22 
     | 
    
         
            +
                    downloadTemplate: null
         
     | 
| 
      
 23 
     | 
    
         
            +
                  });
         
     | 
| 
      
 24 
     | 
    
         
            +
                  
         
     | 
| 
      
 25 
     | 
    
         
            +
                  // Load existing files:
         
     | 
| 
      
 26 
     | 
    
         
            +
                  $.getJSON('/source_files', function (files) {
         
     | 
| 
      
 27 
     | 
    
         
            +
                    $.each(files, function(index, value) { 
         
     | 
| 
      
 28 
     | 
    
         
            +
                      $('#upload_files tbody').append(tmpl('template-uploaded', value));
         
     | 
| 
      
 29 
     | 
    
         
            +
                    });
         
     | 
| 
      
 30 
     | 
    
         
            +
                  });
         
     | 
| 
      
 31 
     | 
    
         
            +
                });
         
     | 
| 
      
 32 
     | 
    
         
            +
                
         
     | 
| 
      
 33 
     | 
    
         
            +
                // used by the jQuery File Upload
         
     | 
| 
      
 34 
     | 
    
         
            +
                var fileUploadErrors = {
         
     | 
| 
      
 35 
     | 
    
         
            +
                  maxFileSize: 'File is too big',
         
     | 
| 
      
 36 
     | 
    
         
            +
                  minFileSize: 'File is too small',
         
     | 
| 
      
 37 
     | 
    
         
            +
                  acceptFileTypes: 'Filetype not allowed',
         
     | 
| 
      
 38 
     | 
    
         
            +
                  maxNumberOfFiles: 'Max number of files exceeded',
         
     | 
| 
      
 39 
     | 
    
         
            +
                  uploadedBytes: 'Uploaded bytes exceed file size',
         
     | 
| 
      
 40 
     | 
    
         
            +
                  emptyResult: 'Empty file upload result'
         
     | 
| 
      
 41 
     | 
    
         
            +
                };
         
     | 
| 
      
 42 
     | 
    
         
            +
              //]]>
         
     | 
| 
      
 43 
     | 
    
         
            +
            </script>
         
     | 
| 
         @@ -0,0 +1,8 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            if defined?(::Rails)
         
     | 
| 
      
 2 
     | 
    
         
            +
              require 's3_cors_fileupload/rails/config'
         
     | 
| 
      
 3 
     | 
    
         
            +
              require 's3_cors_fileupload/rails/engine' if ::Rails.version >= '3.1'
         
     | 
| 
      
 4 
     | 
    
         
            +
              require 's3_cors_fileupload/rails/policy_helper'
         
     | 
| 
      
 5 
     | 
    
         
            +
              require 's3_cors_fileupload/rails/form_helper'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              ActionView::Base.send(:include, S3CorsFileupload::FormHelper) if defined?(ActionView::Base)
         
     | 
| 
      
 8 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module S3CorsFileupload
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Config
         
     | 
| 
      
 3 
     | 
    
         
            +
                require 'yaml'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                # this allows us to lazily instantiate the configuration by reading it in when it needs to be accessed
         
     | 
| 
      
 6 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # if a method is called on the class, attempt to look it up in the config array
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def method_missing(meth, *args, &block)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    if args.empty? && block.nil?
         
     | 
| 
      
 10 
     | 
    
         
            +
                      config[meth.to_s]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    else
         
     | 
| 
      
 12 
     | 
    
         
            +
                      super
         
     | 
| 
      
 13 
     | 
    
         
            +
                    end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  private
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def config
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @config ||= YAML.load_file(File.join(::Rails.root, 'config', 'amazon_s3.yml'))[::Rails.env]
         
     | 
| 
      
 20 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 21 
     | 
    
         
            +
                    warn('WARNING: s3_cors_fileupload gem was unable to locate a configuration file in config/amazon_s3.yml and may not ' +
         
     | 
| 
      
 22 
     | 
    
         
            +
                         'be able to function properly.  Please run `rails generate s3_cors_upload:install` before proceeding.')
         
     | 
| 
      
 23 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,91 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module S3CorsFileupload
         
     | 
| 
      
 2 
     | 
    
         
            +
              module FormHelper
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Options:
         
     | 
| 
      
 4 
     | 
    
         
            +
                # :access_key_id    The AWS Access Key ID of the owner of the bucket.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #                   Defaults to the `Config.access_key_id` (read from the yaml config file).
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # :acl              One of S3's Canned Access Control Lists, must be one of:
         
     | 
| 
      
 8 
     | 
    
         
            +
                #                   'private', 'public-read', 'public-read-write', 'authenticated-read'.
         
     | 
| 
      
 9 
     | 
    
         
            +
                #                   Defaults to `'public-read'`.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # :max_file_size    The max file size (in bytes) that you wish to allow to be uploaded.
         
     | 
| 
      
 12 
     | 
    
         
            +
                #                   Defaults to `Config.max_file_size` or, if no value is set on the yaml file `524288000` (500 MB)
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # :bucket           The name of the bucket on S3 you wish for the files to be uploaded to.
         
     | 
| 
      
 15 
     | 
    
         
            +
                #                   Defaults to `Config.bucket` (read from the yaml config file).
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                # Any other key creates standard HTML options for the form tag.
         
     | 
| 
      
 18 
     | 
    
         
            +
                def s3_cors_fileupload_form(options = {}, &block)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  policy_helper = PolicyHelper.new(options)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # initialize the hidden form fields
         
     | 
| 
      
 21 
     | 
    
         
            +
                  hidden_form_fields = {
         
     | 
| 
      
 22 
     | 
    
         
            +
                    :key => '',
         
     | 
| 
      
 23 
     | 
    
         
            +
                    'Content-Type' => '',
         
     | 
| 
      
 24 
     | 
    
         
            +
                    'AWSAccessKeyId' => options[:access_key_id] || Config.access_key_id,
         
     | 
| 
      
 25 
     | 
    
         
            +
                    :acl => policy_helper.options[:acl],
         
     | 
| 
      
 26 
     | 
    
         
            +
                    :policy => policy_helper.policy_document,
         
     | 
| 
      
 27 
     | 
    
         
            +
                    :signature => policy_helper.upload_signature,
         
     | 
| 
      
 28 
     | 
    
         
            +
                    :success_action_status => '201'
         
     | 
| 
      
 29 
     | 
    
         
            +
                  }
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # assume that all of the non-documented keys are 
         
     | 
| 
      
 31 
     | 
    
         
            +
                  _html_options = options.reject { |key, val| [:access_key_id, :acl, :max_file_size, :bucket].include?(key) }
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # return the form html
         
     | 
| 
      
 33 
     | 
    
         
            +
                  construct_form_html(hidden_form_fields, policy_helper.options[:bucket], _html_options,  &block)
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
                
         
     | 
| 
      
 36 
     | 
    
         
            +
                private
         
     | 
| 
      
 37 
     | 
    
         
            +
                
         
     | 
| 
      
 38 
     | 
    
         
            +
                def build_form_options(options = {})
         
     | 
| 
      
 39 
     | 
    
         
            +
                  { :id => 'fileupload' }.merge(options).merge(:multipart => true, :authenticity_token => false)
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
                
         
     | 
| 
      
 42 
     | 
    
         
            +
                # hidden fields argument should be a hash of key value pairs (values may be blank if desired)
         
     | 
| 
      
 43 
     | 
    
         
            +
                def construct_form_html(hidden_fields, bucket, html_options = {}, &block)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # now build the html for the form
         
     | 
| 
      
 45 
     | 
    
         
            +
                  form_text = form_tag("https://#{bucket}.s3.amazonaws.com", build_form_options(html_options)) do
         
     | 
| 
      
 46 
     | 
    
         
            +
                    hidden_fields.map do |name, value|
         
     | 
| 
      
 47 
     | 
    
         
            +
                      hidden_field_tag(name, value)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end.join.html_safe + "
         
     | 
| 
      
 49 
     | 
    
         
            +
                    <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->  
         
     | 
| 
      
 50 
     | 
    
         
            +
                    <div class='row fileupload-buttonbar'>
         
     | 
| 
      
 51 
     | 
    
         
            +
                      <div class='span7'>
         
     | 
| 
      
 52 
     | 
    
         
            +
                        <button class='btn btn-success fileinput-button'>
         
     | 
| 
      
 53 
     | 
    
         
            +
                          <i class='icon-plus icon-white'></i>
         
     | 
| 
      
 54 
     | 
    
         
            +
                          <span>Add files...</span>
         
     | 
| 
      
 55 
     | 
    
         
            +
                          ".html_safe +
         
     | 
| 
      
 56 
     | 
    
         
            +
                          file_field_tag(:file, :multiple => true) + "
         
     | 
| 
      
 57 
     | 
    
         
            +
                        </button>
         
     | 
| 
      
 58 
     | 
    
         
            +
                        <button class='btn btn-primary start' type='submit'>
         
     | 
| 
      
 59 
     | 
    
         
            +
                          <i class='icon-upload icon-white'></i>
         
     | 
| 
      
 60 
     | 
    
         
            +
                          <span>Start upload</span>
         
     | 
| 
      
 61 
     | 
    
         
            +
                        </button>
         
     | 
| 
      
 62 
     | 
    
         
            +
                        <button class='btn btn-warning cancel' type='reset'>
         
     | 
| 
      
 63 
     | 
    
         
            +
                          <i class='icon-ban-circle icon-white'></i>
         
     | 
| 
      
 64 
     | 
    
         
            +
                          <span>Cancel upload</span>
         
     | 
| 
      
 65 
     | 
    
         
            +
                        </button>
         
     | 
| 
      
 66 
     | 
    
         
            +
                        <button class='btn btn-danger delete' type='button'>
         
     | 
| 
      
 67 
     | 
    
         
            +
                          <i class='icon-trash icon-white'></i>
         
     | 
| 
      
 68 
     | 
    
         
            +
                          <span>Delete</span>
         
     | 
| 
      
 69 
     | 
    
         
            +
                        </button>
         
     | 
| 
      
 70 
     | 
    
         
            +
                        <input class='toggle' type='checkbox'></input>
         
     | 
| 
      
 71 
     | 
    
         
            +
                      </div>
         
     | 
| 
      
 72 
     | 
    
         
            +
                      <div class='span5'>
         
     | 
| 
      
 73 
     | 
    
         
            +
                        <!-- The global progress bar -->
         
     | 
| 
      
 74 
     | 
    
         
            +
                        <div class='progress progress-success progress-striped active fade'>
         
     | 
| 
      
 75 
     | 
    
         
            +
                          <div class='bar' style='width: 0%'></div>
         
     | 
| 
      
 76 
     | 
    
         
            +
                        </div>
         
     | 
| 
      
 77 
     | 
    
         
            +
                      </div>
         
     | 
| 
      
 78 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 79 
     | 
    
         
            +
                    <!-- The loading indicator is shown during image processing -->
         
     | 
| 
      
 80 
     | 
    
         
            +
                    <div class='fileupload-loading'></div>
         
     | 
| 
      
 81 
     | 
    
         
            +
                    <br>
         
     | 
| 
      
 82 
     | 
    
         
            +
                    <!-- The table listing the files available for upload/download -->
         
     | 
| 
      
 83 
     | 
    
         
            +
                    <table class='table table-striped' id='upload_files'>
         
     | 
| 
      
 84 
     | 
    
         
            +
                      <tbody class='files' data-target='#modal-gallery' data-toggle='modal-gallery'></tbody>
         
     | 
| 
      
 85 
     | 
    
         
            +
                    </table>".html_safe
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                  form_text += capture(&block) if block
         
     | 
| 
      
 88 
     | 
    
         
            +
                  form_text
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'base64'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'openssl'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'digest/sha1'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module S3CorsFileupload
         
     | 
| 
      
 7 
     | 
    
         
            +
              class PolicyHelper
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :options
         
     | 
| 
      
 9 
     | 
    
         
            +
                
         
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(_options = {})
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # default max_file_size to 500 MB if nothing is received
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @options = {
         
     | 
| 
      
 13 
     | 
    
         
            +
                    :acl => 'public-read',
         
     | 
| 
      
 14 
     | 
    
         
            +
                    :max_file_size => Config.max_file_size || 524288000,
         
     | 
| 
      
 15 
     | 
    
         
            +
                    :bucket => Config.bucket
         
     | 
| 
      
 16 
     | 
    
         
            +
                  }.merge(_options).merge(:secret_access_key => Config.secret_access_key)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
                
         
     | 
| 
      
 19 
     | 
    
         
            +
                # generate the policy document that amazon is expecting.
         
     | 
| 
      
 20 
     | 
    
         
            +
                def policy_document
         
     | 
| 
      
 21 
     | 
    
         
            +
                  Base64.encode64(
         
     | 
| 
      
 22 
     | 
    
         
            +
                    {
         
     | 
| 
      
 23 
     | 
    
         
            +
                      expiration: 1.hour.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
         
     | 
| 
      
 24 
     | 
    
         
            +
                      conditions: [
         
     | 
| 
      
 25 
     | 
    
         
            +
                        { bucket: options[:bucket] },
         
     | 
| 
      
 26 
     | 
    
         
            +
                        { acl: options[:acl] },
         
     | 
| 
      
 27 
     | 
    
         
            +
                        { success_action_status: '201' },
         
     | 
| 
      
 28 
     | 
    
         
            +
                        ["content-length-range", 0, options[:max_file_size]],
         
     | 
| 
      
 29 
     | 
    
         
            +
                        ["starts-with", "$utf8", ""],
         
     | 
| 
      
 30 
     | 
    
         
            +
                        ["starts-with", "$key", ""],
         
     | 
| 
      
 31 
     | 
    
         
            +
                        ["starts-with", "$Content-Type", ""]
         
     | 
| 
      
 32 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 33 
     | 
    
         
            +
                    }.to_json
         
     | 
| 
      
 34 
     | 
    
         
            +
                  ).gsub(/\n|\r/, '')
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                # sign our request by Base64 encoding the policy document.
         
     | 
| 
      
 38 
     | 
    
         
            +
                def upload_signature
         
     | 
| 
      
 39 
     | 
    
         
            +
                  Base64.encode64(
         
     | 
| 
      
 40 
     | 
    
         
            +
                    OpenSSL::HMAC.digest(
         
     | 
| 
      
 41 
     | 
    
         
            +
                      OpenSSL::Digest::Digest.new('sha1'),
         
     | 
| 
      
 42 
     | 
    
         
            +
                      options[:secret_access_key],
         
     | 
| 
      
 43 
     | 
    
         
            +
                      self.policy_document
         
     | 
| 
      
 44 
     | 
    
         
            +
                    )
         
     | 
| 
      
 45 
     | 
    
         
            +
                  ).gsub(/\n/, '')
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     |