line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::Google::Calendar; |
2
|
|
|
|
|
|
|
{ |
3
|
|
|
|
|
|
|
$Net::Google::Calendar::VERSION = '1.05'; |
4
|
|
|
|
|
|
|
} |
5
|
|
|
|
|
|
|
|
6
|
5
|
|
|
5
|
|
3668
|
use strict; |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
310
|
|
7
|
5
|
|
|
5
|
|
5570
|
use LWP::UserAgent; |
|
5
|
|
|
|
|
292384
|
|
|
5
|
|
|
|
|
170
|
|
8
|
5
|
|
|
5
|
|
11900
|
use HTTP::Cookies; |
|
5
|
|
|
|
|
54473
|
|
|
5
|
|
|
|
|
177
|
|
9
|
5
|
|
|
5
|
|
42
|
use HTTP::Request; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
117
|
|
10
|
5
|
|
|
5
|
|
27
|
use HTTP::Headers; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
156
|
|
11
|
5
|
|
|
5
|
|
4365
|
use HTTP::Request::Common; |
|
5
|
|
|
|
|
18838
|
|
|
5
|
|
|
|
|
407
|
|
12
|
5
|
|
|
5
|
|
7344
|
use XML::Atom::Feed; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use XML::Atom::Entry; |
14
|
|
|
|
|
|
|
use Data::Dumper; |
15
|
|
|
|
|
|
|
use Net::Google::AuthSub; |
16
|
|
|
|
|
|
|
use Net::Google::Calendar::Entry; |
17
|
|
|
|
|
|
|
use Net::Google::Calendar::Person; |
18
|
|
|
|
|
|
|
use Net::Google::Calendar::Calendar; |
19
|
|
|
|
|
|
|
use URI; |
20
|
|
|
|
|
|
|
use URI::Escape; |
21
|
|
|
|
|
|
|
use Carp qw(confess); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
use vars qw($VERSION $APP_NAME $REDIRECT_MAX); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
$APP_NAME = $Net::Google::OAuth::APP_NAME = __PACKAGE__."-${VERSION}"; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
$REDIRECT_MAX = 10; #Maximum number of redirects to allow |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# ABSTRACT: Interface to Google calendars |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 NAME |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
Net::Google::Calendar - programmatic access to Google's Calendar API |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 SYNOPSIS |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# this will only get you a read only feed |
39
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new( url => $private_url ); |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
or |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# this will get you a read-write feed. |
44
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new; |
45
|
|
|
|
|
|
|
$cal->login($username, $password); |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
or |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# this will also get you a read-write feed |
50
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new; |
51
|
|
|
|
|
|
|
$cal->auth($username, $auth_token); |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
or |
54
|
|
|
|
|
|
|
# this will again get you a read-write feed |
55
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new; |
56
|
|
|
|
|
|
|
$cal->oauth(Net::Google::OAuth); |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
or you can pass in a url to specify a particular calendar |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new( url => $non_default_url ); |
61
|
|
|
|
|
|
|
$cal->login($username, $password); |
62
|
|
|
|
|
|
|
# or $cal->auth($username, $auth_token) obviously |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
then |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
for ($cal->get_events()) { |
68
|
|
|
|
|
|
|
print $_->title."\n"; |
69
|
|
|
|
|
|
|
print $_->content->body."\n*****\n\n"; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
my $c; |
73
|
|
|
|
|
|
|
for ($cal->get_calendars) { |
74
|
|
|
|
|
|
|
print $_->title."\n"; |
75
|
|
|
|
|
|
|
print $_->id."\n\n"; |
76
|
|
|
|
|
|
|
$c = $_ if ($_->title eq 'My Non Default Calendar'); |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
$cal->set_calendar($c); |
79
|
|
|
|
|
|
|
print $cal->id." has ".scalar($cal->get_events)." events\n"; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# everything below here requires a read-write feed |
83
|
|
|
|
|
|
|
my $entry = Net::Google::Calendar::Entry->new(); |
84
|
|
|
|
|
|
|
$entry->title($title); |
85
|
|
|
|
|
|
|
$entry->content("My content"); |
86
|
|
|
|
|
|
|
$entry->location('London, England'); |
87
|
|
|
|
|
|
|
$entry->transparency('transparent'); |
88
|
|
|
|
|
|
|
$entry->status('confirmed'); |
89
|
|
|
|
|
|
|
$entry->when(DateTime->now, DateTime->now() + DateTime::Duration->new( hours => 6 ) ); |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
my $author = Net::Google::Calendar::Person->new(); |
93
|
|
|
|
|
|
|
$author->name('Foo Bar'); |
94
|
|
|
|
|
|
|
$author->email('foo@bar.com'); |
95
|
|
|
|
|
|
|
$entry->author($author); |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
By default new or updated entries are modified in place with |
98
|
|
|
|
|
|
|
any new information provided by Google. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
$cal->add_entry($entry); |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
$entry->content('Updated'); |
103
|
|
|
|
|
|
|
$cal->update_entry($entry); |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
$cal->delete_entry($entry); |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
However if you don't want the entry updated in place pass |
108
|
|
|
|
|
|
|
C in to the C method. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new( no_event_modification => 1 ); |
111
|
|
|
|
|
|
|
$cal->login($user, $pass); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
my $tmp = $cal->add_entry($entry); |
114
|
|
|
|
|
|
|
die "Couldn't add event: $@\n" unless defined $tmp; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
print "Events=".scalar($cal->get_events())."\n"; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
$tmp->content('Updated'); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
$tmp = $cal->update_entry($tmp) || die "Couldn't update ".$tmp->id.": $@\n"; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
$cal->delete_entry($tmp) || die "Couldn't delete ".$tmp->id.": $@\n"; |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head1 DESCRIPTION |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Interact with Google's new calendar using the GData API. |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=head1 AUTHENTICATION AND READ-WRITE CALENDARS |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
There are effectively four ways to get events from a Google calendar. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
You can get any public events by querying |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
http://www.google.com/calendar/feeds//public/full |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Then there are the three ways to get private entries. The first of these |
140
|
|
|
|
|
|
|
involves a magic cookie in the url like this: |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
http://www.google.com/calendar/feeds//private-/full |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
Google has information on how to find this url here |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
http://code.google.com/apis/calendar/developers_guide_protocol.html#find_feed_url |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
To use either the private or public feeds do |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new( url => $url); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Both these feeds will be read only however. This means that you won't be able to |
153
|
|
|
|
|
|
|
add, update or delete entries. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
You can also get all the private entries in a read-write feed by either logging in |
156
|
|
|
|
|
|
|
or using C. |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Logging in is the easiest. Simply do |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new; |
161
|
|
|
|
|
|
|
$cal->login($username, $password); |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
Where C<$username> and C<$password> are the same as if you were logging into the |
164
|
|
|
|
|
|
|
Google Calendar site. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
Alternatively if you don't want to use username and password (if, for example you were |
167
|
|
|
|
|
|
|
providing Calendar reading as a service on your website and didn't want to have to ask |
168
|
|
|
|
|
|
|
your users for their Google login details) you can use C. |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
http://code.google.com/apis/accounts/AuthForWebApps.html |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
Once you have an AuthSub token (or you user has supplied you with one) |
173
|
|
|
|
|
|
|
then you can login using |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
my $cal = Net::Google::Calendar->new; |
176
|
|
|
|
|
|
|
$cal->auth($username, $token); |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head1 METHODS |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=cut |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=head2 new |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
Create a new instance. C is a hash which must contain your private Google url |
185
|
|
|
|
|
|
|
as the key C unless you plan to log in or authenticate. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
See |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
http://code.google.com/apis/gdata/calendar.html#find_feed_url |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
for how to get that. |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
If you pass the option C as a psotive value then |
194
|
|
|
|
|
|
|
add_entry and update_entry will not modify the entry in place. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=cut |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub new { |
199
|
|
|
|
|
|
|
my ($class, %opts) = @_; |
200
|
|
|
|
|
|
|
$opts{_ua} = LWP::UserAgent->new( max_redirect => 0 ); |
201
|
|
|
|
|
|
|
$opts{_ua}->env_proxy; |
202
|
|
|
|
|
|
|
$opts{_auth} = Net::Google::AuthSub->new( service => 'cl' ); |
203
|
|
|
|
|
|
|
$opts{_cookie_jar} = HTTP::Cookies->new; |
204
|
|
|
|
|
|
|
$opts{no_event_modification} ||= 0; |
205
|
|
|
|
|
|
|
my $self = bless \%opts, $class; |
206
|
|
|
|
|
|
|
$self->_find_calendar_id if $opts{url}; |
207
|
|
|
|
|
|
|
return $self; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=head2 login [opt[s]] |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
Login to google. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Can optionally take a hash of options which will override the |
216
|
|
|
|
|
|
|
default login params. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=over 4 |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=item service |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Name of the Google service for which authorization is requested. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Defaults to 'cl' for calendar. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=item source |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Short string identifying your application, for logging purposes. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Defaults to 'Net::Google::Calendar-' |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=item accountType |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
Type of account to be authenticated. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Defaults to 'HOSTED_OR_GOOGLE'. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=back |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
See http://code.google.com/apis/accounts/AuthForInstalledApps.html#ClientLogin for more details. |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=cut |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub login { |
245
|
|
|
|
|
|
|
my $self = shift; |
246
|
|
|
|
|
|
|
my $user = shift; |
247
|
|
|
|
|
|
|
my $pass = shift; |
248
|
|
|
|
|
|
|
my $r = $self->{_auth}->login($user, $pass); |
249
|
|
|
|
|
|
|
my $error; |
250
|
|
|
|
|
|
|
if (!defined $r) { |
251
|
|
|
|
|
|
|
$error = $@; |
252
|
|
|
|
|
|
|
} elsif (!$r->is_success) { |
253
|
|
|
|
|
|
|
$error = $r->error; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
die "Couldn't log in - $error" if defined $error; |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
$self->{user} = $user; |
258
|
|
|
|
|
|
|
$self->_generate_url(); |
259
|
|
|
|
|
|
|
return 1; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head2 auth |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
Use the AuthSub method for calendar access. |
266
|
|
|
|
|
|
|
See http://code.google.com/apis/accounts/AuthForWebApps.html |
267
|
|
|
|
|
|
|
for details. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=cut |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub auth { |
273
|
|
|
|
|
|
|
my $self = shift; |
274
|
|
|
|
|
|
|
my $user = shift; |
275
|
|
|
|
|
|
|
my $token = shift; |
276
|
|
|
|
|
|
|
$self->{_auth}->auth($user, $token); |
277
|
|
|
|
|
|
|
$self->{user} = $user; |
278
|
|
|
|
|
|
|
$self->_generate_url(); |
279
|
|
|
|
|
|
|
return 1; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=head2 oauth Net::Google::OAuth |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Use OAuth for calendar access |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=cut |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub oauth { |
289
|
|
|
|
|
|
|
my $self = shift; |
290
|
|
|
|
|
|
|
$self->{_auth} = shift; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
sub _generate_url { |
294
|
|
|
|
|
|
|
my $self= shift; |
295
|
|
|
|
|
|
|
$self->{url} ||= $self->_get_protocol()."://google.com/calendar/feeds/$self->{user}/private/full"; |
296
|
|
|
|
|
|
|
$self->{url} =~ s!/private-[^/]+!/private!; |
297
|
|
|
|
|
|
|
$self->_find_calendar_id; |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=head2 auth_object [Net::Google::AuthSub] |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
Get or set the current C object. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=cut |
306
|
|
|
|
|
|
|
sub auth_object { |
307
|
|
|
|
|
|
|
my $self = shift; |
308
|
|
|
|
|
|
|
$self->{_auth} = shift if @_; |
309
|
|
|
|
|
|
|
return $self->{_auth}; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub _find_calendar_id { |
313
|
|
|
|
|
|
|
my $self = shift; |
314
|
|
|
|
|
|
|
($self->{calendar_id}) = ($self->{url} =~ m!/feeds/([^/]+)/!); |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=head2 ssl bool |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
Use ssl or not. Auth tokens (AuthSub and OAuth) have a scope that includes http:// or https://. Make sure you use ssl(1) if your scope is https://www.google.com/calendar/feeds/. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=cut |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
sub ssl { |
324
|
|
|
|
|
|
|
my $self = shift; |
325
|
|
|
|
|
|
|
$self->{_use_ssl} = shift; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
sub _get_protocol { |
329
|
|
|
|
|
|
|
my $self = shift; |
330
|
|
|
|
|
|
|
if ($self->{_use_ssl}) { |
331
|
|
|
|
|
|
|
return 'https'; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
return 'http'; |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=head2 get_events [ %opts ] |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Return a list of Net::Google::Calendar::Entry objects; |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
You can pass in a hash of options which map to the Google Data API's generic |
341
|
|
|
|
|
|
|
searching mechanisms plus the specific calendar ones. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
See |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
http://code.google.com/apis/gdata/protocol.html#query-requests |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
for more details. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=over 4 |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=item q |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
Full-text query string |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
When creating a query, list search terms separated by spaces, in the |
357
|
|
|
|
|
|
|
form q=term1 term2 term3. (As with all of the query parameter values, |
358
|
|
|
|
|
|
|
the spaces must be URL encoded.) The GData service returns all entries |
359
|
|
|
|
|
|
|
that match all of the search terms (like using AND between terms). Like |
360
|
|
|
|
|
|
|
Google's web search, a GData service searches on complete words (and |
361
|
|
|
|
|
|
|
related words with the same stem), not substrings. |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
To search for an exact phrase, enclose the phrase in quotation marks: |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
q => '"exact phrase' |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
To exclude entries that match a given term, use the form |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
q => '-term' |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
The search is case-insensitive. |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Example: to search for all entries that contain the exact phrase |
374
|
|
|
|
|
|
|
'Elizabeth Bennet' and the word 'Darcy' but don't contain the word |
375
|
|
|
|
|
|
|
'Austen', use the following query: |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
q => '"Elizabeth Bennet" Darcy -Austen' |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=item category |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
Category filter |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
To search in just one category do |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
category => 'Fritz' |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
You can query on multiple categories by listing multiple category parameters. For example |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
category => [ 'Fritz', 'Laurie' ] |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
returns entries that match both categories. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
To do an OR between terms, use a pipe character (|). For example |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
category => 'Fritz|Laurie' |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
returns entries that match either category. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
To exclude entries that match a given category, use the form |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
category => '-categoryname' |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
You can, of course, mix and match |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
[ 'Jo', 'Fritz|Laurie', '-Simon' ] |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
means in category |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
(Jo AND ( Fritz OR Laurie ) AND (NOT Simon)) |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
=item author |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Entry author |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
The service returns entries where the author name and/or email address |
420
|
|
|
|
|
|
|
match your query string. |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=item updated-min |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=item updated-max |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
Bounds on the entry publication date. |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
Use DateTime objects or the RFC 3339 timestamp format. For example: |
429
|
|
|
|
|
|
|
2005-08-09T10:57:00-08:00. |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
The lower bound is inclusive, whereas the upper bound is exclusive. |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=item start-min |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=item start-max |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
Respectively, the earliest event start time to match (If not specified, |
438
|
|
|
|
|
|
|
default is 1970-01-01) and the latest event start time to match (If |
439
|
|
|
|
|
|
|
not specified, default is 2031-01-01). |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Use DateTime objects or the RFC 3339 timestamp format. For example: |
442
|
|
|
|
|
|
|
2005-08-09T10:57:00-08:00. |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
The lower bound is inclusive, whereas the upper bound is exclusive. |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=item start-index |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
1-based index of the first result to be retrieved |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
Note that this isn't a general cursoring mechanism. If you first send a |
451
|
|
|
|
|
|
|
query with |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
start-index => 1, |
454
|
|
|
|
|
|
|
max-results => 10 |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
and then send another query with |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
start-index => 11, |
459
|
|
|
|
|
|
|
max-results => 10 |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
the service cannot guarantee that the results are equivalent to |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
start-index => 1 |
464
|
|
|
|
|
|
|
max-results => 20 |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
because insertions and deletions could have taken place in between the |
467
|
|
|
|
|
|
|
two queries. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=item max-results |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
Maximum number of results to be retrieved. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
For any service that has a default max-results value (to limit default |
474
|
|
|
|
|
|
|
feed size), you can specify a very large number if you want to receive |
475
|
|
|
|
|
|
|
the entire feed. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=item entryID |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
ID of a specific entry to be retrieved. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
If you specify an entry ID, you can't specify any other parameters. |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=back |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
=cut |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
sub get_events { |
488
|
|
|
|
|
|
|
my ($self, %opts) = @_; |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
# check for DateTime objects and convert them to RFC 3339 |
492
|
|
|
|
|
|
|
for (keys %opts) { |
493
|
|
|
|
|
|
|
next unless UNIVERSAL::isa($opts{$_}, 'DateTime'); |
494
|
|
|
|
|
|
|
# maybe we should chuck an error if it's a Ref and *not* a DateTime |
495
|
|
|
|
|
|
|
#next unless $opts{$_}->isa('DateTime'); |
496
|
|
|
|
|
|
|
$opts{$_} = $opts{$_}->iso8601 . 'Z'; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
my $url = URI->new($self->{url}); |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
# special handling for single entryID lookup |
502
|
|
|
|
|
|
|
if (exists $opts{entryID}) { |
503
|
|
|
|
|
|
|
if (scalar(keys %opts)>1) { |
504
|
|
|
|
|
|
|
$@ = "You can't specify entryID and anything else"; |
505
|
|
|
|
|
|
|
return undef; |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
my $path = $url->path; |
508
|
|
|
|
|
|
|
$url->path("$path/".$opts{entryID}); |
509
|
|
|
|
|
|
|
return $self->_get_entry("$url", "Net::Google::Calendar::Entry"); |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
if (exists $opts{category} && 'ARRAY' eq ref($opts{category})) { |
513
|
|
|
|
|
|
|
my $path = $url->path."/".join("/", ( '-', @{delete $opts{category}})); |
514
|
|
|
|
|
|
|
$url->path("$path"); |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
$url->query_form(%opts); |
518
|
|
|
|
|
|
|
$self->_get("$url", "Net::Google::Calendar::Entry"); |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=head2 add_entry |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
Create a new entry. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
Returns the new entry with extra data provided by Google but will |
527
|
|
|
|
|
|
|
also modify the entry in place unless the C |
528
|
|
|
|
|
|
|
option is passed to C. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
Returns undef on failure. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=cut |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
sub add_entry { |
535
|
|
|
|
|
|
|
my ($self, $entry) = @_; |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
# TODO for neatness' sake we could make calendar_id = 'default' when calendar_id = user |
538
|
|
|
|
|
|
|
my $url = $self->_get_protocol()."://www.google.com/calendar/feeds/$self->{calendar_id}/private/full"; |
539
|
|
|
|
|
|
|
push @_, ($url, 'POST'); |
540
|
|
|
|
|
|
|
goto $self->can('_do'); |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=head2 delete_entry |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
Delete a given entry. |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Returns undef on failure or the old entry on success. |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=cut |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
sub delete_entry { |
553
|
|
|
|
|
|
|
my ($self, $entry) = @_; |
554
|
|
|
|
|
|
|
my $force = (scalar(@_)>2)? pop @_ : 0; |
555
|
|
|
|
|
|
|
my $url = $entry->edit_url($force) || return undef; |
556
|
|
|
|
|
|
|
push @_, ($url, 'DELETE'); |
557
|
|
|
|
|
|
|
goto $self->can('_do'); |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=head2 update_entry |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
Update a given entry. |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
Returns the updated entry with extra data provided by Google but will |
565
|
|
|
|
|
|
|
also modify the entry in place unless the C |
566
|
|
|
|
|
|
|
option is passed to C. |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
Returns undef on failure. |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=cut |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
sub update_entry { |
573
|
|
|
|
|
|
|
my ($self, $entry) = @_; |
574
|
|
|
|
|
|
|
my $url = $entry->edit_url || return undef; |
575
|
|
|
|
|
|
|
push @_, ($url, 'PUT'); |
576
|
|
|
|
|
|
|
goto $self->can('_do'); |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=head2 get_calendars |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
Get a list of all of a user's Calendars as C objects. |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
If C is true then only get the ones a user owns. |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=cut |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub get_calendars { |
588
|
|
|
|
|
|
|
my $self = shift; |
589
|
|
|
|
|
|
|
my $owned = shift || 0; |
590
|
|
|
|
|
|
|
my $which = ($owned)? "owncalendars" : "allcalendars"; |
591
|
|
|
|
|
|
|
my $url = $self->_get_protocol()."://www.google.com/calendar/feeds/default/$which/full"; |
592
|
|
|
|
|
|
|
return $self->_get("$url", "Net::Google::Calendar::Calendar"); |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
sub _get { |
597
|
|
|
|
|
|
|
my ($self, $url, $class, %opts) = @_; |
598
|
|
|
|
|
|
|
my $feed = $self->get_feed(URI->new("$url"), %opts); |
599
|
|
|
|
|
|
|
return map { bless $_, $class; $_->_initialize(); $_ } $feed->entries; |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=head2 get_feed [feed] [opt[s]] |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
If C is a C object then feed is fetch remotely. |
605
|
|
|
|
|
|
|
Otherwise it is assumed to be XML data and is parsed. |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
Returns an C object. |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=cut |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
sub get_feed { |
612
|
|
|
|
|
|
|
my ($self, $feed, %opts) = @_; |
613
|
|
|
|
|
|
|
if (ref($feed)){ |
614
|
|
|
|
|
|
|
return $feed if $feed->isa('XML::Atom::Feed'); |
615
|
|
|
|
|
|
|
if ($feed->isa('URI')) { |
616
|
|
|
|
|
|
|
my %params = ($self->{_auth}->auth_params('GET', $feed), %opts); |
617
|
|
|
|
|
|
|
my $r = $self->{_ua}->get("$feed", %params); |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
my $redirect_tries = 0; |
620
|
|
|
|
|
|
|
while ($r->code == 302 || $r->code == 301) { |
621
|
|
|
|
|
|
|
my $location = $r->header('location'); |
622
|
|
|
|
|
|
|
%params = ($self->{_auth}->auth_params('GET', $location), %opts); |
623
|
|
|
|
|
|
|
$r = $self->{_ua}->get($location, %params); |
624
|
|
|
|
|
|
|
$redirect_tries++; |
625
|
|
|
|
|
|
|
die "Too many redirects ($redirect_tries)" |
626
|
|
|
|
|
|
|
if $redirect_tries > $REDIRECT_MAX; |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
die $r->status_line unless $r->is_success; |
630
|
|
|
|
|
|
|
$feed = $r->content; |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
return XML::Atom::Feed->new(\$feed); |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=head2 update_feed |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
Take an C object with a C link and post it. |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=cut |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
sub update_feed { |
643
|
|
|
|
|
|
|
my ($self, $feed) = @_; |
644
|
|
|
|
|
|
|
#my $uri = Net::Google::Calendar::Base::_generic_url($feed, 'http://schemas.google.com/g/2005#post') || die("Couldn't get url"); |
645
|
|
|
|
|
|
|
my $uri = Net::Google::Calendar::Base::_generic_url($feed, 'edit') || die("Couldn't get url"); |
646
|
|
|
|
|
|
|
push @_, ($uri, 'POST'); |
647
|
|
|
|
|
|
|
goto $self->can('_do'); |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# TODO collapse this with _get somehow |
651
|
|
|
|
|
|
|
sub _get_entry { |
652
|
|
|
|
|
|
|
my ($self, $url, $class) = @_; |
653
|
|
|
|
|
|
|
my %params = ($self->{_auth}->auth_params); |
654
|
|
|
|
|
|
|
my $r = $self->{_ua}->get("$url", %params); |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
if (!$r->is_success) { |
657
|
|
|
|
|
|
|
if ($r->code == 404) { |
658
|
|
|
|
|
|
|
$@ = "EntryID not found"; |
659
|
|
|
|
|
|
|
} else { |
660
|
|
|
|
|
|
|
$@ = $r->status_line; |
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
return; |
663
|
|
|
|
|
|
|
} |
664
|
|
|
|
|
|
|
my $atom = $r->content; |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
my $entry = XML::Atom::Entry->new(\$atom); |
667
|
|
|
|
|
|
|
$entry = bless $entry, $class; |
668
|
|
|
|
|
|
|
$entry->_initialize(); |
669
|
|
|
|
|
|
|
return $entry; |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=head2 set_calendar |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
Set the current calendar to use. |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=cut |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
sub set_calendar { |
679
|
|
|
|
|
|
|
my $self = shift; |
680
|
|
|
|
|
|
|
my $cal = shift; |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
($self->{calendar_id}) = (uri_unescape($cal->id) =~ m!([^/]+)$!); |
683
|
|
|
|
|
|
|
$self->{url} = $self->_get_protocol()."://www.google.com/calendar/feeds/$self->{calendar_id}/private/full"; |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=head2 add_calendar |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Create a new calendar |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
Returns the new calendar with extra data provided by Google but will |
692
|
|
|
|
|
|
|
also modify the entry in place unless the C |
693
|
|
|
|
|
|
|
option is passed to C. |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
Returns undef on failure. |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
=cut |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
sub add_calendar { |
700
|
|
|
|
|
|
|
my ($self, $entry) = @_; |
701
|
|
|
|
|
|
|
my $url = $self->_get_protocol()."://www.google.com/calendar/feeds/$self->{calendar_id}/owncalendars/full"; |
702
|
|
|
|
|
|
|
push @_, ($url, 'POST'); |
703
|
|
|
|
|
|
|
goto $self->can('_do'); |
704
|
|
|
|
|
|
|
} |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=head2 update_calendar |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
Update a calendar. |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
Returns the updated calendar with extra data provided by Google but will |
711
|
|
|
|
|
|
|
also modify the entry in place unless the C |
712
|
|
|
|
|
|
|
option is passed to C. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
Returns undef on failure. |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
=cut |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub update_calendar { |
719
|
|
|
|
|
|
|
my $self = shift; |
720
|
|
|
|
|
|
|
$self->update_entry(@_); |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
=head2 delete_calendar [force] |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
Delete a given calendar. |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Returns undef on failure or the old entry on success. |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
Note that, at the moment, only C objects returned |
731
|
|
|
|
|
|
|
by C with the C parameter set to C |
732
|
|
|
|
|
|
|
can be deleted (unlike editing - I don't know if this is a Google |
733
|
|
|
|
|
|
|
bug or not). |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
However, you can pass in an optional true C parameter to this |
736
|
|
|
|
|
|
|
method that will allow C objects returned by C |
737
|
|
|
|
|
|
|
where no positive C paramemter was passed to be deleted. It uses |
738
|
|
|
|
|
|
|
an egregious hack though and might suddenly stop working if Google change |
739
|
|
|
|
|
|
|
things or I suddenly decide to remove it. |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
=cut |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
sub delete_calendar { |
745
|
|
|
|
|
|
|
my $self = shift; |
746
|
|
|
|
|
|
|
$self->delete_entry(@_); |
747
|
|
|
|
|
|
|
} |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
sub _do { |
750
|
|
|
|
|
|
|
my ($self, $entry, $url, $method) = @_; |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
unless (defined $self->{_auth}) { |
753
|
|
|
|
|
|
|
$@ = "You must log in to do a $method\n"; |
754
|
|
|
|
|
|
|
return undef; |
755
|
|
|
|
|
|
|
} |
756
|
|
|
|
|
|
|
my $class = ref($entry); |
757
|
|
|
|
|
|
|
my $xml = eval { $entry->as_xml }; |
758
|
|
|
|
|
|
|
confess($@) if $@; |
759
|
|
|
|
|
|
|
_utf8_off($xml); |
760
|
|
|
|
|
|
|
my %params = $self->{_auth}->auth_params; |
761
|
|
|
|
|
|
|
$params{Content_Type} = 'application/atom+xml; charset=UTF-8'; |
762
|
|
|
|
|
|
|
$params{Content} = $xml; |
763
|
|
|
|
|
|
|
$params{'X-HTTP-Method-Override'} = $method unless "POST" eq $method; |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
if (defined $self->{_session_id} && !$self->{_force_no_session_id}) { |
766
|
|
|
|
|
|
|
my $tmp = URI->new($url); |
767
|
|
|
|
|
|
|
$tmp->query_form({ gsessionid => $self->{_session_id} }); |
768
|
|
|
|
|
|
|
$url = "$tmp"; |
769
|
|
|
|
|
|
|
} |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
REQUEST: while (1) { |
774
|
|
|
|
|
|
|
my $rq = POST "$url", %params; |
775
|
|
|
|
|
|
|
$self->{_cookie_jar}->add_cookie_header($rq); |
776
|
|
|
|
|
|
|
#my $h = HTTP::Headers->new(%params); |
777
|
|
|
|
|
|
|
#my $rq = HTTP::Request->new($method => $url, $h); |
778
|
|
|
|
|
|
|
my $r = $self->{_ua}->request( $rq ); |
779
|
|
|
|
|
|
|
$self->{_cookie_jar}->extract_cookies($r); |
780
|
|
|
|
|
|
|
my $redirect_tries = 0; |
781
|
|
|
|
|
|
|
while (302 == $r->code || 301 == $r->code) { |
782
|
|
|
|
|
|
|
$url = $r->header('location'); |
783
|
|
|
|
|
|
|
my %args = URI->new($url)->query_form; |
784
|
|
|
|
|
|
|
$self->{_session_id} = $args{gsessionid}; |
785
|
|
|
|
|
|
|
$redirect_tries++; |
786
|
|
|
|
|
|
|
die "Too many redirects ($redirect_tries)" |
787
|
|
|
|
|
|
|
if $redirect_tries > $REDIRECT_MAX; |
788
|
|
|
|
|
|
|
next REQUEST; |
789
|
|
|
|
|
|
|
} |
790
|
|
|
|
|
|
|
#print $rq->as_string unless $params{'X-HTTP-Method-Override'} ; |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
if (!$r->is_success) { |
793
|
|
|
|
|
|
|
$@ = $r->status_line." - ".$r->content." - $url"; |
794
|
|
|
|
|
|
|
return undef; |
795
|
|
|
|
|
|
|
} |
796
|
|
|
|
|
|
|
my $c = $r->content; |
797
|
|
|
|
|
|
|
if (defined $c && length($c)) { |
798
|
|
|
|
|
|
|
my $tmp = $class->new(Stream => \$c); |
799
|
|
|
|
|
|
|
$_[1] = $tmp unless $self->{no_event_modification}; |
800
|
|
|
|
|
|
|
return $tmp; |
801
|
|
|
|
|
|
|
} else { |
802
|
|
|
|
|
|
|
# in the case of DELETE should we return 1 instead? |
803
|
|
|
|
|
|
|
return $entry; |
804
|
|
|
|
|
|
|
} |
805
|
|
|
|
|
|
|
} |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
} |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
sub _utf8_off { |
811
|
|
|
|
|
|
|
if ($] >= 5.008) { |
812
|
|
|
|
|
|
|
require Encode; |
813
|
|
|
|
|
|
|
return Encode::_utf8_off($_[0]); |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
=head1 WARNING |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
This is ALPHA level software. |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
Don't use it. Ever. Or something. |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
=head1 TODO |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
Abstract this out to Net::Google::Data |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
=head1 LATEST VERSION |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
The latest version can always be obtained from my |
830
|
|
|
|
|
|
|
Subversion repository. |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
http://svn.unixbeard.net/simon/Net-Google-Calendar |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
=head1 AUTHOR |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
Simon Wistow |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
=head1 COPYRIGHT |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Copyright Simon Wistow, 2006 |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
Distributed under the same terms as Perl itself. |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
=head1 SEE ALSO |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
http://code.google.com/apis/gdata/calendar.html |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
=cut |
849
|
|
|
|
|
|
|
1; |