adobeshare 0.0.2

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/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2007-10-08
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 KATO Hideyuki
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/Manifest.txt ADDED
@@ -0,0 +1,12 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/adobeshare.rb
7
+ lib/adobeshare/version.rb
8
+ lib/adobeshare/client.rb
9
+ lib/adobeshare/node.rb
10
+ examples/sample.rb
11
+ examples/config.yaml.sample
12
+ setup.rb
data/README.txt ADDED
@@ -0,0 +1,70 @@
1
+ = AdobeShare::Client -- Adobe Share API for Ruby
2
+
3
+ AdobeShare::Client is a library for upload and download documents using REST APIs for Adobe Share.
4
+
5
+ * http://labs.adobe.com/technologies/share/
6
+ * http://labs.adobe.com/wiki/index.php/Share:API
7
+
8
+ == Example
9
+
10
+ === Create client class
11
+
12
+ client = AdobeShare::Client.new
13
+ if you want to access via proxy,
14
+ add proxy param Adobe::Client.new "http://your.proxy.server:8080/"
15
+
16
+ === Set credentials
17
+
18
+ client.apikey = Your API Key
19
+ client.secret = Your Shared Secret
20
+ client.username = Your Adobe ID
21
+ client.password = Your Adobe ID passowrd
22
+
23
+ === Login
24
+
25
+ client.login
26
+
27
+ === Access your Adobe Share Documents
28
+
29
+ ==== Upload
30
+
31
+ client.add_file `binary obj`, "test.pdf", "This is Test File"
32
+
33
+ ==== Download
34
+
35
+ client.get_source `nodeid`
36
+
37
+ ==== Delete
38
+
39
+ client.delete_node `nodeid`
40
+
41
+
42
+ # `nodeid` is the same as `docid` in the Flash web interface.
43
+ # `nodeid` indicates both the documents and the folders. Maybe.
44
+
45
+ === Logout
46
+ client.logout
47
+
48
+ For details, please see examples/sample.rb
49
+
50
+ == TODO
51
+
52
+ * Test::Unit !!!
53
+
54
+ and, these APIs are not implemented.
55
+ * Moving or renaming a file or folder
56
+ * Sharing a file
57
+ * Adding or removing recipients from a share
58
+
59
+ == Requirements
60
+
61
+ * httpclient
62
+ * rubygems
63
+
64
+ == Installation
65
+
66
+ * gem install adobeshare
67
+
68
+ == Author
69
+
70
+ * KATO Hideyuki <hideyuki at kato dot jp>
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,5 @@
1
+ ---
2
+ apikey: Your API Key
3
+ secret: Your Shared Secret
4
+ username: Your Adobe ID
5
+ password: Your Adobe ID passowrd
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'adobeshare'
4
+ require 'yaml'
5
+ require 'pp'
6
+
7
+ class SampleClient
8
+ def initialize
9
+ config = YAML.load_file("config.yaml")
10
+ @client = AdobeShare::Client.new
11
+ @client.apikey = config["apikey"]
12
+ @client.secret = config["secret"]
13
+ @client.username = config["username"]
14
+ @client.password = config["password"]
15
+ end
16
+
17
+ def login
18
+ puts "=== Login ==="
19
+ @client.login
20
+ puts "sessionid : #{@client.sessionid}"
21
+ #puts "secret : #{@client.secret}"
22
+ puts "name : #{@client.name}"
23
+ end
24
+
25
+ def logout
26
+ puts "=== Logout ==="
27
+ @client.logout
28
+ end
29
+
30
+ def list nodeid=nil
31
+ puts "=== Contents List ==="
32
+ node = @client.get_node
33
+ children = node[:children]
34
+ children.each{|e|
35
+ puts "#{e[:nodeid]} : #{e[:name]}" if e[:hascontent]
36
+ }
37
+ puts ""
38
+ puts children.size.to_s + " documents(or folders)"
39
+ puts " in this folder (nodeid=#{node[:nodeid]})"
40
+ end
41
+
42
+ def info nodeid
43
+ puts "=== Content Info ==="
44
+ node = @client.get_node nodeid
45
+ pp node
46
+ end
47
+
48
+ def upload filename
49
+ list
50
+ puts "=== Upload ==="
51
+ return unless filename
52
+ node = @client.get_node
53
+ data = nil
54
+ File.open(filename, "rb"){|f|
55
+ data = f.read
56
+ }
57
+ puts "Uploading file `#{filename}` ..."
58
+ @client.add_file data, File.basename(filename), "Test File", node[:nodeid]
59
+ list
60
+ end
61
+
62
+ def download nodeid
63
+ puts "=== File Download ==="
64
+ node = @client.get_node nodeid
65
+ pp node
66
+
67
+ if node[:hascontent]
68
+ filename = node[:name]
69
+ puts "Downloading file `#{filename}` ..."
70
+ data = @client.get_source node[:nodeid]
71
+ File.open(filename, "wb"){|f|
72
+ f.write(data)
73
+ }
74
+ if node[:thumbnailstate] == "1"
75
+ thumbnail = node[:name] + ".thumb.jpg"
76
+ puts "Downloading file `#{thumbnail}` ..."
77
+ data = @client.get_thumbnail node[:nodeid]
78
+ File.open(thumbnail, "wb"){|f|
79
+ f.write(data)
80
+ }
81
+ end
82
+ end
83
+ end
84
+
85
+ def delete nodeid
86
+ node = info nodeid
87
+ puts "=== File Delete ==="
88
+ puts ""
89
+ print "Are you OK? [yN]"
90
+ ans = STDIN.getc.chr.downcase
91
+ return if ans != 'y'
92
+ @client.delete_node nodeid
93
+ list
94
+ end
95
+ end
96
+
97
+ if __FILE__ == $0
98
+ sample = SampleClient.new
99
+ if ARGV[0]
100
+ command = ARGV[0]
101
+ param = ARGV[1]
102
+ sample.login
103
+ sample.send(command, param)
104
+ sample.logout
105
+ else
106
+ puts "Usage: #{$0} command param"
107
+ puts "commands:"
108
+ puts " list"
109
+ puts " info `nodeid`"
110
+ puts " upload `filename`"
111
+ puts " download `nodeid`"
112
+ puts " delete `nodeid`"
113
+ end
114
+ end
data/lib/adobeshare.rb ADDED
@@ -0,0 +1,9 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'adobeshare/version'
4
+ require 'adobeshare/client'
5
+ require 'adobeshare/node'
6
+
7
+ module AdobeShare
8
+
9
+ end
@@ -0,0 +1,303 @@
1
+ #
2
+ # Adobe Share API for Ruby
3
+ #
4
+ # Copyright (c) KATO Hideyuki, 2007. All rights reserved.
5
+ #
6
+ require 'http-access2'
7
+ require 'rexml/document'
8
+
9
+ include REXML
10
+
11
+ module AdobeShare
12
+ DEFAULT_ENDPOINT = "https://api.share.adobe.com/webservices/api/v1/" # :nodoc:
13
+ ENDPOINT_AUTH = DEFAULT_ENDPOINT + "auth/" # :nodoc:
14
+ ENDPOINT_SESSIONS = DEFAULT_ENDPOINT + "sessions/" # :nodoc:
15
+ ENDPOINT_DC = DEFAULT_ENDPOINT + "dc/" # :nodoc:
16
+
17
+ #
18
+ # Client Access Class
19
+ #
20
+ class Client
21
+ # User-Agent
22
+ UA = "AdobeShare::Client-#{VERSION::STRING}" # :nodoc:
23
+
24
+ # User's API Key:
25
+ attr_accessor :apikey
26
+ # User's session shared secret
27
+ attr_accessor :secret
28
+ # User's Adobe ID
29
+ attr_accessor :username
30
+ # User's password
31
+ attr_accessor :password
32
+ # User's name
33
+ attr_reader :name
34
+ # User's session ID
35
+ attr_reader :sessionid
36
+
37
+ #
38
+ def initialize proxy=nil
39
+ @client = HTTPAccess2::Client.new(proxy)
40
+ end
41
+
42
+ # Login to use Adobe Share service
43
+ def login
44
+ authtoken = get_authtoken
45
+ start_sessions authtoken
46
+ end
47
+
48
+ # Logout from the servce
49
+ def logout
50
+ end_sessions
51
+ end
52
+
53
+ # Get a list of nodes (files and folders) in a user's account
54
+ def get_node(nodeid=nil)
55
+ if nodeid
56
+ uri = ENDPOINT_DC + nodeid + "/"
57
+ else
58
+ uri = ENDPOINT_DC
59
+ end
60
+ res = http_client_request("GET", uri)
61
+ node = Node.new res
62
+ node.to_hash
63
+ end
64
+
65
+ # Get the source of a document
66
+ def get_source nodeid
67
+ get_node_content nodeid, RENDITION_SOURCE
68
+ end
69
+
70
+ # Get the thumbnail of a document
71
+ def get_thumbnail nodeid
72
+ get_node_content nodeid, RENDITION_THUMBNAIL
73
+ end
74
+
75
+ # Get the specified rendition of a document
76
+ def get_node_content nodeid, rendition
77
+ uri = ENDPOINT_DC + nodeid + "/" + rendition + "/"
78
+ res = http_client_request("GET", uri)
79
+ end
80
+
81
+ # Delete a node
82
+ def delete_node nodeid
83
+ uri = ENDPOINT_DC + nodeid + "/"
84
+ res = http_client_request("DELETE", uri)
85
+ end
86
+
87
+ # Upload and add file
88
+ def add_file(data, filename, description, nodeid="", renditions=true)
89
+ uri = ENDPOINT_DC + nodeid + "/"
90
+
91
+ # request xml
92
+ req_xml = Element.new "request"
93
+ file = req_xml.add_element "file"
94
+ file.add_element "name"
95
+ file.add_element "description"
96
+ file.add_element "renditions"
97
+ file.elements["name"].text = filename
98
+ file.elements["description"].text = description
99
+ file.elements["renditions"].text = renditions
100
+ # FIXME
101
+ # XXX Is there any good library or something ???
102
+ # to make multipart/form-data ...
103
+ boundary = "mime-part-separator" + Time.new.to_i.to_s
104
+ req = "--#{boundary}\r\n"
105
+ req << "Content-Disposition: form-data; name=\"request\"\r\n"
106
+ req << "\r\n"
107
+ req << req_xml.to_s
108
+ req << "\r\n"
109
+ req << "--#{boundary}\r\n"
110
+ req << "Content-Disposition: form-data; name=\"file\"; filename=\"#{URI.encode(filename)}\"\r\n"
111
+ req << "Content-Type: application/octet-stream\r\n"
112
+ req << "Content-Transfer-Encoding: binary\r\n"
113
+ req << "\r\n"
114
+ req << data
115
+ req << "\r\n"
116
+ req << "--#{boundary}--\r\n"
117
+
118
+ headers = Hash.new
119
+ headers["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
120
+ res = http_client_request("POST", uri, req, headers)
121
+ end
122
+
123
+ private
124
+
125
+ # TODO this method is not tested
126
+ # Rename a node
127
+ def rename_node(nodeid, newname)
128
+ uri = ENDPOINT_DC + nodeid + "/?newname=" + newname
129
+ res = http_client_request("MOVE", uri)
130
+ end
131
+
132
+ # TODO this method is not tested
133
+ # Move a node
134
+ def move_node(nodeid, destnodeid, newname)
135
+ uri = ENDPOINT_DC + nodeid + "/?destnodeid=" + destnodeid + "&newname=" + URI.encode(newname)
136
+ res = http_client_request("MOVE", uri)
137
+ end
138
+
139
+ # TODO this method is not tested
140
+ # Replace file in an already existing node
141
+ def replace_file(file_name, description, nodeid, renditions=true)
142
+ add_file(file_name, description, nodeid, renditions)
143
+ end
144
+
145
+ # TODO this method is not tested
146
+ # Add new folder
147
+ def add_folder(name, description, nodeid)
148
+ if nodeid
149
+ uri = ENDPOINT_DC + nodeid + "/"
150
+ else
151
+ uri = ENDPOINT_DC
152
+ end
153
+ req = Element.new "request"
154
+ folder = req.add_element "folder"
155
+ folder.add_element "name"
156
+ folder.add_element "description"
157
+ folder.elements["name"].text = name
158
+ folder.elements["description"].text = description
159
+ res = http_client_request("POST", uri, req)
160
+ doc = Document.new res
161
+ end
162
+
163
+ # TODO this method is not tested
164
+ # Share a file
165
+ def share_file(nodeid, users, message, level)
166
+ uri = ENDPOINT_DC + nodeid + "/share/"
167
+ req = Element.new "request"
168
+ share = req.add_element "share"
169
+ users.each{|e|
170
+ user = Element.new "user"
171
+ user.text = e
172
+ share.add_element user
173
+ }
174
+ req.add_element "message"
175
+ req.add_element "level"
176
+ req.elements["message"].text = message
177
+ req.elements["level"].text = level
178
+ res = http_client_request("POST", uri, req)
179
+ doc = Document.new res
180
+ end
181
+
182
+ # TODO this method is not tested
183
+ # Unshare a file
184
+ def unshare_file(nodeid)
185
+ share_file(nodeid, nil, nil, 0)
186
+ end
187
+
188
+ # TODO this method is not tested
189
+ # Update a share file
190
+ def update_share(nodeid, users_to_add, users_to_remove, message)
191
+ uri = ENDPOINT_DC + nodeid + "/share/"
192
+ req = Element.new "request"
193
+ share = req.add_element "share"
194
+ users_to_add.each{|e|
195
+ user = Element.new "user"
196
+ user.text = e
197
+ share.add_element user
198
+ }
199
+ unshare = req.add_element "unshare"
200
+ users_to_remove.each{|e|
201
+ user = Element.new "user"
202
+ user.text = e
203
+ unshare.add_element user
204
+ }
205
+ req.add_element "message"
206
+ req.add_element "level"
207
+ req.elements["message"].text = message
208
+ res = http_client_request("POST", uri, req)
209
+ doc = Document.new res
210
+ end
211
+
212
+ private
213
+
214
+ # Requesting an authorization token
215
+ def get_authtoken
216
+ raise ParameterError unless @username && @password
217
+ uri = ENDPOINT_AUTH
218
+ req = Element.new "request"
219
+ req.add_element "username"
220
+ req.add_element "password"
221
+ req.elements["username"].text = username
222
+ req.elements["password"].text = password
223
+ res = http_client_request("POST", uri, req)
224
+ doc = Document.new res
225
+ authtoken = doc.elements['/response/authtoken/'].text
226
+ authtoken
227
+ end
228
+
229
+ # Starting a new session
230
+ def start_sessions authtoken
231
+ uri = ENDPOINT_SESSIONS
232
+ req = Element.new "request"
233
+ req.add_element "authtoken"
234
+ req.elements["authtoken"].text = authtoken
235
+ res = http_client_request("POST", uri, req)
236
+ doc = Document.new res
237
+ @sessionid = doc.elements['/response/sessionid/'].text
238
+ @secret = doc.elements['/response/secret'].text
239
+ @name = doc.elements['/response/name'].text
240
+ end
241
+
242
+ # Ending a session
243
+ def end_sessions
244
+ uri = ENDPOINT_SESSIONS + @sessionid +'/'
245
+ res = http_client_request("DELETE", uri)
246
+ @sessionid = nil
247
+ @secret = nil
248
+ end
249
+
250
+ # Make HTTP Headers (Authorization)
251
+ def make_headers(method, uri)
252
+ raise ParameterError unless @apikey && @secret
253
+ time = Time.new
254
+ calltime = time.to_i.to_s + sprintf("%06d", time.tv_usec)
255
+ data = "#{method} #{uri} #{calltime}"
256
+ sig = Digest::MD5.hexdigest(data + @secret)
257
+ headers = Hash.new
258
+ auth_string = "AdobeAuth "
259
+ auth_string << "sessionid=\"#{@sessionid}\"," if @sessionid
260
+ auth_string << "apikey=\"#{@apikey}\",data=\"#{data}\",sig=\"#{sig}\""
261
+ headers["Authorization"] = auth_string
262
+ headers["User-Agent"] = UA
263
+ headers
264
+ end
265
+
266
+ # Performs a generic HTTP request.
267
+ def http_client_request(method, uri_s, request=nil, header_add={})
268
+ uri_s << "?method=MOVE" if method == "MOVE"
269
+ uri = URI.parse uri_s
270
+ headers = make_headers method, uri
271
+ headers = headers.merge header_add
272
+ case method
273
+ when "GET"
274
+ res = @client.get(uri, nil, headers)
275
+ when "POST"
276
+ res = @client.post(uri, request, headers)
277
+ when "PUT"
278
+ res = @client.put(uri, request, headers)
279
+ when "DELETE"
280
+ res = @client.delete(uri, headers)
281
+ when "MOVE"
282
+ res = @client.post(uri, request, headers)
283
+ end
284
+ body = res.body.content
285
+ status_code = res.header.response_status_code
286
+ reason_phrase = res.header.reason_phrase
287
+ if status_code >= 500
288
+ raise ServerError, "#{status_code} #{reason_phrase}"
289
+ elsif status_code >= 400
290
+ body = res.body.content
291
+ raise RequestError, "#{status_code} #{reason_phrase} (#{body})"
292
+ end
293
+ body
294
+ end
295
+ end
296
+
297
+ class ServerError < RuntimeError
298
+ end
299
+ class RequestError < RuntimeError
300
+ end
301
+ class ParameterError < RuntimeError
302
+ end
303
+ end