sinatra-s3 0.98

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/README +23 -0
  2. data/Rakefile +51 -0
  3. data/bin/sinatra-s3 +30 -0
  4. data/db/migrate/001_create_bits.rb +28 -0
  5. data/db/migrate/002_create_users.rb +24 -0
  6. data/db/migrate/003_create_bits_users.rb +16 -0
  7. data/db/migrate/004_create_torrents.rb +22 -0
  8. data/db/migrate/005_create_torrent_peers.rb +26 -0
  9. data/examples/README +9 -0
  10. data/examples/wiki.rb +199 -0
  11. data/examples/wiki.ru +5 -0
  12. data/examples/wikicloth/MIT-LICENSE +20 -0
  13. data/examples/wikicloth/README +81 -0
  14. data/examples/wikicloth/Rakefile +23 -0
  15. data/examples/wikicloth/init.rb +1 -0
  16. data/examples/wikicloth/install.rb +0 -0
  17. data/examples/wikicloth/lib/core_ext.rb +43 -0
  18. data/examples/wikicloth/lib/wiki_buffer/html_element.rb +237 -0
  19. data/examples/wikicloth/lib/wiki_buffer/link.rb +70 -0
  20. data/examples/wikicloth/lib/wiki_buffer/table.rb +159 -0
  21. data/examples/wikicloth/lib/wiki_buffer/var.rb +77 -0
  22. data/examples/wikicloth/lib/wiki_buffer.rb +279 -0
  23. data/examples/wikicloth/lib/wiki_cloth.rb +61 -0
  24. data/examples/wikicloth/lib/wiki_link_handler.rb +138 -0
  25. data/examples/wikicloth/lib/wikicloth.rb +5 -0
  26. data/examples/wikicloth/run_tests.rb +48 -0
  27. data/examples/wikicloth/sample_documents/air_force_one.wiki +170 -0
  28. data/examples/wikicloth/sample_documents/cheatsheet.wiki +205 -0
  29. data/examples/wikicloth/sample_documents/default.css +34 -0
  30. data/examples/wikicloth/sample_documents/elements.wiki +7 -0
  31. data/examples/wikicloth/sample_documents/george_washington.wiki +526 -0
  32. data/examples/wikicloth/sample_documents/images.wiki +15 -0
  33. data/examples/wikicloth/sample_documents/lists.wiki +421 -0
  34. data/examples/wikicloth/sample_documents/pipe_trick.wiki +68 -0
  35. data/examples/wikicloth/sample_documents/random.wiki +55 -0
  36. data/examples/wikicloth/sample_documents/tv.wiki +312 -0
  37. data/examples/wikicloth/sample_documents/wiki.png +0 -0
  38. data/examples/wikicloth/sample_documents/wiki_tables.wiki +410 -0
  39. data/examples/wikicloth/tasks/wikicloth_tasks.rake +0 -0
  40. data/examples/wikicloth/test/test_helper.rb +3 -0
  41. data/examples/wikicloth/test/wiki_cloth_test.rb +8 -0
  42. data/examples/wikicloth/uninstall.rb +0 -0
  43. data/examples/wikicloth/wikicloth-0.1.3.gem +0 -0
  44. data/examples/wikicloth/wikicloth.gemspec +69 -0
  45. data/lib/sinatra-s3/admin.rb +626 -0
  46. data/lib/sinatra-s3/base.rb +526 -0
  47. data/lib/sinatra-s3/errors.rb +51 -0
  48. data/lib/sinatra-s3/ext.rb +20 -0
  49. data/lib/sinatra-s3/helpers/acp.rb +100 -0
  50. data/lib/sinatra-s3/helpers/admin.rb +41 -0
  51. data/lib/sinatra-s3/helpers/tracker.rb +42 -0
  52. data/lib/sinatra-s3/helpers/versioning.rb +27 -0
  53. data/lib/sinatra-s3/helpers.rb +79 -0
  54. data/lib/sinatra-s3/models/bit.rb +180 -0
  55. data/lib/sinatra-s3/models/bucket.rb +81 -0
  56. data/lib/sinatra-s3/models/file_info.rb +3 -0
  57. data/lib/sinatra-s3/models/git_bucket.rb +3 -0
  58. data/lib/sinatra-s3/models/slot.rb +47 -0
  59. data/lib/sinatra-s3/models/torrent.rb +6 -0
  60. data/lib/sinatra-s3/models/torrent_peer.rb +5 -0
  61. data/lib/sinatra-s3/models/user.rb +35 -0
  62. data/lib/sinatra-s3/s3.rb +57 -0
  63. data/lib/sinatra-s3/tasks.rb +62 -0
  64. data/lib/sinatra-s3/tracker.rb +134 -0
  65. data/lib/sinatra-s3.rb +1 -0
  66. data/public/css/control.css +225 -0
  67. data/public/css/wiki.css +47 -0
  68. data/public/images/external-link.gif +0 -0
  69. data/public/js/prototype.js +2539 -0
  70. data/public/js/upload_status.js +117 -0
  71. data/public/test.html +8 -0
  72. data/s3.yml.example +17 -0
  73. data/test/s3api_test.rb +121 -0
  74. data/test/test_helper.rb +25 -0
  75. metadata +156 -0
@@ -0,0 +1,626 @@
1
+ require 'aws/s3'
2
+
3
+ module S3
4
+
5
+ class Admin < Sinatra::Base
6
+
7
+ helpers do
8
+ include S3::Helpers
9
+ include S3::AdminHelpers
10
+ end
11
+
12
+ set :sessions, :on
13
+ enable :inline_templates
14
+
15
+ before do
16
+ ActiveRecord::Base.verify_active_connections!
17
+ end
18
+
19
+ get '/control/?' do
20
+ login_required
21
+ redirect '/control/buckets'
22
+ end
23
+
24
+ get %r{^/control/s/(.*)} do
25
+ open(File.join(PUBLIC_PATH, params[:captures].first))
26
+ end
27
+
28
+ get '/control/login' do
29
+ r :login, "Login"
30
+ end
31
+
32
+ post '/control/login' do
33
+ @user = User.find_by_login params[:login]
34
+ if @user
35
+ if @user.password == hmac_sha1( params[:password], @user.secret )
36
+ session[:user_id] = @user.id
37
+ redirect '/control/buckets'
38
+ else
39
+ @user.errors.add(:password, 'is incorrect')
40
+ end
41
+ else
42
+ @user = User.new
43
+ @user.errors.add(:login, 'not found')
44
+ end
45
+ login_view
46
+ end
47
+
48
+ get '/control/logout' do
49
+ session[:user_id] = nil
50
+ redirect '/control'
51
+ end
52
+
53
+ get '/control/buckets/?' do
54
+ login_required
55
+ load_buckets
56
+ r :buckets, "Buckets"
57
+ end
58
+
59
+ post '/control/buckets/?' do
60
+ login_required
61
+ begin
62
+ Bucket.find_root(params['bucket']['name'])
63
+ load_buckets
64
+ @bucket.errors.add_to_base("A bucket named `#{@input['bucket']['name']}' already exists.")
65
+ rescue NoSuchBucket
66
+ bucket = Bucket.new(params['bucket'])
67
+ redirect '/control/buckets' if bucket.save()
68
+ load_buckets
69
+ @bucket.errors.add_to_base("Invalid bucket name.")
70
+ end
71
+ r :buckets, "Buckets"
72
+ end
73
+
74
+ get '/control/buckets/:bucket/?' do
75
+ login_required
76
+ @bucket = Bucket.find_root(params[:bucket])
77
+ only_can_read @bucket
78
+ @files = Slot.find :all, :conditions => ['deleted = 0 AND parent_id = ?', @bucket.id], :order => 'name'
79
+ r :files, "/#{@bucket.name}"
80
+ end
81
+
82
+ post '/control/buckets/:bucket/?' do
83
+ login_required
84
+ @bucket = Bucket.find_root(params[:bucket])
85
+ only_can_write @bucket
86
+
87
+ if params['upfile'].nil? || params['upfile'].instance_of?(String)
88
+ @files = Slot.find :all, :conditions => ['deleted = 0 AND parent_id = ?', @bucket.id], :order => 'name'
89
+ redirect "/control/buckets/#{params[:bucket]}"
90
+ end
91
+
92
+ tmpf = params['upfile'][:tempfile]
93
+ readlen, md5 = 0, MD5.new
94
+ while part = tmpf.read(BUFSIZE)
95
+ readlen += part.size
96
+ md5 << part
97
+ end
98
+
99
+ fileinfo = FileInfo.new
100
+ fileinfo.mime_type = params['upfile'][:type] || "binary/octet-stream"
101
+ fileinfo.size = readlen
102
+ fileinfo.md5 = md5.hexdigest
103
+ fileinfo.etag = '"' + md5.hexdigest + '"'
104
+
105
+ mdata = {}
106
+ if defined?(EXIFR) && fileinfo.mime_type =~ /jpg|jpeg/
107
+ photo_data = EXIFR::JPEG.new(tmpf.path).to_hash
108
+ photo_data.each_pair do |key,value|
109
+ tmp = key.to_s.gsub(/[^a-z0-9]+/i, '-').downcase.gsub(/-$/,'')
110
+ mdata[tmp] = value.to_s
111
+ end
112
+ end
113
+
114
+ params['fname'] = params['upfile'][:filename] if params['fname'].blank?
115
+ begin
116
+ slot = @bucket.find_slot(params['fname'])
117
+ if slot.versioning_enabled?
118
+ nslot = slot.clone()
119
+ slot.update_attributes(:deleted => true)
120
+ slot = nslot
121
+ end
122
+ fileinfo.path = slot.obj.path
123
+ file_path = File.join(STORAGE_PATH,fileinfo.path)
124
+ slot.update_attributes(:owner_id => @user.id, :meta => mdata, :obj => fileinfo, :size => fileinfo.size)
125
+ FileUtils.mv(tmpf.path, file_path,{ :force => true })
126
+ rescue NoSuchKey
127
+ fileinfo.path = File.join(params[:bucket], rand(10000).to_s(36) + '_' + File.basename(tmpf.path))
128
+ fileinfo.path.succ! while File.exists?(File.join(STORAGE_PATH, fileinfo.path))
129
+ file_path = File.join(STORAGE_PATH,fileinfo.path)
130
+ FileUtils.mkdir_p(File.dirname(file_path))
131
+ FileUtils.mv(tmpf.path, file_path)
132
+ slot = Slot.create(:name => params['fname'], :owner_id => @user.id, :meta => mdata, :obj => fileinfo, :size => fileinfo.size)
133
+ slot.grant(:access => params['facl'].to_i)
134
+ @bucket.add_child(slot)
135
+ end
136
+
137
+ if slot.versioning_enabled?
138
+ begin
139
+ slot.git_repository.add(File.basename(fileinfo.path))
140
+ slot.git_repository.commit("Added #{slot.name} to the Git repository.")
141
+ slot.git_update
142
+ slot.update_attributes(:version => slot.git_object.objectish)
143
+ rescue => err
144
+ puts "[#{Time.now}] GIT: #{err}"
145
+ end
146
+ end
147
+
148
+ redirect "/control/buckets/#{params[:bucket]}"
149
+ end
150
+
151
+ post '/control/buckets/:bucket/versioning/?' do
152
+ login_required
153
+ @bucket = Bucket.find_root(params[:bucket])
154
+ only_can_write @bucket
155
+ @bucket.git_init if defined?(Git)
156
+ redirect "/control/buckets/#{@bucket.name}"
157
+ end
158
+
159
+ get %r{^/control/changes/(.+?)/(.+)$} do
160
+ login_required
161
+ @bucket = Bucket.find_root params[:captures].first
162
+ @file = @bucket.find_slot(params[:captures].last)
163
+ only_owner_of @bucket
164
+ @versions = @bucket.git_repository.log.path(File.basename(@file.obj.path))
165
+ r :changes, "Commit Log", :popup
166
+ end
167
+
168
+ post '/control/delete/:bucket/?' do
169
+ login_required
170
+ @bucket = Bucket.find_root(params[:bucket])
171
+ only_owner_of @bucket
172
+ if Slot.count(:conditions => ['deleted = 0 AND parent_id = ?', @bucket.id]) > 0
173
+ # FIXME: error message, bucket is not empty
174
+ else
175
+ @bucket.remove_from_filesystem()
176
+ @bucket.destroy()
177
+ end
178
+ redirect "/control/buckets"
179
+ end
180
+
181
+ post %r{^/control/delete/(.+?)/(.+)$} do
182
+ login_required
183
+ @bucket = Bucket.find_root(params[:captures].first)
184
+ only_can_write @bucket
185
+ @slot = @bucket.find_slot(params[:captures].last)
186
+
187
+ if @slot.versioning_enabled?
188
+ @slot.git_repository.remove(File.basename(@slot.obj.path))
189
+ @slot.git_repository.commit("Removed #{@slot.name} from the Git repository.")
190
+ @slot.git_update
191
+ end
192
+
193
+ @slot.remove_from_filesystem()
194
+ @slot.destroy()
195
+ redirect "/control/buckets/#{params[:captures].first}"
196
+ end
197
+
198
+ get "/control/profile/?" do
199
+ login_required
200
+ @usero = @user
201
+ r :profile, "Your Profile"
202
+ end
203
+
204
+ post "/control/profile/?" do
205
+ login_required
206
+ @user.update_attributes(params['user'])
207
+ @usero = @user
208
+ r :profile, "Your Profile"
209
+ end
210
+
211
+ get "/control/users/?" do
212
+ login_required
213
+ only_superusers
214
+ @usero = User.new
215
+ @users = User.find :all, :conditions => ['deleted != 1'], :order => 'login'
216
+ r :users, "User List"
217
+ end
218
+
219
+ post "/control/users/?" do
220
+ login_required
221
+ only_superusers
222
+ @usero = User.new params['user'].merge(:activated_at => Time.now)
223
+ if @usero.valid?
224
+ @usero.save()
225
+ redirect "/control/users"
226
+ else
227
+ @users = User.find :all, :conditions => ['deleted != 1'], :order => 'login'
228
+ r :users, "User List"
229
+ end
230
+ end
231
+
232
+ get "/control/users/:login/?" do
233
+ login_required
234
+ only_superusers
235
+ @usero = User.find_by_login params[:login]
236
+ r :profile, @usero.login
237
+ end
238
+
239
+ post "/control/users/:login/?" do
240
+ login_required
241
+ only_superusers
242
+ @usero = User.find_by_login params[:login]
243
+
244
+ # if were not changing passwords remove blank values
245
+ if params['user']['password'].blank? && params['user']['password_confirmation'].blank?
246
+ params['user'].delete('password')
247
+ params['user'].delete('password_confirmation')
248
+ end
249
+
250
+ if @usero.update_attributes(params['user'])
251
+ redirect "/control/users/#{@usero.login}"
252
+ else
253
+ r :profile, @usero.login
254
+ end
255
+ end
256
+
257
+ post "/control/users/delete/:login/?" do
258
+ login_required
259
+ only_superusers
260
+ @usero = User.find_by_login params[:login]
261
+ if @usero.id == @user.id
262
+ # FIXME: notify user they cannot delete themselves
263
+ else
264
+ @usero.destroy
265
+ end
266
+ redirect "/control/users"
267
+ end
268
+
269
+ get %r{^/control/acl/(.+?)/(.+)$} do
270
+ login_required
271
+ @bucket = Bucket.find_root(params[:captures].first)
272
+ only_can_write_acp @bucket
273
+ @slot = @bucket.find_slot(params[:captures].last)
274
+ only_can_write @slot
275
+ r :acl, "Modify File Access", :popup
276
+ end
277
+
278
+ post %r{^/control/acl/(.+?)/(.+)$} do
279
+ login_required
280
+ @bucket = Bucket.find_root(params[:captures].first)
281
+ @slot = @bucket.find_slot(params[:captures].last)
282
+ only_owner_of @slot
283
+ case params[:acl]['type']
284
+ when "email"
285
+ @user = User.find(:first, :conditions => [ 'key = ? OR email = ?', params[:acl]['user_id'], params[:acl]['user_id'] ])
286
+ update_user_access(@slot,@user,"#{Bit.acl_text.invert[params[:acl]['access']]}00".to_i(8)) if @user
287
+ when "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
288
+ @slot.access &= ~(@slot.access.to_s(8)[1,1].to_i*10)
289
+ @slot.access |= (Bit.acl_text.invert[params[:acl]['access']]*10).to_s.to_i(8)
290
+ when "http://acs.amazonaws.com/groups/global/AllUsers"
291
+ @slot.access &= ~@slot.access.to_s(8)[2,1].to_i
292
+ @slot.access |= Bit.acl_text.invert[params[:acl]['access']].to_s.to_i(8)
293
+ end
294
+ @slot.save()
295
+ redirect "/control/acl/#{@bucket.name}/#{@slot.name}"
296
+ end
297
+
298
+ get %r{^/control/meta/(.+?)/(.+)$} do
299
+ login_required
300
+ @bucket = Bucket.find_root(params[:captures].first)
301
+ @slot = @bucket.find_slot(params[:captures].last)
302
+ only_can_write @slot
303
+ r :meta, "Metadata", :popup
304
+ end
305
+
306
+ post %r{^/control/meta/(.+?)/(.+)$} do
307
+ login_required
308
+ @bucket = Bucket.find_root(params[:captures].first)
309
+ @slot = @bucket.find_slot(params[:captures].last)
310
+ only_can_write @slot
311
+
312
+ newm = {}
313
+ params[:m].each do |k,v|
314
+ newm[k] = v unless v.blank?
315
+ end
316
+ if !params[:meta]['key'].blank? && !params[:meta]['value'].blank?
317
+ if params[:meta]['key'] =~ /^[A-Za-z0-9\-]+$/
318
+ newm[params[:meta]['key']] = params[:meta]['value']
319
+ else
320
+ @slot.errors.add(:key, "can only contain letters, numbers and dashes")
321
+ end
322
+ end
323
+
324
+ @slot.update_attributes({ :meta => newm })
325
+ r :meta, "Metadata", :popup
326
+ end
327
+
328
+ end
329
+
330
+ end
331
+
332
+ __END__
333
+
334
+ @@ layout
335
+ %html
336
+ %head
337
+ %title Control Center &raquo; #{@title}
338
+ %script{ :language => "JavaScript", :type => "text/javascript", :src => "/control/s/js/prototype.js" }
339
+ %style{ :type => "text/css" }
340
+ @import '/control/s/css/control.css';
341
+ %body
342
+ %div#page
343
+ - if @user and not @login
344
+ %div.menu
345
+ %ul
346
+ %li
347
+ %a{ :href => "/control/buckets" } buckets
348
+ - if @user.superuser?
349
+ %a{ :href => "/control/users" } users
350
+ %a{ :href => "/control/profile" } profile
351
+ %a{ :href => "/control/logout" } logout
352
+ %div#header
353
+ %h1 Control Center
354
+ %h2 #{@title}
355
+ %div#content
356
+ = yield
357
+
358
+ @@ popup
359
+ %html
360
+ %head
361
+ %title #{@title}
362
+ %style{ :type => "text/css" }
363
+ @import '/control/s/css/control.css';
364
+ %script{ :language => 'javascript', :src => '/control/s/js/prototype.js' }
365
+ %body
366
+ %div#content
367
+ = yield
368
+
369
+ @@ login
370
+ %form.create{ :method => "post" }
371
+ %div.required
372
+ %label{ :for => "login" } User
373
+ %input#login{ :type => "text", :name => "login" }
374
+ %div.required
375
+ %label{ :for => "password" } Password
376
+ %input#password{ :type => "password", :name => "password" }
377
+ %input#loggo{ :type => "submit", :value => "Login", :name => "loggo" }
378
+
379
+ @@ buckets
380
+ - if @buckets.any?
381
+ %table
382
+ %thead
383
+ %tr
384
+ %th Name
385
+ %th Contains
386
+ %th Updated on
387
+ %th Info
388
+ %th Actions
389
+ %tbody
390
+ - @buckets.each do |bucket|
391
+ %tr
392
+ %th
393
+ %a{ :href => "/control/buckets/#{bucket.name}" } #{bucket.name}
394
+ %td #{bucket.total_children rescue 0} files
395
+ %td #{bucket.updated_at}
396
+ %td #{bucket.access_readable + (bucket.versioning_enabled? ? ",versioned" : "")}
397
+ %td
398
+ %a{ :href => "/control/delete/#{bucket.name}", :onClick => S3::POST, :title => "Delete Bucket #{bucket.name}" } Delete
399
+ - else
400
+ %p A sad day. You have no buckets yet.
401
+ %h3 Create a Bucket
402
+ %form.create{ :method => "post" }
403
+ = preserve errors_for(@bucket)
404
+ %input{ :name => "bucket[owner_id]", :type => "hidden", :value => @bucket.owner_id }
405
+ %div.required
406
+ %label{ :for => "bucket[name]" } Bucket Name
407
+ %input{ :name => "bucket[name]", :type => "text", :value => @bucket.name }
408
+ %div.required
409
+ %label{ :for => "bucket[access]" } Permissions
410
+ %select{ :name => "bucket[access]" }
411
+ - S3::CANNED_ACLS.sort.each do |acl,perm|
412
+ - opts = { :value => perm }
413
+ - opts[:selected] = true if perm == @bucket.access
414
+ %option{ opts } #{acl}
415
+ %input#newbucket{ :type => "submit", :value => "Create", :name => "newbucket" }
416
+
417
+ @@ files
418
+ %p Click on a file name to get file details.
419
+ %table
420
+ %caption
421
+ - if defined?(Git)
422
+ %span{ :style => "float:right" }
423
+ - if !@bucket.versioning_enabled?
424
+ %a{ :href => "/control/buckets/#{@bucket.name}/versioning", :onClick => S3::POST } Enable Versioning For This Bucket
425
+ - else
426
+ Versioning Enabled
427
+ %a{ :href => "/control/buckets" } &larr; Buckets
428
+ %thead
429
+ %tr
430
+ %th File
431
+ %th Size
432
+ %th Permission
433
+ %tbody
434
+ - if @files.empty?
435
+ %tr
436
+ %td{ :colspan => "3", :style => "padding:15px;text-align:center" } No Files
437
+ - @files.each do |file|
438
+ %tr
439
+ %td
440
+ %a{ :href => "javascript:///", :onclick => "$('details-#{file.id}').toggle()" } #{file.name}
441
+ %div.details{ :id => "details-#{file.id}", :style => "display:none" }
442
+ - if @bucket.versioning_enabled? && !file.git_object.nil?
443
+ %p Revision: #{file.git_object.objectish}
444
+ - if file.torrent
445
+ %p #{file.torrent.seeders} seeders &bull; #{file.torrent.leechers} leechers &bull; #{file.torrent.total} downloads
446
+ %p Last modified on #{file.updated_at}
447
+ %p
448
+ %a{ :href => signed_url("/#{@bucket.name}/#{file.name}"), :target => "_blank" } Get
449
+ &bull;
450
+ %a{ :href => "/control/acl/#{@bucket.name}/#{file.name}", :onclick => S3::POPUP } Access
451
+ &bull;
452
+ %a{ :href => "/control/meta/#{@bucket.name}/#{file.name}", :onclick => S3::POPUP } Meta
453
+ &bull;
454
+ - if @bucket.versioning_enabled?
455
+ %a{ :href => "/control/changes/#{@bucket.name}/#{file.name}", :onclick => S3::POPUP } Changes
456
+ &bull;
457
+ - if defined?(RubyTorrent)
458
+ %a{ :href => signed_url("/#{@bucket.name}/#{file.name}") + "&torrent", :target => "_blank" } Torrent
459
+ &bull;
460
+ %a{ :href => "/control/delete/#{@bucket.name}/#{file.name}", :onclick => S3::POST, :title => "Delete file #{file.name}" } Delete
461
+ %td #{number_to_human_size(file.size)}
462
+ %td #{file.access_readable}
463
+ %div#results
464
+ %div#progress-bar{ :style => "display:none" }
465
+ %iframe#upload{ :name => "upload", :style => "display:none" }
466
+ - @upid = Time.now.to_f
467
+ - form_options = { :action => "?upload_id=#{@upid}", :id => "upload-form", :method => 'post', :enctype => 'multipart/form-data' }
468
+ - form_options.merge!({ :onsubmit => "UploadProgress.monitor('#{@upid}')", :target => "upload" }) if $UPLOAD_PROGRESS
469
+ %form.create{ form_options }
470
+ %h3 Upload a File
471
+ %div.required
472
+ %input{ :name => "upfile", :type => "file" }
473
+ %div.optional
474
+ %label{ :for => "fname"} File Name
475
+ %input{ :name => "fname", :type => "text" }
476
+ %div.required
477
+ %label{ :for => "facl" } Permissions
478
+ %select{ :name => "facl" }
479
+ - S3::CANNED_ACLS.sort.each do |acl, perm|
480
+ - opts = { :value => perm }
481
+ - opts[:selected] = true if perm == @bucket.access
482
+ %option{ opts } #{acl}
483
+ %input#newfile{ :name => "newfile", :value => "Create", :type => "submit" }
484
+
485
+ @@ users
486
+ %table
487
+ %thead
488
+ %tr
489
+ %th Login
490
+ %th Activated On
491
+ %th Total Storage
492
+ %th Actions
493
+ %tbody
494
+ - @users.each do |user|
495
+ %tr
496
+ %th
497
+ %a{ :href => "/control/users/#{user.login}" } #{user.login}
498
+ %td #{user.activated_at}
499
+ %td #{number_to_human_size(Bit.sum(:size, :conditions => [ 'owner_id = ?', user.id ]))}
500
+ %td
501
+ %a{ :href => "/control/users/delete/#{user.login}", :onclick => S3::POST, :title => "Delete user #{user.login}" } Delete
502
+ %h3 Create a User
503
+ %form.create{ :action => "/control/users", :method => "post" }
504
+ = preserve errors_for(@usero)
505
+ %div.required
506
+ %label{ :for => "user[login]" } Login
507
+ %input.large{ :type => "text", :value => @usero.login, :name => "user[login]" }
508
+ %div.required.inline
509
+ %label{ :for => "user[superuser]" } Is a super-admin?
510
+ %input{ :type => "checkbox", :name => "user[superuser]", :value => @usero.superuser }
511
+ %div.required
512
+ %label{ :for => "user[password]" } Password
513
+ %input.fixed{ :type => "password", :name => "user[email]" }
514
+ %div.required
515
+ %label{ :for => "user[password_confirmation]" } Password again
516
+ %input.fixed{ :type => "password", :name => "user[password_confirmation]" }
517
+ %div.required
518
+ %label{ :for => "user[email]" } Email
519
+ %input{ :type => "text", :value => @usero.email, :name => "user[email]" }
520
+ %div.required
521
+ %label{ :for => "user[key]" } Key (must be unique)
522
+ %input.fixed.long{ :type => "text", :value => (@usero.key || generate_key), :name => "user[key]" }
523
+ %div.required
524
+ %label{ :for => "user[secret]" } Secret
525
+ %input.fixed.long{ :type => "text", :value => (@usero.secret || generate_secret), :name => "user[secret]" }
526
+ %input.newuser{ :type => "submit", :value => "Create", :name => "newuser" }
527
+
528
+ @@ profile
529
+ %form.create{ :method => "post" }
530
+ = preserve errors_for(@usero)
531
+ - if @user.superuser?
532
+ %div.required.inline
533
+ %label{ :for => "user[superuser]" } Is a super-admin?
534
+ %input{ :type => "checkbox", :name => "user[superuser]", :value => @usero.superuser }
535
+ %div.required
536
+ %label{ :for => "user[password]" } Password
537
+ %input.fixed{ :type => "password", :name => "user[email]" }
538
+ %div.required
539
+ %label{ :for => "user[password_confirmation]" } Password again
540
+ %input.fixed{ :type => "password", :name => "user[password_confirmation]" }
541
+ %div.required
542
+ %label{ :for => "user[email]" } Email
543
+ %input{ :type => "text", :value => @usero.email, :name => "user[email]" }
544
+ %div.required
545
+ %label{ :for => "key" } Key
546
+ %h4 #{@usero.key}
547
+ %div.required
548
+ %label{ :for => "secret" } Secret
549
+ %h4 #{@usero.secret}
550
+ %input#saveuser{ :type => "submit", :value => "Save", :name => "saveuser" }
551
+
552
+ @@ meta
553
+ %form.create{ :method => "post", :style => "text-align:left" }
554
+ = preserve errors_for(@slot)
555
+ %table
556
+ %thead
557
+ %tr
558
+ %th Key
559
+ %th Value
560
+ %tbody
561
+ - if @slot.meta.empty?
562
+ %tr
563
+ %td{ :colspan => "2", :style => "padding:8px;text-align:center" } No Metadata for #{@slot.name}
564
+ - else
565
+ - @slot.meta.each do |k,v|
566
+ %tr
567
+ %td #{k}
568
+ %td
569
+ %input{ :name => "m[#{k}]", :type => "text", :value => v, :style => "width:100%" }
570
+ %thead
571
+ %tr
572
+ %th{ :colspan => "2" } New Key
573
+ %tbody
574
+ %tr
575
+ %td
576
+ %input{ :name => "meta[key]", :type => "text", :style => "width:100%" }
577
+ %td
578
+ %input{ :name => "meta[value]", :type => "text", :style => "width:100%" }
579
+ %div{ :style => "text-align:center;margin-top:15px" }
580
+ %input{ :type => "submit", :value => "Update" }
581
+
582
+ @@ changes
583
+ %table
584
+ %thead
585
+ %tr
586
+ %th Commit Log For #{@file.name}
587
+ %tbody
588
+ - @versions.each do |version|
589
+ %tr
590
+ %td
591
+ %div
592
+ %a{ :target => "_blank", :href => signed_url("/#{@bucket.name}/#{@file.name}") + "&version-id=#{version.sha}" } #{version.sha}
593
+ %div On: #{version.date}
594
+ %div By: #{version.author.name} <#{version.author.email}>
595
+
596
+ @@ acl
597
+ %table
598
+ %thead
599
+ %tr
600
+ %th For
601
+ %th Access
602
+ %tbody
603
+ - @slot.acl_list.each_pair do |key,acl|
604
+ %tr
605
+ %td #{acl[:type] == "CanonicalUser" ? "#{acl[:id]} (#{acl[:name]})" : acl[:uri].split("/").last}
606
+ %td #{acl[:access]}
607
+ %div{ :style => "text-align:left;margin-top:10px" }
608
+ %h3 Modify File Access
609
+ %form.create{ :method => "post" }
610
+ %div.required
611
+ %label{ :for => "acl_type" } For
612
+ %select#acl_type{ :name => "acl[type]", :onchange => "$('user_id_box').style.display = this.value == 'email' ? 'block' : 'none';" }
613
+ %option{ :value => "http://acs.amazonaws.com/groups/global/AllUsers" } AllUsers
614
+ %option{ :value => "http://acs.amazonaws.com/groups/global/AuthenticatedUsers" } AuthenticatedUsers
615
+ %option{ :value => "email" } By ID/Email
616
+ %div#user_id_box.required{ :style => "display:none" }
617
+ %label{ :for => "user_id" } User ID/Email
618
+ %input#user_id{ :type => "text", :name => "acl[user_id]" }
619
+ %div.required
620
+ %label{ :for => "user_access" } Access
621
+ %select#user_access{ :name => "acl[access]" }
622
+ %option{ :value => "READ" } READ
623
+ %option{ :value => "READ_ACP" } READ_ACP
624
+ %option{ :value => "WRITE" } WRITE
625
+ %option{ :value => "WRITE_ACP" } WRITE_ACP
626
+ %input{ :type => "submit", :value => "Update" }