rails-uploader 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -98,19 +98,19 @@ Stylesheets:
98
98
  or FormBuilder:
99
99
 
100
100
  ``` ruby
101
- <%= form.uploader_field :photo %>
101
+ <%= form.uploader_field :photo, :sortable => true %>
102
102
  ```
103
103
 
104
104
  ### Formtastic
105
105
 
106
106
  ``` ruby
107
- <%= f.input :picture, :as => :uploader %>
107
+ <%= f.input :pictures, :as => :uploader %>
108
108
  ```
109
109
 
110
110
  ### SimpleForm
111
111
 
112
112
  ``` ruby
113
- <%= f.input :picture, :as => :uploader %>
113
+ <%= f.input :pictures, :as => :uploader, :input_html => {:sortable => true} %>
114
114
  ```
115
115
 
116
116
  ## Contributing
@@ -11,6 +11,16 @@ module Uploader
11
11
  render_resourse(@asset, 201)
12
12
  end
13
13
 
14
+ def update
15
+ @assets = Array.wrap(params[:assets] || [])
16
+
17
+ @assets.each_with_index do |id, index|
18
+ @klass.where(:id => id).update_all(:sort_order => index)
19
+ end
20
+
21
+ render_json(:files => [])
22
+ end
23
+
14
24
  def destroy
15
25
  @asset = @klass.find(params[:id])
16
26
  @asset.uploader_destroy(params, request)
@@ -20,22 +30,22 @@ module Uploader
20
30
  protected
21
31
 
22
32
  def find_klass
23
- @klass = params[:klass].blank? ? nil : params[:klass].safe_constantize
33
+ @klass = Uploader.constantize(params[:klass])
24
34
  raise ActionController::RoutingError.new("Class not found #{params[:klass]}") if @klass.nil?
25
35
  end
26
36
 
27
37
  def render_resourse(record, status = 200)
28
38
  if record.errors.empty?
29
- render_json([record].to_json(:root => false), status)
39
+ render_json({:files => [record]}, status)
30
40
  else
31
- render_json([record.errors].to_json, 422)
41
+ render_json(record.errors, 422)
32
42
  end
33
43
  end
34
44
 
35
- def render_json(body, status = 200)
45
+ def render_json(hash_or_object, status = 200)
36
46
  self.status = status
37
47
  self.content_type = "application/json"
38
- self.response_body = body
48
+ self.response_body = hash_or_object.to_json(:root => false)
39
49
  end
40
50
  end
41
51
  end
@@ -21,10 +21,12 @@
21
21
  <%= render :partial => "uploader/#{field.theme}/init", :locals => {:field => field} %>
22
22
  <%= render :partial => "uploader/#{field.theme}/upload", :locals => {:field => field} %>
23
23
  <%= render :partial => "uploader/#{field.theme}/download", :locals => {:field => field} %>
24
+ <%= render :partial => "uploader/#{field.theme}/sortable", :locals => {:field => field} if field.sortable? %>
24
25
 
25
26
  <script type="text/javascript">
26
27
  $(function() {
27
- var container = $("#<%= field.id %> div.uploader-files");
28
+ var uploader, container;
29
+ container = $("#<%= field.id %> div.uploader-files");
28
30
 
29
31
  $('#<%= field.id %> input[type="file"]').bind("init.uploader", function(e){
30
32
  $(this).fileupload({
@@ -40,7 +42,8 @@
40
42
  });
41
43
 
42
44
  <% if field.exists? -%>
43
- $(this).data("fileupload")
45
+ uploader = ($(this).data('blueimp-fileupload') || $(this).data('fileupload'));
46
+ uploader
44
47
  ._renderDownload(<%=raw field.values.to_json(:root => false) %>)
45
48
  .appendTo(container);
46
49
  <% end -%>
@@ -1,12 +1,15 @@
1
1
  <!-- The template to display files available for download -->
2
2
  <script id="template-download-<%= field.klass %>" type="text/x-tmpl">
3
3
  {% for (var i=0, file; file=o.files[i]; i++) { %}
4
- <div class="attach_item template-download">
4
+
5
+ <div id="asset_{%=file.id%}" class="attach_item template-download">
5
6
  <div class="delete">
6
- <a href="/" class="del_btn" data-type="DELETE" data-url="/uploader/attachments/{%=file.id%}?klass=<%= field.klass %>"></a>
7
+ <a href="#" class="del_btn" data-type="DELETE" data-url="/uploader/attachments/{%=file.id%}?klass=<%= field.klass %>"></a>
7
8
  </div>
8
9
  <div class="thumbnail preview">
9
- <a href="{%=file.url%}"><img src="{%=file.thumb_url%}" title="{%=file.filename%}" rel="gallery"></a>
10
+ <a href="{%=file.url%}" download="{%=file.filename%}">
11
+ <img src="{%=file.thumb_url%}" title="{%=file.filename%}" rel="gallery" />
12
+ </a>
10
13
  </div>
11
14
  <div class="infoHolder">
12
15
  <div class="fileName">{%=file.filename%}</div>
@@ -0,0 +1,24 @@
1
+ <script type="text/javascript">
2
+ $(function() {
3
+ var container, data;
4
+
5
+ container = $("#<%= field.id %> div.uploader-files");
6
+
7
+ container.sortable({
8
+ placeholder: "attach_item",
9
+ cursor: "move",
10
+ update: function(event, ui) {
11
+ data = $(this).sortable("serialize", {key: "assets[]"});
12
+
13
+ $.rails.ajax({
14
+ url: "<%= uploader.attachment_path(field.klass) %>",
15
+ type: "PUT",
16
+ dataType: "json",
17
+ data: data + "&klass=<%= field.klass %>"
18
+ });
19
+ }
20
+ });
21
+
22
+ container.disableSelection();
23
+ });
24
+ </script>
data/config/routes.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  Uploader::Engine.routes.draw do
2
- resources :attachments, :only => [:create, :destroy]
2
+ resources :attachments, :only => [:create, :update, :destroy]
3
3
  end
data/lib/uploader.rb CHANGED
@@ -10,7 +10,7 @@ module Uploader
10
10
  autoload :FormBuilder, 'uploader/helpers/form_builder'
11
11
  autoload :FieldTag, 'uploader/helpers/field_tag'
12
12
  end
13
-
13
+
14
14
  def self.guid
15
15
  SecureRandom.base64(15).tr('+/=', 'xyz').slice(0, 10)
16
16
  end
@@ -25,6 +25,11 @@ module Uploader
25
25
  list
26
26
  end
27
27
  end
28
+
29
+ def self.constantize(klass)
30
+ return if klass.blank?
31
+ klass.safe_constantize
32
+ end
28
33
  end
29
34
 
30
35
  require 'uploader/engine'
@@ -55,6 +55,10 @@ module Uploader
55
55
  def exists?
56
56
  values.map(&:persisted?).any?
57
57
  end
58
+
59
+ def sortable?
60
+ @options[:sortable] == true
61
+ end
58
62
 
59
63
  def klass
60
64
  @klass ||= @object.class.fileupload_klass(method_name)
@@ -1,3 +1,3 @@
1
1
  module Uploader
2
- VERSION = "0.0.8".freeze
2
+ VERSION = "0.1.0".freeze
3
3
  end
Binary file
@@ -274,3 +274,55 @@ Started POST "/uploader/attachments" for 127.0.0.1 at 2012-09-12 16:41:07 +0300
274
274
   (79.3ms) DELETE FROM sqlite_sequence where name = 'assets';
275
275
   (79.6ms) DELETE FROM "articles";
276
276
   (79.3ms) DELETE FROM sqlite_sequence where name = 'articles';
277
+ Connecting to database specified by database.yml
278
+  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" 
279
+ Migrating to CreateAssets (20120508093416)
280
+ Migrating to CreateArticles (20120508093830)
281
+  (256.6ms) DELETE FROM "assets";
282
+  (0.2ms) DELETE FROM sqlite_sequence where name = 'assets';
283
+  (256.3ms) DELETE FROM "articles";
284
+  (0.2ms) DELETE FROM sqlite_sequence where name = 'articles';
285
+
286
+
287
+ Started POST "/uploader/attachments" for 127.0.0.1 at 2013-02-25 01:51:39 +0200
288
+  (0.2ms) begin transaction
289
+ SQL (142.8ms) INSERT INTO "assets" ("assetable_id", "assetable_type", "created_at", "data_content_type", "data_file_name", "data_file_size", "guid", "type", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["assetable_id", 1], ["assetable_type", "Article"], ["created_at", Sun, 24 Feb 2013 23:51:40 UTC +00:00], ["data_content_type", nil], ["data_file_name", "rails.png"], ["data_file_size", nil], ["guid", "SOMESTRING"], ["type", "Picture"], ["updated_at", Sun, 24 Feb 2013 23:51:40 UTC +00:00], ["user_id", nil]]
290
+  (577.5ms) commit transaction
291
+  (0.1ms) begin transaction
292
+ SQL (0.7ms) INSERT INTO "articles" ("content", "created_at", "title", "updated_at") VALUES (?, ?, ?, ?) [["content", "MyText"], ["created_at", Sun, 24 Feb 2013 23:51:41 UTC +00:00], ["title", "MyString"], ["updated_at", Sun, 24 Feb 2013 23:51:41 UTC +00:00]]
293
+  (398.0ms) commit transaction
294
+  (0.2ms) begin transaction
295
+ SQL (0.7ms) INSERT INTO "assets" ("assetable_id", "assetable_type", "created_at", "data_content_type", "data_file_name", "data_file_size", "guid", "type", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["assetable_id", 1], ["assetable_type", "Article"], ["created_at", Sun, 24 Feb 2013 23:51:41 UTC +00:00], ["data_content_type", "image/png"], ["data_file_name", "rails.png"], ["data_file_size", nil], ["guid", nil], ["type", "Picture"], ["updated_at", Sun, 24 Feb 2013 23:51:41 UTC +00:00], ["user_id", nil]]
296
+  (341.3ms) commit transaction
297
+  (0.3ms) SELECT COUNT(*) FROM "assets" WHERE "assets"."type" IN ('Picture')
298
+
299
+
300
+ Started DELETE "/uploader/attachments/2" for 127.0.0.1 at 2013-02-25 01:51:42 +0200
301
+ Picture Load (0.4ms) SELECT "assets".* FROM "assets" WHERE "assets"."type" IN ('Picture') AND "assets"."id" = ? LIMIT 1 [["id", "2"]]
302
+  (0.1ms) begin transaction
303
+ SQL (0.4ms) DELETE FROM "assets" WHERE "assets"."type" IN ('Picture') AND "assets"."id" = ? [["id", 2]]
304
+  (379.4ms) commit transaction
305
+  (0.3ms) SELECT COUNT(*) FROM "assets" WHERE "assets"."type" IN ('Picture')
306
+
307
+
308
+ Started POST "/uploader/attachments" for 127.0.0.1 at 2013-02-25 01:51:42 +0200
309
+  (490.1ms) DELETE FROM "assets";
310
+  (442.8ms) DELETE FROM sqlite_sequence where name = 'assets';
311
+  (335.7ms) DELETE FROM "articles";
312
+  (548.4ms) DELETE FROM sqlite_sequence where name = 'articles';
313
+  (0.1ms) begin transaction
314
+ SQL (0.6ms) INSERT INTO "articles" ("content", "created_at", "title", "updated_at") VALUES (?, ?, ?, ?) [["content", "MyText"], ["created_at", Sun, 24 Feb 2013 23:51:44 UTC +00:00], ["title", "MyString"], ["updated_at", Sun, 24 Feb 2013 23:51:44 UTC +00:00]]
315
+  (487.8ms) commit transaction
316
+  (0.1ms) begin transaction
317
+ SQL (0.6ms) INSERT INTO "assets" ("assetable_id", "assetable_type", "created_at", "data_content_type", "data_file_name", "data_file_size", "guid", "type", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["assetable_id", 1], ["assetable_type", "Article"], ["created_at", Sun, 24 Feb 2013 23:51:45 UTC +00:00], ["data_content_type", "image/png"], ["data_file_name", "rails.png"], ["data_file_size", nil], ["guid", nil], ["type", "Picture"], ["updated_at", Sun, 24 Feb 2013 23:51:45 UTC +00:00], ["user_id", nil]]
318
+  (481.1ms) commit transaction
319
+ Picture Load (0.5ms) SELECT "assets".* FROM "assets" WHERE "assets"."type" IN ('Picture') AND "assets"."guid" IS NULL LIMIT 1
320
+ SQL (2057.4ms) UPDATE "assets" SET "assetable_id" = 1000, "guid" = NULL WHERE "assets"."type" IN ('Picture') AND "assets"."guid" IS NULL AND "assets"."assetable_type" = 'Article'
321
+ Picture Load (0.2ms) SELECT "assets".* FROM "assets" WHERE "assets"."type" IN ('Picture') AND "assets"."id" = ? LIMIT 1 [["id", 1]]
322
+ Picture Load (0.4ms) SELECT "assets".* FROM "assets" WHERE "assets"."type" IN ('Picture') AND "assets"."guid" = 'h4uBeA8SVy' LIMIT 1
323
+  (0.2ms) begin transaction
324
+  (0.1ms) commit transaction
325
+  (901.4ms) DELETE FROM "assets";
326
+  (684.1ms) DELETE FROM sqlite_sequence where name = 'assets';
327
+  (900.7ms) DELETE FROM "articles";
328
+  (764.7ms) DELETE FROM sqlite_sequence where name = 'articles';
@@ -0,0 +1,223 @@
1
+ /*
2
+ * jQuery File Upload File Processing Plugin 1.2.1
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2012, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ */
11
+
12
+ /*jslint nomen: true, unparam: true, regexp: true */
13
+ /*global define, window, document */
14
+
15
+ (function (factory) {
16
+ 'use strict';
17
+ if (typeof define === 'function' && define.amd) {
18
+ // Register as an anonymous AMD module:
19
+ define([
20
+ 'jquery',
21
+ 'load-image',
22
+ 'canvas-to-blob',
23
+ './jquery.fileupload'
24
+ ], factory);
25
+ } else {
26
+ // Browser globals:
27
+ factory(
28
+ window.jQuery,
29
+ window.loadImage
30
+ );
31
+ }
32
+ }(function ($, loadImage) {
33
+ 'use strict';
34
+
35
+ // The File Upload FP version extends the fileupload widget
36
+ // with file processing functionality:
37
+ $.widget('blueimp.fileupload', $.blueimp.fileupload, {
38
+
39
+ options: {
40
+ // The list of file processing actions:
41
+ process: [
42
+ /*
43
+ {
44
+ action: 'load',
45
+ fileTypes: /^image\/(gif|jpeg|png)$/,
46
+ maxFileSize: 20000000 // 20MB
47
+ },
48
+ {
49
+ action: 'resize',
50
+ maxWidth: 1920,
51
+ maxHeight: 1200,
52
+ minWidth: 800,
53
+ minHeight: 600
54
+ },
55
+ {
56
+ action: 'save'
57
+ }
58
+ */
59
+ ],
60
+
61
+ // The add callback is invoked as soon as files are added to the
62
+ // fileupload widget (via file input selection, drag & drop or add
63
+ // API call). See the basic file upload widget for more information:
64
+ add: function (e, data) {
65
+ $(this).fileupload('process', data).done(function () {
66
+ data.submit();
67
+ });
68
+ }
69
+ },
70
+
71
+ processActions: {
72
+ // Loads the image given via data.files and data.index
73
+ // as img element if the browser supports canvas.
74
+ // Accepts the options fileTypes (regular expression)
75
+ // and maxFileSize (integer) to limit the files to load:
76
+ load: function (data, options) {
77
+ var that = this,
78
+ file = data.files[data.index],
79
+ dfd = $.Deferred();
80
+ if (window.HTMLCanvasElement &&
81
+ window.HTMLCanvasElement.prototype.toBlob &&
82
+ ($.type(options.maxFileSize) !== 'number' ||
83
+ file.size < options.maxFileSize) &&
84
+ (!options.fileTypes ||
85
+ options.fileTypes.test(file.type))) {
86
+ loadImage(
87
+ file,
88
+ function (img) {
89
+ if (!img.src) {
90
+ return dfd.rejectWith(that, [data]);
91
+ }
92
+ data.img = img;
93
+ dfd.resolveWith(that, [data]);
94
+ }
95
+ );
96
+ } else {
97
+ dfd.rejectWith(that, [data]);
98
+ }
99
+ return dfd.promise();
100
+ },
101
+ // Resizes the image given as data.img and updates
102
+ // data.canvas with the resized image as canvas element.
103
+ // Accepts the options maxWidth, maxHeight, minWidth and
104
+ // minHeight to scale the given image:
105
+ resize: function (data, options) {
106
+ var img = data.img,
107
+ canvas;
108
+ options = $.extend({canvas: true}, options);
109
+ if (img) {
110
+ canvas = loadImage.scale(img, options);
111
+ if (canvas.width !== img.width ||
112
+ canvas.height !== img.height) {
113
+ data.canvas = canvas;
114
+ }
115
+ }
116
+ return data;
117
+ },
118
+ // Saves the processed image given as data.canvas
119
+ // inplace at data.index of data.files:
120
+ save: function (data, options) {
121
+ // Do nothing if no processing has happened:
122
+ if (!data.canvas) {
123
+ return data;
124
+ }
125
+ var that = this,
126
+ file = data.files[data.index],
127
+ name = file.name,
128
+ dfd = $.Deferred(),
129
+ callback = function (blob) {
130
+ if (!blob.name) {
131
+ if (file.type === blob.type) {
132
+ blob.name = file.name;
133
+ } else if (file.name) {
134
+ blob.name = file.name.replace(
135
+ /\..+$/,
136
+ '.' + blob.type.substr(6)
137
+ );
138
+ }
139
+ }
140
+ // Store the created blob at the position
141
+ // of the original file in the files list:
142
+ data.files[data.index] = blob;
143
+ dfd.resolveWith(that, [data]);
144
+ };
145
+ // Use canvas.mozGetAsFile directly, to retain the filename, as
146
+ // Gecko doesn't support the filename option for FormData.append:
147
+ if (data.canvas.mozGetAsFile) {
148
+ callback(data.canvas.mozGetAsFile(
149
+ (/^image\/(jpeg|png)$/.test(file.type) && name) ||
150
+ ((name && name.replace(/\..+$/, '')) ||
151
+ 'blob') + '.png',
152
+ file.type
153
+ ));
154
+ } else {
155
+ data.canvas.toBlob(callback, file.type);
156
+ }
157
+ return dfd.promise();
158
+ }
159
+ },
160
+
161
+ // Resizes the file at the given index and stores the created blob at
162
+ // the original position of the files list, returns a Promise object:
163
+ _processFile: function (files, index, options) {
164
+ var that = this,
165
+ dfd = $.Deferred().resolveWith(that, [{
166
+ files: files,
167
+ index: index
168
+ }]),
169
+ chain = dfd.promise();
170
+ that._processing += 1;
171
+ $.each(options.process, function (i, settings) {
172
+ chain = chain.pipe(function (data) {
173
+ return that.processActions[settings.action]
174
+ .call(this, data, settings);
175
+ });
176
+ });
177
+ chain.always(function () {
178
+ that._processing -= 1;
179
+ if (that._processing === 0) {
180
+ that.element
181
+ .removeClass('fileupload-processing');
182
+ }
183
+ });
184
+ if (that._processing === 1) {
185
+ that.element.addClass('fileupload-processing');
186
+ }
187
+ return chain;
188
+ },
189
+
190
+ // Processes the files given as files property of the data parameter,
191
+ // returns a Promise object that allows to bind a done handler, which
192
+ // will be invoked after processing all files (inplace) is done:
193
+ process: function (data) {
194
+ var that = this,
195
+ options = $.extend({}, this.options, data);
196
+ if (options.process && options.process.length &&
197
+ this._isXHRUpload(options)) {
198
+ $.each(data.files, function (index, file) {
199
+ that._processingQueue = that._processingQueue.pipe(
200
+ function () {
201
+ var dfd = $.Deferred();
202
+ that._processFile(data.files, index, options)
203
+ .always(function () {
204
+ dfd.resolveWith(that);
205
+ });
206
+ return dfd.promise();
207
+ }
208
+ );
209
+ });
210
+ }
211
+ return this._processingQueue;
212
+ },
213
+
214
+ _create: function () {
215
+ this._super();
216
+ this._processing = 0;
217
+ this._processingQueue = $.Deferred().resolveWith(this)
218
+ .promise();
219
+ }
220
+
221
+ });
222
+
223
+ }));