sunrise-file-upload 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,43 @@
1
+ = Sunrise CMS: File uploader
2
+
3
+ == Install
4
+
5
+ rails generate sunrise:file_upload:install
6
+
7
+ == Callbacks
8
+
9
+ Sunrise::FileUpload::Manager.before_create do |env, asset|
10
+ asset.user = env['warden'].user if env['warden']
11
+ end
12
+
13
+ Sunrise::FileUpload::Manager.after_create do |env, asset|
14
+ asset.user = env['warden'].user if env['warden']
15
+ end
16
+
17
+ == Usage
18
+
19
+ class User < ActiveRecord::Base
20
+ has_one :picture, :as => :assetable, :dependent => :destroy
21
+
22
+ fileuploads :picture
23
+ end
24
+
25
+ Find asset by foreign key or guid:
26
+
27
+ @user.fileupload_asset(:picture)
28
+
29
+ === Views
30
+
31
+ <%= stylesheet_link_tag "fileupload/jquery.fileupload-ui.css" %>
32
+ <%= javascript_include_tag :fileupload %>
33
+
34
+ <%= form.fileupload :picture %>
35
+ <%= form.hidden_field :fileupload_guid if form.object.new_record? %>
36
+
37
+ <script id="fileupload_tmpl" type="text/x-jquery-tmpl">
38
+ <div class="fileupload-container">
39
+ <div class="fileupload-preview"><img alt="Фото" src="/images/userico.jpg"></div>
40
+ <div class="fileupload-button"><input type="image" value="Оберіть фаіл" src="/images/but_set.png"></div>
41
+ <ul class="fileupload-list" style="display:none;"></ul>
42
+ </div>
43
+ </script>
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require File.join(File.dirname(__FILE__), 'lib', 'sunrise', 'file_upload', 'version')
6
+
7
+ desc 'Default: run unit tests.'
8
+ task :default => :test
9
+
10
+ desc 'Test the sunrise plugin.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = true
16
+ end
17
+
18
+ desc 'Generate documentation for the sunrise-file-upload plugin.'
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'Sunrise FileUpload'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+ begin
28
+ require 'jeweler'
29
+ Jeweler::Tasks.new do |s|
30
+ s.name = "sunrise-file-upload"
31
+ s.version = Sunrise::FileUpload::VERSION.dup
32
+ s.summary = "Rails FileUpload"
33
+ s.description = "Sunrise is a Aimbulance CMS"
34
+ s.email = "galeta.igor@gmail.com"
35
+ s.homepage = "https://github.com/galetahub/sunrise-file-upload"
36
+ s.authors = ["Igor Galeta", "Pavlo Galeta"]
37
+ s.files = FileList["[A-Z]*", "{app,config,lib}/**/*"]
38
+ s.extra_rdoc_files = FileList["[A-Z]*"] - %w(Gemfile Rakefile)
39
+ end
40
+
41
+ Jeweler::GemcutterTasks.new
42
+ rescue LoadError
43
+ puts "Jeweler not available. Install it with: gem install jeweler"
44
+ end
@@ -0,0 +1,7 @@
1
+ Usage:
2
+ rails generate sunrise:file_upload:install
3
+
4
+ Options:
5
+
6
+ Example:
7
+ rails g sunrise:file_upload:install
@@ -0,0 +1,26 @@
1
+ require 'rails/generators'
2
+
3
+ module Sunrise
4
+ module Generators
5
+ module FileUpload
6
+ class InstallGenerator < Rails::Generators::Base
7
+ desc "This generator downloads and installs FileUploader"
8
+ source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
9
+
10
+ def copy_javascripts
11
+ copy_file "fileuploader-input.js", 'public/javascripts/fileupload/fileuploader-input.js'
12
+ end
13
+
14
+ def download_stylesheet
15
+ say_status("fetching fileuploader.css", "", :green)
16
+ get "https://github.com/galetahub/file-uploader/raw/master/client/fileuploader.css", "public/javascripts/fileupload/fileuploader.css"
17
+ end
18
+
19
+ def download_fileupload
20
+ say_status("fetching fileuploader.js", "", :green)
21
+ get "https://github.com/galetahub/file-uploader/raw/master/client/fileuploader.js", "public/javascripts/fileupload/fileuploader.js"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,217 @@
1
+ // Collection of all instances on page
2
+ qq.FileUploader.instances = new Object();
3
+
4
+ /**
5
+ * Class that creates upload widget with drag-and-drop and file list
6
+ * @inherits qq.FileUploaderBasic
7
+ */
8
+ qq.FileUploaderInput = function(o){
9
+ // call parent constructor
10
+ qq.FileUploaderBasic.apply(this, arguments);
11
+
12
+ // additional options
13
+ qq.extend(this._options, {
14
+ element: null,
15
+ // if set, will be used instead of qq-upload-list in template
16
+ listElement: null,
17
+
18
+ template_id: '#fileupload_tmpl',
19
+
20
+ // template for one item in file list
21
+ fileTemplate: '<li>' +
22
+ '<span class="fileupload-file"></span>' +
23
+ '<span class="fileupload-spinner"></span>' +
24
+ '<span class="fileupload-size"></span>' +
25
+ '<a class="fileupload-cancel" href="#">Cancel</a>' +
26
+ '<span class="fileupload-failed-text">Failed</span>' +
27
+ '</li>',
28
+
29
+ classes: {
30
+ // used to get elements from templates
31
+ button: 'fileupload-button',
32
+ drop: 'fileupload-drop-area',
33
+ dropActive: 'fileupload-drop-area-active',
34
+ list: 'fileupload-list',
35
+ preview: 'fileupload-preview',
36
+
37
+ file: 'fileupload-file',
38
+ spinner: 'fileupload-spinner',
39
+ size: 'fileupload-size',
40
+ cancel: 'fileupload-cancel',
41
+
42
+ // added to list item when upload completes
43
+ // used in css to hide progress spinner
44
+ success: 'fileupload-success',
45
+ fail: 'fileupload-fail'
46
+ }
47
+ });
48
+ // overwrite options with user supplied
49
+ qq.extend(this._options, o);
50
+
51
+ this._element = document.getElementById(this._options.element);
52
+ this._element.innerHTML = $(this._options.template_id).tmpl(this._options).html();
53
+ this._listElement = this._options.listElement || this._find(this._element, 'list');
54
+
55
+ this._classes = this._options.classes;
56
+
57
+ this._button = this._createUploadButton(this._find(this._element, 'button'));
58
+
59
+ this._bindCancelEvent();
60
+ //this._setupDragDrop();
61
+
62
+ qq.FileUploader.instances[this._element.id] = this;
63
+ };
64
+
65
+ // inherit from Basic Uploader
66
+ qq.extend(qq.FileUploaderInput.prototype, qq.FileUploaderBasic.prototype);
67
+
68
+ qq.extend(qq.FileUploaderInput.prototype, {
69
+ /**
70
+ * Gets one of the elements listed in this._options.classes
71
+ **/
72
+ _find: function(parent, type){
73
+ var element = qq.getByClass(parent, this._options.classes[type])[0];
74
+ if (!element){
75
+ throw new Error('element not found ' + type);
76
+ }
77
+
78
+ return element;
79
+ },
80
+ _setupDragDrop: function(){
81
+ var self = this,
82
+ dropArea = this._find(this._element, 'drop');
83
+
84
+ var dz = new qq.UploadDropZone({
85
+ element: dropArea,
86
+ onEnter: function(e){
87
+ qq.addClass(dropArea, self._classes.dropActive);
88
+ e.stopPropagation();
89
+ },
90
+ onLeave: function(e){
91
+ e.stopPropagation();
92
+ },
93
+ onLeaveNotDescendants: function(e){
94
+ qq.removeClass(dropArea, self._classes.dropActive);
95
+ },
96
+ onDrop: function(e){
97
+ dropArea.style.display = 'none';
98
+ qq.removeClass(dropArea, self._classes.dropActive);
99
+ self._uploadFileList(e.dataTransfer.files);
100
+ }
101
+ });
102
+
103
+ dropArea.style.display = 'none';
104
+
105
+ qq.attach(document, 'dragenter', function(e){
106
+ if (!dz._isValidFileDrag(e)) return;
107
+
108
+ dropArea.style.display = 'block';
109
+ });
110
+ qq.attach(document, 'dragleave', function(e){
111
+ if (!dz._isValidFileDrag(e)) return;
112
+
113
+ var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
114
+ // only fire when leaving document out
115
+ if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){
116
+ dropArea.style.display = 'none';
117
+ }
118
+ });
119
+ },
120
+ _onSubmit: function(id, fileName){
121
+ qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments);
122
+ this._addToList(id, fileName);
123
+ },
124
+ _onProgress: function(id, fileName, loaded, total){
125
+ qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments);
126
+
127
+ var item = this._getItemByFileId(id);
128
+ var size = this._find(item, 'size');
129
+ size.style.display = 'inline';
130
+
131
+ var text;
132
+ if (loaded != total){
133
+ text = Math.round(loaded / total * 100) + '% from ' + this._formatSize(total);
134
+ } else {
135
+ text = this._formatSize(total);
136
+ }
137
+
138
+ qq.setText(size, text);
139
+ },
140
+ _onComplete: function(id, fileName, result){
141
+ qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments);
142
+
143
+ var item = this._getItemByFileId(id);
144
+ var asset = result.asset;
145
+
146
+ // mark completed
147
+ qq.remove(this._find(item, 'cancel'));
148
+ qq.remove(this._find(item, 'spinner'));
149
+
150
+ if (asset && asset.id){
151
+ qq.addClass(item, this._classes.success);
152
+ this._updatePreview(result);
153
+ } else {
154
+ qq.addClass(item, this._classes.fail);
155
+ }
156
+ },
157
+ _addToList: function(id, fileName){
158
+ if (this._listElement) {
159
+ var item = qq.toElement(this._options.fileTemplate);
160
+ item.qqFileId = id;
161
+
162
+ var fileElement = this._find(item, 'file');
163
+ qq.setText(fileElement, this._formatFileName(fileName));
164
+ this._find(item, 'size').style.display = 'none';
165
+
166
+ this._listElement.appendChild(item);
167
+ }
168
+ },
169
+ _getItemByFileId: function(id){
170
+ var item = this._listElement.firstChild;
171
+
172
+ // there can't be txt nodes in dynamically created list
173
+ // and we can use nextSibling
174
+ while (item){
175
+ if (item.qqFileId == id) return item;
176
+ item = item.nextSibling;
177
+ }
178
+ },
179
+ /**
180
+ * delegate click event for cancel link
181
+ **/
182
+ _bindCancelEvent: function(){
183
+ var self = this,
184
+ list = this._listElement;
185
+
186
+ if (list) {
187
+ qq.attach(list, 'click', function(e){
188
+ e = e || window.event;
189
+ var target = e.target || e.srcElement;
190
+
191
+ if (qq.hasClass(target, self._classes.cancel)){
192
+ qq.preventDefault(e);
193
+
194
+ var item = target.parentNode;
195
+ self._handler.cancel(item.qqFileId);
196
+ qq.remove(item);
197
+ }
198
+ });
199
+ }
200
+ },
201
+
202
+ _updatePreview: function(result) {
203
+ var preview = this._find(this._element, 'preview'),
204
+ asset = result.asset || result;
205
+ img = null;
206
+
207
+ if (preview && asset) {
208
+ img = document.createElement('img');
209
+ img.src = asset.thumb_url;
210
+ img.alt = asset.filename;
211
+ img.setAttribute('data-url', asset.url);
212
+
213
+ preview.innerHTML = '';
214
+ preview.appendChild(img);
215
+ }
216
+ }
217
+ });
@@ -0,0 +1,2 @@
1
+ # encoding: utf-8
2
+ require 'sunrise/file_upload'
@@ -0,0 +1,20 @@
1
+ require 'active_support/secure_random'
2
+
3
+ module Sunrise
4
+ module FileUpload
5
+ autoload :Http, 'sunrise/file_upload/http'
6
+ autoload :Manager, 'sunrise/file_upload/manager'
7
+ autoload :Request, 'sunrise/file_upload/request'
8
+ autoload :ActiveRecord, 'sunrise/file_upload/active_record'
9
+ autoload :Callbacks, 'sunrise/file_upload/callbacks'
10
+
11
+ autoload :ViewHelper, 'sunrise/file_upload/view_helper'
12
+ autoload :FormBuilder, 'sunrise/file_upload/form_builder'
13
+
14
+ def self.guid
15
+ ActiveSupport::SecureRandom.base64(15).tr('+/=', 'xyz').slice(0, 10)
16
+ end
17
+ end
18
+ end
19
+
20
+ require 'sunrise/file_upload/engine'
@@ -0,0 +1,95 @@
1
+ module Sunrise
2
+ module FileUpload
3
+ module ActiveRecord
4
+ def self.included(base)
5
+ base.send :extend, SingletonMethods
6
+ end
7
+
8
+ module SingletonMethods
9
+ def fileuploads(*args)
10
+ options = args.extract_options!
11
+
12
+ class_attribute :fileuploads_options, :instance_writer => false
13
+ self.fileuploads_options = options
14
+
15
+ class_attribute :fileuploads_columns, :instance_writer => false
16
+ self.fileuploads_columns = args.map(&:to_sym)
17
+
18
+ unless self.is_a?(ClassMethods)
19
+ include InstanceMethods
20
+ extend ClassMethods
21
+
22
+ attr_accessible :fileupload_guid
23
+ after_save :fileuploads_update, :if => :fileupload_changed?
24
+
25
+ args.each do |asset|
26
+ accepts_nested_attributes_for asset, :allow_destroy => true
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ # Update reflection klass by guid
34
+ def fileupload_update(record_id, guid, method)
35
+ klass = fileupload_klass(method)
36
+ klass.update_all(["assetable_id = ?, guid = ?", record_id, nil], ["assetable_type = ? AND guid = ?", name, guid])
37
+ end
38
+
39
+ # Find asset by guid
40
+ def fileupload_find(method, guid)
41
+ klass = fileupload_klass(method)
42
+ klass.where(:guid => guid).first
43
+ end
44
+
45
+ protected
46
+
47
+ def fileupload_klass(method)
48
+ reflections[method.to_sym].klass
49
+ end
50
+ end
51
+
52
+ module InstanceMethods
53
+ # Generate unique key
54
+ def fileupload_guid
55
+ @fileupload_guid ||= Sunrise::FileUpload.guid
56
+ end
57
+
58
+ def fileupload_guid=(value)
59
+ @fileupload_changed = true unless value.blank?
60
+ @fileupload_guid = value.blank? ? nil : value
61
+ end
62
+
63
+ def fileupload_changed?
64
+ @fileupload_changed
65
+ end
66
+
67
+ def fileupload_multiple?(method)
68
+ association = self.class.reflect_on_association(method)
69
+ association.collection?
70
+ end
71
+
72
+ # Find or build new asset object
73
+ def fileupload_asset(method)
74
+ if fileuploads_columns.include?(method.to_sym)
75
+ asset = new_record? ? self.class.fileupload_find(method, fileupload_guid) : send(method)
76
+ asset ||= send("build_#{method}") if respond_to?("build_#{method}")
77
+ end
78
+ end
79
+
80
+ def fileuploads_columns
81
+ self.class.fileuploads_columns
82
+ end
83
+
84
+ protected
85
+
86
+ def fileuploads_update
87
+ fileuploads_columns.each do |method|
88
+ self.class.fileupload_update(id, fileupload_guid, method)
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ module Sunrise
3
+ module FileUpload
4
+ module Callbacks
5
+ # Hook to _run_callbacks asserting for conditions.
6
+ def _run_callbacks(kind, *args) #:nodoc:
7
+ options = args.last # Last callback arg MUST be a Hash
8
+
9
+ send("_#{kind}").each do |callback, conditions|
10
+ invalid = conditions.find do |key, value|
11
+ value.is_a?(Array) ? !value.include?(options[key]) : (value != options[key])
12
+ end
13
+
14
+ callback.call(*args) unless invalid
15
+ end
16
+ end
17
+
18
+ # A callback that runs before create asset
19
+ # Example:
20
+ # Sunrise::FileUpload::Manager.before_create do |env, opts|
21
+ # end
22
+ #
23
+ def before_create(options = {}, method = :push, &block)
24
+ raise BlockNotGiven unless block_given?
25
+ _before_create.send(method, [block, options])
26
+ end
27
+
28
+ # Provides access to the callback array for before_create
29
+ # :api: private
30
+ def _before_create
31
+ @_before_create ||= []
32
+ end
33
+
34
+ # A callback that runs after asset created
35
+ # Example:
36
+ # Sunrise::FileUpload::Manager.after_create do |env, opts|
37
+ # end
38
+ #
39
+ def after_create(options = {}, method = :push, &block)
40
+ raise BlockNotGiven unless block_given?
41
+ _after_create.send(method, [block, options])
42
+ end
43
+
44
+ # Provides access to the callback array for after_create
45
+ # :api: private
46
+ def _after_create
47
+ @_after_create ||= []
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,25 @@
1
+ require 'rails'
2
+ require 'sunrise-file-upload'
3
+
4
+ module Sunrise
5
+ module FileUpload
6
+ class Engine < ::Rails::Engine
7
+ # Initialize Rack file upload
8
+ config.app_middleware.use Sunrise::FileUpload::Manager, :paths => "/sunrise/fileupload"
9
+
10
+ config.before_initialize do
11
+ ActiveSupport.on_load :active_record do
12
+ ::ActiveRecord::Base.send :include, Sunrise::FileUpload::ActiveRecord
13
+ end
14
+
15
+ ActiveSupport.on_load :action_view do
16
+ ActionView::Base.send :include, Sunrise::FileUpload::ViewHelper
17
+ ActionView::Helpers::FormBuilder.send :include, Sunrise::FileUpload::FormBuilder
18
+
19
+ ActionView::Helpers::AssetTagHelper.register_javascript_expansion :fileupload =>
20
+ ["fileupload/fileuploader.js", "fileupload/fileuploader-input.js"]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module Sunrise
2
+ module FileUpload
3
+ module FormBuilder
4
+ def self.included(base)
5
+ base.send(:include, ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ # Example:
10
+ # <%= form_for @post do |form| %>
11
+ # ...
12
+ # <%= form.fileupload :picture %>
13
+ # <% end %>
14
+ #
15
+ def fileupload(method, options = {})
16
+ @template.fileupload_tag(@object_name, method, objectify_options(options))
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ require 'digest/sha1'
3
+ require 'mime/types'
4
+
5
+ module Sunrise
6
+ module FileUpload
7
+ module Http
8
+ # Create file from hash
9
+ class UploadedFile
10
+ attr_accessor :original_filename, :content_type, :tempfile, :headers
11
+
12
+ def initialize(hash)
13
+ @original_filename = hash[:filename]
14
+ @content_type = hash[:type]
15
+ @headers = hash[:head]
16
+ @tempfile = hash[:tempfile]
17
+ raise(ArgumentError, ':tempfile is required') unless @tempfile
18
+ end
19
+
20
+ def open
21
+ @tempfile.open
22
+ end
23
+
24
+ def path
25
+ @tempfile.path
26
+ end
27
+
28
+ def read(*args)
29
+ @tempfile.read(*args)
30
+ end
31
+
32
+ def rewind
33
+ @tempfile.rewind
34
+ end
35
+
36
+ def size
37
+ @tempfile.size
38
+ end
39
+ end
40
+
41
+ # Usage (paperclip example)
42
+ # @asset.data = QqFile.new(params[:qqfile], request)
43
+ class QqFile < ::Tempfile
44
+
45
+ def initialize(filename, request, tmpdir = Dir::tmpdir)
46
+ @original_filename = filename
47
+ @request = request
48
+
49
+ super Digest::SHA1.hexdigest(filename), tmpdir
50
+ fetch
51
+ end
52
+
53
+ def fetch
54
+ self.write @request.raw_post
55
+ self.rewind
56
+ self
57
+ end
58
+
59
+ def original_filename
60
+ @original_filename
61
+ end
62
+
63
+ def content_type
64
+ types = MIME::Types.type_for(@request.content_type)
65
+ types.empty? ? @request.content_type : types.first.to_s
66
+ end
67
+ end
68
+
69
+ # Convert nested Hash to HashWithIndifferentAccess and replace
70
+ # file upload hash with UploadedFile objects
71
+ def self.normalize_param(*args)
72
+ value = args.first
73
+ if Hash === value && value.has_key?(:tempfile)
74
+ UploadedFile.new(value)
75
+ elsif value.is_a?(String)
76
+ QqFile.new(*args)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,76 @@
1
+ module Sunrise
2
+ module FileUpload
3
+ class Manager
4
+ extend Sunrise::FileUpload::Callbacks
5
+
6
+ def initialize(app, options = {})
7
+ @app = app
8
+ @paths = [options[:paths]].flatten
9
+ end
10
+
11
+ def call(env)
12
+ raw_file_post?(env) ? create(env) : @app.call(env)
13
+ end
14
+
15
+ # :api: private
16
+ def _run_callbacks(*args) #:nodoc:
17
+ self.class._run_callbacks(*args)
18
+ end
19
+
20
+ protected
21
+
22
+ def create(env)
23
+ request = Request.new(env)
24
+ params = request.params.symbolize_keys
25
+
26
+ klass = find_klass(params)
27
+ asset = find_asset(klass, params) || klass.new(params[:asset])
28
+
29
+ asset.assetable_type = params[:assetable_type]
30
+ asset.assetable_id = params[:assetable_id].blank? ? 0 : params[:assetable_id].to_i
31
+ asset.guid = params[:guid]
32
+ asset.data = Http.normalize_param(params[:qqfile], request)
33
+
34
+ _run_callbacks(:before_create, env, asset)
35
+
36
+ if asset.save
37
+ body = asset.to_json
38
+ status = 200
39
+
40
+ _run_callbacks(:after_create, env, asset)
41
+ else
42
+ body = asset.errors.to_json
43
+ status = 422
44
+ end
45
+
46
+ [status, {'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s}, body]
47
+ end
48
+
49
+ def find_klass(params)
50
+ params[:klass].blank? ? Asset : params[:klass].classify.constantize
51
+ end
52
+
53
+ def find_asset(klass, params)
54
+ query = klass.scoped
55
+
56
+ unless params[:assetable_id].blank? || params[:assetable_type].blank?
57
+ query = query.where(:assetable_id => params[:assetable_id].to_i)
58
+ query = query.where(:assetable_type => params[:assetable_type])
59
+ else
60
+ query = query.where(:guid => params[:guid])
61
+ end
62
+
63
+ query.first
64
+ end
65
+
66
+ def raw_file_post?(env)
67
+ env['REQUEST_METHOD'] == 'POST' && upload_path?(env['PATH_INFO'])
68
+ end
69
+
70
+ def upload_path?(request_path)
71
+ return true if @paths.nil?
72
+ @paths.any? { |candidate| candidate == request_path }
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,26 @@
1
+ require 'rack/request'
2
+ require 'stringio'
3
+
4
+ module Sunrise
5
+ module FileUpload
6
+ class Request < ::Rack::Request
7
+
8
+ def raw_post
9
+ unless @env.include? 'RAW_POST_DATA'
10
+ @env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i)
11
+ body.rewind if body.respond_to?(:rewind)
12
+ end
13
+ @env['RAW_POST_DATA']
14
+ end
15
+
16
+ def body
17
+ if raw_post = @env['RAW_POST_DATA']
18
+ raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
19
+ StringIO.new(raw_post)
20
+ else
21
+ @env['rack.input']
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ module Sunrise
2
+ module FileUpload
3
+ VERSION = "0.1.1".freeze
4
+ end
5
+ end
@@ -0,0 +1,58 @@
1
+ module Sunrise
2
+ module FileUpload
3
+ module ViewHelper
4
+ include ActionView::Helpers::JavaScriptHelper
5
+
6
+ def fileupload_tag(object_name, method, options = {})
7
+ object = options.delete(:object) if options.key?(:object)
8
+ object ||= @template.instance_variable_get("@#{object_name}")
9
+
10
+ value = options.delete(:value) if options.key?(:value)
11
+ value ||= object.fileupload_asset(method)
12
+
13
+ element_guid = object.fileupload_guid
14
+ element_id = dom_id(object, [method, element_guid].join('_'))
15
+
16
+ script_options = (options.delete(:script) || {}).stringify_keys
17
+
18
+ params = {
19
+ :klass => value.class.name,
20
+ :assetable_id => object.new_record? ? nil : object.id,
21
+ :assetable_type => object.class.name,
22
+ :guid => element_guid
23
+ }.merge(script_options.delete(:params) || {})
24
+
25
+ script_options['action'] ||= '/sunrise/fileupload?' + Rack::Utils.build_query(params)
26
+ script_options['allowedExtensions'] ||= ['jpg', 'jpeg', 'png', 'gif']
27
+ script_options['multiple'] ||= object.fileupload_multiple?(method)
28
+
29
+ content_tag(:div, :class => 'fileupload') do
30
+ content_tag(:div, :id => element_id) do
31
+ content_tag(:noscript) do
32
+ fields_for object do |form|
33
+ form.fields_for method, value do |f|
34
+ f.file_field :data
35
+ end
36
+ end
37
+ end
38
+ end + javascript_tag( fileupload_script(element_id, value, script_options) )
39
+ end
40
+ end
41
+
42
+ protected
43
+
44
+ def fileupload_script(element_id, value = nil, options = {})
45
+ options = { 'element' => element_id }.merge(options)
46
+ formatted_options = options.inspect.gsub('=>', ':')
47
+ js = [ "new qq.FileUploaderInput(#{formatted_options});" ]
48
+
49
+ if value && !value.new_record?
50
+ js << "qq.FileUploader.instances['#{element_id}']._updatePreview(#{value.to_json});"
51
+ end
52
+
53
+ "$(document).ready(function(){ #{js.join} });"
54
+ end
55
+
56
+ end
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sunrise-file-upload
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Igor Galeta
14
+ - Pavlo Galeta
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-05-19 00:00:00 Z
20
+ dependencies: []
21
+
22
+ description: Sunrise is a Aimbulance CMS
23
+ email: galeta.igor@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ files:
31
+ - README.rdoc
32
+ - Rakefile
33
+ - lib/generators/sunrise/file_upload/USAGE
34
+ - lib/generators/sunrise/file_upload/install_generator.rb
35
+ - lib/generators/sunrise/file_upload/templates/fileuploader-input.js
36
+ - lib/sunrise-file-upload.rb
37
+ - lib/sunrise/file_upload.rb
38
+ - lib/sunrise/file_upload/active_record.rb
39
+ - lib/sunrise/file_upload/callbacks.rb
40
+ - lib/sunrise/file_upload/engine.rb
41
+ - lib/sunrise/file_upload/form_builder.rb
42
+ - lib/sunrise/file_upload/http.rb
43
+ - lib/sunrise/file_upload/manager.rb
44
+ - lib/sunrise/file_upload/request.rb
45
+ - lib/sunrise/file_upload/version.rb
46
+ - lib/sunrise/file_upload/view_helper.rb
47
+ homepage: https://github.com/galetahub/sunrise-file-upload
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.2
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Rails FileUpload
80
+ test_files: []
81
+