browse-everything 0.1.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.
- data/.gitignore +19 -0
- data/.travis.yml +6 -0
- data/CONTRIBUTING.md +113 -0
- data/Gemfile +4 -0
- data/HISTORY.md +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +164 -0
- data/Rakefile +10 -0
- data/app/.DS_Store +0 -0
- data/app/assets/javascripts/browse_everything.js +3 -0
- data/app/assets/javascripts/browse_everything/behavior.js.coffee +110 -0
- data/app/assets/stylesheets/browse_everything.css.scss +82 -0
- data/app/controllers/browse_everything_controller.rb +74 -0
- data/app/helpers/browse_everything_helper.rb +11 -0
- data/app/views/.DS_Store +0 -0
- data/app/views/browse_everything/_auth.html.erb +3 -0
- data/app/views/browse_everything/_files.html.erb +12 -0
- data/app/views/browse_everything/_providers.html.erb +10 -0
- data/app/views/browse_everything/auth.html.erb +7 -0
- data/app/views/browse_everything/index.html.erb +28 -0
- data/app/views/browse_everything/resolve.html.erb +1 -0
- data/app/views/browse_everything/show.html.erb +9 -0
- data/app/views/layouts/browse_everything.html.erb +11 -0
- data/browse-everything.gemspec +40 -0
- data/config/locales/en_browse_everything.yml +13 -0
- data/config/routes.rb +6 -0
- data/lib/browse-everything.rb +1 -0
- data/lib/browse_everything.rb +37 -0
- data/lib/browse_everything/browser.rb +25 -0
- data/lib/browse_everything/driver/base.rb +55 -0
- data/lib/browse_everything/driver/box.rb +90 -0
- data/lib/browse_everything/driver/drop_box.rb +74 -0
- data/lib/browse_everything/driver/file_system.rb +62 -0
- data/lib/browse_everything/driver/google_drive.rb +103 -0
- data/lib/browse_everything/driver/sky_drive.rb +132 -0
- data/lib/browse_everything/engine.rb +6 -0
- data/lib/browse_everything/file_entry.rb +19 -0
- data/lib/browse_everything/version.rb +3 -0
- data/spec/fixtures/file_system/dir_1/dir_3/file_3.m4v +0 -0
- data/spec/fixtures/file_system/dir_1/file_2.txt +1 -0
- data/spec/fixtures/file_system/dir_2/file_4.docx +1 -0
- data/spec/fixtures/file_system/file_1.pdf +0 -0
- data/spec/fixtures/vcr_cassettes/dropbox.yml +231 -0
- data/spec/rake/app_spec.rb +121 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/spec_helper.rb.orig +47 -0
- data/spec/support/app/controllers/file_handler_controller.rb +8 -0
- data/spec/support/app/views/file_handler/index.html.erb +22 -0
- data/spec/support/config/browse_everything_providers.yml.example +15 -0
- data/spec/support/lib/generators/test_app_generator.rb +50 -0
- data/spec/unit/base_spec.rb +28 -0
- data/spec/unit/browser_spec.rb +76 -0
- data/spec/unit/drop_box_spec.rb +119 -0
- data/spec/unit/file_entry_spec.rb +44 -0
- data/spec/unit/file_system_spec.rb +85 -0
- data/spec/unit/sky_drive_spec.rb +58 -0
- data/tasks/browse-everything-dev.rake +63 -0
- data/tasks/ci.rake +7 -0
- metadata +419 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
module BrowseEverything
|
2
|
+
module Driver
|
3
|
+
class Box < Base
|
4
|
+
require 'ruby-box'
|
5
|
+
|
6
|
+
def icon
|
7
|
+
'cloud'
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate_config
|
11
|
+
unless config[:client_id]
|
12
|
+
raise BrowseEverything::InitializationError, "Box driver requires a :client_id argument"
|
13
|
+
end
|
14
|
+
unless config[:client_secret]
|
15
|
+
raise BrowseEverything::InitializationError, "Box driver requires a :client_secret argument"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def contents(path='')
|
20
|
+
path.sub!(/^[\/.]+/,'')
|
21
|
+
result = []
|
22
|
+
unless path.empty?
|
23
|
+
result << BrowseEverything::FileEntry.new(
|
24
|
+
Pathname(path).join('..'),
|
25
|
+
'', '..', 0, Time.now, true
|
26
|
+
)
|
27
|
+
end
|
28
|
+
folder = path.empty? ? box_client.root_folder : box_client.folder(path)
|
29
|
+
result += folder.items.collect do |f|
|
30
|
+
BrowseEverything::FileEntry.new(
|
31
|
+
File.join(path,f.name),#id here
|
32
|
+
"#{self.key}:#{File.join(path,f.name)}",#single use link
|
33
|
+
f.name,
|
34
|
+
f.size,
|
35
|
+
f.created_at,
|
36
|
+
f.type == 'folder'
|
37
|
+
)
|
38
|
+
end
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
def link_for(path)
|
43
|
+
file = box_client.file(path)
|
44
|
+
file.create_shared_link
|
45
|
+
link = file.shared_link
|
46
|
+
extras = link.unshared_at.nil? ? {} : { expires: link.unshared_at }
|
47
|
+
[link.download_url,extras]
|
48
|
+
end
|
49
|
+
|
50
|
+
def details(f)
|
51
|
+
end
|
52
|
+
|
53
|
+
def auth_link
|
54
|
+
callback = connector_response_url(config[:url_options])
|
55
|
+
oauth_client.authorize_url(callback.to_s)
|
56
|
+
end
|
57
|
+
|
58
|
+
def authorized?
|
59
|
+
#false
|
60
|
+
@token.present?
|
61
|
+
end
|
62
|
+
|
63
|
+
def connect(params,data)
|
64
|
+
@token = oauth_client.get_access_token(params[:code]).token
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def oauth_client
|
69
|
+
session = RubyBox::Session.new({
|
70
|
+
client_id: config[:client_id],
|
71
|
+
client_secret: config[:client_secret]
|
72
|
+
})
|
73
|
+
|
74
|
+
session
|
75
|
+
#todo error checking here
|
76
|
+
end
|
77
|
+
|
78
|
+
def box_client
|
79
|
+
session = RubyBox::Session.new({
|
80
|
+
client_id: config[:client_id],
|
81
|
+
client_secret: config[:client_secret],
|
82
|
+
access_token: @token
|
83
|
+
})
|
84
|
+
RubyBox::Client.new(session)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'dropbox_sdk'
|
2
|
+
|
3
|
+
module BrowseEverything
|
4
|
+
module Driver
|
5
|
+
class DropBox < Base
|
6
|
+
|
7
|
+
def icon
|
8
|
+
'dropbox'
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_config
|
12
|
+
unless [:app_key,:app_secret].all? { |key| config[key].present? }
|
13
|
+
raise BrowseEverything::InitializationError, "DropBox driver requires :app_key and :app_secret"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def contents(path='')
|
18
|
+
path.sub!(/^[\/.]+/,'')
|
19
|
+
result = []
|
20
|
+
unless path.empty?
|
21
|
+
result << BrowseEverything::FileEntry.new(
|
22
|
+
Pathname(path).join('..'),
|
23
|
+
'', '..', 0, Time.now, true
|
24
|
+
)
|
25
|
+
end
|
26
|
+
result += client.metadata(path)['contents'].collect do |info|
|
27
|
+
path = info['path']
|
28
|
+
BrowseEverything::FileEntry.new(
|
29
|
+
path,
|
30
|
+
[self.key,path].join(':'),
|
31
|
+
File.basename(path),
|
32
|
+
info['size'],
|
33
|
+
Time.parse(info['modified']),
|
34
|
+
info['is_dir']
|
35
|
+
)
|
36
|
+
end
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def link_for(path)
|
41
|
+
[client.media(path)['url'], { expires: 4.hours.from_now }]
|
42
|
+
end
|
43
|
+
|
44
|
+
def details(path)
|
45
|
+
contents(path).first
|
46
|
+
end
|
47
|
+
|
48
|
+
def auth_link
|
49
|
+
[ auth_flow.start('drop_box'), @csrf ]
|
50
|
+
end
|
51
|
+
|
52
|
+
def connect(params,data)
|
53
|
+
@csrf = data
|
54
|
+
@token, user, state = auth_flow.finish(params)
|
55
|
+
@token
|
56
|
+
end
|
57
|
+
|
58
|
+
def authorized?
|
59
|
+
token.present?
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def auth_flow
|
64
|
+
@csrf ||= {}
|
65
|
+
DropboxOAuth2Flow.new(config[:app_key], config[:app_secret], connector_response_url(config[:url_options]).to_s,@csrf,:token)
|
66
|
+
end
|
67
|
+
|
68
|
+
def client
|
69
|
+
DropboxClient.new(token)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module BrowseEverything
|
2
|
+
module Driver
|
3
|
+
class FileSystem < Base
|
4
|
+
|
5
|
+
def icon
|
6
|
+
'file'
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate_config
|
10
|
+
unless config[:home]
|
11
|
+
raise BrowseEverything::InitializationError, "FileSystem driver requires a :home argument"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def contents(path='')
|
16
|
+
relative_path = path.sub(%r{^[/.]+},'')
|
17
|
+
real_path = File.join(config[:home], relative_path)
|
18
|
+
result = []
|
19
|
+
if File.directory?(real_path)
|
20
|
+
if relative_path.present?
|
21
|
+
result << details('..')
|
22
|
+
end
|
23
|
+
result += Dir[File.join(real_path,'*')].collect { |f| details(f) }
|
24
|
+
elsif File.exists?(real_path)
|
25
|
+
result += [details(real_path)]
|
26
|
+
end
|
27
|
+
result.sort do |a,b|
|
28
|
+
if b.container?
|
29
|
+
a.container? ? a.name.downcase <=> b.name.downcase : 1
|
30
|
+
else
|
31
|
+
a.container? ? -1 : a.name.downcase <=> b.name.downcase
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def details(path)
|
37
|
+
if File.exists?(path)
|
38
|
+
info = File::Stat.new(path)
|
39
|
+
BrowseEverything::FileEntry.new(
|
40
|
+
Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(config[:home])),
|
41
|
+
[self.key,path].join(':'),
|
42
|
+
File.basename(path),
|
43
|
+
info.size,
|
44
|
+
info.mtime,
|
45
|
+
info.directory?
|
46
|
+
)
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def link_for(path)
|
53
|
+
"file://#{File.expand_path(path)}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def authorized?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module BrowseEverything
|
2
|
+
module Driver
|
3
|
+
class GoogleDrive < Base
|
4
|
+
|
5
|
+
require 'google/api_client'
|
6
|
+
|
7
|
+
def icon
|
8
|
+
'google-plus-sign'
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_config
|
12
|
+
unless config[:client_id]
|
13
|
+
raise BrowseEverything::InitializationError, "GoogleDrive driver requires a :client_id argument"
|
14
|
+
end
|
15
|
+
unless config[:client_secret]
|
16
|
+
raise BrowseEverything::InitializationError, "GoogleDrive driver requires a :client_secret argument"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def contents(path='')
|
21
|
+
default_params = { }
|
22
|
+
page_token = nil
|
23
|
+
files = []
|
24
|
+
begin
|
25
|
+
unless path.blank?
|
26
|
+
default_params[:q] = "'#{path}' in parents"
|
27
|
+
end
|
28
|
+
unless page_token.blank?
|
29
|
+
default_params[:pageToken] = page_token
|
30
|
+
end
|
31
|
+
api_result = oauth_client.execute( api_method: drive.files.list, parameters: default_params )
|
32
|
+
response = JSON.parse(api_result.response.body)
|
33
|
+
page_token = response["nextPageToken"]
|
34
|
+
response["items"].select do |file|
|
35
|
+
path.blank? ? (file["parents"].blank? or file["parents"].any?{|p| p["isRoot"] }) : true
|
36
|
+
end.each do |file|
|
37
|
+
files << details(file, path)
|
38
|
+
end
|
39
|
+
end while !page_token.blank?
|
40
|
+
files.compact
|
41
|
+
end
|
42
|
+
|
43
|
+
def details(file, path='')
|
44
|
+
if file["downloadUrl"] or file["mimeType"] == "application/vnd.google-apps.folder"
|
45
|
+
BrowseEverything::FileEntry.new(
|
46
|
+
file["id"],
|
47
|
+
"#{self.key}:#{file["id"]}",
|
48
|
+
file["title"],
|
49
|
+
(file["fileSize"] || 0),
|
50
|
+
Time.parse(file["modifiedDate"]),
|
51
|
+
file["mimeType"] == "application/vnd.google-apps.folder",
|
52
|
+
file["mimeType"] == "application/vnd.google-apps.folder" ?
|
53
|
+
"directory" :
|
54
|
+
file["mimeType"]
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def link_for(id)
|
60
|
+
api_method = drive.files.get
|
61
|
+
api_result = oauth_client.execute(api_method: api_method, parameters: {fileId: id})
|
62
|
+
download_url = JSON.parse(api_result.response.body)["downloadUrl"]
|
63
|
+
auth_header = {'Authorization' => "Bearer #{oauth_client.authorization.access_token.to_s}"}
|
64
|
+
[download_url,{ auth_header: auth_header, expires: 1.hour.from_now }]
|
65
|
+
end
|
66
|
+
|
67
|
+
def auth_link
|
68
|
+
oauth_client.authorization.authorization_uri.to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def authorized?
|
72
|
+
@token.present?
|
73
|
+
end
|
74
|
+
|
75
|
+
def connect(params, data)
|
76
|
+
oauth_client.authorization.code = params[:code]
|
77
|
+
@token = oauth_client.authorization.fetch_access_token!
|
78
|
+
end
|
79
|
+
|
80
|
+
def drive
|
81
|
+
oauth_client.discovered_api('drive', 'v2')
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def oauth_client
|
87
|
+
if @client.nil?
|
88
|
+
callback = connector_response_url(config[:url_options])
|
89
|
+
@client = Google::APIClient.new
|
90
|
+
@client.authorization.client_id = config[:client_id]
|
91
|
+
@client.authorization.client_secret = config[:client_secret]
|
92
|
+
@client.authorization.scope = "https://www.googleapis.com/auth/drive"
|
93
|
+
@client.authorization.redirect_uri = callback
|
94
|
+
@client.authorization.update_token!(@token) if @token.present?
|
95
|
+
end
|
96
|
+
#todo error checking here
|
97
|
+
@client
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module BrowseEverything
|
2
|
+
module Driver
|
3
|
+
class SkyDrive < Base
|
4
|
+
|
5
|
+
require 'skydrive'
|
6
|
+
|
7
|
+
def icon
|
8
|
+
'windows'
|
9
|
+
end
|
10
|
+
|
11
|
+
def container_items
|
12
|
+
["folder","album"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_config
|
16
|
+
unless config[:client_id]
|
17
|
+
raise BrowseEverything::InitializationError, "SkyDrive driver requires a :client_id argument"
|
18
|
+
end
|
19
|
+
unless config[:client_secret]
|
20
|
+
raise BrowseEverything::InitializationError, "SkyDrive driver requires a :client_secret argument"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def contents(path='')
|
25
|
+
result = []
|
26
|
+
token_obj = rehydrate_token
|
27
|
+
client = Skydrive::Client.new(token_obj)
|
28
|
+
if (path == '')
|
29
|
+
folder = client.my_skydrive
|
30
|
+
#todo do some loop to get down to my path
|
31
|
+
else
|
32
|
+
folder = client.get("/#{path.gsub("-",".")}/")
|
33
|
+
result += [parent_folder_details(folder)] if folder.parent_id
|
34
|
+
end
|
35
|
+
|
36
|
+
files = folder.files
|
37
|
+
files.items.each do |item|
|
38
|
+
if container_items.include? item.type
|
39
|
+
result += [folder_details(item)]
|
40
|
+
else
|
41
|
+
Rails.logger.warn("\n\nID #{item.id} #{item.type}")
|
42
|
+
result += [file_details(item)]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
def link_for(path)
|
49
|
+
response = Skydrive::Client.new(rehydrate_token).get("/#{real_id(path)}/")
|
50
|
+
[response.download_link, {expires: 1.hour.from_now}]
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
def file_details(file)
|
56
|
+
BrowseEverything::FileEntry.new(
|
57
|
+
safe_id(file.id),
|
58
|
+
"#{key}:#{safe_id(file.id)}",
|
59
|
+
file.name,
|
60
|
+
file.size,
|
61
|
+
file.updated_time,
|
62
|
+
false
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def parent_folder_details(file)
|
67
|
+
BrowseEverything::FileEntry.new(
|
68
|
+
safe_id(file.parent_id),
|
69
|
+
"#{key}:#{safe_id(file.parent_id)}",
|
70
|
+
"..",
|
71
|
+
0,
|
72
|
+
Time.now,
|
73
|
+
true
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
def folder_details(folder)
|
81
|
+
BrowseEverything::FileEntry.new(
|
82
|
+
safe_id(folder.id),
|
83
|
+
"#{key}:#{safe_id(folder.id)}",
|
84
|
+
folder.name,
|
85
|
+
0,
|
86
|
+
folder.updated_time,
|
87
|
+
true,
|
88
|
+
'directory'#todo how are we getting mime type
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def auth_link
|
94
|
+
oauth_client.authorize_url
|
95
|
+
end
|
96
|
+
|
97
|
+
def authorized?
|
98
|
+
return false unless @token.present?
|
99
|
+
return !rehydrate_token.expired?
|
100
|
+
end
|
101
|
+
|
102
|
+
def connect(params,data)
|
103
|
+
Rails.logger.warn "params #{params.inspect}"
|
104
|
+
token = oauth_client.get_access_token(params[:code])
|
105
|
+
@token = {token:token.token, expires_at:token.expires_at}
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
def oauth_client
|
110
|
+
callback = connector_response_url(config[:url_options])
|
111
|
+
Skydrive::Oauth::Client.new(config[:client_id], config[:client_secret], callback.to_s,"wl.skydrive")
|
112
|
+
#todo error checking here
|
113
|
+
end
|
114
|
+
|
115
|
+
def rehydrate_token
|
116
|
+
return @rehydrate_token if @rehydrate_token
|
117
|
+
token_str = @token[:token]
|
118
|
+
token_expires = @token[:expires_at]
|
119
|
+
Rails.logger.warn "\n\n Rehydrating: #{@token} #{token_str} #{token_expires}"
|
120
|
+
@rehydrate_token = oauth_client.get_access_token_from_hash(token_str,{expires_at:token_expires})
|
121
|
+
end
|
122
|
+
|
123
|
+
def safe_id(id)
|
124
|
+
id.gsub(".","-")
|
125
|
+
end
|
126
|
+
|
127
|
+
def real_id(id)
|
128
|
+
id.gsub("-",".")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|