browse-everything 0.6.3 → 0.7.0

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.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NTM5M2NhYWU1YjhlZGRmNjkwNDdmZWI5MGFkMjQ5MTliY2E0M2E4Mg==
5
- data.tar.gz: !binary |-
6
- NTg2NmE1MWZlMjQzZGUwYzAyZjljNWNmZTJlYjlhODdiMGY4NTIyYw==
2
+ SHA1:
3
+ metadata.gz: 6a4e41392cdc521780aedf3a678593444dd23663
4
+ data.tar.gz: a34d71298ffa7a50258c4b7e9f1129c71bfa7b9c
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NTY1MzhjMGVlOGZlOTNhMDlmMzAzOWYwMGI1NWYyMzlmMmU4N2U1MGU5NDdj
10
- ZjQxMWMyYmM4NWZhN2E4NTQyMmVlZDVhOWI5NmNiYjVkNDU3ODhiNjgyZDFk
11
- MmE2ZWFhMmNjMzRmNjIzZjYyZDdkZjUwNDE2MTA3YWVlMTU4MWU=
12
- data.tar.gz: !binary |-
13
- NjAxMDg3M2JmZmRiZTAwMWE4NTc4YTU0MmI0NmQzNTI5OTQ5YzNlZjZhY2Nk
14
- N2MxOTBiZjAzMzljOGVkYjk5NzRkOTY4Njg0YTMzZTI3YzVjMmFkOTcwYmVh
15
- NjhmMmE1NDRjMThjODE5NjhiOGIwYzVmYmZhOTNhZjM0ZGY2MTE=
6
+ metadata.gz: a92791fc15d5ea12d87ee7d33716cc1b5cf7e14507a7127b99bb2d7faf12747db4d652012cfbc15d16182593302e9a833ea767ee235f985d329c3b1c3e03ff63
7
+ data.tar.gz: 4cd5134d5e4204172e18dc5dff19d057b20f69a595c769f3666fdb8b08ad6cdfc63d893d8d13dc3045ad3f6010828ca8c5a1007713f1022ec2587d7cb1f0f435
data/.travis.yml CHANGED
@@ -2,8 +2,9 @@ language: ruby
2
2
  rvm:
3
3
  - "1.9.3"
4
4
  - "2.0.0"
5
+ - "2.1.0"
5
6
  notifications:
6
7
  irc: "irc.freenode.org#projecthydra"
7
8
  env:
8
9
  global:
9
- - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
10
+ - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
data/Gemfile CHANGED
@@ -4,3 +4,9 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'spring', group: :development
7
+
8
+ file = File.expand_path("Gemfile", ENV['ENGINE_CART_DESTINATION'] || ENV['RAILS_ROOT'] || File.expand_path("../spec/internal", __FILE__))
9
+ if File.exists?(file)
10
+ puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
11
+ instance_eval File.read(file)
12
+ end
data/HISTORY.md CHANGED
@@ -1,3 +1,8 @@
1
+ ### 0.7.0 (2014-12-10)
2
+ - Add BrowseEverything::Retriever
3
+ - Accessibility improvements
4
+ - Bug fixes
5
+
1
6
  ### 0.6.3 (2014-08-06)
2
7
  - Treat FontAwesome version issues independently of Bootstrap version issues
3
8
 
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/browse-everything.png)](http://badge.fury.io/rb/browse-everything)
2
- [![Build Status](https://travis-ci.org/projecthydra/browse-everything.png?branch=master)](https://travis-ci.org/projecthydra/browse-everything)
2
+ [![Build Status](https://travis-ci.org/projecthydra-labs/browse-everything.svg?branch=master)](https://travis-ci.org/projecthydra-labs/browse-everything)
3
3
 
4
4
  # BrowseEverything
5
5
 
@@ -12,6 +12,8 @@ The gem uses [OAuth](http://oauth.net/) to connect to a user's account and
12
12
  generate a list of single use urls that your application can then use to
13
13
  download the files.
14
14
 
15
+ **This gem does not depend on hydra-head**
16
+
15
17
  ## Installation
16
18
 
17
19
  Add this line to your application's Gemfile:
@@ -121,7 +123,7 @@ If you initialized browse-everything via JavaScript, the results data passed to
121
123
  }, {
122
124
  "url": "https://dl.dropbox.com/fake/Getting%20Started.pdf",
123
125
  "expires": "2014-03-31T20:37:36.731Z",
124
- "file_name": "Getting+Started.pdf"
126
+ "file_name": "Getting Started.pdf"
125
127
  }
126
128
  ]
127
129
  ```
@@ -138,11 +140,37 @@ If you initialized browse-everything via data-attributes and set the _target_ op
138
140
  "1"=>{
139
141
  "url"=>"https://dl.dropbox.com/fake/Getting%20Started.pdf",
140
142
  "expires"=>"2014-03-31T20:37:36.731Z",
141
- "file_name"=>"Getting+Started.pdf"
143
+ "file_name"=>"Getting Started.pdf"
142
144
  }
143
145
  }
144
146
  ```
145
147
 
148
+ ### Retrieving Files
149
+
150
+ The `BrowseEverything::Retriever` class has two methods, `#retrieve` and `#download`, that
151
+ can be used to retrieve selected content. `#retrieve` streams the file by yielding it, chunk
152
+ by chunk, to a block, while `#download` saves it to a local file.
153
+
154
+ Given the above response data:
155
+
156
+ ```ruby
157
+ retriever = BrowseEverything::Retriever.new
158
+ download_spec = params['selected_files']['1']
159
+
160
+ # Retrieve the file, yielding each chunk to a block
161
+ retriever.retrieve(download_spec) do |chunk, retrieved, total|
162
+ # do something with the `chunk` of data received, and/or
163
+ # display some progress using `retrieved` and `total` bytes.
164
+ end
165
+
166
+ # Download the file. If `target_file` isn't specified, the
167
+ # retriever will create a tempfile and return the name.
168
+ retriever.download(download_spec, target_file) do |filename, retrieved, total|
169
+ # The block is still useful for showing progress, but the
170
+ # first argument is the filename instead of a chunk of data.
171
+ end
172
+ ```
173
+
146
174
  ### Examples
147
175
 
148
176
  See `spec/support/app/views/file_handler/index.html` for an example use case. You can also run `rake app:generate` to
@@ -156,3 +184,7 @@ create a fully-functioning demo app in `spec/internal` (though you will have to
156
184
  3. Commit your changes (`git commit -am 'Add some feature'`)
157
185
  4. Push to the branch (`git push origin my-new-feature`)
158
186
  5. Create new Pull Request
187
+
188
+ ## Help
189
+
190
+ For help with Questioning Authority, contact <hydra-tech@googlegroups.com>.
data/Rakefile CHANGED
@@ -6,5 +6,6 @@ Dir.glob('tasks/*.rake').each { |r| import r }
6
6
  ENV["RAILS_ROOT"] ||= 'spec/internal'
7
7
 
8
8
  require 'rspec/core/rake_task'
9
+ require 'engine_cart/rake_task'
9
10
 
10
- task :default => [:ci]
11
+ task :default => [:ci]
@@ -3,7 +3,8 @@ $ ->
3
3
 
4
4
  initialize = (obj,options) ->
5
5
  if $('div#browse-everything').length == 0
6
- dialog = $('<div id="browse-everything" class="ev-browser modal fade"></div>').hide().appendTo('body')
6
+ dialog = $('<div tabindex="-1" id="browse-everything" class="ev-browser modal fade" aria-live="polite" role="dialog" aria-labelledby="beModalLabel"></div>').hide().appendTo('body')
7
+
7
8
  dialog.modal
8
9
  backdrop: 'static'
9
10
  show: false
@@ -25,7 +26,7 @@ $ ->
25
26
  toHiddenFields = (data) ->
26
27
  fields = $.param(data)
27
28
  .split('&')
28
- .map (t) -> t.split('=',2)
29
+ .map (t) -> t.replace('+',' ','g').split('=',2)
29
30
  elements = $(fields).map () ->
30
31
  $("<input type='hidden'/>")
31
32
  .attr('name',decodeURIComponent(this[0]))
@@ -45,22 +46,29 @@ $ ->
45
46
  onNodeExpand: ->
46
47
  node = this
47
48
  $('body').css('cursor','wait')
48
- $.ajax
49
- async: false # Must be false, otherwise loadBranch happens after showChildren?
50
- url: $('a.ev-link',node.row).attr('href')
51
- data:
52
- parent: node.row.data('tt-id')
53
- accept: dialog.data('context').opts.accept
54
- context: dialog.data('context').opts.context
55
- .done (html) ->
56
- rows = $('tbody tr',$(html))
57
- table.treetable("loadBranch", node, rows)
58
- sizeColumns(table)
59
- indicateSelected()
60
- .always ->
61
- $('body').css('cursor','default')
49
+ $("html").addClass("wait")
50
+ size = $(node.row).find('td.ev-file-size').text().trim()
51
+ start = 1
52
+ increment = 1
53
+ if (size.indexOf("MB") >-1)
54
+ start = 10
55
+ increment = 5
56
+ if (size.indexOf("KB") >-1)
57
+ start = 50
58
+ increment = 10
59
+ setProgress(start)
60
+ progressIntervalID = setInterval (->
61
+ start = start + increment
62
+ if start > 99
63
+ start = 99
64
+ setProgress(start)
65
+ ), 2000
66
+ setTimeout (->
67
+ loadFiles(node, table, progressIntervalID)
68
+ ), 10
69
+ $("#file-list tr:first").focus()
62
70
  sizeColumns(table)
63
-
71
+
64
72
  sizeColumns = (table) ->
65
73
  full_width = $('.ev-files').width()
66
74
  table.width(full_width)
@@ -71,6 +79,32 @@ $ ->
71
79
  set_size '.ev-kind', 0.3
72
80
  set_size '.ev-date', 0.2
73
81
 
82
+ loadFiles = (node, table, progressIntervalID)->
83
+ $.ajax
84
+ async: true # Must be false, otherwise loadBranch happens after showChildren?
85
+ url: $('a.ev-link',node.row).attr('href')
86
+ data:
87
+ parent: node.row.data('tt-id')
88
+ accept: dialog.data('context').opts.accept
89
+ context: dialog.data('context').opts.context
90
+ .done (html) ->
91
+ setProgress('100')
92
+ clearInterval progressIntervalID
93
+ rows = $('tbody tr',$(html))
94
+ table.treetable("loadBranch", node, rows)
95
+ $(node).show()
96
+ sizeColumns(table)
97
+ indicateSelected()
98
+ .always ->
99
+ clearInterval progressIntervalID
100
+ $('body').css('cursor','default')
101
+ $("html").removeClass("wait")
102
+
103
+ setProgress = (done)->
104
+ $('#loading_progress').css('width',done+'%')
105
+ $('#loading_progress').html(done+'% complete')
106
+ $('#loading_progress').attr('aria-valuenow', done)
107
+
74
108
  refreshFiles = ->
75
109
  $('.ev-providers select').change()
76
110
 
@@ -86,6 +120,7 @@ $ ->
86
120
  setTimeout refreshFiles, 500
87
121
  ctx.callbacks.show.fire()
88
122
  dialog.modal('show')
123
+
89
124
  if ctx
90
125
  ctx.callback_proxy
91
126
  else
@@ -125,6 +160,7 @@ $ ->
125
160
  .always ->
126
161
  $('body').css('cursor','default')
127
162
  $('.ev-browser').modal('hide')
163
+ $('#browse-btn').focus()
128
164
 
129
165
  $(document).on 'click', '.ev-files table tr', (event) ->
130
166
  $('a.ev-link',this).click() unless event.target.nodeName == 'A'
@@ -148,9 +184,13 @@ $ ->
148
184
  .done (data) ->
149
185
  $('.ev-files').html(data)
150
186
  indicateSelected();
187
+ $('#provider_auth').focus();
151
188
  tableSetup($('table#file-list'))
152
189
  .fail (xhr,status,error) ->
153
- $('.ev-files').html(xhr.responseText)
190
+ if (xhr.responseText.indexOf("Refresh token has expired")>-1)
191
+ $('.ev-files').html("Your sessison has expired please clear your cookies.")
192
+ else
193
+ $('.ev-files').html(xhr.responseText)
154
194
  .always ->
155
195
  $('body').css('cursor','default')
156
196
 
@@ -1,3 +1,3 @@
1
1
  <h2><%=t('browse_everything.auth_prompt.head', provider: provider.name)%></h2>
2
2
  <p><%=t('browse_everything.auth_prompt.text', provider: provider.name)%></p>
3
- <p><%= link_to t('browse_everything.auth_prompt.button_text', provider: provider.name), auth_link, class:"btn btn-primary ev-auth", target:'blank' %></p>
3
+ <p><%= link_to t('browse_everything.auth_prompt.button_text', provider: provider.name), auth_link, class:"btn btn-primary ev-auth", target:'blank', id:'provider_auth' %></p>
@@ -1,17 +1,19 @@
1
1
  <% unless file.relative_parent_path? %>
2
- <tr data-ev-location="<%= file.location %>" data-tt-id="<%=path%>" data-tt-parent-id="<%=parent%>" data-tt-branch="<%=file.container? ? 'true' : 'false'%>">
3
- <td class="<%=file.container? ? 'ev-container' : 'ev-file'%> ev-file-name">
4
- <span class="<%=file.container? ? 'folder' : 'file'%>">
5
- <%= link_to(file.name, browse_everything_engine.contents_path(provider_name,file.id), {:class=>'ev-link'}) %>
6
- </span>
2
+ <tr role="row" tabindex="-1" data-ev-location="<%= file.location %>" data-tt-id="<%=path%>" data-tt-parent-id="<%=parent%>" data-tt-branch="<%=file.container? ? 'true' : 'false'%>">
3
+ <td role="gridcell" class="<%=file.container? ? 'ev-container' : 'ev-file'%> ev-file-name">
4
+ <%= link_to browse_everything_engine.contents_path(provider_name,file.id), {:class=>'ev-link'} do %>
5
+ <span class="<%=file.container? ? 'folder' : 'file'%>" aria-hidden="true"/>
6
+ <%= file.name %>
7
+ <span class="sr-only"><%= file.container? ? ', folder' : ', file' %> </span>
8
+ <% end %>
7
9
  </td>
8
- <td class="ev-file-size">
10
+ <td role="gridcell" class="ev-file-size">
9
11
  <%= number_to_human_size(file.size).sub(/Bytes/,'bytes') %>
10
12
  </td>
11
- <td class="ev-file-kind">
13
+ <td role="gridcell" class="ev-file-kind">
12
14
  <%= file.type %>
13
15
  </td>
14
- <td class="ev-file-date">
16
+ <td role="gridcell" class="ev-file-date">
15
17
  <%= file.mtime.strftime('%F %R') %>
16
18
  </td>
17
19
  </tr>
@@ -1,11 +1,16 @@
1
1
  <% if provider.present? %>
2
- <table id="file-list">
2
+ <div class="progress" id="loading_progress" aria-live="polite">
3
+ <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 100%;">
4
+ 100% Complete
5
+ </div>
6
+ </div>
7
+ <table id="file-list" role="grid" tabindex="-1" title="Choose files to upload from the table below" aria-live="polite">
3
8
  <thead>
4
- <tr>
5
- <th>Name</th>
6
- <th>Size</th>
7
- <th>Kind</th>
8
- <th>Modified</th>
9
+ <tr role="row" tabindex="-1">
10
+ <th role="columnheader">Name</th>
11
+ <th role="columnheader">Size</th>
12
+ <th role="columnheader">Kind</th>
13
+ <th role="columnheader">Modified</th>
9
14
  </tr>
10
15
  </thead>
11
16
  <% provider.contents(browse_path).each_with_index do |file,index| %>
@@ -1,11 +1,12 @@
1
- <% if bootstrap_three? %><div class="modal-content"><% end %>
1
+ <% if bootstrap_three? %><div class="modal-dialog"><div class="modal-content"><% end %>
2
2
  <div class="modal-header">
3
+ <h4 class="sr-only" id="beModalLabel">Select a provider in the list to browse your files.</h4>
3
4
  <div class="ev-providers">
4
5
  <%= render :partial => 'providers' %>
5
6
  </div>
6
7
  </div>
7
- <div class="modal-body ev-body">
8
- <div class="ev-browser row<%=bs2('-fluid')%>">
8
+ <div class="modal-body ev-body" tabindex="-1">
9
+ <div class="ev-browser row<%=bs2('-fluid')%>" aria-live="polite">
9
10
  <div class="<%=fa3or4('fa3','fa4')%> <%=bs2or3('bs2 span','bs3 col-xs-')%>12 ev-files list">
10
11
  <%= render :partial => 'files' %>
11
12
  </div>
@@ -19,4 +20,4 @@
19
20
  <button class="ev-submit btn btn-primary" data-loading-text="Loading..."><%= t('browse_everything.modal_form.submit')%></button>
20
21
  <% end %>
21
22
  </div>
22
- <% if bootstrap_three? %></div><% end %>
23
+ <% if bootstrap_three? %></div></div><% end %>
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency "bootstrap-sass"
28
28
  spec.add_dependency "font-awesome-rails"
29
29
  spec.add_dependency "google-api-client"
30
+ spec.add_dependency "httparty"
30
31
  spec.add_development_dependency "rspec", "~> 3.0"
31
32
  spec.add_development_dependency "rspec-rails"
32
33
  spec.add_development_dependency "rspec-its"
@@ -38,5 +39,6 @@ Gem::Specification.new do |spec|
38
39
  spec.add_development_dependency "vcr"
39
40
  spec.add_development_dependency "sqlite3"
40
41
  spec.add_development_dependency "factory_girl_rails"
42
+ spec.add_development_dependency "engine_cart"
41
43
 
42
44
  end
@@ -1,6 +1,7 @@
1
1
  require "rails"
2
2
  require "browse_everything/version"
3
3
  require "browse_everything/engine"
4
+ require "browse_everything/retriever"
4
5
 
5
6
  module BrowseEverything
6
7
  class InitializationError < RuntimeError; end
@@ -43,7 +43,7 @@ module BrowseEverything
43
43
  file = box_client.file(path)
44
44
  download_url = file.download_url
45
45
  auth_header = {'Authorization' => "Bearer #{@token}"}
46
- extras = { auth_header: auth_header, expires: 1.hour.from_now, file_name:file.name }
46
+ extras = { auth_header: auth_header, expires: 1.hour.from_now, file_name: file.name, file_size: file.size.to_i }
47
47
  [download_url,extras]
48
48
  end
49
49
 
@@ -120,4 +120,4 @@ module BrowseEverything
120
120
  end
121
121
 
122
122
  end
123
- end
123
+ end
@@ -38,7 +38,7 @@ module BrowseEverything
38
38
  end
39
39
 
40
40
  def link_for(path)
41
- [client.media(path)['url'], { expires: 4.hours.from_now, file_name: File.basename(path) }]
41
+ [client.media(path)['url'], { expires: 4.hours.from_now, file_name: File.basename(path), file_size: client.metadata(path)['bytes'].to_i }]
42
42
  end
43
43
 
44
44
  def details(path)
@@ -50,7 +50,9 @@ module BrowseEverything
50
50
  end
51
51
 
52
52
  def link_for(path)
53
- ["file://#{File.expand_path(path)}", { file_name: File.basename(path) }]
53
+ full_path = File.expand_path(path)
54
+ file_size = File.size(full_path).to_i rescue 0
55
+ ["file://#{full_path}", { file_name: File.basename(path), file_size: file_size }]
54
56
  end
55
57
 
56
58
  def authorized?
@@ -59,4 +61,4 @@ module BrowseEverything
59
61
  end
60
62
 
61
63
  end
62
- end
64
+ end
@@ -61,7 +61,12 @@ module BrowseEverything
61
61
  api_result = oauth_client.execute(api_method: api_method, parameters: {fileId: id})
62
62
  download_url = JSON.parse(api_result.response.body)["downloadUrl"]
63
63
  auth_header = {'Authorization' => "Bearer #{oauth_client.authorization.access_token.to_s}"}
64
- extras = { auth_header: auth_header, expires: 1.hour.from_now, file_name:api_result.data.title }
64
+ extras = {
65
+ auth_header: auth_header,
66
+ expires: 1.hour.from_now,
67
+ file_name: api_result.data.title,
68
+ file_size: api_result.data.fileSize.to_i
69
+ }
65
70
  [download_url, extras]
66
71
  end
67
72
 
@@ -123,4 +128,4 @@ module BrowseEverything
123
128
  end
124
129
 
125
130
  end
126
- end
131
+ end
@@ -47,7 +47,7 @@ module BrowseEverything
47
47
 
48
48
  def link_for(path)
49
49
  response = Skydrive::Client.new(rehydrate_token).get("/#{real_id(path)}/")
50
- [response.download_link, {expires: 1.hour.from_now, file_name: File.basename(path)}]
50
+ [response.download_link, {expires: 1.hour.from_now, file_name: File.basename(path), file_size: response.size.to_i}]
51
51
  end
52
52
 
53
53
 
@@ -0,0 +1,60 @@
1
+ require 'httparty'
2
+ require 'tempfile'
3
+
4
+ module BrowseEverything
5
+ class Retriever
6
+ attr_accessor :chunk_size
7
+
8
+ def initialize
9
+ @chunk_size = 16384
10
+ end
11
+
12
+ def download(spec, target=nil)
13
+ if target.nil?
14
+ ext = File.extname(spec['file_name'])
15
+ base = File.basename(spec['file_name'],ext)
16
+ target = Dir::Tmpname.create([base,ext]) {}
17
+ end
18
+
19
+ File.open(target, 'wb') do |output|
20
+ self.retrieve(spec) do |chunk, retrieved, total|
21
+ output.write(chunk)
22
+ yield(target, retrieved, total) if block_given?
23
+ end
24
+ end
25
+ return target
26
+ end
27
+
28
+ def retrieve(spec, &block)
29
+ if spec.has_key?('expires') and Time.parse(spec['expires']) < Time.now
30
+ raise ArugumentError, "Download spec expired at #{spec['expires']}"
31
+ end
32
+
33
+ url = URI.parse(spec['url'])
34
+ retrieved = 0
35
+ case url.scheme
36
+ when 'file'
37
+ File.open(url.path,'rb') do |f|
38
+ while not f.eof?
39
+ chunk = f.read(chunk_size)
40
+ retrieved += chunk.length
41
+ yield(chunk, retrieved, spec['file_size'].to_i)
42
+ end
43
+ end
44
+ when /https?/
45
+ headers = spec['auth_header'] || {}
46
+ headers.each_pair do |k,v|
47
+ headers[k] = v.gsub(/\+/,' ')
48
+ end
49
+
50
+ HTTParty.get(url.to_s, headers: headers) do |chunk|
51
+ retrieved += chunk.length
52
+ yield(chunk, retrieved, spec['file_size'].to_i)
53
+ end
54
+ else
55
+ raise URI::BadURIError, "Unknown URI scheme: #{uri.scheme}"
56
+ end
57
+ end
58
+
59
+ end
60
+ end