dropbox-sdk 1.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +23 -0
- data/LICENSE +20 -0
- data/README +7 -0
- data/cli_example.rb +197 -0
- data/dropbox_controller.rb +57 -0
- data/lib/dropbox_sdk.rb +607 -0
- data/web_file_browser.rb +184 -0
- metadata +101 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
1.0 (2011-8-16)
|
2
|
+
* Backwards compatibility broken
|
3
|
+
- Changed interface
|
4
|
+
- Change 'sandbox' references to 'app_folder'
|
5
|
+
* Updated SDK to Dropbox API Version 1, supporting all calls
|
6
|
+
- Added 'rev' parameter to metadata and get_file
|
7
|
+
- Added 'parent_rev' parameter to put_file
|
8
|
+
- Added search, share, media, revisions, and restore
|
9
|
+
- put_file uses /files_put instead of multipart POST
|
10
|
+
- Removed methods for calls that were removed from v1 of the REST API
|
11
|
+
* Changed return format for calls
|
12
|
+
- On error (non-200 response), an exception is raised
|
13
|
+
- On success, the JSON is parsed and a Hash is returned
|
14
|
+
* Updated examples
|
15
|
+
- Improved CLI example
|
16
|
+
- Added a Ruby on Rails 3 controller example
|
17
|
+
- Added a web based file browser/uploader that uses Sinatra
|
18
|
+
* put_file no longer takes a "name" arugment, only takes a full path
|
19
|
+
* Removed reliance on config files
|
20
|
+
* Assorted bugfixes and improvements
|
21
|
+
* All calls are now made over SSL
|
22
|
+
* Fully documented code for RDoc generation
|
23
|
+
* Added a CHANGELOG
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009-2011 Dropbox Inc., http://www.dropbox.com/
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Getting started with the Dropbox Ruby SDK:
|
2
|
+
1. Install json and oauth ruby gems with:
|
3
|
+
gem install json oauth
|
4
|
+
2. Edit cli_example.rb to to include your APP_KEY and APP_SECRET
|
5
|
+
3. Try running the example app: 'ruby clie_example.rb'.
|
6
|
+
4. See dropbox_controller.rb for an example Ruby on Rails controller. It needs an APP_KEY and APP_SECRET set too.
|
7
|
+
5. Check out the dropbox website for reference and help! http://dropbox.com/developers_beta
|
data/cli_example.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require './lib/dropbox_sdk'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
####
|
5
|
+
# An example app using the Dropbox API Ruby Client
|
6
|
+
# This ruby script sets up a basic command line interface (CLI)
|
7
|
+
# that prompts a user to authenticate on the web, then
|
8
|
+
# allows them to type commands to manipulate their dropbox.
|
9
|
+
####
|
10
|
+
|
11
|
+
# You must use your Dropbox App key and secret to use the API.
|
12
|
+
# Find this at https://www.dropbox.com/developers
|
13
|
+
APP_KEY = ""
|
14
|
+
APP_SECRET = ""
|
15
|
+
ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
|
16
|
+
#The default is :app_folder, but your application might be
|
17
|
+
#set to have full :dropbox access. Check your app at
|
18
|
+
#https://www.dropbox.com/developers/apps
|
19
|
+
|
20
|
+
class DropboxCLI
|
21
|
+
LOGIN_REQUIRED = %w{put get cp mv rm ls mkdir info logout search}
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
if APP_KEY == '' or APP_SECRET == ''
|
25
|
+
puts "You must set your APP_KEY and APP_SECRET in cli_example.rb!"
|
26
|
+
puts "Find this in your apps page at https://www.dropbox.com/developers/"
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
@session = DropboxSession.new(APP_KEY, APP_SECRET)
|
31
|
+
@client = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def login
|
35
|
+
########
|
36
|
+
# Instead of going to a authorize URL, you can set a access token key and secret
|
37
|
+
# from a previous session
|
38
|
+
########
|
39
|
+
# @session.set_access_token('key', 'secret')
|
40
|
+
|
41
|
+
if @session.authorized?
|
42
|
+
puts "already logged in!"
|
43
|
+
else
|
44
|
+
|
45
|
+
# grab the request token for session
|
46
|
+
@session.get_request_token
|
47
|
+
|
48
|
+
authorize_url = @session.get_authorize_url
|
49
|
+
puts "Got a request token. Your request token key is #{@session.token} and your token secret is #{@session.secret}"
|
50
|
+
|
51
|
+
# make the user log in and authorize this token
|
52
|
+
puts "AUTHORIZING", authorize_url, "Please visit that web page and hit 'Allow', then hit Enter here."
|
53
|
+
gets
|
54
|
+
|
55
|
+
# get the access token from the server. Its then stored in the session.
|
56
|
+
@session.get_access_token
|
57
|
+
|
58
|
+
end
|
59
|
+
puts "You are logged in. Your access token key is #{@session.token} your secret is #{@session.secret}"
|
60
|
+
@client = DropboxClient.new(@session, ACCESS_TYPE)
|
61
|
+
end
|
62
|
+
|
63
|
+
def command_loop
|
64
|
+
puts "Enter a command or 'help' or 'exit'"
|
65
|
+
command_line = ''
|
66
|
+
while command_line.strip != 'exit'
|
67
|
+
begin
|
68
|
+
execute_dropbox_command(command_line)
|
69
|
+
rescue RuntimeError => e
|
70
|
+
puts "Command Line Error! #{e.class}: #{e}"
|
71
|
+
puts e.backtrace
|
72
|
+
end
|
73
|
+
print '> '
|
74
|
+
command_line = gets.strip
|
75
|
+
end
|
76
|
+
puts 'goodbye'
|
77
|
+
exit(0)
|
78
|
+
end
|
79
|
+
|
80
|
+
def execute_dropbox_command(cmd_line)
|
81
|
+
command = cmd_line.split
|
82
|
+
method = command.first
|
83
|
+
if LOGIN_REQUIRED.include? method
|
84
|
+
if @client
|
85
|
+
send(method.to_sym, command)
|
86
|
+
else
|
87
|
+
puts 'must be logged in; type \'login\' to get started.'
|
88
|
+
end
|
89
|
+
elsif ['login', 'help'].include? method
|
90
|
+
send(method.to_sym)
|
91
|
+
else
|
92
|
+
if command.first && !command.first.strip.empty?
|
93
|
+
puts 'invalid command. type \'help\' to see commands.'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def logout
|
99
|
+
@session.clear_access_token
|
100
|
+
puts "You are logged out."
|
101
|
+
@client = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def put(command)
|
105
|
+
fname = command[1]
|
106
|
+
|
107
|
+
#If the user didn't specifiy the file name, just use the name of the file on disk
|
108
|
+
if command[2]
|
109
|
+
new_name = command[2]
|
110
|
+
else
|
111
|
+
new_name = File.basename(fname)
|
112
|
+
end
|
113
|
+
|
114
|
+
if fname && !fname.empty? && File.exists?(fname) && (File.ftype(fname) == 'file') && File.stat(fname).readable?
|
115
|
+
#This is where we call the the Dropbox Client
|
116
|
+
pp @client.put_file(new_name, open(fname))
|
117
|
+
else
|
118
|
+
puts "couldn't find the file #{ fname }"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def get(command)
|
123
|
+
dest = command[2]
|
124
|
+
if !command[1] || command[1].empty?
|
125
|
+
puts "please specify item to get"
|
126
|
+
elsif !dest || dest.empty?
|
127
|
+
puts "please specify full local path to dest, i.e. the file to write to"
|
128
|
+
elsif File.exists?(dest)
|
129
|
+
puts "error: File #{dest} already exists."
|
130
|
+
else
|
131
|
+
src = clean_up(command[1])
|
132
|
+
out = @client.get_file('/' + src)
|
133
|
+
open(dest, 'w'){|f| f.puts out }
|
134
|
+
puts "wrote file #{dest}."
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def mkdir(command)
|
139
|
+
pp @client.file_create_folder(command[1])
|
140
|
+
end
|
141
|
+
|
142
|
+
def thumbnail(command)
|
143
|
+
command[2] ||= 'small'
|
144
|
+
pp @client.thumbnail(command[1], command[2])
|
145
|
+
end
|
146
|
+
|
147
|
+
def cp(command)
|
148
|
+
src = clean_up(command[1])
|
149
|
+
dest = clean_up(command[2])
|
150
|
+
pp @client.file_copy(src, dest)
|
151
|
+
end
|
152
|
+
|
153
|
+
def mv(command)
|
154
|
+
src = clean_up(command[1])
|
155
|
+
dest = clean_up(command[2])
|
156
|
+
pp @client.file_move(src, dest)
|
157
|
+
end
|
158
|
+
|
159
|
+
def rm(command)
|
160
|
+
pp @client.file_delete(clean_up(command[1]))
|
161
|
+
end
|
162
|
+
|
163
|
+
def search(command)
|
164
|
+
resp = @client.search('/',clean_up(command[1]))
|
165
|
+
|
166
|
+
for item in resp
|
167
|
+
puts item['path']
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def info(command)
|
172
|
+
pp @client.account_info
|
173
|
+
end
|
174
|
+
|
175
|
+
def ls(command)
|
176
|
+
command[1] = '/' + clean_up(command[1] || '')
|
177
|
+
resp = @client.metadata(command[1])
|
178
|
+
|
179
|
+
if resp['contents'].length > 0
|
180
|
+
for item in resp['contents']
|
181
|
+
puts item['path']
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def help
|
187
|
+
puts "commands are: login #{LOGIN_REQUIRED.join(' ')} help exit"
|
188
|
+
end
|
189
|
+
|
190
|
+
def clean_up(str)
|
191
|
+
return str.gsub(/^\/+/, '') if str
|
192
|
+
str
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
cli = DropboxCLI.new
|
197
|
+
cli.command_loop
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'dropbox_sdk'
|
2
|
+
|
3
|
+
# This is an example of a Rails 3 controller that authorizes an application
|
4
|
+
# and then uploads a file to the user's Dropbox.
|
5
|
+
|
6
|
+
# You must set these
|
7
|
+
APP_KEY = ""
|
8
|
+
APP_SECRET = ""
|
9
|
+
ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
|
10
|
+
#The default is :app_folder, but your application might be
|
11
|
+
#set to have full :dropbox access. Check your app at
|
12
|
+
#https://www.dropbox.com/developers/apps
|
13
|
+
|
14
|
+
|
15
|
+
# Examples routes for config/routes.rb (Rails 3)
|
16
|
+
#match 'db/authorize', :controller => 'db', :action => 'authorize'
|
17
|
+
#match 'db/upload', :controller => 'db', :action => 'upload'
|
18
|
+
|
19
|
+
class DbController < ApplicationController
|
20
|
+
def authorize
|
21
|
+
if not params[:oauth_token] then
|
22
|
+
dbsession = DropboxSession.new(APP_KEY, APP_SECRET)
|
23
|
+
|
24
|
+
session[:dropbox_session] = dbsession.serialize #serialize and save this DropboxSession
|
25
|
+
|
26
|
+
#pass to get_authorize_url a callback url that will return the user here
|
27
|
+
redirect_to dbsession.get_authorize_url url_for(:action => 'authorize')
|
28
|
+
else
|
29
|
+
# the user has returned from Dropbox
|
30
|
+
dbsession = DropboxSession.deserialize(session[:dropbox_session])
|
31
|
+
dbsession.get_access_token #we've been authorized, so now request an access_token
|
32
|
+
session[:dropbox_session] = dbsession.serialize
|
33
|
+
|
34
|
+
redirect_to :action => 'upload'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def upload
|
39
|
+
# Check if user has no dropbox session...re-direct them to authorize
|
40
|
+
return redirect_to(:action => 'authorize') unless session[:dropbox_session]
|
41
|
+
|
42
|
+
dbsession = DropboxSession.deserialize(session[:dropbox_session])
|
43
|
+
client = DropboxClient.new(dbsession, ACCESS_TYPE) #raise an exception if session not authorized
|
44
|
+
info = client.account_info # look up account information
|
45
|
+
|
46
|
+
if request.method != "POST"
|
47
|
+
# show a file upload page
|
48
|
+
render :inline =>
|
49
|
+
"#{info['email']} <br/><%= form_tag({:action => :upload}, :multipart => true) do %><%= file_field_tag 'file' %><%= submit_tag %><% end %>"
|
50
|
+
return
|
51
|
+
else
|
52
|
+
# upload the posted file to dropbox keeping the same name
|
53
|
+
resp = client.put_file(params[:file].original_filename, params[:file].read)
|
54
|
+
render :text => "Upload successful! File now at #{resp['path']}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/dropbox_sdk.rb
ADDED
@@ -0,0 +1,607 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'oauth'
|
3
|
+
require 'json'
|
4
|
+
require 'uri'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
DROPBOX_API_SERVER = "api.dropbox.com"
|
8
|
+
DROPBOX_API_CONTENT_SERVER = "api-content.dropbox.com"
|
9
|
+
|
10
|
+
API_VERSION = 1
|
11
|
+
|
12
|
+
# DropboxSession is responsible for holding OAuth information. It knows how to take your consumer key and secret
|
13
|
+
# and request an access token, an authorize url, and get an access token. You just need to pass it to
|
14
|
+
# DropboxClient after its been authorized.
|
15
|
+
class DropboxSession
|
16
|
+
def initialize(key, secret)
|
17
|
+
@consumer_key = key
|
18
|
+
@consumer_secret = secret
|
19
|
+
@oauth_conf = {
|
20
|
+
:site => "https://" + DROPBOX_API_SERVER,
|
21
|
+
:scheme => :header,
|
22
|
+
:http_method => :post,
|
23
|
+
:request_token_url => "/#{API_VERSION}/oauth/request_token",
|
24
|
+
:access_token_url => "/#{API_VERSION}/oauth/access_token",
|
25
|
+
:authorize_url => "/#{API_VERSION}/oauth/authorize",
|
26
|
+
}
|
27
|
+
|
28
|
+
@consumer = OAuth::Consumer.new(@consumer_key, @consumer_secret, @oauth_conf)
|
29
|
+
@access_token = nil
|
30
|
+
@request_token = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# This gets a request token. Callbacks are excluded, and any arguments provided are passed on
|
34
|
+
# to the oauth gem's get_request_token call.
|
35
|
+
def get_request_token(*args)
|
36
|
+
begin
|
37
|
+
@request_token ||= @consumer.get_request_token({:exclude_callback => true}, *args)
|
38
|
+
rescue OAuth::Unauthorized => e
|
39
|
+
raise DropboxAuthError.new("Could not get request token, unauthorized. Is your app key and secret correct? #{e}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# This returns a URL that your user must visit to grant
|
44
|
+
# permissions to this application.
|
45
|
+
def get_authorize_url(callback=nil, *args)
|
46
|
+
get_request_token(*args)
|
47
|
+
|
48
|
+
url = @request_token.authorize_url
|
49
|
+
if callback
|
50
|
+
url += "&oauth_callback=" + URI.escape(callback)
|
51
|
+
end
|
52
|
+
|
53
|
+
"https://www.dropbox.com" + url
|
54
|
+
end
|
55
|
+
|
56
|
+
# Clears the access_token
|
57
|
+
def clear_access_token
|
58
|
+
@access_token = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
# Given a saved request token and secret, set this location's token and secret
|
62
|
+
# * token - this is the request token
|
63
|
+
# * secret - this is the request token secret
|
64
|
+
def set_request_token(key, secret)
|
65
|
+
@request_token = OAuth::RequestToken.new(@consumer, key, secret)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Given a saved access token and secret, you set this Session to use that token and secret
|
69
|
+
# * token - this is the access token
|
70
|
+
# * secret - this is the access token secret
|
71
|
+
def set_access_token(key, secret)
|
72
|
+
@access_token = OAuth::AccessToken.from_hash(@consumer, {:oauth_token => key, :oauth_token_secret => secret})
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_authorized
|
76
|
+
##this check is applied before the token and secret methods
|
77
|
+
raise DropboxError.new('Session does not yet have a request token') unless authorized?
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the current oauth access or request token.
|
81
|
+
def token
|
82
|
+
(@access_token || @request_token).token
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns the current oauth access or request token secret.
|
86
|
+
def secret
|
87
|
+
(@access_token || @request_token).secret
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the access token. If this DropboxSession doesn't yet have an access_token, it requests one
|
91
|
+
# using the request_token generate from your app's token and secret. This request will fail unless
|
92
|
+
# your user has got to the authorize_url and approved your request
|
93
|
+
def get_access_token
|
94
|
+
if @access_token.nil?
|
95
|
+
if @request_token.nil?
|
96
|
+
raise DropboxAuthError.new("No request token. You must set this or get an authorize url first.")
|
97
|
+
end
|
98
|
+
|
99
|
+
begin
|
100
|
+
@access_token = @request_token.get_access_token
|
101
|
+
rescue OAuth::Unauthorized => e
|
102
|
+
raise DropboxAuthError.new("Could not get access token, unauthorized. Did you go to authorize_url? #{e}")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
@access_token
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns true if this Session has been authorized and has an access_token.
|
109
|
+
def authorized?
|
110
|
+
!!@access_token
|
111
|
+
end
|
112
|
+
|
113
|
+
# serialize the DropboxSession.
|
114
|
+
# At DropboxSession's state is capture in three key/secret pairs. Consumer, request, and access.
|
115
|
+
# This takes the form of an array that is then converted to yaml
|
116
|
+
# [consumer_key, consumer_secret, request_token.token, request_token.secret, access_token.token, access_token.secret]
|
117
|
+
# access_token is only included if it already exists in the DropboxSesssion
|
118
|
+
def serialize
|
119
|
+
toreturn = []
|
120
|
+
if @access_token
|
121
|
+
toreturn.push @access_token.secret, @access_token.token
|
122
|
+
end
|
123
|
+
|
124
|
+
get_request_token unless @request_token
|
125
|
+
|
126
|
+
toreturn.push @request_token.secret, @request_token.token
|
127
|
+
toreturn.push @consumer_secret, @consumer_key
|
128
|
+
|
129
|
+
toreturn.to_yaml
|
130
|
+
end
|
131
|
+
|
132
|
+
# Takes a serialized DropboxSession and returns a new DropboxSession object
|
133
|
+
def self.deserialize(ser)
|
134
|
+
ser = YAML::load(ser)
|
135
|
+
session = DropboxSession.new(ser.pop, ser.pop)
|
136
|
+
session.set_request_token(ser.pop, ser.pop)
|
137
|
+
|
138
|
+
if ser.length > 0
|
139
|
+
session.set_access_token(ser.pop, ser.pop)
|
140
|
+
end
|
141
|
+
session
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
# This is the usual error raised on any Dropbox related Errors
|
148
|
+
class DropboxError < RuntimeError
|
149
|
+
attr_accessor :http_response, :error, :user_error
|
150
|
+
def initialize(error, http_response=nil, user_error=nil)
|
151
|
+
@error = error
|
152
|
+
@http_response = http_response
|
153
|
+
@user_error = user_error
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s
|
157
|
+
return "#{user_error} (#{error})" if user_error
|
158
|
+
"#{error}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# This is the error raised on Authentication failures. Usually this means
|
163
|
+
# one of three things
|
164
|
+
# * Your user failed to go to the authorize url and approve your application
|
165
|
+
# * You set an invalid or expired token and secret on your Session
|
166
|
+
# * Your user deauthorized the application after you stored a valid token and secret
|
167
|
+
class DropboxAuthError < DropboxError
|
168
|
+
end
|
169
|
+
|
170
|
+
# This is raised when you call metadata with a hash and that hash matches
|
171
|
+
# See documentation in metadata function
|
172
|
+
class DropboxNotModified < DropboxError
|
173
|
+
end
|
174
|
+
|
175
|
+
# This is the Dropbox Client API you'll be working with most often. You need to give it
|
176
|
+
# a DropboxSession which has already been authorize, or which it can authorize.
|
177
|
+
class DropboxClient
|
178
|
+
|
179
|
+
# Initialize a new DropboxClient. You need to get it a session which either has been authorized. See
|
180
|
+
# documentation on DropboxSession for how to authorize it.
|
181
|
+
def initialize(session, root="app_folder", locale=nil)
|
182
|
+
if not session.authorized?
|
183
|
+
begin
|
184
|
+
## attempt to get an access token and authorize the session
|
185
|
+
session.get_access_token
|
186
|
+
rescue OAuth::Unauthorized => e
|
187
|
+
raise DropboxAuthError.new("Could not initialize. Failed to get access token from Session. Error was: #{ e.message }")
|
188
|
+
# If this was raised, the user probably didn't go to auth.get_authorize_url
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
@root = root.to_s # If they passed in a symbol, make it a string
|
193
|
+
|
194
|
+
if not ["dropbox","app_folder"].include?(@root)
|
195
|
+
raise DropboxError.new("root must be :dropbox or :app_folder")
|
196
|
+
end
|
197
|
+
if @root == "app_folder"
|
198
|
+
#App Folder is the name of the access type, but for historical reasons
|
199
|
+
#sandbox is the URL root compontent that indicates this
|
200
|
+
@root = "sandbox"
|
201
|
+
end
|
202
|
+
|
203
|
+
@locale = locale
|
204
|
+
@session = session
|
205
|
+
@token = session.get_access_token
|
206
|
+
|
207
|
+
#There's no gurantee that @token is still valid, so be sure to handle any DropboxAuthErrors that can be raised
|
208
|
+
end
|
209
|
+
|
210
|
+
# Parse response. You probably shouldn't be calling this directly. This takes responses from the server
|
211
|
+
# and parses them. It also checks for errors and raises exceptions with the appropriate messages.
|
212
|
+
def parse_response(response, raw=false) # :nodoc:
|
213
|
+
if response.kind_of?(Net::HTTPServerError)
|
214
|
+
raise DropboxError.new("Dropbox Server Error: #{response} - #{response.body}", response)
|
215
|
+
elsif response.kind_of?(Net::HTTPUnauthorized)
|
216
|
+
raise DropboxAuthError.new(response, "User is not authenticated.")
|
217
|
+
elsif not response.kind_of?(Net::HTTPSuccess)
|
218
|
+
begin
|
219
|
+
d = JSON.parse(response.body)
|
220
|
+
rescue
|
221
|
+
raise DropboxError.new("Dropbox Server Error: body=#{response.body}", response)
|
222
|
+
end
|
223
|
+
if d['user_error'] and d['error']
|
224
|
+
raise DropboxError.new(d['error'], response, d['user_error']) #user_error is translated
|
225
|
+
elsif d['error']
|
226
|
+
raise DropboxError.new(d['error'], response)
|
227
|
+
else
|
228
|
+
raise DropboxError.new(response.body, response)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
return response.body if raw
|
233
|
+
|
234
|
+
begin
|
235
|
+
return JSON.parse(response.body)
|
236
|
+
rescue JSON::ParserError
|
237
|
+
raise DropboxError.new("Unable to parse JSON response", response)
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
# Returns account info in a Hash object
|
244
|
+
#
|
245
|
+
# For a detailed description of what this call returns, visit:
|
246
|
+
# https://www.dropbox.com/developers/docs#account-info
|
247
|
+
def account_info()
|
248
|
+
response = @token.get build_url("/account/info")
|
249
|
+
parse_response(response)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Uploads a file to a server. This uses the HTTP PUT upload method for simplicity
|
253
|
+
#
|
254
|
+
# Arguments:
|
255
|
+
# * to_path: The directory path to upload the file to. If the destination
|
256
|
+
# directory does not yet exist, it will be created.
|
257
|
+
# * file_obj: A file-like object to upload. If you would like, you can
|
258
|
+
# pass a string as file_obj.
|
259
|
+
# * overwrite: Whether to overwrite an existing file at the given path. [default is False]
|
260
|
+
# If overwrite is False and a file already exists there, Dropbox
|
261
|
+
# will rename the upload to make sure it doesn't overwrite anything.
|
262
|
+
# You must check the returned metadata to know what this new name is.
|
263
|
+
# This field should only be True if your intent is to potentially
|
264
|
+
# clobber changes to a file that you don't know about.
|
265
|
+
# * parent_rev: The rev field from the 'parent' of this upload. [optional]
|
266
|
+
# If your intent is to update the file at the given path, you should
|
267
|
+
# pass the parent_rev parameter set to the rev value from the most recent
|
268
|
+
# metadata you have of the existing file at that path. If the server
|
269
|
+
# has a more recent version of the file at the specified path, it will
|
270
|
+
# automatically rename your uploaded file, spinning off a conflict.
|
271
|
+
# Using this parameter effectively causes the overwrite parameter to be ignored.
|
272
|
+
# The file will always be overwritten if you send the most-recent parent_rev,
|
273
|
+
# and it will never be overwritten you send a less-recent one.
|
274
|
+
# Returns:
|
275
|
+
# * a Hash containing the metadata of the newly uploaded file. The file may have a different name if it conflicted.
|
276
|
+
#
|
277
|
+
# Simple Example
|
278
|
+
# client = DropboxClient(session, "app_folder")
|
279
|
+
# #session is a DropboxSession I've already authorized
|
280
|
+
# client.put_file('/test_file_on_dropbox', open('/tmp/test_file'))
|
281
|
+
# This will upload the "/tmp/test_file" from my computer into the root of my App's app folder
|
282
|
+
# and call it "test_file_on_dropbox".
|
283
|
+
# The file will not overwrite any pre-existing file.
|
284
|
+
def put_file(to_path, file_obj, overwrite=false, parent_rev=nil)
|
285
|
+
|
286
|
+
path = "/files_put/#{@root}#{format_path(to_path)}"
|
287
|
+
|
288
|
+
params = {
|
289
|
+
'overwrite' => overwrite.to_s
|
290
|
+
}
|
291
|
+
|
292
|
+
params['parent_rev'] = parent_rev unless parent_rev.nil?
|
293
|
+
|
294
|
+
response = @token.put(build_url(path, params, content_server=true),
|
295
|
+
file_obj,
|
296
|
+
"Content-Type" => "application/octet-stream")
|
297
|
+
|
298
|
+
parse_response(response)
|
299
|
+
end
|
300
|
+
|
301
|
+
# Download a file
|
302
|
+
#
|
303
|
+
# Args:
|
304
|
+
# * from_path: The path to the file to be downloaded
|
305
|
+
# * rev: A previous revision value of the file to be downloaded
|
306
|
+
#
|
307
|
+
# Returns:
|
308
|
+
# * The file contents.
|
309
|
+
def get_file(from_path, rev=nil)
|
310
|
+
params = {}
|
311
|
+
params['rev'] = rev.to_s if rev
|
312
|
+
|
313
|
+
path = "/files/#{@root}#{format_path(from_path)}"
|
314
|
+
response = @token.get(build_url(path, params, content_server=true))
|
315
|
+
|
316
|
+
parse_response(response, raw=true)
|
317
|
+
end
|
318
|
+
|
319
|
+
# Copy a file or folder to a new location.
|
320
|
+
#
|
321
|
+
# Arguments:
|
322
|
+
# * from_path: The path to the file or folder to be copied.
|
323
|
+
# * to_path: The destination path of the file or folder to be copied.
|
324
|
+
# This parameter should include the destination filename (e.g.
|
325
|
+
# from_path: '/test.txt', to_path: '/dir/test.txt'). If there's
|
326
|
+
# already a file at the to_path, this copy will be renamed to
|
327
|
+
# be unique.
|
328
|
+
#
|
329
|
+
# Returns:
|
330
|
+
# * A hash with the metadata of the new copy of the file or folder.
|
331
|
+
# For a detailed description of what this call returns, visit:
|
332
|
+
# https://www.dropbox.com/developers/docs#fileops-copy
|
333
|
+
def file_copy(from_path, to_path)
|
334
|
+
params = {
|
335
|
+
"root" => @root,
|
336
|
+
"from_path" => format_path(from_path, false),
|
337
|
+
"to_path" => format_path(to_path, false),
|
338
|
+
}
|
339
|
+
response = @token.get(build_url("/fileops/copy", params))
|
340
|
+
parse_response(response)
|
341
|
+
end
|
342
|
+
|
343
|
+
# Create a folder.
|
344
|
+
#
|
345
|
+
# Arguments:
|
346
|
+
# * path: The path of the new folder.
|
347
|
+
#
|
348
|
+
# Returns:
|
349
|
+
# * A hash with the metadata of the newly created folder.
|
350
|
+
# For a detailed description of what this call returns, visit:
|
351
|
+
# https://www.dropbox.com/developers/docs#fileops-create-folder
|
352
|
+
def file_create_folder(path)
|
353
|
+
params = {
|
354
|
+
"root" => @root,
|
355
|
+
"path" => format_path(from_path, false),
|
356
|
+
}
|
357
|
+
response = @token.get(build_url("/fileops/create_folder", params))
|
358
|
+
|
359
|
+
parse_response(response)
|
360
|
+
end
|
361
|
+
|
362
|
+
# Deletes a file
|
363
|
+
#
|
364
|
+
# Arguments:
|
365
|
+
# * path: The path of the file to delete
|
366
|
+
#
|
367
|
+
# Returns:
|
368
|
+
# * A Hash with the metadata of file just deleted.
|
369
|
+
# For a detailed description of what this call returns, visit:
|
370
|
+
# https://www.dropbox.com/developers/docs#fileops-delete
|
371
|
+
def file_delete(path)
|
372
|
+
params = {
|
373
|
+
"root" => @root,
|
374
|
+
"path" => format_path(from_path, false),
|
375
|
+
}
|
376
|
+
response = @token.get(build_url("/fileops/delete", params))
|
377
|
+
parse_response(response)
|
378
|
+
end
|
379
|
+
|
380
|
+
# Moves a file
|
381
|
+
#
|
382
|
+
# Arguments:
|
383
|
+
# * from_path: The path of the file to be moved
|
384
|
+
# * to_path: The destination path of the file or folder to be moved
|
385
|
+
# If the file or folder already exists, it will be renamed to be unique.
|
386
|
+
#
|
387
|
+
# Returns:
|
388
|
+
# * A Hash with the metadata of file or folder just moved.
|
389
|
+
# For a detailed description of what this call returns, visit:
|
390
|
+
# https://www.dropbox.com/developers/docs#fileops-delete
|
391
|
+
def file_move(from_path, to_path)
|
392
|
+
params = {
|
393
|
+
"root" => @root,
|
394
|
+
"from_path" => format_path(from_path, false),
|
395
|
+
"to_path" => format_path(to_path, false),
|
396
|
+
}
|
397
|
+
response = @token.post(build_url("/fileops/move", params))
|
398
|
+
parse_response(response)
|
399
|
+
end
|
400
|
+
|
401
|
+
# Retrives metadata for a file or folder
|
402
|
+
#
|
403
|
+
# Arguments:
|
404
|
+
# * path: The path to the file or folder.
|
405
|
+
# * list: Whether to list all contained files (only applies when
|
406
|
+
# path refers to a folder).
|
407
|
+
# * file_limit: The maximum number of file entries to return within
|
408
|
+
# a folder. If the number of files in the directory exceeds this
|
409
|
+
# limit, an exception is raised. The server will return at max
|
410
|
+
# 10,000 files within a folder.
|
411
|
+
# * hash: Every directory listing has a hash parameter attached that
|
412
|
+
# can then be passed back into this function later to save on
|
413
|
+
# bandwidth. Rather than returning an unchanged folder's contents, if
|
414
|
+
# the hash matches a DropboxNotModified exception is raised.
|
415
|
+
#
|
416
|
+
# Returns:
|
417
|
+
# * A Hash object with the metadata of the file or folder (and contained files if
|
418
|
+
# appropriate). For a detailed description of what this call returns, visit:
|
419
|
+
# https://www.dropbox.com/developers/docs#metadata
|
420
|
+
def metadata(path, file_limit=10000, list=true, hash=nil)
|
421
|
+
params = {
|
422
|
+
"file_limit" => file_limit.to_s,
|
423
|
+
"list" => list.to_s
|
424
|
+
}
|
425
|
+
|
426
|
+
params["hash"] = hash if hash
|
427
|
+
|
428
|
+
response = @token.get build_url("/metadata/#{@root}#{format_path(path)}", params=params)
|
429
|
+
if response.kind_of? Net::HTTPRedirection
|
430
|
+
raise DropboxNotModified.new("metadata not modified")
|
431
|
+
end
|
432
|
+
parse_response(response)
|
433
|
+
end
|
434
|
+
|
435
|
+
# Search directory for filenames matching query
|
436
|
+
#
|
437
|
+
# Arguments:
|
438
|
+
# * path: The directory to search within
|
439
|
+
# * query: The query to search on (3 character minimum)
|
440
|
+
# * file_limit: The maximum number of file entries to return/
|
441
|
+
# If the number of files exceeds this
|
442
|
+
# limit, an exception is raised. The server will return at max 10,000
|
443
|
+
# * include_deleted: Whether to include deleted files in search results
|
444
|
+
#
|
445
|
+
# Returns:
|
446
|
+
# * A Hash object with a list the metadata of the file or folders matching query
|
447
|
+
# inside path. For a detailed description of what this call returns, visit:
|
448
|
+
# https://www.dropbox.com/developers/docs#search
|
449
|
+
def search(path, query, file_limit=10000, include_deleted=false)
|
450
|
+
|
451
|
+
params = {
|
452
|
+
'query' => query,
|
453
|
+
'file_limit' => file_limit.to_s,
|
454
|
+
'include_deleted' => include_deleted.to_s
|
455
|
+
}
|
456
|
+
|
457
|
+
response = @token.get(build_url("/search/#{@root}#{format_path(path)}", params))
|
458
|
+
parse_response(response)
|
459
|
+
|
460
|
+
end
|
461
|
+
|
462
|
+
# Retrive revisions of a file
|
463
|
+
#
|
464
|
+
# Arguments:
|
465
|
+
# * path: The file to fetch revisions for. Note that revisions
|
466
|
+
# are not available for folders.
|
467
|
+
# * rev_limit: The maximum number of file entries to return within
|
468
|
+
# a folder. The server will return at max 1,000 revisions.
|
469
|
+
#
|
470
|
+
# Returns:
|
471
|
+
# * A Hash object with a list of the metadata of the all the revisions of
|
472
|
+
# all matches files (up to rev_limit entries)
|
473
|
+
# For a detailed description of what this call returns, visit:
|
474
|
+
# https://www.dropbox.com/developers/docs#revisions
|
475
|
+
def revisions(path, rev_limit=1000)
|
476
|
+
|
477
|
+
params = {
|
478
|
+
'rev_limit' => rev_limit.to_s
|
479
|
+
}
|
480
|
+
|
481
|
+
response = @token.get(build_url("/revisions/#{@root}#{format_path(path)}", params))
|
482
|
+
parse_response(response)
|
483
|
+
|
484
|
+
end
|
485
|
+
|
486
|
+
# Restore a file to a previous revision.
|
487
|
+
#
|
488
|
+
# Arguments:
|
489
|
+
# * path: The file to restore. Note that folders can't be restored.
|
490
|
+
# * rev: A previous rev value of the file to be restored to.
|
491
|
+
#
|
492
|
+
# Returns:
|
493
|
+
# * A Hash object with a list the metadata of the file or folders restored
|
494
|
+
# For a detailed description of what this call returns, visit:
|
495
|
+
# https://www.dropbox.com/developers/docs#search
|
496
|
+
def restore(path, rev)
|
497
|
+
params = {
|
498
|
+
'rev' => rev.to_s
|
499
|
+
}
|
500
|
+
|
501
|
+
response = @token.get(build_url("/restore/#{@root}#{format_path(path)}", params))
|
502
|
+
parse_response(response)
|
503
|
+
end
|
504
|
+
|
505
|
+
# Returns a direct link to a media file
|
506
|
+
# All of Dropbox's API methods require OAuth, which may cause problems in
|
507
|
+
# situations where an application expects to be able to hit a URL multiple times
|
508
|
+
# (for example, a media player seeking around a video file). This method
|
509
|
+
# creates a time-limited URL that can be accessed without any authentication.
|
510
|
+
#
|
511
|
+
# Arguments:
|
512
|
+
# * path: The file to stream.
|
513
|
+
#
|
514
|
+
# Returns:
|
515
|
+
# * A Hash object that looks like the following:
|
516
|
+
# {'url': 'https://dl.dropbox.com/0/view/wvxv1fw6on24qw7/file.mov', 'expires': 'Thu, 16 Sep 2011 01:01:25 +0000'}
|
517
|
+
def media(path)
|
518
|
+
response = @token.get(build_url("/media/#{@root}#{format_path(path)}"))
|
519
|
+
parse_response(response)
|
520
|
+
end
|
521
|
+
|
522
|
+
# Get a URL to share a media file
|
523
|
+
# Shareable links created on Dropbox are time-limited, but don't require any
|
524
|
+
# authentication, so they can be given out freely. The time limit should allow
|
525
|
+
# at least a day of shareability, though users have the ability to disable
|
526
|
+
# a link from their account if they like.
|
527
|
+
#
|
528
|
+
# Arguments:
|
529
|
+
# * path: The file to share.
|
530
|
+
#
|
531
|
+
# Returns:
|
532
|
+
# * A Hash object that looks like the following example:
|
533
|
+
# {'url': 'http://www.dropbox.com/s/m/a2mbDa2', 'expires': 'Thu, 16 Sep 2011 01:01:25 +0000'}
|
534
|
+
# For a detailed description of what this call returns, visit:
|
535
|
+
# https://www.dropbox.com/developers/docs#share
|
536
|
+
def shares(path)
|
537
|
+
response = @token.get(build_url("/shares/#{@root}#{format_path(path)}"))
|
538
|
+
parse_response(response)
|
539
|
+
end
|
540
|
+
|
541
|
+
# Download a thumbnail for an image.
|
542
|
+
#
|
543
|
+
# Arguments:
|
544
|
+
# * from_path: The path to the file to be thumbnailed.
|
545
|
+
# * size: A string describing the desired thumbnail size. At this time,
|
546
|
+
# 'small', 'medium', and 'large' are officially supported sizes
|
547
|
+
# (32x32, 64x64, and 128x128 respectively), though others may
|
548
|
+
# be available. Check https://www.dropbox.com/developers/docs#thumbnails
|
549
|
+
# for more details. [defaults to large]
|
550
|
+
# Returns:
|
551
|
+
# * The thumbnail data
|
552
|
+
def thumbnail(from_path, size='large')
|
553
|
+
from_path = format_path(from_path, false)
|
554
|
+
|
555
|
+
raise DropboxError.new("size must be small medium or large. (not '#{size})") unless ['small','medium','large'].include?(size)
|
556
|
+
|
557
|
+
params = {
|
558
|
+
"size" => size
|
559
|
+
}
|
560
|
+
|
561
|
+
url = build_url("/thumbnails/#{@root}#{from_path}", params, content_server=true)
|
562
|
+
|
563
|
+
response = @token.get(url)
|
564
|
+
parse_response(response, raw=true)
|
565
|
+
end
|
566
|
+
|
567
|
+
def build_url(url, params=nil, content_server=false) # :nodoc:
|
568
|
+
port = 443
|
569
|
+
host = content_server ? DROPBOX_API_CONTENT_SERVER : DROPBOX_API_SERVER
|
570
|
+
versioned_url = "/#{API_VERSION}#{url}"
|
571
|
+
|
572
|
+
target = URI::Generic.new("https", nil, host, port, nil, versioned_url, nil, nil, nil)
|
573
|
+
|
574
|
+
#add a locale param if we have one
|
575
|
+
#initialize a params object is we don't have one
|
576
|
+
if @locale
|
577
|
+
(params ||= {})['locale']=@locale
|
578
|
+
end
|
579
|
+
|
580
|
+
if params
|
581
|
+
target.query = params.collect {|k,v|
|
582
|
+
URI.escape(k) + "=" + URI.escape(v)
|
583
|
+
}.join("&")
|
584
|
+
end
|
585
|
+
|
586
|
+
target.to_s
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
|
591
|
+
#From the oauth spec plus "/". Slash should not be ecsaped
|
592
|
+
RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~\/]/
|
593
|
+
|
594
|
+
def format_path(path, escape=true) # :nodoc:
|
595
|
+
path = path.gsub(/\/+/,"/")
|
596
|
+
# replace multiple slashes with a single one
|
597
|
+
|
598
|
+
path = path.gsub(/^\/?/,"/")
|
599
|
+
# ensure the path starts with a slash
|
600
|
+
|
601
|
+
path.gsub(/\/?$/,"")
|
602
|
+
# ensure the path doesn't end with a slash
|
603
|
+
|
604
|
+
return URI.escape(path, RESERVED_CHARACTERS) if escape
|
605
|
+
path
|
606
|
+
end
|
607
|
+
|
data/web_file_browser.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# -------------------------------------------------------------------
|
2
|
+
# An example webapp that lets you browse and upload files to Dropbox.
|
3
|
+
# Demonstrates:
|
4
|
+
# - The webapp OAuth process.
|
5
|
+
# - The metadata() and put_file() calls.
|
6
|
+
#
|
7
|
+
# To run:
|
8
|
+
# 1. Install Sinatra $ gem install sinatra
|
9
|
+
# 2. Launch server $ ruby web_file_browser.rb
|
10
|
+
# 3. Browse to http://localhost:4567/
|
11
|
+
# -------------------------------------------------------------------
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'sinatra'
|
15
|
+
require 'pp'
|
16
|
+
require './lib/dropbox_sdk'
|
17
|
+
|
18
|
+
# Get your app's key and secret from https://www.dropbox.com/developers/
|
19
|
+
APP_KEY = ''
|
20
|
+
APP_SECRET = ''
|
21
|
+
ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
|
22
|
+
#The default is :app_folder, but your application might be
|
23
|
+
#set to have full :dropbox access. Check your app at
|
24
|
+
#https://www.dropbox.com/developers/apps
|
25
|
+
|
26
|
+
# -------------------------------------------------------------------
|
27
|
+
# OAuth stuff
|
28
|
+
|
29
|
+
get '/oauth-start' do
|
30
|
+
# OAuth Step 1: Get a request token from Dropbox.
|
31
|
+
db_session = DropboxSession.new(APP_KEY, APP_SECRET)
|
32
|
+
begin
|
33
|
+
db_session.get_request_token
|
34
|
+
rescue DropboxError => e
|
35
|
+
return html_page "Exception in OAuth step 1", "<p>#{h e}</p>"
|
36
|
+
end
|
37
|
+
|
38
|
+
session[:request_db_session] = db_session.serialize
|
39
|
+
|
40
|
+
# OAuth Step 2: Send the user to the Dropbox website so they can authorize
|
41
|
+
# our app. After the user authorizes our app, Dropbox will redirect them
|
42
|
+
# to our '/oauth-callback' endpoint.
|
43
|
+
auth_url = db_session.get_authorize_url url('/oauth-callback')
|
44
|
+
redirect auth_url
|
45
|
+
end
|
46
|
+
|
47
|
+
get '/oauth-callback' do
|
48
|
+
# Finish OAuth Step 2
|
49
|
+
ser = session[:request_db_session]
|
50
|
+
unless ser
|
51
|
+
return html_page "Error in OAuth step 2", "<p>Couldn't find OAuth state in session.</p>"
|
52
|
+
end
|
53
|
+
db_session = DropboxSession.deserialize(ser)
|
54
|
+
|
55
|
+
# OAuth Step 3: Get an access token from Dropbox.
|
56
|
+
begin
|
57
|
+
db_session.get_access_token
|
58
|
+
rescue DropboxError => e
|
59
|
+
return html_page "Exception in OAuth step 3", "<p>#{h e}</p>"
|
60
|
+
end
|
61
|
+
session.delete(:request_db_session)
|
62
|
+
session[:authorized_db_session] = db_session.serialize
|
63
|
+
redirect url('/')
|
64
|
+
# In this simple example, we store the authorized DropboxSession in the web
|
65
|
+
# session hash. A "real" webapp might store it somewhere more persistent.
|
66
|
+
end
|
67
|
+
|
68
|
+
# If we already have an authorized DropboxSession, returns a DropboxClient.
|
69
|
+
def get_db_client
|
70
|
+
if session[:authorized_db_session]
|
71
|
+
db_session = DropboxSession.deserialize(session[:authorized_db_session])
|
72
|
+
begin
|
73
|
+
return DropboxClient.new(db_session, ACCESS_TYPE)
|
74
|
+
rescue DropboxAuthError => e
|
75
|
+
# The stored session didn't work. Fall through and start OAuth.
|
76
|
+
session[:authorized_db_session].delete
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# -------------------------------------------------------------------
|
82
|
+
# File/folder display stuff
|
83
|
+
|
84
|
+
get '/' do
|
85
|
+
# Get the DropboxClient object. Redirect to OAuth flow if necessary.
|
86
|
+
db_client = get_db_client
|
87
|
+
unless db_client
|
88
|
+
redirect url("/oauth-start")
|
89
|
+
end
|
90
|
+
|
91
|
+
# Call DropboxClient.metadata
|
92
|
+
path = params[:path] || '/'
|
93
|
+
begin
|
94
|
+
entry = db_client.metadata(path)
|
95
|
+
rescue DropboxAuthError => e
|
96
|
+
session.delete(:authorized_db_session) # An auth error means the db_session is probably bad
|
97
|
+
return html_page "Dropbox auth error", "<p>#{h e}</p>"
|
98
|
+
rescue DropboxError => e
|
99
|
+
if e.http_response.code == '404'
|
100
|
+
return html_page "Path not found: #{h path}", ""
|
101
|
+
else
|
102
|
+
return html_page "Dropbox API error", "<pre>#{h e.http_response}</pre>"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if entry['is_dir']
|
107
|
+
render_folder(db_client, entry)
|
108
|
+
else
|
109
|
+
render_file(db_client, entry)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def render_folder(db_client, entry)
|
114
|
+
# Provide an upload form (so the user can add files to this folder)
|
115
|
+
out = "<form action='/upload' method='post' enctype='multipart/form-data'>"
|
116
|
+
out += "<label for='file'>Upload file:</label> <input name='file' type='file'/>"
|
117
|
+
out += "<input type='submit' value='Upload'/>"
|
118
|
+
out += "<input name='folder' type='hidden' value='#{h entry['path']}'/>"
|
119
|
+
out += "</form>" # TODO: Add a token to counter CSRF attacks.
|
120
|
+
# List of folder contents
|
121
|
+
entry['contents'].each do |child|
|
122
|
+
cp = child['path'] # child path
|
123
|
+
cn = File.basename(cp) # child name
|
124
|
+
if (child['is_dir']) then cn += '/' end
|
125
|
+
out += "<div><a style='text-decoration: none' href='/?path=#{h cp}'>#{h cn}</a></div>"
|
126
|
+
end
|
127
|
+
|
128
|
+
html_page "Folder: #{entry['path']}", out
|
129
|
+
end
|
130
|
+
|
131
|
+
def render_file(db_client, entry)
|
132
|
+
# Just dump out metadata hash
|
133
|
+
html_page "File: #{entry['path']}", "<pre>#{h entry.pretty_inspect}</pre>"
|
134
|
+
end
|
135
|
+
|
136
|
+
# -------------------------------------------------------------------
|
137
|
+
# File upload handler
|
138
|
+
|
139
|
+
post '/upload' do
|
140
|
+
# Check POST parameter.
|
141
|
+
file = params[:file]
|
142
|
+
unless file && (temp_file = file[:tempfile]) && (name = file[:filename])
|
143
|
+
return html_page "Upload error", "<p>No file selected.</p>"
|
144
|
+
end
|
145
|
+
|
146
|
+
# Get the DropboxClient object.
|
147
|
+
db_client = get_db_client
|
148
|
+
unless db_client
|
149
|
+
return html_page "Upload error", "<p>Not linked with a Dropbox account.</p>"
|
150
|
+
end
|
151
|
+
|
152
|
+
# Call DropboxClient.put_file
|
153
|
+
begin
|
154
|
+
entry = db_client.put_file("#{params[:folder]}/#{name}", temp_file.read)
|
155
|
+
rescue DropboxAuthError => e
|
156
|
+
session.delete(:authorized_db_session) # An auth error means the db_session is probably bad
|
157
|
+
return html_page "Dropbox auth error", "<p>#{h e}</p>"
|
158
|
+
rescue DropboxError => e
|
159
|
+
return html_page "Dropbox API error", "<p>#{h e}</p>"
|
160
|
+
end
|
161
|
+
|
162
|
+
html_page "Upload complete", "<pre>#{h entry.pretty_inspect}</pre>"
|
163
|
+
end
|
164
|
+
|
165
|
+
# -------------------------------------------------------------------
|
166
|
+
|
167
|
+
def html_page(title, body)
|
168
|
+
"<html>" +
|
169
|
+
"<head><title>#{h title}</title></head>" +
|
170
|
+
"<body><h1>#{h title}</h1>#{body}</body>" +
|
171
|
+
"</html>"
|
172
|
+
end
|
173
|
+
|
174
|
+
enable :sessions
|
175
|
+
|
176
|
+
helpers do
|
177
|
+
include Rack::Utils
|
178
|
+
alias_method :h, :escape_html
|
179
|
+
end
|
180
|
+
|
181
|
+
if APP_KEY == '' or APP_SECRET == ''
|
182
|
+
puts "You must set APP_KEY and APP_SECRET at the top of \"#{__FILE__}\"!"
|
183
|
+
exit 1
|
184
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dropbox-sdk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 31098137
|
5
|
+
prerelease: 4
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- beta
|
10
|
+
version: 1.0.beta
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Dropbox, Inc.
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-28 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: oauth
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: json
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: " A library that provides a plain function-call interface to the\n Dropbox API web endpoints.\n"
|
49
|
+
email:
|
50
|
+
- support-api@dropbox.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- CHANGELOG
|
59
|
+
- LICENSE
|
60
|
+
- README
|
61
|
+
- cli_example.rb
|
62
|
+
- dropbox_controller.rb
|
63
|
+
- web_file_browser.rb
|
64
|
+
- lib/dropbox_sdk.rb
|
65
|
+
homepage: http://www.dropbox.com/developers/
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 3
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 25
|
88
|
+
segments:
|
89
|
+
- 1
|
90
|
+
- 3
|
91
|
+
- 1
|
92
|
+
version: 1.3.1
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.8.6
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Dropbox REST API Client.
|
100
|
+
test_files: []
|
101
|
+
|