Merge lp:~axiometry/shutter/devel into lp:shutter
- devel
- Merge into devel
Proposed by
Marco van Hilst
Status: | Merged |
---|---|
Merge reported by: | Mario Kemper (Romario) |
Merged at revision: | not available |
Proposed branch: | lp:~axiometry/shutter/devel |
Merge into: | lp:shutter |
Diff against target: |
989 lines (+905/-17) 2 files modified
share/shutter/resources/modules/WebService/Dropbox.pm (+881/-0) share/shutter/resources/system/upload_plugins/upload/Dropbox.pm (+24/-17) |
To merge this branch: | bzr merge lp:~axiometry/shutter/devel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mario Kemper (Romario) | Approve | ||
Review via email: mp+228554@code.launchpad.net |
Commit message
Description of the change
Update Dropbox plugin to generate direct links via the /media Dropbox API.
* Switch to WebService::Dropbox
* Use WebService:
* Use WebService:
* Uses LWP backend just as Net::Dropbox::API did
Add support for changing the upload folder.
* Uses the config in ~/.dropbox-
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'share/shutter/resources/modules/WebService' |
2 | === added file 'share/shutter/resources/modules/WebService/Dropbox.pm' |
3 | --- share/shutter/resources/modules/WebService/Dropbox.pm 1970-01-01 00:00:00 +0000 |
4 | +++ share/shutter/resources/modules/WebService/Dropbox.pm 2014-07-28 18:27:24 +0000 |
5 | @@ -0,0 +1,881 @@ |
6 | +package WebService::Dropbox; |
7 | +use strict; |
8 | +use warnings; |
9 | +use Carp (); |
10 | +use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK SEEK_SET SEEK_END); |
11 | +use JSON; |
12 | +use Net::OAuth; |
13 | +use URI; |
14 | +use URI::Escape; |
15 | + |
16 | +our $VERSION = '1.21'; |
17 | + |
18 | +my $request_token_url = 'https://api.dropbox.com/1/oauth/request_token'; |
19 | +my $access_token_url = 'https://api.dropbox.com/1/oauth/access_token'; |
20 | +my $authorize_url = 'https://www.dropbox.com/1/oauth/authorize'; |
21 | + |
22 | +__PACKAGE__->mk_accessors(qw/ |
23 | + key |
24 | + secret |
25 | + request_token |
26 | + request_secret |
27 | + access_token |
28 | + access_secret |
29 | + root |
30 | + |
31 | + no_decode_json |
32 | + error |
33 | + code |
34 | + request_url |
35 | + request_method |
36 | + timeout |
37 | +/); |
38 | + |
39 | +$WebService::Dropbox::USE_LWP = 0; |
40 | + |
41 | +sub import { |
42 | + eval { |
43 | + require Furl::HTTP; |
44 | + require IO::Socket::SSL; |
45 | + };if ($@) { |
46 | + __PACKAGE__->use_lwp; |
47 | + } |
48 | +} |
49 | + |
50 | +sub use_lwp { |
51 | + require LWP::UserAgent; |
52 | + require HTTP::Request; |
53 | + $WebService::Dropbox::USE_LWP++; |
54 | +} |
55 | + |
56 | +sub new { |
57 | + my ($class, $args) = @_; |
58 | + |
59 | + bless { |
60 | + key => $args->{key} || '', |
61 | + secret => $args->{secret} || '', |
62 | + request_token => $args->{request_token} || '', |
63 | + request_secret => $args->{request_secret} || '', |
64 | + access_token => $args->{access_token} || '', |
65 | + access_secret => $args->{access_secret} || '', |
66 | + root => $args->{root} || 'dropbox', |
67 | + timeout => $args->{timeout} || (60 * 60 * 24), |
68 | + no_decode_json => $args->{no_decode_json} || 0, |
69 | + no_uri_escape => $args->{no_uri_escape} || 0, |
70 | + env_proxy => $args->{lwp_env_proxy} || $args->{env_proxy} || 0, |
71 | + }, $class; |
72 | +} |
73 | + |
74 | +sub login { |
75 | + my ($self, $callback_url) = @_; |
76 | + |
77 | + my $body = $self->api({ |
78 | + method => 'POST', |
79 | + url => $request_token_url |
80 | + }) or return; |
81 | + |
82 | + my $response = Net::OAuth->response('request token')->from_post_body($body); |
83 | + $self->request_token($response->token); |
84 | + $self->request_secret($response->token_secret); |
85 | + |
86 | + my $url = URI->new($authorize_url); |
87 | + $url->query_form( |
88 | + oauth_token => $response->token, |
89 | + oauth_callback => $callback_url |
90 | + ); |
91 | + $url->as_string; |
92 | +} |
93 | + |
94 | +sub auth { |
95 | + my $self = shift; |
96 | + |
97 | + my $body = $self->api({ |
98 | + method => 'POST', |
99 | + url => $access_token_url |
100 | + }) or return; |
101 | + |
102 | + my $response = Net::OAuth->response('access token')->from_post_body($body); |
103 | + $self->access_token($response->token); |
104 | + $self->access_secret($response->token_secret); |
105 | +} |
106 | + |
107 | +sub account_info { |
108 | + my $self = shift; |
109 | + |
110 | + $self->api_json({ |
111 | + url => 'https://api.dropbox.com/1/account/info' |
112 | + }); |
113 | +} |
114 | + |
115 | +sub files { |
116 | + my ($self, $path, $output, $params, $opts) = @_; |
117 | + |
118 | + $opts ||= {}; |
119 | + if (ref $output eq 'CODE') { |
120 | + $opts->{write_code} = $output; # code ref |
121 | + } elsif (ref $output) { |
122 | + $opts->{write_file} = $output; # file handle |
123 | + binmode $opts->{write_file}; |
124 | + } else { |
125 | + open $opts->{write_file}, '>', $output; # file path |
126 | + Carp::croak("invalid output, output must be code ref or filehandle or filepath.") |
127 | + unless $opts->{write_file}; |
128 | + binmode $opts->{write_file}; |
129 | + } |
130 | + $self->api({ |
131 | + url => $self->url('https://api-content.dropbox.com/1/files/' . $self->root, $path), |
132 | + extra_params => $params, |
133 | + %$opts |
134 | + }); |
135 | + |
136 | + return if $self->error; |
137 | + return 1; |
138 | +} |
139 | + |
140 | +sub files_put { |
141 | + my ($self, $path, $content, $params, $opts) = @_; |
142 | + |
143 | + $opts ||= {}; |
144 | + $self->api_json({ |
145 | + method => 'PUT', |
146 | + url => $self->url('https://api-content.dropbox.com/1/files_put/' . $self->root, $path), |
147 | + content => $content, |
148 | + extra_params => $params, |
149 | + %$opts |
150 | + }); |
151 | +} |
152 | + |
153 | +sub files_post { |
154 | + my ($self, $path, $content, $params, $opts) = @_; |
155 | + |
156 | + $opts ||= {}; |
157 | + $self->api_json({ |
158 | + method => 'POST', |
159 | + url => $self->url('https://api-content.dropbox.com/1/files/' . $self->root, $path), |
160 | + content => $content, |
161 | + extra_params => $params, |
162 | + %$opts |
163 | + }); |
164 | +} |
165 | + |
166 | +sub files_put_chunked { |
167 | + my ($self, $path, $content, $params, $opts, $limit) = @_; |
168 | + |
169 | + $limit ||= 4 * 1024 * 1024; # A typical chunk is 4 MB |
170 | + |
171 | + my $upload; |
172 | + $upload = sub { |
173 | + my $data = shift; |
174 | + my $buf; |
175 | + my $total = $limit; |
176 | + my $chunk = 1024; |
177 | + my $tmp = File::Temp->new; |
178 | + while (my $read = read($content, $buf, $chunk)) { |
179 | + $tmp->print($buf); |
180 | + $total -= $read; |
181 | + if ($chunk > $total) { |
182 | + $chunk = $total; |
183 | + } |
184 | + last unless $chunk; |
185 | + } |
186 | + |
187 | + if ($total == $limit) { |
188 | + $data->{upload_id} or die 'read error.'; |
189 | + return $self->commit_chunked_upload( |
190 | + $path, { |
191 | + upload_id => $data->{upload_id}, |
192 | + ( $params ? %$params : () ) |
193 | + }, $opts) || die $self->error; |
194 | + } |
195 | + |
196 | + $tmp->flush; |
197 | + $tmp->seek(0, 0); |
198 | + $data = $self->chunked_upload($tmp, { |
199 | + ( $data ? %$data : () ), |
200 | + ( $params ? %$params : () ) |
201 | + }, $opts) or die $self->error; |
202 | + $upload->({ |
203 | + upload_id => $data->{upload_id}, |
204 | + offset => $data->{offset} |
205 | + }); |
206 | + }; |
207 | + $upload->(); |
208 | +} |
209 | + |
210 | +sub chunked_upload { |
211 | + my ($self, $content, $params, $opts) = @_; |
212 | + |
213 | + $opts ||= {}; |
214 | + $self->api_json({ |
215 | + method => 'PUT', |
216 | + url => $self->url('https://api-content.dropbox.com/1/chunked_upload', ''), |
217 | + content => $content, |
218 | + extra_params => $params, |
219 | + %$opts |
220 | + }); |
221 | +} |
222 | + |
223 | +sub commit_chunked_upload { |
224 | + my ($self, $path, $params, $opts) = @_; |
225 | + |
226 | + $opts ||= {}; |
227 | + $self->api_json({ |
228 | + method => 'POST', |
229 | + url => $self->url('https://api-content.dropbox.com/1/commit_chunked_upload/' . $self->root, $path), |
230 | + extra_params => $params, |
231 | + %$opts |
232 | + }); |
233 | +} |
234 | + |
235 | +sub metadata { |
236 | + my ($self, $path, $params) = @_; |
237 | + |
238 | + $self->api_json({ |
239 | + url => $self->url('https://api.dropbox.com/1/metadata/' . $self->root, $path), |
240 | + extra_params => $params |
241 | + }); |
242 | +} |
243 | + |
244 | +sub delta { |
245 | + my ($self, $params) = @_; |
246 | + |
247 | + $self->api_json({ |
248 | + method => 'POST', |
249 | + url => $self->url('https://api.dropbox.com/1/delta', ''), |
250 | + extra_params => $params |
251 | + }); |
252 | +} |
253 | + |
254 | +sub revisions { |
255 | + my ($self, $path, $params) = @_; |
256 | + |
257 | + $self->api_json({ |
258 | + url => $self->url('https://api.dropbox.com/1/revisions/' . $self->root, $path), |
259 | + extra_params => $params |
260 | + }); |
261 | +} |
262 | + |
263 | +sub restore { |
264 | + my ($self, $path, $params) = @_; |
265 | + |
266 | + $self->api_json({ |
267 | + method => 'POST', |
268 | + url => $self->url('https://api.dropbox.com/1/restore/' . $self->root, $path), |
269 | + extra_params => $params |
270 | + }); |
271 | +} |
272 | + |
273 | +sub search { |
274 | + my ($self, $path, $params) = @_; |
275 | + |
276 | + $self->api_json({ |
277 | + url => $self->url('https://api.dropbox.com/1/search/' . $self->root, $path), |
278 | + extra_params => $params |
279 | + }); |
280 | +} |
281 | + |
282 | +sub shares { |
283 | + my ($self, $path, $params) = @_; |
284 | + |
285 | + $self->api_json({ |
286 | + method => 'POST', |
287 | + url => $self->url('https://api.dropbox.com/1/shares/' . $self->root, $path), |
288 | + extra_params => $params |
289 | + }); |
290 | +} |
291 | + |
292 | +sub media { |
293 | + my ($self, $path, $params) = @_; |
294 | + |
295 | + $self->api_json({ |
296 | + method => 'POST', |
297 | + url => $self->url('https://api.dropbox.com/1/media/' . $self->root, $path), |
298 | + extra_params => $params |
299 | + }); |
300 | +} |
301 | + |
302 | +sub copy_ref { |
303 | + my ($self, $path, $params) = @_; |
304 | + |
305 | + $self->api_json({ |
306 | + method => 'GET', |
307 | + url => $self->url('https://api.dropbox.com/1/copy_ref/' . $self->root, $path), |
308 | + extra_params => $params |
309 | + }); |
310 | +} |
311 | + |
312 | +sub thumbnails { |
313 | + my ($self, $path, $output, $params, $opts) = @_; |
314 | + |
315 | + $opts ||= {}; |
316 | + if (ref $output eq 'CODE') { |
317 | + $opts->{write_code} = $output; # code ref |
318 | + } elsif (ref $output) { |
319 | + $opts->{write_file} = $output; # file handle |
320 | + binmode $opts->{write_file}; |
321 | + } else { |
322 | + open $opts->{write_file}, '>', $output; # file path |
323 | + Carp::croak("invalid output, output must be code ref or filehandle or filepath.") |
324 | + unless $opts->{write_file}; |
325 | + binmode $opts->{write_file}; |
326 | + } |
327 | + $opts->{extra_params} = $params if $params; |
328 | + $self->api({ |
329 | + url => $self->url('https://api-content.dropbox.com/1/thumbnails/' . $self->root, $path), |
330 | + extra_params => $params, |
331 | + %$opts, |
332 | + }); |
333 | + return if $self->error; |
334 | + return 1; |
335 | +} |
336 | + |
337 | +sub create_folder { |
338 | + my ($self, $path, $params) = @_; |
339 | + |
340 | + $params ||= {}; |
341 | + $params->{root} ||= $self->root; |
342 | + $params->{path} = $self->path($path); |
343 | + |
344 | + $self->api_json({ |
345 | + method => 'POST', |
346 | + url => $self->url('https://api.dropbox.com/1/fileops/create_folder', ''), |
347 | + extra_params => $params |
348 | + }); |
349 | +} |
350 | + |
351 | +sub copy { |
352 | + my ($self, $from, $to_path, $params) = @_; |
353 | + |
354 | + $params ||= {}; |
355 | + $params->{root} ||= $self->root; |
356 | + $params->{to_path} = $self->path($to_path); |
357 | + if (ref $from) { |
358 | + $params->{from_copy_ref} = $from->{copy_ref}; |
359 | + } else { |
360 | + $params->{from_path} = $self->path($from); |
361 | + } |
362 | + |
363 | + $self->api_json({ |
364 | + method => 'POST', |
365 | + url => $self->url('https://api.dropbox.com/1/fileops/copy', ''), |
366 | + extra_params => $params |
367 | + }); |
368 | +} |
369 | + |
370 | +sub move { |
371 | + my ($self, $from_path, $to_path, $params) = @_; |
372 | + |
373 | + $params ||= {}; |
374 | + $params->{root} ||= $self->root; |
375 | + $params->{from_path} = $self->path($from_path); |
376 | + $params->{to_path} = $self->path($to_path); |
377 | + |
378 | + $self->api_json({ |
379 | + method => 'POST', |
380 | + url => $self->url('https://api.dropbox.com/1/fileops/move', ''), |
381 | + extra_params => $params |
382 | + }); |
383 | +} |
384 | + |
385 | +sub delete { |
386 | + my ($self, $path, $params) = @_; |
387 | + |
388 | + $params ||= {}; |
389 | + $params->{root} ||= $self->root; |
390 | + $params->{path} ||= $self->path($path); |
391 | + $self->api_json({ |
392 | + method => 'POST', |
393 | + url => $self->url('https://api.dropbox.com/1/fileops/delete', ''), |
394 | + extra_params => $params |
395 | + }); |
396 | +} |
397 | + |
398 | +# private |
399 | + |
400 | +sub api { |
401 | + my ($self, $args) = @_; |
402 | + |
403 | + $args->{method} ||= 'GET'; |
404 | + $args->{url} = $self->oauth_request_url($args); |
405 | + |
406 | + $self->request_url($args->{url}); |
407 | + $self->request_method($args->{method}); |
408 | + |
409 | + return $self->api_lwp($args) if $WebService::Dropbox::USE_LWP; |
410 | + |
411 | + my ($minor_version, $code, $msg, $headers, $body) = $self->furl->request(%$args); |
412 | + |
413 | + $self->code($code); |
414 | + if ($code != 200) { |
415 | + $self->error($body); |
416 | + return; |
417 | + } else { |
418 | + $self->error(undef); |
419 | + } |
420 | + |
421 | + return $body; |
422 | +} |
423 | + |
424 | +sub api_lwp { |
425 | + my ($self, $args) = @_; |
426 | + |
427 | + my $headers = []; |
428 | + if ($args->{write_file}) { |
429 | + $args->{write_code} = sub { |
430 | + my $buf = shift; |
431 | + $args->{write_file}->print($buf); |
432 | + }; |
433 | + } |
434 | + if ($args->{content}) { |
435 | + my $buf; |
436 | + my $content = delete $args->{content}; |
437 | + $args->{content} = sub { |
438 | + read($content, $buf, 1024); |
439 | + return $buf; |
440 | + }; |
441 | + my $assert = sub { |
442 | + $_[0] or Carp::croak( |
443 | + "Failed to $_[1] for Content-Length: $!", |
444 | + ); |
445 | + }; |
446 | + $assert->(defined(my $cur_pos = tell($content)), 'tell'); |
447 | + $assert->(seek($content, 0, SEEK_END), 'seek'); |
448 | + $assert->(defined(my $end_pos = tell($content)), 'tell'); |
449 | + $assert->(seek($content, $cur_pos, SEEK_SET), 'seek'); |
450 | + my $content_length = $end_pos - $cur_pos; |
451 | + push @$headers, 'Content-Length' => $content_length; |
452 | + } |
453 | + if ($args->{headers}) { |
454 | + push @$headers, @{ $args->{headers} }; |
455 | + } |
456 | + my $req = HTTP::Request->new($args->{method}, $args->{url}, $headers, $args->{content}); |
457 | + my $ua = LWP::UserAgent->new; |
458 | + $ua->timeout($self->timeout); |
459 | + $ua->env_proxy if $self->{env_proxy}; |
460 | + my $res = $ua->request($req, $args->{write_code}); |
461 | + $self->code($res->code); |
462 | + if ($res->is_success) { |
463 | + $self->error(undef); |
464 | + } else { |
465 | + $self->error($res->decoded_content); |
466 | + } |
467 | + return $res->decoded_content; |
468 | +} |
469 | + |
470 | +sub api_json { |
471 | + my ($self, $args) = @_; |
472 | + |
473 | + my $body = $self->api($args) or return; |
474 | + return if $self->error; |
475 | + return $body if $self->no_decode_json; |
476 | + return decode_json($body); |
477 | +} |
478 | + |
479 | +sub oauth_request_url { |
480 | + my ($self, $args) = @_; |
481 | + |
482 | + Carp::croak("missing url.") unless $args->{url}; |
483 | + Carp::croak("missing method.") unless $args->{method}; |
484 | + |
485 | + my ($type, $token, $token_secret); |
486 | + if ($args->{url} eq $request_token_url) { |
487 | + $type = 'request token'; |
488 | + } elsif ($args->{url} eq $access_token_url) { |
489 | + Carp::croak("missing request_token.") unless $self->request_token; |
490 | + Carp::croak("missing request_secret.") unless $self->request_secret; |
491 | + $type = 'access token'; |
492 | + $token = $self->request_token; |
493 | + $token_secret = $self->request_secret; |
494 | + } else { |
495 | + Carp::croak("missing access_token, please `\$dropbox->auth;`.") unless $self->access_token; |
496 | + Carp::croak("missing access_secret, please `\$dropbox->auth;`.") unless $self->access_secret; |
497 | + $type = 'protected resource'; |
498 | + $token = $self->access_token; |
499 | + $token_secret = $self->access_secret; |
500 | + } |
501 | + |
502 | + my $request = Net::OAuth->request($type)->new( |
503 | + consumer_key => $self->key, |
504 | + consumer_secret => $self->secret, |
505 | + request_url => $args->{url}, |
506 | + request_method => uc($args->{method}), |
507 | + signature_method => 'PLAINTEXT', # HMAC-SHA1 can't delete %20.txt bug... |
508 | + timestamp => time, |
509 | + nonce => $self->nonce, |
510 | + token => $token, |
511 | + token_secret => $token_secret, |
512 | + extra_params => $args->{extra_params}, |
513 | + ); |
514 | + $request->sign; |
515 | + $request->to_url; |
516 | +} |
517 | + |
518 | +sub furl { |
519 | + my $self = shift; |
520 | + unless ($self->{furl}) { |
521 | + $self->{furl} = Furl::HTTP->new( |
522 | + timeout => $self->timeout, |
523 | + ssl_opts => { |
524 | + SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_PEER(), |
525 | + }, |
526 | + ); |
527 | + $self->{furl}->env_proxy if $self->{env_proxy}; |
528 | + } |
529 | + $self->{furl}; |
530 | +} |
531 | + |
532 | +sub url { |
533 | + my ($self, $base, $path, $params) = @_; |
534 | + my $url = URI->new($base . uri_escape_utf8($self->path($path), q{^a-zA-Z0-9_.~/-})); |
535 | + $url->query_form($params) if $params; |
536 | + $url->as_string; |
537 | +} |
538 | + |
539 | +sub path { |
540 | + my ($self, $path) = @_; |
541 | + return '' unless defined $path; |
542 | + return '' unless length $path; |
543 | + $path =~ s|^/||; |
544 | + return '/' . $path; |
545 | +} |
546 | + |
547 | +sub nonce { |
548 | + my $length = 16; |
549 | + my @chars = ( 'A'..'Z', 'a'..'z', '0'..'9' ); |
550 | + my $ret; |
551 | + for (1..$length) { |
552 | + $ret .= $chars[int rand @chars]; |
553 | + } |
554 | + return $ret; |
555 | +} |
556 | + |
557 | +sub mk_accessors { |
558 | + my $package = shift; |
559 | + no strict 'refs'; |
560 | + foreach my $field ( @_ ) { |
561 | + *{ $package . '::' . $field } = sub { |
562 | + return $_[0]->{ $field } if scalar( @_ ) == 1; |
563 | + return $_[0]->{ $field } = scalar( @_ ) == 2 ? $_[1] : [ @_[1..$#_] ]; |
564 | + }; |
565 | + } |
566 | +} |
567 | + |
568 | +sub env_proxy { $_[0]->{env_proxy} = defined $_[1] ? $_[1] : 1 } |
569 | + |
570 | +# Backward Compatibility |
571 | +sub lwp_env_proxy { shift->env_proxy(@_) } |
572 | + |
573 | +1; |
574 | +__END__ |
575 | + |
576 | +=head1 NAME |
577 | + |
578 | +WebService::Dropbox - Perl interface to Dropbox API |
579 | + |
580 | +=head1 SYNOPSIS |
581 | + |
582 | + use WebService::Dropbox; |
583 | + |
584 | + my $dropbox = WebService::Dropbox->new({ |
585 | + key => '...', # App Key |
586 | + secret => '...' # App Secret |
587 | + }); |
588 | + |
589 | + # get access token |
590 | + if (!$access_token or !$access_secret) { |
591 | + my $url = $dropbox->login or die $dropbox->error; |
592 | + warn "Please Access URL and press Enter: $url"; |
593 | + <STDIN>; |
594 | + $dropbox->auth or die $dropbox->error; |
595 | + warn "access_token: " . $dropbox->access_token; |
596 | + warn "access_secret: " . $dropbox->access_secret; |
597 | + } else { |
598 | + $dropbox->access_token($access_token); |
599 | + $dropbox->access_secret($access_secret); |
600 | + } |
601 | + |
602 | + my $info = $dropbox->account_info or die $dropbox->error; |
603 | + |
604 | + # download |
605 | + # https://www.dropbox.com/developers/reference/api#files |
606 | + my $fh_get = IO::File->new('some file', '>'); |
607 | + $dropbox->files('make_test_folder/test.txt', $fh_get) or die $dropbox->error; |
608 | + $fh_get->close; |
609 | + |
610 | + # upload |
611 | + # https://www.dropbox.com/developers/reference/api#files_put |
612 | + my $fh_put = IO::File->new('some file'); |
613 | + $dropbox->files_put('make_test_folder/test.txt', $fh_put) or die $dropbox->error; |
614 | + $fh_put->close; |
615 | + |
616 | + # filelist(metadata) |
617 | + # https://www.dropbox.com/developers/reference/api#metadata |
618 | + my $data = $dropbox->metadata('folder_a'); |
619 | + |
620 | +=head1 DESCRIPTION |
621 | + |
622 | +WebService::Dropbox is Perl interface to Dropbox API |
623 | + |
624 | +- Support Dropbox v1 REST API |
625 | + |
626 | +- Support Furl (Fast!!!) |
627 | + |
628 | +- Streaming IO (Low Memory) |
629 | + |
630 | +- Default URI Escape (The specified path is utf8 decoded string) |
631 | + |
632 | +=head1 API |
633 | + |
634 | +=head2 login(callback_url) - get request token and request secret |
635 | + |
636 | + my $callback_url = '...'; # optional |
637 | + my $url = $dropbox->login($callback_url) or die $dropbox->error; |
638 | + warn "Please Access URL and press Enter: $url"; |
639 | + |
640 | +=head2 auth - get access token and access secret |
641 | + |
642 | + $dropbox->auth or die $dropbox->error; |
643 | + warn "access_token: " . $dropbox->access_token; |
644 | + warn "access_secret: " . $dropbox->access_secret; |
645 | + |
646 | +=head2 root - set access type |
647 | + |
648 | + # Access Type is App folder |
649 | + # Your app only needs access to a single folder within the user's Dropbox |
650 | + $dropbox->root('sandbox'); |
651 | + |
652 | + # Access Type is Full Dropbox (default) |
653 | + # Your app needs access to the user's entire Dropbox |
654 | + $dropbox->root('dropbox'); |
655 | + |
656 | +L<https://www.dropbox.com/developers/start/core> |
657 | + |
658 | +=head2 account_info |
659 | + |
660 | + my $info = $dropbox->account_info or die $dropbox->error; |
661 | + |
662 | + # { |
663 | + # "referral_link": "https://www.dropbox.com/referrals/r1a2n3d4m5s6t7", |
664 | + # "display_name": "John P. User", |
665 | + # "uid": 12345678, |
666 | + # "country": "US", |
667 | + # "quota_info": { |
668 | + # "shared": 253738410565, |
669 | + # "quota": 107374182400000, |
670 | + # "normal": 680031877871 |
671 | + # }, |
672 | + # "email": "john@example.com" |
673 | + # } |
674 | + |
675 | +L<https://www.dropbox.com/developers/reference/api#account-info> |
676 | + |
677 | +=head2 files(path, output, [params, opts]) - download (no file list, file list is metadata) |
678 | + |
679 | + # Current Rev |
680 | + my $fh_get = IO::File->new('some file', '>'); |
681 | + $dropbox->files('folder/file.txt', $fh_get) or die $dropbox->error; |
682 | + $fh_get->close; |
683 | + |
684 | + # Specified Rev |
685 | + $dropbox->files('folder/file.txt', $fh_get, { rev => ... }) or die $dropbox->error; |
686 | + |
687 | + # Code ref |
688 | + $dropbox->files('folder/file.txt', sub { |
689 | + # compatible with LWP::UserAgent and Furl::HTTP |
690 | + my $chunk = @_ == 4 ? @_[3] : $_[0]; |
691 | + print $chunk; |
692 | + }) or die $dropbox->error; |
693 | + |
694 | + # Range |
695 | + $dropbox->files('folder/file.txt', $fh_get) or die $dropbox->error; |
696 | + > "0123456789" |
697 | + $dropbox->files('folder/file.txt', $fh_get, undef, { headers => ['Range' => 'bytes=5-6'] }) or die $dropbox->error; |
698 | + > "56" |
699 | + |
700 | +L<https://www.dropbox.com/developers/reference/api#files-GET> |
701 | + |
702 | +=head2 files_put(path, input) - Uploads a files |
703 | + |
704 | + my $fh_put = IO::File->new('some file'); |
705 | + $dropbox->files_put('folder/test.txt', $fh_put) or die $dropbox->error; |
706 | + $fh_put->close; |
707 | + |
708 | + # no overwrite (default true) |
709 | + $dropbox->files_put('folder/test.txt', $fh_put, { overwrite => 0 }) or die $dropbox->error; |
710 | + # create 'folder/test (1).txt' |
711 | + |
712 | + # Specified Parent Rev |
713 | + $dropbox->files_put('folder/test.txt', $fh_put, { parent_rev => ... }) or die $dropbox->error; |
714 | + # conflict prevention |
715 | + |
716 | +L<https://www.dropbox.com/developers/reference/api#files_put> |
717 | + |
718 | +=head2 files_put_chunked(path, input) - Uploads large files by chunked_upload and commit_chunked_upload. |
719 | + |
720 | + my $fh_put = IO::File->new('some large file'); |
721 | + $dropbox->files_put('folder/test.txt', $fh_put) or die $dropbox->error; |
722 | + $fh_put->close; |
723 | + |
724 | +L<https://www.dropbox.com/developers/reference/api#chunked_upload> |
725 | + |
726 | +=head2 chunked_upload(input, [params]) - Uploads large files |
727 | + |
728 | + # large file 1/3 |
729 | + my $fh_put = IO::File->new('large file part 1'); |
730 | + my $data = $dropbox->chunked_upload($fh_put) or die $dropbox->error; |
731 | + $fh_put->close; |
732 | + |
733 | + # large file 2/3 |
734 | + $fh_put = IO::File->new('large file part 2'); |
735 | + $data = $dropbox->chunked_upload($fh_put, { |
736 | + upload_id => $data->{upload_id}, |
737 | + offset => $data->{offset} |
738 | + }) or die $dropbox->error; |
739 | + $fh_put->close; |
740 | + |
741 | + # large file 3/3 |
742 | + $fh_put = IO::File->new('large file part 3'); |
743 | + $data = $dropbox->chunked_upload($fh_put, { |
744 | + upload_id => $data->{upload_id}, |
745 | + offset => $data->{offset} |
746 | + }) or die $dropbox->error; |
747 | + $fh_put->close; |
748 | + |
749 | + # commit |
750 | + $dropbox->commit_chunked_upload('folder/test.txt', { |
751 | + upload_id => $data->{upload_id} |
752 | + }) or die $dropbox->error; |
753 | + |
754 | +L<https://www.dropbox.com/developers/reference/api#chunked_upload> |
755 | + |
756 | +=head2 commit_chunked_upload(path, params) - Completes an upload initiated by the chunked_upload method. |
757 | + |
758 | + $dropbox->commit_chunked_upload('folder/test.txt', { |
759 | + upload_id => $data->{upload_id} |
760 | + }) or die $dropbox->error; |
761 | + |
762 | +L<https://www.dropbox.com/developers/reference/api#commit_chunked_upload> |
763 | + |
764 | +=head2 copy(from_path or from_copy_ref, to_path) |
765 | + |
766 | + # from_path |
767 | + $dropbox->copy('folder/test.txt', 'folder/test_copy.txt') or die $dropbox->error; |
768 | + |
769 | + # from_copy_ref |
770 | + my $copy_ref = $dropbox->copy_ref('folder/test.txt') or die $dropbox->error; |
771 | + |
772 | + $dropbox->copy($copy_ref, 'folder/test_copy.txt') or die $dropbox->error; |
773 | + |
774 | +L<https://www.dropbox.com/developers/reference/api#fileops-copy> |
775 | + |
776 | +=head2 move(from_path, to_path) |
777 | + |
778 | + $dropbox->move('folder/test.txt', 'folder/test_move.txt') or die $dropbox->error; |
779 | + |
780 | +L<https://www.dropbox.com/developers/reference/api#fileops-move> |
781 | + |
782 | +=head2 delete(path) |
783 | + |
784 | + # folder delete |
785 | + $dropbox->delete('folder') or die $dropbox->error; |
786 | + |
787 | + # file delete |
788 | + $dropbox->delete('folder/test.txt') or die $dropbox->error; |
789 | + |
790 | +L<https://www.dropbox.com/developers/reference/api#fileops-delete> |
791 | + |
792 | +=head2 create_folder(path) |
793 | + |
794 | + $dropbox->create_folder('some_folder') or die $dropbox->error; |
795 | + |
796 | +L<https://www.dropbox.com/developers/reference/api#fileops-create-folder> |
797 | + |
798 | +=head2 metadata(path, [params]) - get file list |
799 | + |
800 | + my $data = $dropbox->metadata('some_folder') or die $dropbox->error; |
801 | + |
802 | + my $data = $dropbox->metadata('some_file') or die $dropbox->error; |
803 | + |
804 | + # 304 |
805 | + my $data = $dropbox->metadata('some_folder', { hash => ... }); |
806 | + return if $dropbox->code == 304; # not modified |
807 | + die $dropbox->error if $dropbox->error; |
808 | + return $data; |
809 | + |
810 | +L<https://www.dropbox.com/developers/reference/api#metadata> |
811 | + |
812 | +=head2 delta([params]) - get file list |
813 | + |
814 | + my $data = $dropbox->delta() or die $dropbox->error; |
815 | + |
816 | +L<https://www.dropbox.com/developers/reference/api#delta> |
817 | + |
818 | +=head2 revisions(path, [params]) |
819 | + |
820 | + my $data = $dropbox->revisions('some_file') or die $dropbox->error; |
821 | + |
822 | +L<https://www.dropbox.com/developers/reference/api#revisions> |
823 | + |
824 | +=head2 restore(path, [params]) |
825 | + |
826 | + # params rev is Required |
827 | + my $data = $dropbox->restore('some_file', { rev => $rev }) or die $dropbox->error; |
828 | + |
829 | +L<https://www.dropbox.com/developers/reference/api#restore> |
830 | + |
831 | +=head2 search(path, [params]) |
832 | + |
833 | + # query rev is Required |
834 | + my $data = $dropbox->search('some_file', { query => $query }) or die $dropbox->error; |
835 | + |
836 | +L<https://www.dropbox.com/developers/reference/api#search> |
837 | + |
838 | +=head2 shares(path, [params]) |
839 | + |
840 | + my $data = $dropbox->shares('some_file') or die $dropbox->error; |
841 | + |
842 | +L<https://www.dropbox.com/developers/reference/api#shares> |
843 | + |
844 | +=head2 media(path, [params]) |
845 | + |
846 | + my $data = $dropbox->media('some_file') or die $dropbox->error; |
847 | + |
848 | +L<https://www.dropbox.com/developers/reference/api#media> |
849 | + |
850 | +=head2 copy_ref(path) |
851 | + |
852 | + my $copy_ref = $dropbox->copy_ref('folder/test.txt') or die $dropbox->error; |
853 | + |
854 | + $dropbox->copy($copy_ref, 'folder/test_copy.txt') or die $dropbox->error; |
855 | + |
856 | +L<https://www.dropbox.com/developers/reference/api#copy_ref> |
857 | + |
858 | +=head2 thumbnails(path, output) |
859 | + |
860 | + my $fh_get = File::Temp->new; |
861 | + $dropbox->thumbnails('folder/file.txt', $fh_get) or die $dropbox->error; |
862 | + $fh_get->flush; |
863 | + $fh_get->seek(0, 0); |
864 | + |
865 | +L<https://www.dropbox.com/developers/reference/api#thumbnails> |
866 | + |
867 | +=head2 env_proxy |
868 | + |
869 | +enable HTTP_PROXY, NO_PROXY |
870 | + |
871 | + $dropbox->env_proxy; |
872 | + |
873 | +=head1 AUTHOR |
874 | + |
875 | +Shinichiro Aska |
876 | + |
877 | +=head1 SEE ALSO |
878 | + |
879 | +- L<https://www.dropbox.com/developers/reference/api> |
880 | + |
881 | +=head1 LICENSE |
882 | + |
883 | +This library is free software; you can redistribute it and/or modify |
884 | +it under the same terms as Perl itself. |
885 | + |
886 | +=cut |
887 | |
888 | === modified file 'share/shutter/resources/system/upload_plugins/upload/Dropbox.pm' |
889 | --- share/shutter/resources/system/upload_plugins/upload/Dropbox.pm 2013-08-25 18:40:51 +0000 |
890 | +++ share/shutter/resources/system/upload_plugins/upload/Dropbox.pm 2014-07-28 18:27:24 +0000 |
891 | @@ -70,12 +70,15 @@ |
892 | my $self = shift; |
893 | |
894 | #do custom stuff here |
895 | - use Net::Dropbox::API; |
896 | + use WebService::Dropbox; |
897 | + use IO::File; |
898 | use JSON; |
899 | use URI::Escape qw(uri_escape); |
900 | use File::Basename qw(dirname basename); |
901 | use Path::Class; |
902 | |
903 | + $WebService::Dropbox::USE_LWP = TRUE; |
904 | + |
905 | $self->{_box} = undef; |
906 | $self->{_config} = { }; |
907 | $self->{_config_file} = file( $ENV{'HOME'}, '.dropbox-api-config' ); |
908 | @@ -88,9 +91,9 @@ |
909 | |
910 | if(-f $self->{_config_file}){ |
911 | eval{ |
912 | - $self->{_config} = decode_json($self->{_config_file}->slurp); |
913 | - $self->{_box} = Net::Dropbox::API->new($self->{_config}); |
914 | - $self->{_box}->context('dropbox'); |
915 | + $self->{_config} = decode_json($self->{_config_file}->slurp); |
916 | + $self->{_config}->{upload_folder} = $self->{_config}->{upload_folder} || 'Apps/Shutter'; |
917 | + $self->{_box} = WebService::Dropbox->new($self->{_config}); |
918 | }; |
919 | if($@){ |
920 | return FALSE; |
921 | @@ -98,7 +101,8 @@ |
922 | }else{ |
923 | $self->{_config}->{key} = 'fwsv9z8slaw0c0q'; |
924 | $self->{_config}->{secret} = 'hsxflivocvav6ag'; |
925 | - $self->{_config}->{callback_url} = ''; |
926 | + $self->{_config}->{upload_folder} = 'Apps/Shutter'; |
927 | + $self->{_config}->{callback_url} = ''; |
928 | return $self->setup; |
929 | } |
930 | |
931 | @@ -116,8 +120,8 @@ |
932 | my $sd = Shutter::App::SimpleDialogs->new; |
933 | |
934 | #Authentication |
935 | - $self->{_box} = Net::Dropbox::API->new($self->{_config}); |
936 | - my $login_link = $self->{_box}->login; |
937 | + $self->{_box} = WebService::Dropbox->new($self->{_config}); |
938 | + my $login_link = $self->{_box}->login($self->{_config}->{callback_url}); |
939 | if($self->{_box}->error){ |
940 | $sd->dlg_error_message($self->{_box}->error, $d->get("There was an error receiving the authentication URL.")); |
941 | print "ERROR: There was an error while receiving the Dropbox-URL. ", $self->{_box}->error, "\n"; |
942 | @@ -155,8 +159,7 @@ |
943 | chmod 0600, $self->{_config_file}; |
944 | |
945 | #again |
946 | - $self->{_box} = Net::Dropbox::API->new($self->{_config}); |
947 | - $self->{_box}->context('dropbox'); |
948 | + $self->{_box} = WebService::Dropbox->new($self->{_config}); |
949 | |
950 | return TRUE; |
951 | } else { |
952 | @@ -187,14 +190,18 @@ |
953 | utf8::encode $upload_filename; |
954 | |
955 | eval{ |
956 | - my $res = $self->{_box}->putfile($upload_filename, "Public"); |
957 | - if($res->{'http_response_code'} == 200){ |
958 | + my $upload_file = IO::File->new($upload_filename); |
959 | + my $res = $self->{_box}->files_put($self->{_config}->{upload_folder} . "/" . basename($upload_filename), $upload_file); |
960 | + $upload_file->close(); |
961 | + |
962 | + if(! $self->{_box}->error) { |
963 | + $res = $self->{_box}->media($self->{_config}->{upload_folder} . "/" . basename($upload_filename)); |
964 | + } |
965 | + |
966 | + if(! $self->{_box}->error){ |
967 | #set status (success) |
968 | $self->{_links}{'status'} = 200; |
969 | - |
970 | - #...and filename |
971 | - my $prep_filename = basename($upload_filename); |
972 | - $self->{_links}->{'direct_link'} = "http://dl.dropbox.com/u/".$self->get_uid."/".$self->escape($prep_filename); |
973 | + $self->{_links}->{'direct_link'} = $res->{url}; |
974 | |
975 | #print all links (debug) |
976 | if( $self->{_debug_cparam}) { |
977 | @@ -203,10 +210,10 @@ |
978 | } |
979 | } |
980 | }else{ |
981 | - $self->{_links}{'status'} = $res->{'error'}; |
982 | + $self->{_links}{'status'} = $self->{_box}->error; |
983 | if($self->{_box}->error =~ m/401/){ |
984 | unlink $self->{_config_file}; |
985 | - $self->{_links}{'status'} = $res->{'error'}.": ".$d->get("Maybe you or Dropbox revoked or expired an access token. Please close this dialog and try again. Your account will be re-authenticated the next time you upload a file."); |
986 | + $self->{_links}{'status'} = $self->{_box}->error.": ".$d->get("Maybe you or Dropbox revoked or expired an access token. Please close this dialog and try again. Your account will be re-authenticated the next time you upload a file."); |
987 | } |
988 | } |
989 | }; |
Very good! Thanks for your work.