spud_photos 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -6,14 +6,12 @@ Spud Photos is an engine for creating and managing photo galleries, designed for
6
6
 
7
7
  1. In your Gemfile add the following
8
8
 
9
- gem 'spud_core', :git => "git://github.com/davydotcom/spud_core_admin.git"
10
- gem 'spud_photos', :git => "git://github.com/gregawoods/spud_photos.git"
9
+ gem 'spud_photos'
11
10
 
12
11
  2. Run bundle install
13
12
  3. Copy in database migrations to your new rails project
14
13
 
15
- bundle exec rake spud_core:install:migrations
16
- bundle exec rake spud_photos:install:migrations
14
+ bundle exec rake railties:install:migrations
17
15
  rake db:migrate
18
16
 
19
17
  4. Run a rails server instance and point your browser to /spud/admin
@@ -23,7 +21,7 @@ Spud Photos is an engine for creating and managing photo galleries, designed for
23
21
  Spud Photos accepts the following configuration options:
24
22
 
25
23
  Spud::Photos.configure do |config|
26
- self.base_layout = 'application'
24
+ config.base_layout = 'application'
27
25
  config.galleries_enabled = false
28
26
  config.base_path = 'photos'
29
27
  config.photo_styles = {
@@ -32,6 +30,10 @@ Spud Photos accepts the following configuration options:
32
30
  :large => '400x400#',
33
31
  :huge => '600x600'
34
32
  }
33
+ self.paperclip_storage = :filesystem #use :s3 to use s3 storage (aws gem required)
34
+ config.s3_credentials = "#{Rails.root}/config/s3.yml"
35
+ config.storage_path = ":rails_root/public/system/spud_photos/:id/:style/:basename.:extension"
36
+ config.storage_url = "/system/spud_photos/:id/:style/:basename.:extension"
35
37
  end
36
38
 
37
39
  The `photo_styles` option will be passed to [Paperclip][2], so any valid paperclip styles can be added here.
@@ -6,64 +6,111 @@ Spud.Admin.Photos = new function(){
6
6
  var self = this;
7
7
 
8
8
  this.init = function(){
9
+ // event handlers
9
10
  $('.spud_admin_photo_ui_thumbs_sortable').sortable({
10
11
  connectWith:'.spud_admin_photo_ui_thumbs_sortable'
11
12
  });
12
- $('#spud_admin_photo_album_form, #spud_admin_photo_gallery_form').live('submit', self.submittedPhotoAlbumOrGalleryForm)
13
- $('.spud_admin_photo_mass_destroy').live('click', self.massDestroySelected);
14
- $('#spud_admin_photo_form').live('submit', self.submittedPhotoForm);
15
- $('.spud_admin_photo_ui_thumb_selectable input[type=checkbox]').live('click', self.invertPhotoUiThumbCheckbox);
16
- $('.spud_admin_photo_ui_thumb_selectable').live('click', self.selectedPhotoUiThumb);
17
- self.markSelectedPhotoUiThumbs();
18
- };
19
-
20
- this.submittedPhotoAlbumOrGalleryForm = function(e){
21
- // update photo checkboxes
22
- $('.spud_admin_photo_ui_thumb').each(function(){
23
- var item = $(this);
24
- var checkbox = item.find('input[type=checkbox]');
25
- checkbox.attr('checked', (item.parents('.spud_admin_photos_selection_left').length>0));
13
+ $('body').on('submit', '#spud_admin_photo_album_form', self.submittedPhotoAlbumForm)
14
+ $('body').on('submit', '#spud_admin_photo_form', self.submittedPhotoForm);
15
+ $('body').on('click', '.spud_admin_photos_btn_remove', self.clickedPhotoRemoveFromLibrary)
16
+ $('body').on('click', '.spud_admin_photo_ui_thumbs_selectable .spud_admin_photo_ui_thumb', self.selectedPhotoUiThumb);
17
+ $('body').on('click', '#spud_admin_photo_album_action_library', self.clickedPhotoLibrary);
18
+
19
+ // html5 drag and drop file
20
+ var droparea = document.getElementById('spud_admin_photos_selected');
21
+ droparea.addEventListener('dragenter', self.stopDndPropagation, false);
22
+ droparea.addEventListener('dragexit', self.stopDndPropagation, false);
23
+ droparea.addEventListener('dragover', self.stopDndPropagation, false);
24
+ droparea.addEventListener('drop', self.droppedFile, false);
25
+ };
26
+
27
+ this.submittedPhotoAlbumForm = function(){
28
+ var ids = $('#spud_admin_photos_selected .spud_admin_photo_ui_thumb').map(function(i, el){ return $(el).attr('rel') } );
29
+ $('#spud_photo_album_order').val(ids.toArray().join());
30
+ };
31
+
32
+ this.clickedPhotoRemoveFromLibrary = function(e){
33
+ $(this).parents('.spud_admin_photo_ui_thumb').fadeOut(200, function(){
34
+ $(this).remove();
26
35
  });
27
- }
36
+ };
28
37
 
29
- this.massDestroySelected = function(e){
30
- e.preventDefault();
31
- var ids = $.map($('.spud_admin_photo_ui_thumb_selected'), function(val, i){
32
- return $(val).find('input[type=checkbox]').val()
38
+ /* Handle file uploads passed via iframe (legacy support)
39
+ * -------------------------------------------------------- */
40
+
41
+ this.photoLegacyUploadErrors = function(html){
42
+ $('#spud_admin_photo_form').replaceWith(html);
43
+ };
44
+
45
+ this.photoLegacyUploadComplete = function(id, html){
46
+ var element = $('#spud_admin_photo_' + id);
47
+ if(element.length > 0){
48
+ element.replaceWith(htmlhtml);
49
+ }
50
+ else{
51
+ var target = $('#spud_admin_photos_selected .spud_admin_photo_ui_thumbs, #spud_admin_photos');
52
+ target.prepend(html).fadeIn(200);
53
+ }
54
+ $('#dialog').dialog('close');
55
+ };
56
+
57
+ this.selectedPhotoUiThumb = function(e){
58
+ var thumb = $(this);
59
+ if(thumb.hasClass('spud_admin_photo_ui_thumb_selected')){
60
+ $(this).removeClass('spud_admin_photo_ui_thumb_selected');
61
+ }
62
+ else{
63
+ $(this).addClass('spud_admin_photo_ui_thumb_selected');
64
+ }
65
+ };
66
+
67
+ this.markPhotoAsDeleted = function(photo_id){
68
+ var photo = $('#spud_admin_photo_' + photo_id);
69
+ photo.fadeOut(200, function(){
70
+ photo.remove();
71
+ });
72
+ };
73
+
74
+ this.markPhotoAlbumAsDeleted = function(photo_album_id){
75
+ var photo_album = $('#spud_admin_photo_album_' + photo_album_id);
76
+ photo_album.fadeOut(200, function(){
77
+ photo_album.remove();
78
+ });
79
+ };
80
+
81
+ this.markPhotoGalleryAsDeleted = function(photo_gallery_id){
82
+ var photo_gallery = $('#spud_admin_photo_gallery_' + photo_gallery_id);
83
+ photo_gallery.fadeOut(200, function(){
84
+ photo_gallery.remove();
33
85
  });
34
- $.ajax({
35
- type: 'POST',
36
- url: $(this).attr('href'),
37
- data: {spud_photo_ids:ids},
38
- success: function(data, textStatus, jqXHR){
39
- $('.spud_admin_photo_ui_thumb_selected').fadeOut(200, function(){
40
- $(this).remove();
41
- });
42
- },
43
- error: function(jqXHR, textStatus, errorThrown){
44
- console.log('An error occurred:')
45
- console.log(arguments);
46
- }
47
- })
48
86
  };
49
87
 
88
+ /*
89
+ * Single-Photo Form Upload
90
+ -------------------------------- */
91
+
50
92
  this.submittedPhotoForm = function(e){
51
93
  if(FormData && XMLHttpRequest){
52
94
  // create a FormData object and attach form values
53
95
  var fd = new FormData();
54
96
  var form = $(this);
97
+ var file = form.find('#spud_photo_photo')[0].files[0];
55
98
  fd.append('_method', form.find('[name=_method]').val());
56
99
  fd.append('authenticity_token', form.find('[name=authenticity_token]').val());
57
- fd.append('spud_photo[photo]', form.find('#spud_photo_photo')[0].files[0]);
100
+ fd.append('spud_photo[photo]', file);
58
101
  fd.append('spud_photo[title]', form.find('#spud_photo_title').val());
59
102
  fd.append('spud_photo[caption]', form.find('#spud_photo_caption').val());
60
103
 
104
+ // progress bar to send events to
105
+ var progressBar = self.progressBarForUpload(file.fileName);
106
+ form.find('.form-actions').before(progressBar);
107
+
61
108
  // send FormData object as ajax request
62
109
  var xhr = new XMLHttpRequest();
63
- xhr.upload.addEventListener('progress', self.photoUploadProgress, false);
64
- xhr.addEventListener('load', self.photoUploadComplete, false);
65
- xhr.addEventListener('error', self.photoUploadFailed, false);
66
- xhr.addEventListener('abort', self.photoUploadCanceled, false);
110
+ xhr.upload.addEventListener('progress', function(e){ self.onPhotoUploadProgress(e, progressBar) }, false);
111
+ xhr.addEventListener('load', function(e){ self.onPhotoUploadComplete(e, progressBar); }, false);
112
+ xhr.addEventListener('error', function(e){ self.onPhotoUploadFailure(e, progressBar); }, false);
113
+ xhr.addEventListener('abort', function(e){ self.onPhotoUploadCancel(e, progressBar); }, false);
67
114
  xhr.open('POST', form.attr('action'));
68
115
  xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
69
116
  xhr.send(fd);
@@ -71,23 +118,44 @@ Spud.Admin.Photos = new function(){
71
118
  }
72
119
  };
73
120
 
74
- this.photoUploadProgress = function(e){
121
+ /*
122
+ * Upload Progress Monitoring
123
+ -------------------------------- */
124
+
125
+ this.progressBarForUpload = function(fileName){
126
+ return $('\
127
+ <div class="spud_admin_photo_progress" \
128
+ <h6> \
129
+ <span class="spud_admin_photo_progress_filename">'+fileName+'</span>: \
130
+ <span class="spud_admin_photo_progress_status">Uploading</span> \
131
+ </h6> \
132
+ <div class="progress progress-striped active"> \
133
+ <div class="bar" style="width: 0;"></div> \
134
+ </div> \
135
+ </div>');
136
+ };
137
+
138
+ this.onPhotoUploadProgress = function(e, progressBar){
75
139
  var percent = Math.round(e.loaded * 100 / e.total);
76
- console.log('progress: ' + percent + '%');
77
- $('.progress').show();
78
- $('.progress .bar').css({width: percent + '%'});
140
+ progressBar.find('.bar').css({width: percent + '%'});
141
+ if(percent == 100){
142
+ progressBar.find('.progress').addClass('progress-success');
143
+ progressBar.find('.spud_admin_photo_progress_status').text('Processing');
144
+ }
79
145
  };
80
146
 
81
- this.photoUploadComplete = function(e){
147
+ this.onPhotoUploadComplete = function(e, progressBar){
82
148
  // success
83
149
  var photo = $.parseJSON(e.target.response);
84
150
  if(e.target.status == 200){
151
+ progressBar.find('.spud_admin_photo_progress_status').text('Done!');
152
+ progressBar.find('.progress').removeClass('progress-striped active');
85
153
  var element = $('#spud_admin_photo_' + photo.id);
86
154
  if(element.length > 0){
87
155
  element.replaceWith(photo.html);
88
156
  }
89
157
  else{
90
- var target = $('#spud_admin_photos_selected .spud_admin_photo_ui_thumbs, #spud_admin_photos');
158
+ var target = $('#spud_admin_photos_selected, #spud_admin_photos');
91
159
  target.prepend(photo.html).fadeIn(200);
92
160
  }
93
161
  $('#dialog').dialog('close');
@@ -98,61 +166,150 @@ Spud.Admin.Photos = new function(){
98
166
  }
99
167
  };
100
168
 
101
- this.photoUploadFailed = function(e){
102
- console.log('fail!');
103
- console.log(e);
104
- }
169
+ this.onPhotoUploadCancel = function(e, progressBar){
105
170
 
106
- this.photoUploadCanceled = function(e){
107
- console.log('cancel');
108
- };
171
+ };
109
172
 
110
- // need to invert the checkbox state so that it gets properly checked/uncheckd when `selectedPhotoUiThumb` fires
111
- this.invertPhotoUiThumbCheckbox = function(e){
112
- $(this).attr('checked', !$(this).attr('checked'));
173
+ this.onPhotoUploadCancel = function(e, progressBar){
174
+ progressBar.find('.spud_admin_photo_progress_status').text('Done!');
175
+ progressBar.find('.progress').addClass('progress-danger');
113
176
  };
114
177
 
115
- this.selectedPhotoUiThumb = function(e){
116
- var checkbox = $(this).find('input[type=checkbox]');
117
- if(checkbox){
118
- if(checkbox.attr('checked')){
119
- $(this).removeClass('spud_admin_photo_ui_thumb_selected');
120
- checkbox.attr('checked', false);
121
- }
122
- else{
123
- $(this).addClass('spud_admin_photo_ui_thumb_selected');
124
- checkbox.attr('checked', true);
125
- }
126
- }
178
+ /*
179
+ * Add From Photo Library
180
+ ------------------------------- */
181
+
182
+ this.clickedPhotoLibrary = function(e){
183
+ var url = this.href;
184
+ $.ajax({
185
+ url:url,
186
+ success:self.photoLibraryLoaded
187
+ });
188
+ return false;
127
189
  };
128
190
 
129
- this.markSelectedPhotoUiThumbs = function(){
130
- $('.spud_admin_photo_ui_thumb_selectable').each(function(){
131
- var checkbox = $(this).find('input[type=checkbox]');
132
- if(checkbox && checkbox.attr('checked')){
133
- $(this).addClass('spud_admin_photo_ui_thumb_selected');
191
+ this.photoLibraryLoaded = function(html){
192
+ var dialog = $("#dialog");
193
+ if(dialog.length == 0){
194
+ dialog = $('<div id="dialog" style="display:hidden;"></div>').appendTo('body');
195
+ }
196
+ dialog.html(html);
197
+ $('#spud_admin_photos_selected .spud_admin_photo_ui_thumb').each(function(){
198
+ var id = $(this).attr('id');
199
+ var dupe = dialog.find('#'+id);
200
+ if(dupe){
201
+ dupe.remove();
202
+ }
203
+ });
204
+ dialog.dialog({
205
+ width: 660,
206
+ modal: true,
207
+ height: 450,
208
+ title: 'My Photo Library',
209
+ buttons: {
210
+ 'Add Selected': self.addSelectedPhotosFromLibrary,
211
+ 'Delete Selected': self.deleteSelectedPhotosFromLibrary
134
212
  }
135
213
  });
136
214
  };
137
215
 
138
- this.markPhotoAsDeleted = function(photo_id){
139
- var photo = $('#spud_admin_photo_' + photo_id);
140
- photo.fadeOut(200, function(){
141
- photo.remove();
142
- });
216
+ this.addSelectedPhotosFromLibrary = function(e){
217
+ $('#spud_admin_photo_library .spud_admin_photo_ui_thumb_selected')
218
+ .removeClass('spud_admin_photo_ui_thumb_selected')
219
+ .prependTo('#spud_admin_photos_selected')
220
+ .hide()
221
+ .fadeIn(200);
222
+ $(this).dialog('close');
143
223
  };
144
224
 
145
- this.markPhotoAlbumAsDeleted = function(photo_album_id){
146
- var photo_album = $('#spud_admin_photo_album_' + photo_album_id);
147
- photo_album.fadeOut(200, function(){
148
- photo_album.remove();
225
+ this.deleteSelectedPhotosFromLibrary = function(e){
226
+ var ids = $.map($('.spud_admin_photo_ui_thumb_selected'), function(val, i){
227
+ return $(val).attr('rel');
149
228
  });
229
+ $.ajax({
230
+ type: 'POST',
231
+ url: '/spud/admin/photos/mass_destroy',
232
+ data: {spud_photo_ids:ids},
233
+ success: function(data, textStatus, jqXHR){
234
+ $('.spud_admin_photo_ui_thumb_selected').fadeOut(200, function(){
235
+ $(this).remove();
236
+ });
237
+ },
238
+ error: function(jqXHR, textStatus, errorThrown){
239
+ console.log('An error occurred:')
240
+ console.log(arguments);
241
+ }
242
+ })
243
+
150
244
  };
151
245
 
152
- this.markPhotoGalleryAsDeleted = function(photo_gallery_id){
153
- var photo_gallery = $('#spud_admin_photo_gallery_' + photo_gallery_id);
154
- photo_gallery.fadeOut(200, function(){
155
- photo_gallery.remove();
156
- });
246
+ /*
247
+ * Drag & Drop File Upload Queue
248
+ -------------------------------- */
249
+
250
+ this.fileQueue = [];
251
+ this.fileQueueStarted = false;
252
+
253
+ // prevent default browser behavior of opening the dropped file
254
+ this.stopDndPropagation = function(e){
255
+ e.stopPropagation();
256
+ e.preventDefault();
257
+ }
258
+
259
+ // add files to queue. starts queue if not started already
260
+ this.droppedFile = function(e){
261
+ e.stopPropagation();
262
+ e.preventDefault();
263
+ $('#spud_admin_photo_upload_queue').show();
264
+ var files = e.dataTransfer.files;
265
+ var i = 0;
266
+ while(i < files.length){
267
+ self.fileQueue.push(files[i]);
268
+ i++;
269
+ }
270
+ self.updateQueueCountLabel();
271
+ if(!this.fileQueueStarted){
272
+ self.uploadNextPhoto();
273
+ if(self.fileQueue.length > 0){
274
+ self.uploadNextPhoto();
275
+ }
276
+ }
277
+ };
278
+
279
+ this.updateQueueCountLabel = function(){
280
+ $('#spud_admin_photo_upload_queue_label span').text(self.fileQueue.length);
281
+ };
282
+
283
+ this.uploadNextPhoto = function(){
284
+ if(self.fileQueue.length == 0){
285
+ self.fileQueueStarted = false;
286
+ return;
287
+ }
288
+
289
+ // formdata object
290
+ self.fileQueueStarted = true;
291
+ var file = self.fileQueue.pop();
292
+ var fd = new FormData();
293
+ fd.append('spud_photo[photo]', file);
294
+
295
+ // create a progress bar
296
+ var progressBar = self.progressBarForUpload(file.fileName);
297
+ $('#spud_admin_photo_upload_queue_bars').prepend(progressBar);
298
+
299
+ // send formdata as xhr
300
+ var xhr = new XMLHttpRequest();
301
+ xhr.upload.addEventListener('progress', function(e){ self.onPhotoUploadProgress(e, progressBar); }, false);
302
+ xhr.addEventListener('load', function(e){ self.onQueuedPhotoUploadComplete(e, progressBar) }, false);
303
+ xhr.addEventListener('error', function(e){ self.onPhotoUploadFailure(e, progressBar); }, false);
304
+ xhr.addEventListener('abort', function(e){ self.onPhotoUploadCancel(e, progressBar); }, false);
305
+ xhr.open('POST', '/spud/admin/photos');
306
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
307
+ xhr.send(fd);
308
+ };
309
+
310
+ this.onQueuedPhotoUploadComplete = function(e, progressBar){
311
+ self.onPhotoUploadComplete(e, progressBar);
312
+ self.updateQueueCountLabel();
313
+ self.uploadNextPhoto();
157
314
  };
158
315
  };
@@ -12,7 +12,7 @@
12
12
  margin: 15px 0;
13
13
  }
14
14
  .spud_admin_photo_ui_thumbs{
15
- height: 350px;
15
+ height: 300px;
16
16
  padding: 0;
17
17
  border: 1px solid #cacaca;
18
18
  margin: 15px 0;
@@ -50,16 +50,19 @@
50
50
  .spud_admin_photo_ui_thumbs_selectable .spud_admin_photo_ui_thumb{
51
51
  cursor: pointer;
52
52
  }
53
+ .spud_admin_photo_ui_thumbs_selectable .spud_admin_photo_ui_thumb_controls{
54
+ display: none;
55
+ }
53
56
  .spud_admin_photo_ui_thumb_selected{
54
57
  border: 3px solid #006CCC;
55
- margin: -3px 30px 30px -3px;
58
+ margin: 13px;
59
+ }
60
+ .spud_admin_photo_ui_thumb_small.spud_admin_photo_ui_thumb_selected{
61
+ margin: 9px;
56
62
  }
57
63
  .spud_admin_photo_ui_thumbs_sortable .spud_admin_photo_ui_thumb{
58
64
  cursor: move;
59
65
  }
60
- .spud_admin_photo_ui_thumb_selected:nth-child(6n){
61
- margin-right: -3px;
62
- }
63
66
  .spud_admin_photo_ui_thumb h5{
64
67
  font-size: 12px;
65
68
  color: white;
@@ -86,7 +89,7 @@
86
89
  background: rgba(0,0,0,0.5);
87
90
  border-bottom-right-radius: 15px;
88
91
  }
89
- .spud_admin_photos_btn_delete{
92
+ .spud_admin_photos_btn_remove, .spud_admin_photos_btn_delete{
90
93
  background: url('/assets/spud/photos/buttons/x_16x16.png');
91
94
  height: 16px;
92
95
  width: 16px;
@@ -109,4 +112,28 @@
109
112
  position: absolute;
110
113
  top: 0;
111
114
  right: 0;
115
+ }
116
+
117
+ #spud_admin_photo_album_actions{
118
+
119
+ }
120
+
121
+ /* Upload Queue
122
+ ---------------------------- */
123
+ #spud_admin_photo_album_form{
124
+
125
+ }
126
+ .spud_admin_photo_progress{
127
+ margin: 10px 0;
128
+ }
129
+ #spud_admin_photo_upload_queue{
130
+ display: none;
131
+ background: white;
132
+ padding: 15px;
133
+ margin: 0 0 0 10px;
134
+ width: 250px;
135
+ height: 270px;
136
+ float: right;
137
+ border: 1px solid #cacaca;
138
+ overflow-y: scroll;
112
139
  }
@@ -6,10 +6,11 @@ class PhotoAlbumsController < ApplicationController
6
6
  def index
7
7
  if params[:photo_gallery_id]
8
8
  @photo_gallery = SpudPhotoGallery.find_by_url_name(params[:photo_gallery_id])
9
- if @photo_gallery
10
- @photo_albums = @photo_gallery.albums.order('created_at desc')
9
+ if @photo_gallery.blank?
10
+ flash[:error] = 'Photo gallery could not be found!'
11
+ redirect_to photo_galleries_path and return false
11
12
  else
12
- @photo_albums = []
13
+ @photo_albums = @photo_gallery.albums.order('created_at desc')
13
14
  end
14
15
  else
15
16
  @photo_albums = SpudPhotoAlbum.order('created_at desc')
@@ -19,7 +20,16 @@ class PhotoAlbumsController < ApplicationController
19
20
 
20
21
  def show
21
22
  @photo_album = SpudPhotoAlbum.find_by_url_name(params[:id])
22
- respond_with @photo_album
23
+ if @photo_album.blank?
24
+ flash[:error] = "Post not found!"
25
+ if params[:photo_gallery_id]
26
+ redirect_to photo_gallery_photo_albums_path(params[:photo_gallery_id])
27
+ else
28
+ redirect_to photo_albums_path
29
+ end
30
+ else
31
+ respond_with @photo_album
32
+ end
23
33
  end
24
34
 
25
35
  end
@@ -1,6 +1,6 @@
1
1
  class Spud::Admin::PhotoAlbumsController < Spud::Admin::ApplicationController
2
2
 
3
- before_filter :get_album, :only => [:show, :edit, :update, :destroy]
3
+ before_filter :get_album, :only => [:show, :edit, :update, :destroy, :library]
4
4
  respond_to :html, :json, :xml
5
5
  layout 'spud/admin/spud_photos'
6
6
 
@@ -20,7 +20,10 @@ class Spud::Admin::PhotoAlbumsController < Spud::Admin::ApplicationController
20
20
 
21
21
  def create
22
22
  @photo_album = SpudPhotoAlbum.new(params[:spud_photo_album])
23
- flash[:notice] = 'SpudPhotoAlbum created successfully' if @photo_album.save
23
+ if @photo_album.save
24
+ set_photo_order
25
+ @photo_album.spud_photo_albums_photos = photo_albums_photos
26
+ end
24
27
  respond_with @photo_album, :location => spud_admin_photo_albums_path
25
28
  end
26
29
 
@@ -30,7 +33,10 @@ class Spud::Admin::PhotoAlbumsController < Spud::Admin::ApplicationController
30
33
 
31
34
  def update
32
35
  @photo_album.update_attributes(params[:spud_photo_album])
33
- flash[:notice] = 'SpudPhotoAlbum updated successfully' if @photo_album.save
36
+ if @photo_album.save
37
+ set_photo_order
38
+ flash[:notice] = 'SpudPhotoAlbum updated successfully'
39
+ end
34
40
  respond_with @photo_album, :location => spud_admin_photo_albums_path
35
41
  end
36
42
 
@@ -43,4 +49,15 @@ class Spud::Admin::PhotoAlbumsController < Spud::Admin::ApplicationController
43
49
  @photo_album = SpudPhotoAlbum.find(params[:id])
44
50
  end
45
51
 
52
+ private
53
+
54
+ def set_photo_order
55
+ order_ids = params[:spud_photo_album_order].split(',')
56
+ @photo_album.spud_photo_albums_photos.each do |obj|
57
+ logger.debug "##### ID: #{obj.spud_photo_id.to_s}"
58
+ index = order_ids.index(obj.spud_photo_id.to_s)
59
+ obj.update_attribute(:order, index)
60
+ end
61
+ end
62
+
46
63
  end
@@ -1,11 +1,10 @@
1
1
  class Spud::Admin::PhotosController < Spud::Admin::ApplicationController
2
2
 
3
+ include RespondsToParent
4
+
3
5
  before_filter :get_photo, :only => [:show, :edit, :update, :destroy]
4
6
  respond_to :html, :json, :xml, :js
5
- layout 'spud/admin/spud_photos'
6
-
7
- add_breadcrumb 'Photos', :spud_admin_photos_path
8
- belongs_to_spud_app :photos
7
+ layout false
9
8
 
10
9
  def index
11
10
  @photos = SpudPhoto.all
@@ -32,7 +31,9 @@ class Spud::Admin::PhotosController < Spud::Admin::ApplicationController
32
31
  if request.xhr?
33
32
  render json_for_photo(success)
34
33
  else
35
- respond_with @photo, :location => spud_admin_photos_path
34
+ respond_to_parent do
35
+ render 'show.js'
36
+ end
36
37
  end
37
38
  end
38
39
 
@@ -51,10 +52,12 @@ class Spud::Admin::PhotosController < Spud::Admin::ApplicationController
51
52
  if request.xhr?
52
53
  render json_for_photo(success)
53
54
  else
54
- respond_with @photo, :location => spud_admin_photos_path
55
+ respond_to_parent do
56
+ render 'show.js'
57
+ end
55
58
  end
56
59
  end
57
-
60
+
58
61
  def destroy
59
62
  flash[:notice] = 'SpudPhoto deleted successfully' if @photo.destroy
60
63
  respond_with @photo, :location => spud_admin_photos_path
@@ -1,2 +1,7 @@
1
1
  module Spud::Admin::PhotosHelper
2
- end
2
+
3
+ def photo_is_selected
4
+ return (@photo_album && @photo_album.photo_ids.include?(photo.id))
5
+ end
6
+
7
+ end
@@ -1,7 +1,11 @@
1
1
  class SpudPhoto < ActiveRecord::Base
2
- has_and_belongs_to_many :albums,
3
- :class_name => 'SpudPhotoAlbum',
4
- :join_table => 'spud_photo_albums_photos'
2
+
3
+ attr_accessible :title, :caption, :photo
4
+
5
+ has_many :spud_photo_albums_photos
6
+ has_many :albums,
7
+ :through => :spud_photo_albums_photos,
8
+ :source => :spud_photo_album
5
9
 
6
10
  has_attached_file :photo,
7
11
  :styles => lambda { |attachment| attachment.instance.dynamic_styles },
@@ -1,15 +1,22 @@
1
1
  class SpudPhotoAlbum < ActiveRecord::Base
2
- has_and_belongs_to_many :galleries,
3
- :class_name => 'SpudPhotoGallery',
4
- :join_table => 'spud_photo_galleries_albums'
5
- has_and_belongs_to_many :photos,
6
- :class_name => 'SpudPhoto',
7
- :join_table => 'spud_photo_albums_photos',
8
- :order => 'created_at'
2
+
3
+ attr_accessible :title, :url_name, :photos, :photo_ids
4
+
5
+ has_many :spud_photo_albums_photos
6
+ has_many :photos,
7
+ :through => :spud_photo_albums_photos,
8
+ :source => :spud_photo,
9
+ :order => 'spud_photo_albums_photos.order asc'
10
+
11
+ has_many :spud_photo_galleries_albums
12
+ has_many :galleries,
13
+ :through => :spud_photo_galleries_albums,
14
+ :source => :spud_photo_gallery
15
+
9
16
  validates_presence_of :title, :url_name
10
17
  validates_uniqueness_of :title, :url_name
11
18
  before_validation :set_url_name
12
-
19
+ after_save :update_photo_order
13
20
 
14
21
  def top_photo_url(style)
15
22
  unless photos.empty?
@@ -31,4 +38,12 @@ class SpudPhotoAlbum < ActiveRecord::Base
31
38
  self.url_name = self.title.parameterize
32
39
  end
33
40
 
41
+ def update_photo_order
42
+ # order = 0
43
+ # self.photos.each do |p|
44
+ # p.update_attribute(:order, order)
45
+ # order += 1
46
+ # end
47
+ end
48
+
34
49
  end
@@ -0,0 +1,5 @@
1
+ class SpudPhotoAlbumsPhoto < ActiveRecord::Base
2
+ attr_accessible :spud_photo_id, :spud_photo_album_id, :order
3
+ belongs_to :spud_photo
4
+ belongs_to :spud_photo_album
5
+ end
@@ -0,0 +1,5 @@
1
+ class SpudPhotoGalleriesAlbum < ActiveRecord::Base
2
+ attr_accessible :spud_photo_album_id, :spud_photo_gallery_id, :order
3
+ belongs_to :spud_photo_album
4
+ belongs_to :spud_photo_gallery
5
+ end
@@ -1,7 +1,12 @@
1
1
  class SpudPhotoGallery < ActiveRecord::Base
2
- has_and_belongs_to_many :albums,
3
- :class_name => 'SpudPhotoAlbum',
4
- :join_table => 'spud_photo_galleries_albums'
2
+
3
+ attr_accessible :title, :url_name, :albums, :album_ids
4
+
5
+ has_many :spud_photo_galleries_albums
6
+ has_many :albums,
7
+ :through => :spud_photo_galleries_albums,
8
+ :source => :spud_photo_album
9
+
5
10
  validates_presence_of :title, :url_name
6
11
  validates_uniqueness_of :title, :url_name
7
12
  before_validation :set_url_name
@@ -23,7 +28,9 @@ class SpudPhotoGallery < ActiveRecord::Base
23
28
  private
24
29
 
25
30
  def set_url_name
26
- self.url_name = self.title.parameterize
31
+ if self.title
32
+ self.url_name = self.title.parameterize
33
+ end
27
34
  end
28
35
 
29
36
  end
@@ -2,30 +2,30 @@
2
2
 
3
3
  <%= error_messages_for(f.object) %>
4
4
 
5
+ <%= hidden_field_tag :spud_photo_album_order %>
6
+
5
7
  <fieldset>
6
8
  <legend>Photo Album Info</legend>
7
- <div class="control-group">
8
- <%= f.label :title, :class => "control-label" %>
9
- <div class="controls">
10
- <%= f.text_field :title %>
11
- </div>
9
+ <div class="control-group">
10
+ <%= f.label :title, :class => "control-label" %>
11
+ <div class="controls">
12
+ <%= f.text_field :title %>
12
13
  </div>
14
+ </div>
13
15
  </fieldset>
14
16
 
15
17
  <fieldset class="spud_admin_photos_album_fieldset">
16
- <%= link_to "New Photo", new_spud_admin_photo_path, :class => "spud_admin_photo_create ajax btn btn-primary", :title => "New Photo" %>
17
- <legend>Select Photos</legend>
18
- <div id="spud_admin_photos_selected" class="spud_admin_photos_selection_left">
19
- <h4>Selected:</h4>
20
- <div class="spud_admin_photo_ui_thumbs spud_admin_photo_ui_thumbs_sortable">
21
- <%= render :partial => '/spud/admin/photos/photo', :collection => @photo_album.photos %>
22
- </div>
18
+ <legend>Selected Photos</legend>
19
+ <div id="spud_admin_photo_album_actions" class="control-group">
20
+ <%= link_to "Photo Library", spud_admin_photos_path, :class => "btn btn-primary", :id => 'spud_admin_photo_album_action_library' %>
21
+ <%= link_to "Upload Photo", new_spud_admin_photo_path, :class => "ajax btn btn-primary", :title => "New Photo" %>
23
22
  </div>
24
- <div id="spud_admin_photos_available" class="spud_admin_photos_selection_right">
25
- <h4>Available:</h4>
26
- <div class="spud_admin_photo_ui_thumbs spud_admin_photo_ui_thumbs_sortable">
27
- <%= render :partial => '/spud/admin/photos/photo', :collection => @photo_album.photos_available %>
28
- </div>
23
+ <div id="spud_admin_photo_upload_queue">
24
+ <h5 id="spud_admin_photo_upload_queue_label">Queued Uploads: <span>0</span></h5>
25
+ <div id="spud_admin_photo_upload_queue_bars"></div>
26
+ </div>
27
+ <div id="spud_admin_photos_selected" class="control-group spud_admin_photo_ui_thumbs spud_admin_photo_ui_thumbs_sortable">
28
+ <%= render :partial => '/spud/admin/photos/photo', :collection => @photo_album.photos %>
29
29
  </div>
30
30
  </fieldset>
31
31
 
@@ -1,4 +1,4 @@
1
- <%= form_for @photo, :url => (@photo.new_record? ? spud_admin_photos_path : spud_admin_photo_path(@photo)), :html => {:id => 'spud_admin_photo_form', :class => 'form-horizontal'} do |f| %>
1
+ <%= form_for @photo, :url => (@photo.new_record? ? spud_admin_photos_path : spud_admin_photo_path(@photo)), :html => {:id => 'spud_admin_photo_form', :target => 'spud_admin_photo_form_target', :class => 'form-horizontal'} do |f| %>
2
2
 
3
3
  <%=error_messages_for(f.object)%>
4
4
 
@@ -36,13 +36,11 @@
36
36
  <% end %>
37
37
  </fieldset>
38
38
 
39
- <div class="progress progress-striped active" style="display:none;">
40
- <div class="bar" style="width: 0;"></div>
41
- </div>
42
-
43
39
  <div class="form-actions">
44
40
  <%= f.submit "Save Photo", :class=>"btn btn-primary form-btn", "data-loading-text" => "Saving..." %>
45
41
  <!--or <%=link_to "cancel", request.referer, :class => "btn" %> -->
46
42
  </div>
47
43
 
44
+ <iframe id="spud_admin_photo_form_target" name="spud_admin_photo_form_target" style="display:none;"></iframe>
45
+
48
46
  <% end %>
@@ -1,9 +1,13 @@
1
- <div id="spud_admin_photo_<%= photo.id %>" class="spud_admin_photo_ui_thumb spud_admin_photo_ui_thumb_small" style="background-image:url('<%= photo.photo.url(:spud_admin_small) %>')">
1
+ <%= content_tag :div,
2
+ :rel => photo.id,
3
+ :id => "spud_admin_photo_#{photo.id}",
4
+ :style => "background-image:url('#{photo.photo.url(:spud_admin_small)}')",
5
+ :class => "spud_admin_photo_ui_thumb spud_admin_photo_ui_thumb_small" do %>
2
6
  <div style="display:none;">
3
- <%= check_box_tag 'spud_photo_album[photo_ids][]', photo.id, (@photo_album && @photo_album.photo_ids.include?(photo.id)) %>
7
+ <%= hidden_field_tag 'spud_photo_album[photo_ids][]', photo.id %>
4
8
  </div>
5
9
  <div class="spud_admin_photo_ui_thumb_controls">
6
10
  <%= link_to 'Edit', edit_spud_admin_photo_path(photo), :class => 'ajax spud_admin_photos_btn_edit' %>
7
- <%= link_to 'Delete', spud_admin_photo_path(photo), :method => :delete, :remote => true, :class => 'spud_admin_photos_btn_delete', :confirm => 'Are you sure? This will permanently delete the photo from all albums it currently belongs to.' %>
11
+ <%= link_to 'Delete', '#', :class => 'spud_admin_photos_btn_remove' %>
8
12
  </div>
9
- </div>
13
+ <% end %>
@@ -1,12 +1,3 @@
1
- <%= content_for :data_controls do %>
2
- <!--
3
- <%= link_to 'Delete Selected', mass_destroy_spud_admin_photos_path, :class => 'spud_admin_photo_mass_destroy btn btn-danger', :confirm => 'Are you sure?' %>
4
- -->
5
- <%= link_to "New Photo", new_spud_admin_photo_path, :class => "spud_admin_photo_create ajax btn btn-primary", :title => "New Photo" %>
6
- <% end %>
7
-
8
- <%= content_for :detail do %>
9
- <div id="spud_admin_photos" class="spud_admin_photo_ui_thumbs">
10
- <%= render :partial => 'photo', :collection => @photos %>
11
- <div>
12
- <% end %>
1
+ <div id="spud_admin_photo_library" class="spud_admin_photo_ui_thumbs spud_admin_photo_ui_thumbs_selectable">
2
+ <%= render :partial => 'photo', :collection => @photos %>
3
+ <div>
@@ -0,0 +1,5 @@
1
+ <% if @photo.errors.any? %>
2
+ Spud.Admin.Photos.photoLegacyUploadErrors("<%= escape_javascript(render 'form') %>");
3
+ <% else %>
4
+ Spud.Admin.Photos.photoLegacyUploadComplete(<%= @photo.id %>, "<%= escape_javascript(render :partial => 'photo', :locals => {:photo => @photo}) %>");
5
+ <% end %>
data/config/routes.rb CHANGED
@@ -3,6 +3,7 @@ Rails.application.routes.draw do
3
3
  namespace :spud do
4
4
  namespace :admin do
5
5
  resources :photos do
6
+ get 'library', :on => :collection
6
7
  post 'mass_destroy', :on => :collection
7
8
  end
8
9
  resources :photo_albums
@@ -0,0 +1,8 @@
1
+ class UpgradePhotoRelationships < ActiveRecord::Migration
2
+ def change
3
+ add_column :spud_photo_albums_photos, :id, :primary_key
4
+ add_column :spud_photo_albums_photos, :order, :integer, :default => 0
5
+ add_column :spud_photo_galleries_albums, :id, :primary_key
6
+ add_column :spud_photo_galleries_albums, :order, :integer, :default => 0
7
+ end
8
+ end
@@ -0,0 +1,69 @@
1
+ # Copyright (c) 2006 Sean Treadway
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+
23
+ # Module containing the methods useful for child IFRAME to parent window communication
24
+ module RespondsToParent
25
+
26
+ # Executes the response body as JavaScript in the context of the parent window.
27
+ # Use this method of you are posting a form to a hidden IFRAME or if you would like
28
+ # to use IFRAME base RPC.
29
+ def responds_to_parent(&block)
30
+ yield
31
+
32
+ if performed?
33
+ # We're returning HTML instead of JS or XML now
34
+ response.headers['Content-Type'] = 'text/html; charset=UTF-8'
35
+
36
+ # Either pull out a redirect or the request body
37
+ script = if response.headers['Location']
38
+ #TODO: erase_redirect_results is missing in rails 3.0 has to be implemented
39
+ # erase redirect
40
+ "document.location.href = #{location.to_s.inspect}"
41
+ else
42
+ response.body
43
+ end
44
+
45
+ # Escape quotes, linebreaks and slashes, maintaining previously escaped slashes
46
+ # Suggestions for improvement?
47
+ script = (script || '').
48
+ gsub('\\', '\\\\\\').
49
+ gsub(/\r\n|\r|\n/, '\\n').
50
+ gsub(/['"]/, '\\\\\&').
51
+ gsub('</script>','</scr"+"ipt>')
52
+
53
+ # Clear out the previous render to prevent double render
54
+ response.request.env['action_controller.instance'].instance_variable_set(:@_response_body, nil)
55
+
56
+ # Eval in parent scope and replace document location of this frame
57
+ # so back button doesn't replay action on targeted forms
58
+ # loc = document.location to be set after parent is updated for IE
59
+ # with(window.parent) - pull in variables from parent window
60
+ # setTimeout - scope the execution in the windows parent for safari
61
+ # window.eval - legal eval for Opera
62
+ render :text => "<html><body><script type='text/javascript' charset='utf-8'>
63
+ var loc = document.location;
64
+ with(window.parent) { setTimeout(function() { window.eval('#{script}'); if (typeof(loc) !== 'undefined') loc.replace('about:blank'); }, 1) };
65
+ </script></body></html>".html_safe
66
+ end
67
+ end
68
+ alias respond_to_parent responds_to_parent
69
+ end
data/lib/spud_photos.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module Spud
2
2
  module Photos
3
+ require 'responds_to_parent.rb'
3
4
  require 'spud_photos/configuration'
4
5
  require 'spud_photos/engine' if defined?(Rails)
5
6
  end
@@ -15,12 +15,12 @@ module Spud
15
15
  :url => '/spud/admin/photo_albums',
16
16
  :retina => true,
17
17
  :order => 82
18
- },{
19
- :name => 'Photos',
20
- :thumbnail => 'spud/photos/photo_albums_thumb.png',
21
- :url => '/spud/admin/photos',
22
- :retina => true,
23
- :order => 83
18
+ # },{
19
+ # :name => 'Photos',
20
+ # :thumbnail => 'spud/photos/photo_albums_thumb.png',
21
+ # :url => '/spud/admin/photos',
22
+ # :retina => true,
23
+ # :order => 83
24
24
  }]
25
25
  if Spud::Photos.config.galleries_enabled
26
26
  Spud::Core.config.admin_applications += [{
@@ -1,5 +1,5 @@
1
1
  module Spud
2
2
  module Photos
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spud_photos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-31 00:00:00.000000000 Z
12
+ date: 2012-04-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &70105838190880 !ruby/object:Gem::Requirement
16
+ requirement: &70189045047420 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.2.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70105838190880
24
+ version_requirements: *70189045047420
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: spud_core
27
- requirement: &70105838188820 !ruby/object:Gem::Requirement
27
+ requirement: &70189045046840 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -35,10 +35,10 @@ dependencies:
35
35
  version: 0.9.0
36
36
  type: :runtime
37
37
  prerelease: false
38
- version_requirements: *70105838188820
38
+ version_requirements: *70189045046840
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: paperclip
41
- requirement: &70105838186920 !ruby/object:Gem::Requirement
41
+ requirement: &70189045045960 !ruby/object:Gem::Requirement
42
42
  none: false
43
43
  requirements:
44
44
  - - ! '>='
@@ -46,10 +46,10 @@ dependencies:
46
46
  version: '0'
47
47
  type: :runtime
48
48
  prerelease: false
49
- version_requirements: *70105838186920
49
+ version_requirements: *70189045045960
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: mysql2
52
- requirement: &70105838200760 !ruby/object:Gem::Requirement
52
+ requirement: &70189045045160 !ruby/object:Gem::Requirement
53
53
  none: false
54
54
  requirements:
55
55
  - - ! '>='
@@ -57,7 +57,7 @@ dependencies:
57
57
  version: '0'
58
58
  type: :development
59
59
  prerelease: false
60
- version_requirements: *70105838200760
60
+ version_requirements: *70189045045160
61
61
  description: Spud Photos is a feature complete photo management/gallery for the spud
62
62
  engine. Manage multiple galleries, albums, and photos. Use HTML 5 to drag and drop
63
63
  many images at once.
@@ -89,6 +89,8 @@ files:
89
89
  - app/helpers/spud/admin/photos_helper.rb
90
90
  - app/models/spud_photo.rb
91
91
  - app/models/spud_photo_album.rb
92
+ - app/models/spud_photo_albums_photo.rb
93
+ - app/models/spud_photo_galleries_album.rb
92
94
  - app/models/spud_photo_gallery.rb
93
95
  - app/views/layouts/spud/admin/spud_photos.html.erb
94
96
  - app/views/photo_albums/index.html.erb
@@ -111,11 +113,14 @@ files:
111
113
  - app/views/spud/admin/photos/edit.html.erb
112
114
  - app/views/spud/admin/photos/index.html.erb
113
115
  - app/views/spud/admin/photos/new.html.erb
116
+ - app/views/spud/admin/photos/show.js.erb
114
117
  - config/routes.rb
115
118
  - db/migrate/20120228232120_create_spud_photos.rb
116
119
  - db/migrate/20120228232329_create_spud_photo_albums.rb
117
120
  - db/migrate/20120228232344_create_spud_photo_galleries.rb
121
+ - db/migrate/20120405042046_upgrade_photo_relationships.rb
118
122
  - lib/generators/spud/photos/views_generator.rb
123
+ - lib/responds_to_parent.rb
119
124
  - lib/spud_photos/configuration.rb
120
125
  - lib/spud_photos/engine.rb
121
126
  - lib/spud_photos/version.rb
@@ -166,7 +171,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
166
171
  version: '0'
167
172
  segments:
168
173
  - 0
169
- hash: 2069257905353995615
174
+ hash: 4384740055791320075
170
175
  required_rubygems_version: !ruby/object:Gem::Requirement
171
176
  none: false
172
177
  requirements:
@@ -175,10 +180,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
180
  version: '0'
176
181
  segments:
177
182
  - 0
178
- hash: 2069257905353995615
183
+ hash: 4384740055791320075
179
184
  requirements: []
180
185
  rubyforge_project:
181
- rubygems_version: 1.8.15
186
+ rubygems_version: 1.8.10
182
187
  signing_key:
183
188
  specification_version: 3
184
189
  summary: Spud Photos Engine