Merge lp:~axiometry/shutter/devel into lp:shutter

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
Reviewer Review Type Date Requested Status
Mario Kemper (Romario) Approve
Review via email: mp+228554@code.launchpad.net

Description of the change

Update Dropbox plugin to generate direct links via the /media Dropbox API.
 * Switch to WebService::Dropbox
 * Use WebService::Dropbox->files_put to upload files
 * Use WebService::Dropbox->media to generate a direct link to the screenshot file
 * Uses LWP backend just as Net::Dropbox::API did

Add support for changing the upload folder.
 * Uses the config in ~/.dropbox-api-config for backwards compatibility, and adds the key 'upload_folder'

To post a comment you must log in.
Revision history for this message
Mario Kemper (Romario) (mario-kemper) wrote :

Very good! Thanks for your work.

review: Approve

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 };

Subscribers

People subscribed via source and target branches

to status/vote changes: