browse-everything 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +19 -0
  2. data/.travis.yml +6 -0
  3. data/CONTRIBUTING.md +113 -0
  4. data/Gemfile +4 -0
  5. data/HISTORY.md +2 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +164 -0
  8. data/Rakefile +10 -0
  9. data/app/.DS_Store +0 -0
  10. data/app/assets/javascripts/browse_everything.js +3 -0
  11. data/app/assets/javascripts/browse_everything/behavior.js.coffee +110 -0
  12. data/app/assets/stylesheets/browse_everything.css.scss +82 -0
  13. data/app/controllers/browse_everything_controller.rb +74 -0
  14. data/app/helpers/browse_everything_helper.rb +11 -0
  15. data/app/views/.DS_Store +0 -0
  16. data/app/views/browse_everything/_auth.html.erb +3 -0
  17. data/app/views/browse_everything/_files.html.erb +12 -0
  18. data/app/views/browse_everything/_providers.html.erb +10 -0
  19. data/app/views/browse_everything/auth.html.erb +7 -0
  20. data/app/views/browse_everything/index.html.erb +28 -0
  21. data/app/views/browse_everything/resolve.html.erb +1 -0
  22. data/app/views/browse_everything/show.html.erb +9 -0
  23. data/app/views/layouts/browse_everything.html.erb +11 -0
  24. data/browse-everything.gemspec +40 -0
  25. data/config/locales/en_browse_everything.yml +13 -0
  26. data/config/routes.rb +6 -0
  27. data/lib/browse-everything.rb +1 -0
  28. data/lib/browse_everything.rb +37 -0
  29. data/lib/browse_everything/browser.rb +25 -0
  30. data/lib/browse_everything/driver/base.rb +55 -0
  31. data/lib/browse_everything/driver/box.rb +90 -0
  32. data/lib/browse_everything/driver/drop_box.rb +74 -0
  33. data/lib/browse_everything/driver/file_system.rb +62 -0
  34. data/lib/browse_everything/driver/google_drive.rb +103 -0
  35. data/lib/browse_everything/driver/sky_drive.rb +132 -0
  36. data/lib/browse_everything/engine.rb +6 -0
  37. data/lib/browse_everything/file_entry.rb +19 -0
  38. data/lib/browse_everything/version.rb +3 -0
  39. data/spec/fixtures/file_system/dir_1/dir_3/file_3.m4v +0 -0
  40. data/spec/fixtures/file_system/dir_1/file_2.txt +1 -0
  41. data/spec/fixtures/file_system/dir_2/file_4.docx +1 -0
  42. data/spec/fixtures/file_system/file_1.pdf +0 -0
  43. data/spec/fixtures/vcr_cassettes/dropbox.yml +231 -0
  44. data/spec/rake/app_spec.rb +121 -0
  45. data/spec/spec_helper.rb +56 -0
  46. data/spec/spec_helper.rb.orig +47 -0
  47. data/spec/support/app/controllers/file_handler_controller.rb +8 -0
  48. data/spec/support/app/views/file_handler/index.html.erb +22 -0
  49. data/spec/support/config/browse_everything_providers.yml.example +15 -0
  50. data/spec/support/lib/generators/test_app_generator.rb +50 -0
  51. data/spec/unit/base_spec.rb +28 -0
  52. data/spec/unit/browser_spec.rb +76 -0
  53. data/spec/unit/drop_box_spec.rb +119 -0
  54. data/spec/unit/file_entry_spec.rb +44 -0
  55. data/spec/unit/file_system_spec.rb +85 -0
  56. data/spec/unit/sky_drive_spec.rb +58 -0
  57. data/tasks/browse-everything-dev.rake +63 -0
  58. data/tasks/ci.rake +7 -0
  59. 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