sunrise-file-upload 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/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
+