browse-everything 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|