browse-everything 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +61 -9
  3. data/.rubocop_todo.yml +2 -15
  4. data/.travis.yml +19 -19
  5. data/CONTRIBUTING.md +6 -6
  6. data/Gemfile +12 -8
  7. data/README.md +30 -0
  8. data/Rakefile +2 -1
  9. data/app/assets/javascripts/browse_everything/behavior.js.coffee +5 -0
  10. data/app/controllers/browse_everything_controller.rb +75 -23
  11. data/app/helpers/browse_everything_helper.rb +2 -8
  12. data/app/helpers/font_awesome_version_helper.rb +9 -8
  13. data/app/services/browse_everything_session.rb +10 -0
  14. data/app/services/browse_everything_session/provider_session.rb +42 -0
  15. data/app/services/browser_factory.rb +25 -0
  16. data/app/views/browse_everything/_files.html.erb +56 -6
  17. data/browse-everything.gemspec +29 -25
  18. data/config/routes.rb +7 -2
  19. data/lib/browse-everything.rb +2 -0
  20. data/lib/browse_everything.rb +45 -12
  21. data/lib/browse_everything/auth/google/credentials.rb +28 -0
  22. data/lib/browse_everything/auth/google/request_parameters.rb +61 -0
  23. data/lib/browse_everything/browser.rb +11 -4
  24. data/lib/browse_everything/driver/authentication_factory.rb +22 -0
  25. data/lib/browse_everything/driver/base.rb +72 -19
  26. data/lib/browse_everything/driver/box.rb +46 -17
  27. data/lib/browse_everything/driver/dropbox.rb +36 -10
  28. data/lib/browse_everything/driver/file_system.rb +14 -26
  29. data/lib/browse_everything/driver/google_drive.rb +187 -54
  30. data/lib/browse_everything/driver/s3.rb +81 -75
  31. data/lib/browse_everything/engine.rb +3 -2
  32. data/lib/browse_everything/file_entry.rb +3 -1
  33. data/lib/browse_everything/retriever.rb +103 -31
  34. data/lib/browse_everything/version.rb +3 -1
  35. data/lib/generators/browse_everything/assets_generator.rb +3 -2
  36. data/lib/generators/browse_everything/config_generator.rb +11 -9
  37. data/lib/generators/browse_everything/install_generator.rb +3 -2
  38. data/lib/generators/browse_everything/templates/browse_everything_providers.yml.example +12 -11
  39. data/spec/controllers/browse_everything_controller_spec.rb +80 -0
  40. data/spec/features/select_files_spec.rb +13 -13
  41. data/spec/features/test_compiling_stylesheets_spec.rb +2 -0
  42. data/spec/fixtures/vcr_cassettes/google_drive.yml +331 -0
  43. data/spec/fixtures/vcr_cassettes/retriever.yml +93 -0
  44. data/spec/helper/browse_everything_controller_helper_spec.rb +21 -7
  45. data/spec/javascripts/jasmine_spec.rb +2 -0
  46. data/spec/javascripts/support/jasmine_helper.rb +1 -0
  47. data/spec/lib/browse_everything/auth/google/credentials_spec.rb +41 -0
  48. data/spec/{unit → lib/browse_everything}/browse_everything_helper_spec.rb +2 -0
  49. data/spec/lib/browse_everything/browser_spec.rb +109 -0
  50. data/spec/{unit → lib/browse_everything/driver}/base_spec.rb +5 -4
  51. data/spec/{unit → lib/browse_everything/driver}/box_spec.rb +20 -5
  52. data/spec/{unit → lib/browse_everything/driver}/dropbox_spec.rb +15 -18
  53. data/spec/{unit → lib/browse_everything/driver}/file_system_spec.rb +32 -26
  54. data/spec/lib/browse_everything/driver/google_drive_spec.rb +171 -0
  55. data/spec/{unit → lib/browse_everything/driver}/s3_spec.rb +38 -21
  56. data/spec/lib/browse_everything/driver_spec.rb +38 -0
  57. data/spec/{unit → lib/browse_everything}/file_entry_spec.rb +4 -1
  58. data/spec/lib/browse_everything/retriever_spec.rb +200 -0
  59. data/spec/lib/browse_everything_spec.rb +67 -0
  60. data/spec/services/browse_everything_session/provider_session_spec.rb +50 -0
  61. data/spec/services/browser_factory_spec.rb +40 -0
  62. data/spec/spec_helper.rb +39 -18
  63. data/spec/support/app/controllers/file_handler_controller.rb +4 -4
  64. data/spec/support/app/views/file_handler/main.html.erb +1 -1
  65. data/spec/support/capybara.rb +17 -0
  66. data/spec/support/rake.rb +3 -1
  67. data/spec/support/wait_for_ajax.rb +14 -0
  68. data/spec/test_app_templates/Gemfile.extra +1 -0
  69. data/spec/test_app_templates/lib/generators/test_app_generator.rb +10 -4
  70. data/spec/views/browse_everything/{_file.html.erb_spec.rb → _files.html.erb_spec.rb} +24 -18
  71. data/tasks/ci.rake +2 -0
  72. metadata +159 -107
  73. data/app/views/browse_everything/_file.html.erb +0 -52
  74. data/app/views/browse_everything/resolve.html.erb +0 -1
  75. data/spec/unit/browser_spec.rb +0 -76
  76. data/spec/unit/retriever_spec.rb +0 -109
@@ -1,12 +1,6 @@
1
- module BrowseEverythingHelper
2
- def array_to_hidden_fields(array, key)
3
- fields = array.to_query(key).split(Rack::Utils::DEFAULT_SEP).collect do |pair|
4
- key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
5
- hidden_field_tag(key, value)
6
- end
7
- fields.join("\n").html_safe
8
- end
1
+ # frozen_string_literal: true
9
2
 
3
+ module BrowseEverythingHelper
10
4
  # Extracted from Rack::Mime 1.5.2 for use with earlier versions of Rack/Rails
11
5
  # @param [String] value
12
6
  # @param [String] matcher
@@ -1,16 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FontAwesomeVersionHelper
4
+
5
+ # Determines if the App uses Font Awesome releases 3.x or 4.x
6
+ # @return [TrueClass, FalseClass]
2
7
  def font_awesome_four?
3
8
  Bundler.environment.specs['font-awesome-rails'].first.version >= Gem::Version.new('4')
4
9
  end
5
10
 
6
- def fa3(if3)
7
- fa3or4(if3, '')
8
- end
9
-
10
- def fa4(if4)
11
- fa3or4('', if4)
12
- end
13
-
11
+ # Selects one of two HTML element classes depending upon the release of Font Awesome used by the App
12
+ # @param if3 [String] the Font Awesome 3 HTML class
13
+ # @param if4 [String] the Font Awesome 4 HTML class
14
+ # @return [String] the Font Awesome HTML class
14
15
  def fa3or4(if3, if4)
15
16
  font_awesome_four? ? if4 : if3
16
17
  end
@@ -0,0 +1,10 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ class BrowseEverythingSession
5
+ class Base
6
+ def initialize(session:)
7
+ @session = session
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Object for handling session cookies containing cached values
4
+ class BrowseEverythingSession
5
+ class ProviderSession < Base
6
+ class_attribute :sessions
7
+ self.sessions = {}
8
+
9
+ def self.for(session:, name:)
10
+ ProviderSession.new(session: session, name: name)
11
+ end
12
+
13
+ def initialize(session:, name:)
14
+ @name = name
15
+ super(session: session)
16
+ end
17
+
18
+ def token=(value)
19
+ @session["#{@name}_token"] = value
20
+ end
21
+
22
+ def token
23
+ @session["#{@name}_token"]
24
+ end
25
+
26
+ def code=(value)
27
+ @session["#{@name}_code"] = value
28
+ end
29
+
30
+ def code
31
+ @session["#{@name}_code"]
32
+ end
33
+
34
+ def data=(value)
35
+ @session["#{@name}_data"] = value
36
+ end
37
+
38
+ def data
39
+ @session["#{@name}_data"]
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ class BrowserFactory
5
+ class << self
6
+ def build(session:, url_options:)
7
+ browser = BrowseEverything::Browser.new(url_options)
8
+ browser.providers.each_value do |provider_handler|
9
+ # The authentication token must be set here
10
+ provider_session = BrowseEverythingSession::ProviderSession.for(session: session, name: provider_handler.key.to_sym)
11
+ provider_handler.token = provider_session.token if provider_session.token
12
+ end
13
+ browser
14
+ end
15
+
16
+ def for(name:, url_options: {})
17
+ browser(url_options: url_options).providers[name]
18
+ end
19
+ end
20
+
21
+ def self.browser(url_options: {})
22
+ BrowseEverything::Browser.new(url_options)
23
+ end
24
+ private_class_method :browser
25
+ end
@@ -8,9 +8,59 @@
8
8
  <th role="columnheader">Modified</th>
9
9
  </tr>
10
10
  </thead>
11
- <% provider.contents(browse_path).each_with_index do |file, index| %>
12
- <% next if file.relative_parent_path? or file.size.nil? %>
13
- <%= render :partial => 'file', :locals => { :file => file, :index => index,
14
- :path => browse_everything_engine.contents_path(provider_name, file.id), :parent => params[:parent] } %>
15
- <% end %>
16
- </table>
11
+ <% provider_contents.each_with_index do |file, index| %>
12
+ <% path = browse_everything_engine.contents_path(provider_name, file.id) %>
13
+ <% parent = params[:parent] %>
14
+ <% if file.container? || provider.config[:max_upload_file_size].blank? %>
15
+ <% disabled = false %>
16
+ <% else %>
17
+ <% max_size = provider.config[:max_upload_file_size].to_i %>
18
+ <% disabled = file.size > max_size %>
19
+ <% end %>
20
+
21
+ <tr role="row"
22
+ tabindex="-1"
23
+ data-ev-location="<%= file.location %>"
24
+ data-tt-id="<%= path %>"
25
+ data-tt-parent-id="<%= parent %>"
26
+ data-tt-branch="<%= file.container? ? 'true' : 'false' %>">
27
+
28
+ <td role="gridcell" title="<%= file.name %>" class="<%=file.container? ? 'ev-container' : 'ev-file'%> ev-file-name">
29
+ <% if disabled %>
30
+ <span title="<%= t('browse_everything.size_disabled', max_size: number_to_human_size(max_size)) %>"
31
+ class="<%=file.container? ? 'folder' : 'file'%>" aria-hidden="true">
32
+ <%= file.name %>
33
+ </span>
34
+ <span class="sr-only"><%= file.container? ? ', folder' : ', file' %> </span>
35
+ <% else %>
36
+ <%= link_to browse_everything_engine.contents_path(provider_name, file.id), class: 'ev-link' do %>
37
+ <span class="<%=file.container? ? 'folder' : 'file'%>" aria-hidden="true"/>
38
+ <%= file.name %>
39
+ <span class="sr-only"><%= file.container? ? ', folder' : ', file' %> </span>
40
+ <% end %>
41
+ <% end %>
42
+ </td>
43
+ <% if file.container? %>
44
+ <td role="gridcell" class="ev-directory-select">
45
+ <%= check_box_tag(:select_all, "0", false, class: "ev-select-all") %>
46
+ </td>
47
+ <% else %>
48
+ <td role="gridcell" class="ev-file-select">
49
+ <%= check_box_tag(file.id.to_s.parameterize, "0", false, class: "ev-select-file") %>
50
+ </td>
51
+ <% end %>
52
+
53
+ <td role="gridcell" class="ev-file-size">
54
+ <%= number_to_human_size(file.size).sub(/Bytes/,'bytes') %>
55
+ </td>
56
+
57
+ <td role="gridcell" class="ev-file-kind">
58
+ <%= file.type %>
59
+ </td>
60
+
61
+ <td role="gridcell" class="ev-file-date">
62
+ <%= file.mtime.strftime('%F %R') %>
63
+ </td>
64
+ </tr>
65
+ <% end %>
66
+ </table>
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'browse_everything/version'
5
6
 
@@ -13,39 +14,42 @@ Gem::Specification.new do |spec|
13
14
  spec.homepage = 'https://github.com/projecthydra/browse-everything'
14
15
  spec.license = 'Apache 2'
15
16
 
16
- spec.files = `git ls-files`.split($/)
17
+ spec.files = `git ls-files -z`.split(/\000/)
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
20
  spec.require_paths = ['lib']
20
21
 
21
- spec.add_dependency 'rails', '>= 3.1'
22
22
  spec.add_dependency 'addressable', '~> 2.5'
23
- spec.add_dependency 'google_drive'
23
+ spec.add_dependency 'aws-sdk-s3'
24
+ spec.add_dependency 'bootstrap-sass', '~> 3.2'
24
25
  spec.add_dependency 'dropbox_api', '>= 0.1.10'
26
+ spec.add_dependency 'font-awesome-rails'
27
+ spec.add_dependency 'google-api-client', '~> 0.21'
28
+ spec.add_dependency 'google_drive', '~> 2.1'
29
+ spec.add_dependency 'typhoeus'
30
+ spec.add_dependency 'rails', '>= 4.2'
25
31
  spec.add_dependency 'ruby-box'
26
32
  spec.add_dependency 'sass-rails'
27
- spec.add_dependency 'bootstrap-sass', '~> 3.2'
28
- spec.add_dependency 'font-awesome-rails'
29
- spec.add_dependency 'google-api-client', '~> 0.9'
30
- spec.add_dependency 'signet'
31
- spec.add_dependency 'httparty'
32
- spec.add_dependency 'aws-sdk-s3'
33
+ spec.add_dependency 'signet', '~> 0.8'
34
+ spec.add_dependency 'thor', '~> 0.20'
35
+
36
+ spec.add_development_dependency 'bundler', '~> 1.3'
37
+ spec.add_development_dependency 'capybara'
38
+ spec.add_development_dependency 'chromedriver-helper'
39
+ spec.add_development_dependency 'coveralls'
40
+ spec.add_development_dependency 'engine_cart', '~> 2.0'
41
+ spec.add_development_dependency 'factory_bot_rails'
42
+ spec.add_development_dependency 'jasmine', '~> 2.3'
43
+ spec.add_development_dependency 'rails-controller-testing'
44
+ spec.add_development_dependency 'rake'
33
45
  spec.add_development_dependency 'rspec', '~> 3.0'
34
- spec.add_development_dependency 'rspec-rails'
35
46
  spec.add_development_dependency 'rspec-its'
36
- spec.add_development_dependency 'rubocop', '~> 0.42.0'
37
- spec.add_development_dependency 'rubocop-rspec', '~> 1.8.0'
47
+ spec.add_development_dependency 'rspec-rails'
48
+ spec.add_development_dependency 'rubocop', '~> 0.53'
49
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.23'
50
+ spec.add_development_dependency 'selenium-webdriver'
38
51
  spec.add_development_dependency 'simplecov'
39
- spec.add_development_dependency 'bundler', '~> 1.3'
40
- spec.add_development_dependency 'pry'
41
- spec.add_development_dependency 'rake'
42
- spec.add_development_dependency 'webmock'
43
- spec.add_development_dependency 'vcr'
44
52
  spec.add_development_dependency 'sqlite3'
45
- spec.add_development_dependency 'factory_girl_rails'
46
- spec.add_development_dependency 'engine_cart', '~> 1.0'
47
- spec.add_development_dependency 'capybara'
48
- spec.add_development_dependency 'jasmine', '~> 2.3'
49
- spec.add_development_dependency 'coveralls'
50
- spec.add_development_dependency 'poltergeist', '~> 1.10'
53
+ spec.add_development_dependency 'vcr'
54
+ spec.add_development_dependency 'webmock'
51
55
  end
data/config/routes.rb CHANGED
@@ -1,6 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  BrowseEverything::Engine.routes.draw do
2
4
  get 'connect', to: 'browse_everything#auth', as: 'connector_response'
3
- match 'resolve', to: 'browse_everything#resolve', as: 'resolver', via: [:get, :post]
4
- match ':provider(/*path)', to: 'browse_everything#show', as: 'contents', via: [:get, :post]
5
+ match 'resolve', to: 'browse_everything#resolve', as: 'resolver', via: %i[get post]
6
+ # The "format: false" argument ensures that directory paths containing period characters can be parsed
7
+ # By default, ""/dir1/dir.somedirectory.2" will be parsed as "/dir1/dir" with the format requested as "somedirectory.2"
8
+ # Please see http://guides.rubyonrails.org/routing.html#route-globbing-and-wildcard-segments
9
+ match ':provider(/*path)', to: 'browse_everything#show', as: 'contents', via: %i[get post], format: false
5
10
  root to: 'browse_everything#index'
6
11
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bootstrap-sass'
2
4
  require 'font-awesome-rails'
3
5
  require 'browse_everything'
@@ -1,13 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails'
2
4
  require 'browse_everything/version'
3
5
  require 'browse_everything/engine'
4
6
  require 'browse_everything/retriever'
5
7
 
6
8
  module BrowseEverything
7
- class InitializationError < RuntimeError; end
8
-
9
9
  autoload :Browser, 'browse_everything/browser'
10
10
  autoload :FileEntry, 'browse_everything/file_entry'
11
+
11
12
  module Driver
12
13
  autoload :Base, 'browse_everything/driver/base'
13
14
  autoload :FileSystem, 'browse_everything/driver/file_system'
@@ -15,28 +16,60 @@ module BrowseEverything
15
16
  autoload :Box, 'browse_everything/driver/box'
16
17
  autoload :GoogleDrive, 'browse_everything/driver/google_drive'
17
18
  autoload :S3, 'browse_everything/driver/s3'
19
+
20
+ # Access the sorter set for the base driver class
21
+ # @return [Proc]
22
+ def sorter
23
+ BrowseEverything::Driver::Base.sorter
24
+ end
25
+
26
+ # Provide a custom sorter for all driver classes
27
+ # @param [Proc] the sorting lambda (or proc)
28
+ def sorter=(sorting_proc)
29
+ BrowseEverything::Driver::Base.sorter = sorting_proc
30
+ end
31
+
32
+ module_function :sorter, :sorter=
18
33
  end
19
34
 
35
+ module Auth
36
+ module Google
37
+ autoload :Credentials, 'browse_everything/auth/google/credentials'
38
+ autoload :RequestParameters, 'browse_everything/auth/google/request_parameters'
39
+ end
40
+ end
41
+
42
+ class InitializationError < RuntimeError; end
43
+ class NotImplementedError < StandardError; end
44
+ class NotAuthorizedError < StandardError; end
45
+
20
46
  class << self
47
+ attr_writer :config
48
+
21
49
  def configure(value)
22
- if value.nil? || value.is_a?(Hash)
23
- @config = value
50
+ return if value.nil?
51
+ if value.is_a?(Hash)
52
+ @config = ActiveSupport::HashWithIndifferentAccess.new value
24
53
  elsif value.is_a?(String)
25
- @config = YAML.load(ERB.new(File.read(value)).result)
26
-
27
- if @config.include? 'drop_box'
28
- warn '[DEPRECATION] `drop_box` is deprecated. Please use `dropbox` instead.'
29
- @config['dropbox'] = @config.delete('drop_box')
30
- end
31
-
54
+ config_file_content = File.read(value)
55
+ config_file_template = ERB.new(config_file_content)
56
+ config_values = YAML.safe_load(config_file_template.result)
57
+ @config = ActiveSupport::HashWithIndifferentAccess.new config_values
58
+ @config.deep_symbolize_keys
32
59
  else
33
60
  raise InitializationError, "Unrecognized configuration: #{value.inspect}"
34
61
  end
62
+
63
+ if @config.include? 'drop_box'
64
+ warn '[DEPRECATION] `drop_box` is deprecated. Please use `dropbox` instead.'
65
+ @config['dropbox'] = @config.delete('drop_box')
66
+ end
35
67
  end
36
68
 
37
69
  def config
38
70
  if @config.nil?
39
- configure(File.join(Rails.root.to_s, 'config', 'browse_everything_providers.yml'))
71
+ config_path = Rails.root.join 'config', 'browse_everything_providers.yml'
72
+ configure config_path.to_s
40
73
  end
41
74
  @config
42
75
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'googleauth'
4
+
5
+ # Object structuring the credentials retrieved for the Google API's
6
+ module BrowseEverything
7
+ module Auth
8
+ module Google
9
+ class Credentials < ::Google::Auth::UserRefreshCredentials
10
+ # Ensures that every call to retrieve an access token does *not* require an HTTP request
11
+ # @see Google::Auth::UserRefreshCredentials#fetch_access_token
12
+ # @param options [Hash] the access token values
13
+ def fetch_access_token(options = {})
14
+ return build_token_hash if access_token
15
+ super(options)
16
+ end
17
+
18
+ private
19
+
20
+ # Structure a hash from existing access token values (usually cached within a Cookie)
21
+ # @return [Hash]
22
+ def build_token_hash
23
+ { 'access_token' => access_token }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Manages request parameters for the request to the Google Drive API
4
+ module BrowseEverything
5
+ module Auth
6
+ module Google
7
+ class RequestParameters < OpenStruct
8
+ # Overrides the constructor for an OpenStruct instance
9
+ # Provides default parameters
10
+ def initialize(params = {})
11
+ @params = default_params.merge(params)
12
+ super(@params)
13
+ end
14
+
15
+ private
16
+
17
+ # The default query parameters for the Google Drive API
18
+ # @return [Hash]
19
+ def default_params
20
+ {
21
+ q: default_query,
22
+ order_by: 'modifiedTime desc,folder,name',
23
+ fields: 'nextPageToken,files(name,id,mimeType,size,modifiedTime,parents,web_content_link)',
24
+ supports_team_drives: true,
25
+ include_team_drive_items: true,
26
+ corpora: 'user,allTeamDrives',
27
+ page_size: 1000
28
+ }
29
+ end
30
+
31
+ def default_query
32
+ field_queries = []
33
+ contraints.each_pair do |field, constraints|
34
+ field_constraint = constraints.join(" and #{field} ")
35
+ field_queries << "#{field} #{field_constraint}"
36
+ end
37
+ field_queries.join(' ')
38
+ end
39
+
40
+ def contraints
41
+ {
42
+ 'mimeType' => [
43
+ '!= \'application/vnd.google-apps.audio\'',
44
+ '!= \'application/vnd.google-apps.document\'',
45
+ '!= \'application/vnd.google-apps.drawing\'',
46
+ '!= \'application/vnd.google-apps.form\'',
47
+ '!= \'application/vnd.google-apps.fusiontable\'',
48
+ '!= \'application/vnd.google-apps.map\'',
49
+ '!= \'application/vnd.google-apps.photo\'',
50
+ '!= \'application/vnd.google-apps.presentation\'',
51
+ '!= \'application/vnd.google-apps.script\'',
52
+ '!= \'application/vnd.google-apps.site\'',
53
+ '!= \'application/vnd.google-apps.spreadsheet\'',
54
+ '!= \'application/vnd.google-apps.video\''
55
+ ]
56
+ }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end