browse-everything 1.0.0.rc2 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +93 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +43 -35
- data/CONTRIBUTING.md +3 -3
- data/Gemfile +3 -6
- data/LICENSE.txt +205 -22
- data/README.md +17 -15
- data/app/assets/javascripts/browse_everything/behavior.js +201 -158
- data/app/assets/javascripts/treetable.webpack.js +687 -0
- data/app/assets/stylesheets/browse_everything.scss +0 -0
- data/app/assets/stylesheets/{_browse_everything_bootstrap3.scss → browse_everything/_browse_everything_bootstrap3.scss} +6 -5
- data/app/assets/stylesheets/{_browse_everything_bootstrap4.scss → browse_everything/_browse_everything_bootstrap4.scss} +0 -0
- data/app/controllers/browse_everything_controller.rb +60 -60
- data/app/helpers/browse_everything_helper.rb +4 -0
- data/app/views/browse_everything/_files.html.erb +4 -2
- data/browse-everything.gemspec +8 -7
- data/lib/browse_everything.rb +2 -1
- data/lib/browse_everything/auth/google/credentials.rb +5 -5
- data/lib/browse_everything/auth/google/request_parameters.rb +38 -38
- data/lib/browse_everything/driver/base.rb +14 -14
- data/lib/browse_everything/driver/box.rb +62 -55
- data/lib/browse_everything/driver/dropbox.rb +37 -21
- data/lib/browse_everything/driver/file_system.rb +30 -18
- data/lib/browse_everything/driver/google_drive.rb +40 -39
- data/lib/browse_everything/driver/s3.rb +61 -45
- data/lib/browse_everything/file_entry.rb +1 -1
- data/lib/browse_everything/retriever.rb +72 -67
- data/lib/browse_everything/version.rb +1 -1
- data/lib/generators/browse_everything/templates/browse_everything_providers.yml.example +1 -0
- data/spec/features/test_compiling_stylesheets_spec.rb +1 -1
- data/spec/lib/browse_everything/browser_spec.rb +5 -3
- data/spec/lib/browse_everything/driver/dropbox_spec.rb +20 -1
- data/spec/lib/browse_everything/driver_spec.rb +43 -3
- data/spec/spec_helper.rb +9 -2
- data/spec/support/capybara.rb +0 -5
- data/spec/test_app_templates/Gemfile.extra +9 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +56 -5
- metadata +72 -39
- data/.travis.yml +0 -33
File without changes
|
@@ -59,9 +59,6 @@
|
|
59
59
|
border-top: none;
|
60
60
|
}
|
61
61
|
|
62
|
-
&.detail {
|
63
|
-
}
|
64
|
-
|
65
62
|
&.list ul {
|
66
63
|
@include content-columns(3);
|
67
64
|
}
|
@@ -97,11 +94,15 @@
|
|
97
94
|
|
98
95
|
.ev-files {
|
99
96
|
height: 50vh;
|
100
|
-
li {
|
97
|
+
li {
|
98
|
+
@include ev-link;
|
99
|
+
}
|
101
100
|
}
|
102
101
|
|
103
102
|
.ev-providers {
|
104
|
-
select {
|
103
|
+
select {
|
104
|
+
width: 30%
|
105
|
+
}
|
105
106
|
}
|
106
107
|
|
107
108
|
.loading {
|
File without changes
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require File.expand_path('../helpers/browse_everything_helper', __dir__)
|
4
4
|
|
5
|
-
class BrowseEverythingController <
|
5
|
+
class BrowseEverythingController < ApplicationController
|
6
6
|
layout 'browse_everything'
|
7
7
|
helper BrowseEverythingHelper
|
8
8
|
|
@@ -61,72 +61,72 @@ class BrowseEverythingController < ActionController::Base
|
|
61
61
|
|
62
62
|
private
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
# Constructs or accesses an existing session manager Object
|
65
|
+
# @return [BrowseEverythingSession::ProviderSession] the session manager
|
66
|
+
def provider_session
|
67
|
+
BrowseEverythingSession::ProviderSession.new(session: session, name: provider_name)
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
70
|
+
# Clears all authentication tokens, codes, and other data from the Rails session
|
71
|
+
def reset_provider_session!
|
72
|
+
return unless @provider_session
|
73
|
+
@provider_session.token = nil
|
74
|
+
@provider_session.code = nil
|
75
|
+
@provider_session.data = nil
|
76
|
+
@provider_session = nil
|
77
|
+
end
|
78
78
|
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
def connector_response_url_options
|
80
|
+
{ protocol: request.protocol, host: request.host, port: request.port }
|
81
|
+
end
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
83
|
+
# Generates the authentication link for a given provider service
|
84
|
+
# @return [String] the authentication link
|
85
|
+
def auth_link
|
86
|
+
@auth_link ||= if provider.present?
|
87
|
+
link, data = provider.auth_link(connector_response_url_options)
|
88
|
+
provider_session.data = data
|
89
|
+
link = "#{link}&state=#{provider.key}" unless link.to_s.include?('state')
|
90
|
+
link
|
91
|
+
end
|
92
|
+
end
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
94
|
+
# Accesses the relative path for browsing from the Rails session
|
95
|
+
# @return [String]
|
96
|
+
def browse_path
|
97
|
+
params[:path] || ''
|
98
|
+
end
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
100
|
+
# Generate the provider name from the Rails session state value
|
101
|
+
# @return [String]
|
102
|
+
def provider_name_from_state
|
103
|
+
params[:state].to_s.split(/\|/).last
|
104
|
+
end
|
105
105
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
106
|
+
# Generates the name of the provider using Rails session values
|
107
|
+
# @return [String]
|
108
|
+
def provider_name
|
109
|
+
params[:provider] || provider_name_from_state || browser.providers.each_key.to_a.first
|
110
|
+
end
|
111
111
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
112
|
+
# Retrieve the Driver for each request
|
113
|
+
# @return [BrowseEverything::Driver::Base]
|
114
|
+
def provider
|
115
|
+
browser.providers[provider_name.to_sym] || browser.first_provider
|
116
|
+
end
|
117
117
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
118
|
+
# Constructs a browser manager Object
|
119
|
+
# Browser state cannot persist between requests to the Controller
|
120
|
+
# Hence, a Browser must be reinstantiated for each request using the state provided in the Rails session
|
121
|
+
# @return [BrowseEverything::Browser]
|
122
|
+
def browser
|
123
|
+
BrowserFactory.build(session: session, url_options: url_options)
|
124
|
+
end
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
126
|
+
helper_method :auth_link
|
127
|
+
helper_method :browser
|
128
|
+
helper_method :browse_path
|
129
|
+
helper_method :provider
|
130
|
+
helper_method :provider_name
|
131
|
+
helper_method :provider_contents
|
132
132
|
end
|
@@ -9,12 +9,14 @@
|
|
9
9
|
</tr>
|
10
10
|
</thead>
|
11
11
|
<% provider_contents.each_with_index do |file, index| %>
|
12
|
+
<% next if file.nil? %>
|
12
13
|
<% path = browse_everything_engine.contents_path(provider_name, file.id) %>
|
13
14
|
<% parent = params[:parent] %>
|
14
15
|
<% if file.container? || provider.config[:max_upload_file_size].blank? %>
|
15
16
|
<% disabled = false %>
|
16
17
|
<% else %>
|
17
18
|
<% max_size = provider.config[:max_upload_file_size].to_i %>
|
19
|
+
<% max_human_size = file_size_to_human_size(max_size) %>
|
18
20
|
<% disabled = file.size > max_size %>
|
19
21
|
<% end %>
|
20
22
|
|
@@ -27,7 +29,7 @@
|
|
27
29
|
|
28
30
|
<td role="gridcell" title="<%= file.name %>" class="<%=file.container? ? 'ev-container' : 'ev-file'%> ev-file-name">
|
29
31
|
<% if disabled %>
|
30
|
-
<span title="<%= t('browse_everything.size_disabled', max_size:
|
32
|
+
<span title="<%= t('browse_everything.size_disabled', max_size: max_human_size) %>"
|
31
33
|
class="<%=file.container? ? 'folder' : 'file'%>" aria-hidden="true">
|
32
34
|
<%= file.name %>
|
33
35
|
</span>
|
@@ -52,7 +54,7 @@
|
|
52
54
|
|
53
55
|
<% if file.size %>
|
54
56
|
<td role="gridcell" class="ev-file-size">
|
55
|
-
<%=
|
57
|
+
<%= file_size_to_human_size(file.size) %>
|
56
58
|
</td>
|
57
59
|
<% else %>
|
58
60
|
<td role="gridcell" class="ev-file-size">Unknown</td>
|
data/browse-everything.gemspec
CHANGED
@@ -23,18 +23,17 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_dependency 'aws-sdk-s3'
|
24
24
|
spec.add_dependency 'dropbox_api', '>= 0.1.10'
|
25
25
|
spec.add_dependency 'google-api-client', '~> 0.23'
|
26
|
-
spec.add_dependency 'google_drive', '
|
27
|
-
spec.add_dependency 'googleauth', '0.6.6'
|
28
|
-
spec.add_dependency 'rails', '>= 4.2'
|
26
|
+
spec.add_dependency 'google_drive', '>= 2.1', "< 4"
|
27
|
+
spec.add_dependency 'googleauth', '>= 0.6.6', '< 1.0'
|
28
|
+
spec.add_dependency 'rails', '>= 4.2', '< 7.0'
|
29
29
|
spec.add_dependency 'ruby-box'
|
30
30
|
spec.add_dependency 'signet', '~> 0.8'
|
31
|
-
spec.add_dependency '
|
31
|
+
spec.add_dependency 'sprockets', '~> 3.7'
|
32
32
|
spec.add_dependency 'typhoeus'
|
33
33
|
|
34
|
-
spec.add_development_dependency 'bixby', '
|
35
|
-
spec.add_development_dependency 'bundler', '
|
34
|
+
spec.add_development_dependency 'bixby', '~> 3.0'
|
35
|
+
spec.add_development_dependency 'bundler', '>= 1.3'
|
36
36
|
spec.add_development_dependency 'capybara'
|
37
|
-
spec.add_development_dependency 'chromedriver-helper', '1.2.0'
|
38
37
|
spec.add_development_dependency 'coveralls'
|
39
38
|
spec.add_development_dependency 'engine_cart', '~> 2.0'
|
40
39
|
spec.add_development_dependency 'factory_bot_rails'
|
@@ -45,9 +44,11 @@ Gem::Specification.new do |spec|
|
|
45
44
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
46
45
|
spec.add_development_dependency 'rspec-its'
|
47
46
|
spec.add_development_dependency 'rspec-rails'
|
47
|
+
spec.add_development_dependency 'rspec_junit_formatter'
|
48
48
|
spec.add_development_dependency 'rubocop-rspec'
|
49
49
|
spec.add_development_dependency 'selenium-webdriver'
|
50
50
|
spec.add_development_dependency 'simplecov'
|
51
51
|
spec.add_development_dependency 'sqlite3'
|
52
|
+
spec.add_development_dependency 'webdrivers'
|
52
53
|
spec.add_development_dependency 'webmock'
|
53
54
|
end
|
data/lib/browse_everything.rb
CHANGED
@@ -59,7 +59,8 @@ module BrowseEverything
|
|
59
59
|
@config = ActiveSupport::HashWithIndifferentAccess.new config_values
|
60
60
|
@config.deep_symbolize_keys
|
61
61
|
rescue Errno::ENOENT
|
62
|
-
|
62
|
+
Rails.logger.warn 'Missing browse_everything_providers.yml configuration file'
|
63
|
+
@config = ActiveSupport::HashWithIndifferentAccess.new({})
|
63
64
|
end
|
64
65
|
else
|
65
66
|
raise InitializationError, "Unrecognized configuration: #{value.inspect}"
|
@@ -17,11 +17,11 @@ module BrowseEverything
|
|
17
17
|
|
18
18
|
private
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -14,47 +14,47 @@ module BrowseEverything
|
|
14
14
|
|
15
15
|
private
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
field_queries.join(' ')
|
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}"
|
38
36
|
end
|
37
|
+
field_queries.join(' ')
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
58
|
end
|
59
59
|
end
|
60
60
|
end
|
@@ -100,21 +100,21 @@ module BrowseEverything
|
|
100
100
|
|
101
101
|
private
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
103
|
+
# Generate the options for the Rails URL generation for API callbacks
|
104
|
+
# remove the script_name parameter from the url_options since that is causing issues
|
105
|
+
# with the route not containing the engine path in rails 4.2.0
|
106
|
+
# @return [Hash]
|
107
|
+
def callback_options
|
108
|
+
options = config.to_hash
|
109
|
+
options.deep_symbolize_keys!
|
110
|
+
options[:url_options].reject { |k, _v| k == :script_name }
|
111
|
+
end
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
# Generate the URL for the API callback
|
114
|
+
# @return [String]
|
115
|
+
def callback
|
116
|
+
connector_response_url(callback_options)
|
117
|
+
end
|
118
118
|
end
|
119
119
|
end
|
120
120
|
end
|
@@ -33,15 +33,18 @@ module BrowseEverything
|
|
33
33
|
raise InitializationError, 'Box driver requires both :client_id and :client_secret argument'
|
34
34
|
end
|
35
35
|
|
36
|
+
# Retrieves the file entry objects for a given path to Box resource
|
36
37
|
# @param [String] id of the file or folder in Box
|
37
|
-
# @return [Array<
|
38
|
+
# @return [Array<BrowseEverything::FileEntry>]
|
38
39
|
def contents(id = '')
|
39
40
|
folder = id.empty? ? box_client.root_folder : box_client.folder_by_id(id)
|
40
|
-
|
41
|
+
values = []
|
41
42
|
|
42
43
|
folder.items(ITEM_LIMIT, 0, %w[name size created_at]).collect do |f|
|
43
|
-
|
44
|
+
values << directory_entry(f)
|
44
45
|
end
|
46
|
+
@entries = values.compact
|
47
|
+
|
45
48
|
@sorter.call(@entries)
|
46
49
|
end
|
47
50
|
|
@@ -74,68 +77,72 @@ module BrowseEverything
|
|
74
77
|
|
75
78
|
private
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
80
|
+
def token_expired?
|
81
|
+
return true if expiration_time.nil?
|
82
|
+
Time.now.to_i > expiration_time
|
83
|
+
end
|
81
84
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
end
|
87
|
-
RubyBox::Client.new(box_session)
|
85
|
+
def box_client
|
86
|
+
if token_expired?
|
87
|
+
session = box_session
|
88
|
+
register_access_token(session.refresh_token(box_refresh_token))
|
88
89
|
end
|
90
|
+
RubyBox::Client.new(box_session)
|
91
|
+
end
|
89
92
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
93
|
+
def session
|
94
|
+
AuthenticationFactory.new(
|
95
|
+
self.class.authentication_klass,
|
96
|
+
client_id: config[:client_id],
|
97
|
+
client_secret: config[:client_secret],
|
98
|
+
access_token: box_token,
|
99
|
+
refresh_token: box_refresh_token
|
100
|
+
)
|
101
|
+
end
|
99
102
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
+
def authenticate
|
104
|
+
session.authenticate
|
105
|
+
end
|
103
106
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
+
def box_session
|
108
|
+
authenticate
|
109
|
+
end
|
107
110
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
111
|
+
# If there is an active session, {@token} will be set by {BrowseEverythingController} using data stored in the
|
112
|
+
# session. However, if there is no prior session, or the token has expired, we reset it here using # a new
|
113
|
+
# access_token received from {#box_session}.
|
114
|
+
#
|
115
|
+
# @param [OAuth2::AccessToken] access_token
|
116
|
+
def register_access_token(access_token)
|
117
|
+
@token = {
|
118
|
+
'token' => access_token.token,
|
119
|
+
'refresh_token' => access_token.refresh_token,
|
120
|
+
'expires_at' => access_token.expires_at
|
121
|
+
}
|
122
|
+
end
|
120
123
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
124
|
+
def box_token
|
125
|
+
return unless @token
|
126
|
+
@token.fetch('token', nil)
|
127
|
+
end
|
125
128
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
129
|
+
def box_refresh_token
|
130
|
+
return unless @token
|
131
|
+
@token.fetch('refresh_token', nil)
|
132
|
+
end
|
130
133
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
134
|
+
def expiration_time
|
135
|
+
return unless @token
|
136
|
+
@token.fetch('expires_at', nil).to_i
|
137
|
+
end
|
135
138
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
+
# Constructs a BrowseEverything::FileEntry object for a Box file
|
140
|
+
# resource
|
141
|
+
# @param file [String] ID to the file resource
|
142
|
+
# @return [BrowseEverything::File]
|
143
|
+
def directory_entry(file)
|
144
|
+
BrowseEverything::FileEntry.new(file.id, "#{key}:#{file.id}", file.name, file.size, file.created_at, file.type == 'folder')
|
145
|
+
end
|
139
146
|
end
|
140
147
|
end
|
141
148
|
end
|