browse-everything 0.12.0 → 0.13.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.
@@ -3,39 +3,45 @@ require 'dropbox_sdk'
3
3
  module BrowseEverything
4
4
  module Driver
5
5
  class Dropbox < Base
6
+ CONFIG_KEYS = [:app_key, :app_secret].freeze
7
+
6
8
  def icon
7
9
  'dropbox'
8
10
  end
9
11
 
10
12
  def validate_config
11
- unless [:app_key, :app_secret].all? { |key| config[key].present? }
12
- raise BrowseEverything::InitializationError, 'Dropbox driver requires :app_key and :app_secret'
13
- end
13
+ return if CONFIG_KEYS.all? { |key| config[key].present? }
14
+ raise BrowseEverything::InitializationError, "Dropbox driver requires #{CONFIG_KEYS.inspect}"
14
15
  end
15
16
 
17
+ # @return [Array<BrowseEverything::FileEntry>]
16
18
  def contents(path = '')
17
- path.sub!(/^[\/.]+/, '')
18
- result = []
19
- unless path.empty?
20
- result << BrowseEverything::FileEntry.new(
21
- Pathname(path).join('..'),
22
- '', '..', 0, Time.now, true
23
- )
24
- end
25
- result += client.metadata(path)['contents'].collect do |info|
26
- path = info['path']
27
- BrowseEverything::FileEntry.new(
28
- path,
29
- [key, path].join(':'),
30
- File.basename(path),
31
- info['bytes'],
32
- Time.parse(info['modified']),
33
- info['is_dir']
34
- )
35
- end
19
+ path.sub!(%r{ /^[\/.]+/}, '')
20
+ result = add_directory_entry(path)
21
+ result += client.metadata(path)['contents'].collect { |info| make_file_entry(info) }
36
22
  result
37
23
  end
38
24
 
25
+ def add_directory_entry(path)
26
+ return [] if path.empty?
27
+ [BrowseEverything::FileEntry.new(
28
+ Pathname(path).join('..'),
29
+ '', '..', 0, Time.zone.now, true
30
+ )]
31
+ end
32
+
33
+ def make_file_entry(info)
34
+ path = info['path']
35
+ BrowseEverything::FileEntry.new(
36
+ path,
37
+ [key, path].join(':'),
38
+ File.basename(path),
39
+ info['bytes'],
40
+ Time.zone.parse(info['modified']),
41
+ info['is_dir']
42
+ )
43
+ end
44
+
39
45
  def link_for(path)
40
46
  [client.media(path)['url'], { expires: 4.hours.from_now, file_name: File.basename(path), file_size: client.metadata(path)['bytes'].to_i }]
41
47
  end
@@ -60,14 +66,14 @@ module BrowseEverything
60
66
 
61
67
  private
62
68
 
63
- def auth_flow
64
- @csrf ||= {}
65
- DropboxOAuth2Flow.new(config[:app_key], config[:app_secret], callback.to_s, @csrf, 'token')
66
- end
69
+ def auth_flow
70
+ @csrf ||= {}
71
+ DropboxOAuth2Flow.new(config[:app_key], config[:app_secret], callback.to_s, @csrf, 'token')
72
+ end
67
73
 
68
- def client
69
- DropboxClient.new(token)
70
- end
74
+ def client
75
+ DropboxClient.new(token)
76
+ end
71
77
  end
72
78
  end
73
79
  end
@@ -6,54 +6,74 @@ module BrowseEverything
6
6
  end
7
7
 
8
8
  def validate_config
9
- unless config[:home]
10
- raise BrowseEverything::InitializationError, 'FileSystem driver requires a :home argument'
11
- end
9
+ return if config[:home].present?
10
+ raise BrowseEverything::InitializationError, 'FileSystem driver requires a :home argument'
12
11
  end
13
12
 
14
13
  def contents(path = '')
15
14
  relative_path = path.sub(%r{^[\/.]+}, '')
16
15
  real_path = File.join(config[:home], relative_path)
17
- result = []
18
- if File.directory?(real_path)
19
- if relative_path.present?
20
- result << details(File.expand_path('..', real_path), '..')
21
- end
22
- result += Dir[File.join(real_path, '*')].collect { |f| details(f) }
23
- elsif File.exist?(real_path)
24
- result += [details(real_path)]
25
- end
26
- result.sort do |a, b|
27
- if b.container?
28
- a.container? ? a.name.downcase <=> b.name.downcase : 1
29
- else
30
- a.container? ? -1 : a.name.downcase <=> b.name.downcase
31
- end
32
- end
16
+ entries = if File.directory?(real_path)
17
+ make_directory_entry(relative_path, real_path)
18
+ else
19
+ [details(real_path)]
20
+ end
21
+
22
+ sort_entries(entries)
33
23
  end
34
24
 
35
- def details(path, display = nil)
25
+ def link_for(path)
26
+ full_path = File.expand_path(path)
27
+ file_size = file_size(full_path)
28
+ ["file://#{full_path}", { file_name: File.basename(path), file_size: file_size }]
29
+ end
30
+
31
+ def authorized?
32
+ true
33
+ end
34
+
35
+ def details(path, display = File.basename(path))
36
36
  return nil unless File.exist?(path)
37
37
  info = File::Stat.new(path)
38
38
  BrowseEverything::FileEntry.new(
39
- Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(config[:home])),
39
+ make_pathname(path),
40
40
  [key, path].join(':'),
41
- display || File.basename(path),
41
+ display,
42
42
  info.size,
43
43
  info.mtime,
44
44
  info.directory?
45
45
  )
46
46
  end
47
47
 
48
- def link_for(path)
49
- full_path = File.expand_path(path)
50
- file_size = File.size(full_path).to_i rescue 0
51
- ["file://#{full_path}", { file_name: File.basename(path), file_size: file_size }]
52
- end
48
+ private
53
49
 
54
- def authorized?
55
- true
56
- end
50
+ def make_directory_entry(relative_path, real_path)
51
+ entries = []
52
+ if relative_path.present?
53
+ entries << details(File.expand_path('..', real_path), '..')
54
+ end
55
+ entries + Dir[File.join(real_path, '*')].collect { |f| details(f) }
56
+ end
57
+
58
+ def sort_entries(entries)
59
+ entries.sort do |a, b|
60
+ if b.container?
61
+ a.container? ? a.name.downcase <=> b.name.downcase : 1
62
+ else
63
+ a.container? ? -1 : a.name.downcase <=> b.name.downcase
64
+ end
65
+ end
66
+ end
67
+
68
+ def make_pathname(path)
69
+ Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(config[:home]))
70
+ end
71
+
72
+ def file_size(path)
73
+ File.size(path).to_i
74
+ rescue
75
+ 0
76
+ end
57
77
  end
58
78
  end
59
79
  end
@@ -85,22 +85,22 @@ module BrowseEverything
85
85
 
86
86
  private
87
87
 
88
- def authorization
89
- return @auth_client unless @auth_client.nil?
90
- return nil unless token.present?
91
- auth_client.update_token!(token)
92
- self.token = auth_client.fetch_access_token! if auth_client.expired?
93
- auth_client
94
- end
88
+ def authorization
89
+ return @auth_client unless @auth_client.nil?
90
+ return nil unless token.present?
91
+ auth_client.update_token!(token)
92
+ self.token = auth_client.fetch_access_token! if auth_client.expired?
93
+ auth_client
94
+ end
95
95
 
96
- def auth_client
97
- @auth_client ||= Signet::OAuth2::Client.new token_credential_uri: 'https://www.googleapis.com/oauth2/v3/token',
98
- authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
99
- scope: 'https://www.googleapis.com/auth/drive',
100
- client_id: config[:client_id],
101
- client_secret: config[:client_secret],
102
- redirect_uri: callback
103
- end
96
+ def auth_client
97
+ @auth_client ||= Signet::OAuth2::Client.new token_credential_uri: 'https://www.googleapis.com/oauth2/v3/token',
98
+ authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
99
+ scope: 'https://www.googleapis.com/auth/drive',
100
+ client_id: config[:client_id],
101
+ client_secret: config[:client_secret],
102
+ redirect_uri: callback
103
+ end
104
104
  end
105
105
  end
106
106
  end
@@ -3,10 +3,17 @@ require 'aws-sdk'
3
3
  module BrowseEverything
4
4
  module Driver
5
5
  class S3 < Base
6
- DEFAULTS = { signed_url: true, region: 'us-east-1' }.freeze
7
- CONFIG_KEYS = [:app_key, :app_secret, :bucket].freeze
6
+ DEFAULTS = { response_type: :signed_url }.freeze
7
+ RESPONSE_TYPES = [:signed_url, :public_url, :s3_uri].freeze
8
+ CONFIG_KEYS = [:bucket].freeze
9
+
10
+ attr_reader :entries
8
11
 
9
12
  def initialize(config, *args)
13
+ if config.key?(:signed_url) && config.delete(:signed_url) == false
14
+ warn '[DEPRECATION] Amazon S3 driver: `:signed_url` is deprecated. Please use `:response_type` instead.'
15
+ config[:response_type] = :public_url
16
+ end
10
17
  config = DEFAULTS.merge(config)
11
18
  super
12
19
  end
@@ -16,27 +23,48 @@ module BrowseEverything
16
23
  end
17
24
 
18
25
  def validate_config
26
+ if config.values_at(:app_key, :app_secret).compact.length == 1
27
+ raise BrowseEverything::InitializationError, 'Amazon S3 driver: If either :app_key or :app_secret is provided, both must be.'
28
+ end
29
+ unless RESPONSE_TYPES.include?(config[:response_type].to_sym)
30
+ raise BrowseEverything::InitializationError, "Amazon S3 driver: Valid response types: #{RESPONSE_TYPES.join(',')}"
31
+ end
19
32
  return if CONFIG_KEYS.all? { |key| config[key].present? }
20
33
  raise BrowseEverything::InitializationError, "Amazon S3 driver requires #{CONFIG_KEYS.join(',')}"
21
34
  end
22
35
 
36
+ # @return [Array<BrowseEverything::FileEntry>]
37
+ # Appends / to the path before querying S3
23
38
  def contents(path = '')
24
39
  path = File.join(path, '') unless path.empty?
25
- result = []
26
- listing = client.list_objects(bucket: config[:bucket], delimiter: '/', prefix: path)
27
- unless path.empty?
28
- result << BrowseEverything::FileEntry.new(
29
- Pathname(path).join('..'),
30
- '', '..', 0, Time.current, true
31
- )
32
- end
40
+ init_entries(path)
41
+ generate_listing(path)
42
+ sort_entries
43
+ end
44
+
45
+ def generate_listing(path)
46
+ listing = client.list_objects(bucket: config[:bucket], delimiter: '/', prefix: full_path(path))
47
+ add_directories(listing)
48
+ add_files(listing, path)
49
+ end
50
+
51
+ def add_directories(listing)
33
52
  listing.common_prefixes.each do |prefix|
34
- result << entry_for(prefix.prefix, 0, Time.current, true)
53
+ entries << entry_for(from_base(prefix.prefix), 0, Time.current, true)
35
54
  end
36
- listing.contents.reject { |entry| entry.key == path }.each do |entry|
37
- result << entry_for(entry.key, entry.size, entry.last_modified, false)
55
+ end
56
+
57
+ def add_files(listing, path)
58
+ listing.contents.each do |entry|
59
+ key = from_base(entry.key)
60
+ unless strip(key) == strip(path)
61
+ entries << entry_for(key, entry.size, entry.last_modified, false)
62
+ end
38
63
  end
39
- result.sort do |a, b|
64
+ end
65
+
66
+ def sort_entries
67
+ entries.sort do |a, b|
40
68
  if b.container?
41
69
  a.container? ? a.name.downcase <=> b.name.downcase : 1
42
70
  else
@@ -45,28 +73,34 @@ module BrowseEverything
45
73
  end
46
74
  end
47
75
 
76
+ def init_entries(path)
77
+ @entries = if path.empty?
78
+ []
79
+ else
80
+ [BrowseEverything::FileEntry.new(Pathname(path).join('..').to_s, '', '..',
81
+ 0, Time.current, true)]
82
+ end
83
+ end
84
+
48
85
  def entry_for(name, size, date, dir)
49
86
  BrowseEverything::FileEntry.new(name, [key, name].join(':'), File.basename(name), size, date, dir)
50
87
  end
51
88
 
52
89
  def details(path)
53
- entry = client.head_object(path)
90
+ entry = client.head_object(full_path(path))
54
91
  BrowseEverything::FileEntry.new(
55
- entry.key,
56
- [key, entry.key].join(':'),
57
- File.basename(entry.key),
58
- entry.size,
59
- entry.last_modified,
60
- false
92
+ entry.key, [key, entry.key].join(':'),
93
+ File.basename(entry.key), entry.size,
94
+ entry.last_modified, false
61
95
  )
62
96
  end
63
97
 
64
98
  def link_for(path)
65
- obj = bucket.object(path)
66
- if config[:signed_url]
67
- obj.presigned_url(:get, expires_in: 14400)
68
- else
69
- obj.public_url
99
+ obj = bucket.object(full_path(path))
100
+ case config[:response_type].to_sym
101
+ when :signed_url then obj.presigned_url(:get, expires_in: 14400)
102
+ when :public_url then obj.public_url
103
+ when :s3_uri then "s3://#{obj.bucket_name}/#{obj.key}"
70
104
  end
71
105
  end
72
106
 
@@ -79,8 +113,29 @@ module BrowseEverything
79
113
  end
80
114
 
81
115
  def client
82
- @client ||= Aws::S3::Client.new(credentials: Aws::Credentials.new(config[:app_key], config[:app_secret]), region: config[:region])
116
+ @client ||= Aws::S3::Client.new(aws_config)
83
117
  end
118
+
119
+ private
120
+
121
+ def strip(path)
122
+ path.sub %r{^/?(.+?)/?$}, '\1'
123
+ end
124
+
125
+ def from_base(key)
126
+ Pathname.new(key).relative_path_from(Pathname.new(config[:base].to_s)).to_s
127
+ end
128
+
129
+ def full_path(path)
130
+ config[:base].present? ? File.join(config[:base], path) : path
131
+ end
132
+
133
+ def aws_config
134
+ result = {}
135
+ result[:credentials] = Aws::Credentials.new(config[:app_key], config[:app_secret]) if config[:app_key].present?
136
+ result[:region] = config[:region] if config.key?(:region)
137
+ result
138
+ end
84
139
  end
85
140
  end
86
141
  end
@@ -100,26 +100,26 @@ module BrowseEverything
100
100
 
101
101
  private
102
102
 
103
- def oauth_client
104
- Skydrive::Oauth::Client.new(config[:client_id], config[:client_secret], callback.to_s, 'wl.skydrive')
105
- # TODO: error checking here
106
- end
103
+ def oauth_client
104
+ Skydrive::Oauth::Client.new(config[:client_id], config[:client_secret], callback.to_s, 'wl.skydrive')
105
+ # TODO: error checking here
106
+ end
107
107
 
108
- def rehydrate_token
109
- return @rehydrate_token if @rehydrate_token
110
- token_str = @token[:token]
111
- token_expires = @token[:expires_at]
112
- Rails.logger.warn "\n\n Rehydrating: #{@token} #{token_str} #{token_expires}"
113
- @rehydrate_token = oauth_client.get_access_token_from_hash(token_str, expires_at: token_expires)
114
- end
108
+ def rehydrate_token
109
+ return @rehydrate_token if @rehydrate_token
110
+ token_str = @token[:token]
111
+ token_expires = @token[:expires_at]
112
+ Rails.logger.warn "\n\n Rehydrating: #{@token} #{token_str} #{token_expires}"
113
+ @rehydrate_token = oauth_client.get_access_token_from_hash(token_str, expires_at: token_expires)
114
+ end
115
115
 
116
- def safe_id(id)
117
- id.tr('.', '-')
118
- end
116
+ def safe_id(id)
117
+ id.tr('.', '-')
118
+ end
119
119
 
120
- def real_id(id)
121
- id.tr('-', '.')
122
- end
120
+ def real_id(id)
121
+ id.tr('-', '.')
122
+ end
123
123
  end
124
124
  end
125
125
  end
@@ -1,3 +1,3 @@
1
1
  module BrowseEverything
2
- VERSION = '0.12.0'.freeze
2
+ VERSION = '0.13.0'.freeze
3
3
  end
@@ -12,11 +12,12 @@
12
12
  # :client_id: YOUR_GOOGLE_API_CLIENT_ID
13
13
  # :client_secret: YOUR_GOOGLE_API_CLIENT_SECRET
14
14
  # s3:
15
- # :app_key: YOUR_AWS_S3_KEY
16
- # :app_secret: YOUR_AWS_S3_SECRET
17
15
  # :bucket: YOUR_AWS_S3_BUCKET
18
- # :region: YOUR_AWS_S3_REGION # default: us-east-1
19
- # :signed_url: true # set to false for public urls
16
+ # :response_type: :signed_url # set to :public_url for public urls or :s3_uri for an s3://BUCKET/KEY uri
17
+ # :app_key: YOUR_AWS_S3_KEY # :app_key, :app_secret, and :region can be specified
18
+ # :app_secret: YOUR_AWS_S3_SECRET # explicitly here, or left out to use system-configured
19
+ # :region: YOUR_AWS_S3_REGION # defaults.
20
+ # See https://aws.amazon.com/blogs/security/a-new-and-standardized-way-to-manage-credentials-in-the-aws-sdks/
20
21
  # sky_drive:
21
22
  # :client_id: YOUR_MS_LIVE_CONNECT_CLIENT_ID
22
23
  # :client_secret: YOUR_MS_LIVE_CONNECT_CLIENT_SECRET