1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * GData Client
4  * Copyright (C) Philip Withnall 2009, 2010, 2014, 2015 <philip@tecnocode.co.uk>
5  *
6  * GData Client is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * GData Client is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /**
21  * SECTION:gdata-calendar-calendar
22  * @short_description: GData Calendar calendar object
23  * @stability: Stable
24  * @include: gdata/services/calendar/gdata-calendar-calendar.h
25  *
26  * #GDataCalendarCalendar is a subclass of #GDataEntry to represent a calendar from Google Calendar.
27  *
28  * #GDataCalendarCalendar implements #GDataAccessHandler, meaning the access rules to it can be modified using that interface. As well as the
29  * access roles defined for the base #GDataAccessRule (e.g. %GDATA_ACCESS_ROLE_NONE), #GDataCalendarCalendar has its own, such as
30  * %GDATA_CALENDAR_ACCESS_ROLE_EDITOR and %GDATA_CALENDAR_ACCESS_ROLE_FREE_BUSY.
31  *
32  * For more details of Google Calendar's GData API, see the <ulink type="http" url="https://developers.google.com/google-apps/calendar/v3/reference/">
33  * online documentation</ulink>.
34  *
35  * <example>
36  * 	<title>Listing Calendars</title>
37  * 	<programlisting>
38  *	GDataCalendarService *service;
39  *	GDataFeed *feed;
40  *	GList *i;
41  *	GError *error = NULL;
42  *
43  *	/<!-- -->* Create a service *<!-- -->/
44  *	service = create_calendar_service ();
45  *
46  *	/<!-- -->* Query for all of the calendars the currently authenticated user has access to, including those which they have read-only
47  *	 * access to. *<!-- -->/
48  *	feed = gdata_calendar_service_query_all_calendars (service, NULL, NULL, NULL, NULL, &error);
49  *
50  *	g_object_unref (service);
51  *
52  *	if (error != NULL) {
53  *		g_error ("Error querying for calendars: %s", error->message);
54  *		g_error_free (error);
55  *		return;
56  *	}
57  *
58  *	/<!-- -->* Iterate through the returned calendars and do something with them *<!-- -->/
59  *	for (i = gdata_feed_get_entries (feed); i != NULL; i = i->next) {
60  *		const gchar *access_level;
61  *		gboolean has_write_access;
62  *		GDataCalendarCalendar *calendar = GDATA_CALENDAR_CALENDAR (i->data);
63  *
64  *		/<!-- -->* Determine whether we have write access to the calendar, or just read-only or free/busy access. Note that the access levels
65  *		 * are more detailed than this; see the documentation for gdata_calendar_calendar_get_access_level() for more information. *<!-- -->/
66  *		access_level = gdata_calendar_calendar_get_access_level (calendar);
67  *		has_write_access = (access_level != NULL && strcmp (access_level, GDATA_CALENDAR_ACCESS_ROLE_EDITOR) == 0) ? TRUE : FALSE;
68  *
69  *		/<!-- -->* Do something with the calendar here, such as insert it into a UI *<!-- -->/
70  *	}
71  *
72  *	g_object_unref (feed);
73  * 	</programlisting>
74  * </example>
75  */
76 
77 #include <config.h>
78 #include <glib.h>
79 #include <glib/gi18n-lib.h>
80 #include <string.h>
81 
82 #include "gdata-calendar-calendar.h"
83 #include "gdata-private.h"
84 #include "gdata-service.h"
85 #include "gdata-parser.h"
86 #include "gdata-types.h"
87 #include "gdata-access-handler.h"
88 #include "gdata-calendar-service.h"
89 #include "gdata-calendar-access-rule.h"
90 
91 static void gdata_calendar_calendar_access_handler_init (GDataAccessHandlerIface *iface);
92 static void gdata_calendar_calendar_finalize (GObject *object);
93 static void gdata_calendar_calendar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
94 static void gdata_calendar_calendar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
95 static void get_json (GDataParsable *parsable, JsonBuilder *builder);
96 static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error);
97 static const gchar *get_content_type (void);
98 
99 struct _GDataCalendarCalendarPrivate {
100 	gchar *timezone;
101 	gboolean is_hidden;
102 	GDataColor colour;
103 	gboolean is_selected;
104 	gchar *access_level;
105 };
106 
107 enum {
108 	PROP_TIMEZONE = 1,
109 	PROP_TIMES_CLEANED,
110 	PROP_IS_HIDDEN,
111 	PROP_COLOR,
112 	PROP_IS_SELECTED,
113 	PROP_ACCESS_LEVEL,
114 	PROP_EDITED,
115 	PROP_ETAG,
116 };
117 
G_DEFINE_TYPE_WITH_CODE(GDataCalendarCalendar,gdata_calendar_calendar,GDATA_TYPE_ENTRY,G_IMPLEMENT_INTERFACE (GDATA_TYPE_ACCESS_HANDLER,gdata_calendar_calendar_access_handler_init))118 G_DEFINE_TYPE_WITH_CODE (GDataCalendarCalendar, gdata_calendar_calendar, GDATA_TYPE_ENTRY,
119                          G_IMPLEMENT_INTERFACE (GDATA_TYPE_ACCESS_HANDLER, gdata_calendar_calendar_access_handler_init))
120 
121 static void
122 gdata_calendar_calendar_class_init (GDataCalendarCalendarClass *klass)
123 {
124 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
125 	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
126 	GDataEntryClass *entry_class = GDATA_ENTRY_CLASS (klass);
127 
128 	g_type_class_add_private (klass, sizeof (GDataCalendarCalendarPrivate));
129 
130 	gobject_class->set_property = gdata_calendar_calendar_set_property;
131 	gobject_class->get_property = gdata_calendar_calendar_get_property;
132 	gobject_class->finalize = gdata_calendar_calendar_finalize;
133 
134 	parsable_class->parse_json = parse_json;
135 	parsable_class->get_json = get_json;
136 	parsable_class->get_content_type = get_content_type;
137 
138 	entry_class->kind_term = "calendar#calendarListEntry";
139 
140 	/**
141 	 * GDataCalendarCalendar:timezone:
142 	 *
143 	 * The timezone in which the calendar's times are given. This is a timezone name in tz database notation: <ulink type="http"
144 	 * url="http://en.wikipedia.org/wiki/Tz_database#Names_of_time_zones">reference</ulink>.
145 	 */
146 	g_object_class_install_property (gobject_class, PROP_TIMEZONE,
147 	                                 g_param_spec_string ("timezone",
148 	                                                      "Timezone", "The timezone in which the calendar's times are given.",
149 	                                                      NULL,
150 	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151 
152 	/**
153 	 * GDataCalendarCalendar:times-cleaned:
154 	 *
155 	 * The number of times the calendar has been cleared of events.
156 	 *
157 	 * Deprecated: 0.17.2: Unsupported by the online API any more. There
158 	 *   is no replacement; this will always return
159 	 *   <code class="literal">0</code>.
160 	 */
161 	g_object_class_install_property (gobject_class, PROP_TIMES_CLEANED,
162 	                                 g_param_spec_uint ("times-cleaned",
163 	                                                    "Times cleaned", "The number of times the calendar has been cleared of events.",
164 	                                                    0, G_MAXUINT, 0,
165 	                                                    G_PARAM_DEPRECATED |
166 	                                                    G_PARAM_READABLE |
167 	                                                    G_PARAM_STATIC_STRINGS));
168 
169 	/**
170 	 * GDataCalendarCalendar:is-hidden:
171 	 *
172 	 * Indicates whether the calendar is visible.
173 	 *
174 	 * Since: 0.2.0
175 	 */
176 	g_object_class_install_property (gobject_class, PROP_IS_HIDDEN,
177 	                                 g_param_spec_boolean ("is-hidden",
178 	                                                       "Hidden?", "Indicates whether the calendar is visible.",
179 	                                                       FALSE,
180 	                                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
181 
182 	/**
183 	 * GDataCalendarCalendar:color:
184 	 *
185 	 * The background color used to highlight the calendar in the user’s
186 	 * browser. This used to be restricted to a limited set of colours, but
187 	 * since 0.17.2 may be any RGB colour.
188 	 */
189 	g_object_class_install_property (gobject_class, PROP_COLOR,
190 	                                 g_param_spec_boxed ("color",
191 	                                                     "Color", "The background color used to highlight the calendar in the user's browser.",
192 	                                                     GDATA_TYPE_COLOR,
193 	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
194 
195 	/**
196 	 * GDataCalendarCalendar:is-selected:
197 	 *
198 	 * Indicates whether the calendar is selected.
199 	 *
200 	 * Since: 0.2.0
201 	 */
202 	g_object_class_install_property (gobject_class, PROP_IS_SELECTED,
203 	                                 g_param_spec_boolean ("is-selected",
204 	                                                       "Selected?", "Indicates whether the calendar is selected.",
205 	                                                       FALSE,
206 	                                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
207 
208 	/**
209 	 * GDataCalendarCalendar:access-level:
210 	 *
211 	 * Indicates the access level the current user has to the calendar. For example: %GDATA_CALENDAR_ACCESS_ROLE_READ or
212 	 * %GDATA_CALENDAR_ACCESS_ROLE_FREE_BUSY. The "current user" is the one authenticated against the service's #GDataService:authorizer,
213 	 * or the guest user.
214 	 */
215 	g_object_class_install_property (gobject_class, PROP_ACCESS_LEVEL,
216 	                                 g_param_spec_string ("access-level",
217 	                                                      "Access level", "Indicates the access level the current user has to the calendar.",
218 	                                                      NULL,
219 	                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
220 
221 	/**
222 	 * GDataCalendarCalendar:edited:
223 	 *
224 	 * The last time the calendar was edited. If the calendar has not been edited yet, the content indicates the time it was created.
225 	 *
226 	 * For more information, see the <ulink type="http" url="http://www.atomenabled.org/developers/protocol/#appEdited">
227 	 * Atom Publishing Protocol specification</ulink>.
228 	 *
229 	 * Deprecated: 0.17.2: Unsupported by the online API any more. There
230 	 * is no replacement; this will always return -1.
231 	 */
232 	g_object_class_install_property (gobject_class, PROP_EDITED,
233 	                                 g_param_spec_int64 ("edited",
234 	                                                     "Edited", "The last time the calendar was edited.",
235 	                                                     -1, G_MAXINT64, -1,
236 	                                                     G_PARAM_DEPRECATED |
237 	                                                     G_PARAM_READABLE |
238 	                                                     G_PARAM_STATIC_STRINGS));
239 
240 	/* Override the ETag property since ETags don't seem to be supported for calendars. */
241 	g_object_class_override_property (gobject_class, PROP_ETAG, "etag");
242 }
243 
244 static gboolean
is_owner_rule(GDataAccessRule * rule)245 is_owner_rule (GDataAccessRule *rule)
246 {
247 	return (strcmp (gdata_access_rule_get_role (rule), GDATA_CALENDAR_ACCESS_ROLE_OWNER) == 0) ? TRUE : FALSE;
248 }
249 
250 static GDataAuthorizationDomain *
get_authorization_domain(GDataAccessHandler * self)251 get_authorization_domain (GDataAccessHandler *self)
252 {
253 	return gdata_calendar_service_get_primary_authorization_domain ();
254 }
255 
256 static GDataFeed *
get_rules(GDataAccessHandler * self,GDataService * service,GCancellable * cancellable,GDataQueryProgressCallback progress_callback,gpointer progress_user_data,GError ** error)257 get_rules (GDataAccessHandler *self,
258            GDataService *service,
259            GCancellable *cancellable,
260            GDataQueryProgressCallback progress_callback,
261            gpointer progress_user_data,
262            GError **error)
263 {
264 	GDataAccessHandlerIface *iface;
265 	GDataAuthorizationDomain *domain = NULL;
266 	GDataFeed *feed;
267 	GDataLink *_link;
268 	SoupMessage *message;
269 	GList/*<unowned GDataCalendarAccessRule>*/ *rules, *i;
270 	const gchar *calendar_id;
271 
272 	_link = gdata_entry_look_up_link (GDATA_ENTRY (self),
273 	                                  GDATA_LINK_ACCESS_CONTROL_LIST);
274 	g_assert (_link != NULL);
275 
276 	iface = GDATA_ACCESS_HANDLER_GET_IFACE (self);
277 	if (iface->get_authorization_domain != NULL) {
278 		domain = iface->get_authorization_domain (self);
279 	}
280 
281 	message = _gdata_service_query (service, domain,
282 	                                gdata_link_get_uri (_link), NULL,
283 	                                cancellable, error);
284 	if (message == NULL) {
285 		return NULL;
286 	}
287 
288 	g_assert (message->response_body->data != NULL);
289 
290 	feed = _gdata_feed_new_from_json (GDATA_TYPE_FEED,
291 	                                  message->response_body->data,
292 	                                  message->response_body->length,
293 	                                  GDATA_TYPE_CALENDAR_ACCESS_RULE,
294 	                                  progress_callback, progress_user_data,
295 	                                  error);
296 
297 	/* Set the self link on all the ACL rules so they can be deleted.
298 	 * Sigh. */
299 	rules = gdata_feed_get_entries (feed);
300 	calendar_id = gdata_entry_get_id (GDATA_ENTRY (self));
301 
302 	for (i = rules; i != NULL; i = i->next) {
303 		const gchar *id;
304 		gchar *uri = NULL;  /* owned */
305 
306 		/* Set the self link, which is needed for
307 		 * gdata_service_delete_entry(). Unfortunately, it needs the
308 		 * ACL ID _and_ the calendar ID. */
309 		id = gdata_entry_get_id (GDATA_ENTRY (i->data));
310 
311 		if (id == NULL || calendar_id == NULL) {
312 			continue;
313 		}
314 
315 		uri = g_strconcat ("https://www.googleapis.com"
316 		                   "/calendar/v3/calendars/",
317 		                   calendar_id, "/acl/", id, NULL);
318 		_link = gdata_link_new (uri, GDATA_LINK_SELF);
319 		gdata_entry_add_link (GDATA_ENTRY (i->data), _link);
320 		g_object_unref (_link);
321 		g_free (uri);
322 	}
323 
324 	g_object_unref (message);
325 
326 	return feed;
327 }
328 
329 static void
gdata_calendar_calendar_access_handler_init(GDataAccessHandlerIface * iface)330 gdata_calendar_calendar_access_handler_init (GDataAccessHandlerIface *iface)
331 {
332 	iface->is_owner_rule = is_owner_rule;
333 	iface->get_authorization_domain = get_authorization_domain;
334 	iface->get_rules = get_rules;
335 }
336 
337 static void
gdata_calendar_calendar_init(GDataCalendarCalendar * self)338 gdata_calendar_calendar_init (GDataCalendarCalendar *self)
339 {
340 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_CALENDAR_CALENDAR, GDataCalendarCalendarPrivate);
341 }
342 
343 static void
gdata_calendar_calendar_finalize(GObject * object)344 gdata_calendar_calendar_finalize (GObject *object)
345 {
346 	GDataCalendarCalendarPrivate *priv = GDATA_CALENDAR_CALENDAR (object)->priv;
347 
348 	g_free (priv->timezone);
349 	g_free (priv->access_level);
350 
351 	/* Chain up to the parent class */
352 	G_OBJECT_CLASS (gdata_calendar_calendar_parent_class)->finalize (object);
353 }
354 
355 static void
gdata_calendar_calendar_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)356 gdata_calendar_calendar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
357 {
358 	GDataCalendarCalendar *self = GDATA_CALENDAR_CALENDAR (object);
359 	GDataCalendarCalendarPrivate *priv = self->priv;
360 
361 	switch (property_id) {
362 		case PROP_TIMEZONE:
363 			g_value_set_string (value, priv->timezone);
364 			break;
365 		case PROP_TIMES_CLEANED:
366 			G_GNUC_BEGIN_IGNORE_DEPRECATIONS
367 			g_value_set_uint (value, gdata_calendar_calendar_get_times_cleaned (self));
368 			G_GNUC_END_IGNORE_DEPRECATIONS
369 			break;
370 		case PROP_IS_HIDDEN:
371 			g_value_set_boolean (value, priv->is_hidden);
372 			break;
373 		case PROP_COLOR:
374 			g_value_set_boxed (value, &(priv->colour));
375 			break;
376 		case PROP_IS_SELECTED:
377 			g_value_set_boolean (value, priv->is_selected);
378 			break;
379 		case PROP_ACCESS_LEVEL:
380 			g_value_set_string (value, priv->access_level);
381 			break;
382 		case PROP_EDITED:
383 			G_GNUC_BEGIN_IGNORE_DEPRECATIONS
384 			g_value_set_int64 (value,
385 			                   gdata_calendar_calendar_get_edited (self));
386 			G_GNUC_END_IGNORE_DEPRECATIONS
387 			break;
388 		case PROP_ETAG:
389 			/* Never return an ETag */
390 			g_value_set_string (value, NULL);
391 			break;
392 		default:
393 			/* We don't have any other property... */
394 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
395 			break;
396 	}
397 }
398 
399 static void
gdata_calendar_calendar_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)400 gdata_calendar_calendar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
401 {
402 	GDataCalendarCalendar *self = GDATA_CALENDAR_CALENDAR (object);
403 
404 	switch (property_id) {
405 		case PROP_TIMEZONE:
406 			gdata_calendar_calendar_set_timezone (self, g_value_get_string (value));
407 			break;
408 		case PROP_IS_HIDDEN:
409 			gdata_calendar_calendar_set_is_hidden (self, g_value_get_boolean (value));
410 			break;
411 		case PROP_COLOR:
412 			gdata_calendar_calendar_set_color (self, g_value_get_boxed (value));
413 			break;
414 		case PROP_IS_SELECTED:
415 			gdata_calendar_calendar_set_is_selected (self, g_value_get_boolean (value));
416 			break;
417 		case PROP_ETAG:
418 			/* Never set an ETag (note that this doesn't stop it being set in GDataEntry due to XML parsing) */
419 			break;
420 		default:
421 			/* We don't have any other property... */
422 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
423 			break;
424 	}
425 }
426 
427 static gboolean
parse_json(GDataParsable * parsable,JsonReader * reader,gpointer user_data,GError ** error)428 parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
429 {
430 	gboolean success;
431 	GDataCalendarCalendar *self = GDATA_CALENDAR_CALENDAR (parsable);
432 
433 	/* FIXME: Unimplemented:
434 	 *  - location
435 	 *  - summaryOverride
436 	 *  - colorId
437 	 *  - foregroundColor
438 	 *  - defaultReminders
439 	 *  - notificationSettings
440 	 *  - primary
441 	 *  - deleted
442 	 */
443 
444 	if (gdata_parser_string_from_json_member (reader, "timeZone", P_DEFAULT, &self->priv->timezone, &success, error) ||
445 	    gdata_parser_color_from_json_member (reader, "backgroundColor", P_DEFAULT, &self->priv->colour, &success, error) ||
446 	    gdata_parser_boolean_from_json_member (reader, "hidden", P_DEFAULT, &self->priv->is_hidden, &success, error) ||
447 	    gdata_parser_boolean_from_json_member (reader, "selected", P_DEFAULT, &self->priv->is_selected, &success, error)) {
448 		return success;
449 	} else if (g_strcmp0 (json_reader_get_member_name (reader), "summary") == 0) {
450 		gchar *summary = NULL;
451 
452 		g_assert (gdata_parser_string_from_json_member (reader,
453 		                                                "summary",
454 		                                                P_DEFAULT,
455 		                                                &summary,
456 		                                                &success,
457 		                                                error));
458 
459 		if (summary != NULL) {
460 			gdata_entry_set_title (GDATA_ENTRY (parsable), summary);
461 		}
462 
463 		g_free (summary);
464 
465 		return success;
466 	} else if (g_strcmp0 (json_reader_get_member_name (reader), "description") == 0) {
467 		gchar *description = NULL;
468 
469 		g_assert (gdata_parser_string_from_json_member (reader,
470 		                                                "description",
471 		                                                P_DEFAULT,
472 		                                                &description,
473 		                                                &success,
474 		                                                error));
475 
476 		if (description != NULL) {
477 			gdata_entry_set_summary (GDATA_ENTRY (parsable),
478 			                         description);
479 		}
480 
481 		g_free (description);
482 
483 		return success;
484 	} else if (g_strcmp0 (json_reader_get_member_name (reader), "accessRole") == 0) {
485 		gchar *access_role = NULL;
486 
487 		g_assert (gdata_parser_string_from_json_member (reader,
488 		                                                "accessRole",
489 		                                                P_DEFAULT,
490 		                                                &access_role,
491 		                                                &success,
492 		                                                error));
493 
494 		if (access_role != NULL) {
495 			const gchar *level;
496 
497 			/* Convert from v3 format to v2. */
498 			if (g_strcmp0 (access_role, "freeBusyReader") == 0) {
499 				level = GDATA_CALENDAR_ACCESS_ROLE_FREE_BUSY;
500 			} else if (g_strcmp0 (access_role, "reader") == 0) {
501 				level = GDATA_CALENDAR_ACCESS_ROLE_READ;
502 			} else if (g_strcmp0 (access_role, "writer") == 0) {
503 				level = GDATA_CALENDAR_ACCESS_ROLE_EDITOR;
504 			} else if (g_strcmp0 (access_role, "owner") == 0) {
505 				level = GDATA_CALENDAR_ACCESS_ROLE_OWNER;
506 			} else {
507 				level = access_role;
508 			}
509 
510 			self->priv->access_level = g_strdup (level);
511 		}
512 
513 		g_free (access_role);
514 
515 		return success;
516 	} else if (g_strcmp0 (json_reader_get_member_name (reader), "id") == 0) {
517 		GDataLink *_link;
518 		const gchar *id;
519 		gchar *uri;
520 
521 		id = json_reader_get_string_value (reader);
522 		if (id != NULL && *id != '\0') {
523 			/* Calendar entries don’t contain their own selfLink,
524 			 * so we have to add one manually. */
525 			uri = g_strconcat ("https://www.googleapis.com/calendar/v3/calendars/", id, NULL);
526 			_link = gdata_link_new (uri, GDATA_LINK_SELF);
527 			gdata_entry_add_link (GDATA_ENTRY (parsable), _link);
528 			g_object_unref (_link);
529 			g_free (uri);
530 
531 			/* Similarly for the ACL link. */
532 			uri = g_strconcat ("https://www.googleapis.com"
533 			                   "/calendar/v3/calendars/", id,
534 			                   "/acl", NULL);
535 			_link = gdata_link_new (uri,
536 			                        GDATA_LINK_ACCESS_CONTROL_LIST);
537 			gdata_entry_add_link (GDATA_ENTRY (parsable), _link);
538 			g_object_unref (_link);
539 			g_free (uri);
540 		}
541 
542 		return GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->parse_json (parsable, reader, user_data, error);
543 	} else {
544 		return GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->parse_json (parsable, reader, user_data, error);
545 	}
546 
547 	return TRUE;
548 }
549 
550 static void
get_json(GDataParsable * parsable,JsonBuilder * builder)551 get_json (GDataParsable *parsable, JsonBuilder *builder)
552 {
553 	const gchar *id, *etag, *title, *description;
554 	gchar *colour;
555 	GDataCalendarCalendarPrivate *priv = GDATA_CALENDAR_CALENDAR (parsable)->priv;
556 
557 	id = gdata_entry_get_id (GDATA_ENTRY (parsable));
558 	if (id != NULL) {
559 		json_builder_set_member_name (builder, "id");
560 		json_builder_add_string_value (builder, id);
561 	}
562 
563 	json_builder_set_member_name (builder, "kind");
564 	json_builder_add_string_value (builder, "calendar#calendar");
565 
566 	/* Add the ETag, if available. */
567 	etag = gdata_entry_get_etag (GDATA_ENTRY (parsable));
568 	if (etag != NULL) {
569 		json_builder_set_member_name (builder, "etag");
570 		json_builder_add_string_value (builder, etag);
571 	}
572 
573 	/* Calendar labels titles as ‘summary’. */
574 	title = gdata_entry_get_title (GDATA_ENTRY (parsable));
575 	if (title != NULL) {
576 		json_builder_set_member_name (builder, "summary");
577 		json_builder_add_string_value (builder, title);
578 	}
579 
580 	description = gdata_entry_get_summary (GDATA_ENTRY (parsable));
581 	if (description != NULL) {
582 		json_builder_set_member_name (builder, "description");
583 		json_builder_add_string_value (builder, description);
584 	}
585 
586 	/* Add all the calendar-specific JSON */
587 	if (priv->timezone != NULL) {
588 		json_builder_set_member_name (builder, "timeZone");
589 		json_builder_add_string_value (builder, priv->timezone);
590 	}
591 
592 	json_builder_set_member_name (builder, "hidden");
593 	json_builder_add_boolean_value (builder, priv->is_hidden);
594 
595 	colour = gdata_color_to_hexadecimal (&priv->colour);
596 	json_builder_set_member_name (builder, "backgroundColor");
597 	json_builder_add_string_value (builder, colour);
598 	g_free (colour);
599 
600 	json_builder_set_member_name (builder, "selected");
601 	json_builder_add_boolean_value (builder, priv->is_selected);
602 }
603 
604 static const gchar *
get_content_type(void)605 get_content_type (void)
606 {
607 	return "application/json";
608 }
609 
610 /**
611  * gdata_calendar_calendar_new:
612  * @id: (allow-none): the calendar's ID, or %NULL
613  *
614  * Creates a new #GDataCalendarCalendar with the given ID and default properties.
615  *
616  * Return value: a new #GDataCalendarCalendar; unref with g_object_unref()
617  */
618 GDataCalendarCalendar *
gdata_calendar_calendar_new(const gchar * id)619 gdata_calendar_calendar_new (const gchar *id)
620 {
621 	return GDATA_CALENDAR_CALENDAR (g_object_new (GDATA_TYPE_CALENDAR_CALENDAR, "id", id, NULL));
622 }
623 
624 /**
625  * gdata_calendar_calendar_get_timezone:
626  * @self: a #GDataCalendarCalendar
627  *
628  * Gets the #GDataCalendarCalendar:timezone property.
629  *
630  * Return value: the calendar's timezone, or %NULL
631  */
632 const gchar *
gdata_calendar_calendar_get_timezone(GDataCalendarCalendar * self)633 gdata_calendar_calendar_get_timezone (GDataCalendarCalendar *self)
634 {
635 	g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (self), NULL);
636 	return self->priv->timezone;
637 }
638 
639 /**
640  * gdata_calendar_calendar_set_timezone:
641  * @self: a #GDataCalendarCalendar
642  * @_timezone: (allow-none): a new timezone, or %NULL
643  *
644  * Sets the #GDataCalendarCalendar:timezone property to the new timezone, @_timezone.
645  *
646  * Set @_timezone to %NULL to unset the property in the calendar.
647  */
648 void
gdata_calendar_calendar_set_timezone(GDataCalendarCalendar * self,const gchar * _timezone)649 gdata_calendar_calendar_set_timezone (GDataCalendarCalendar *self, const gchar *_timezone)
650 {
651 	/* Blame "timezone" in /usr/include/time.h:291 for the weird parameter naming */
652 	g_return_if_fail (GDATA_IS_CALENDAR_CALENDAR (self));
653 
654 	g_free (self->priv->timezone);
655 	self->priv->timezone = g_strdup (_timezone);
656 	g_object_notify (G_OBJECT (self), "timezone");
657 }
658 
659 /**
660  * gdata_calendar_calendar_get_times_cleaned:
661  * @self: a #GDataCalendarCalendar
662  *
663  * Gets the #GDataCalendarCalendar:times-cleaned property.
664  *
665  * Return value: the number of times the calendar has been totally emptied
666  * Deprecated: 0.17.2: Unsupported by the online API any more. There is no
667  *   replacement; this will always return <code class="literal">0</code>.
668  */
669 guint
gdata_calendar_calendar_get_times_cleaned(GDataCalendarCalendar * self)670 gdata_calendar_calendar_get_times_cleaned (GDataCalendarCalendar *self)
671 {
672 	g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (self), 0);
673 	return 0;
674 }
675 
676 /**
677  * gdata_calendar_calendar_is_hidden:
678  * @self: a #GDataCalendarCalendar
679  *
680  * Gets the #GDataCalendarCalendar:is-hidden property.
681  *
682  * Return value: %TRUE if the calendar is hidden, %FALSE otherwise
683  *
684  * Since: 0.2.0
685  */
686 gboolean
gdata_calendar_calendar_is_hidden(GDataCalendarCalendar * self)687 gdata_calendar_calendar_is_hidden (GDataCalendarCalendar *self)
688 {
689 	g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (self), 0);
690 	return self->priv->is_hidden;
691 }
692 
693 /**
694  * gdata_calendar_calendar_set_is_hidden:
695  * @self: a #GDataCalendarCalendar
696  * @is_hidden: %TRUE to hide the calendar, %FALSE otherwise
697  *
698  * Sets the #GDataCalendarCalendar:is-hidden property to @is_hidden.
699  *
700  * Since: 0.2.0
701  */
702 void
gdata_calendar_calendar_set_is_hidden(GDataCalendarCalendar * self,gboolean is_hidden)703 gdata_calendar_calendar_set_is_hidden (GDataCalendarCalendar *self, gboolean is_hidden)
704 {
705 	g_return_if_fail (GDATA_IS_CALENDAR_CALENDAR (self));
706 	self->priv->is_hidden = is_hidden;
707 	g_object_notify (G_OBJECT (self), "is-hidden");
708 }
709 
710 /**
711  * gdata_calendar_calendar_get_color:
712  * @self: a #GDataCalendarCalendar
713  * @color: (out caller-allocates): a #GDataColor
714  *
715  * Gets the #GDataCalendarCalendar:color property and puts it in @color.
716  */
717 void
gdata_calendar_calendar_get_color(GDataCalendarCalendar * self,GDataColor * color)718 gdata_calendar_calendar_get_color (GDataCalendarCalendar *self, GDataColor *color)
719 {
720 	g_return_if_fail (GDATA_IS_CALENDAR_CALENDAR (self));
721 	g_return_if_fail (color != NULL);
722 	*color = self->priv->colour;
723 }
724 
725 /**
726  * gdata_calendar_calendar_set_color:
727  * @self: a #GDataCalendarCalendar
728  * @color: a new #GDataColor
729  *
730  * Sets the #GDataCalendarCalendar:color property to @color.
731  */
732 void
gdata_calendar_calendar_set_color(GDataCalendarCalendar * self,const GDataColor * color)733 gdata_calendar_calendar_set_color (GDataCalendarCalendar *self, const GDataColor *color)
734 {
735 	g_return_if_fail (GDATA_IS_CALENDAR_CALENDAR (self));
736 	g_return_if_fail (color != NULL);
737 	self->priv->colour = *color;
738 	g_object_notify (G_OBJECT (self), "color");
739 }
740 
741 /**
742  * gdata_calendar_calendar_is_selected:
743  * @self: a #GDataCalendarCalendar
744  *
745  * Gets the #GDataCalendarCalendar:is-selected property.
746  *
747  * Return value: %TRUE if the calendar is selected, %FALSE otherwise
748  *
749  * Since: 0.2.0
750  */
751 gboolean
gdata_calendar_calendar_is_selected(GDataCalendarCalendar * self)752 gdata_calendar_calendar_is_selected (GDataCalendarCalendar *self)
753 {
754 	g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (self), 0);
755 	return self->priv->is_selected;
756 }
757 
758 /**
759  * gdata_calendar_calendar_set_is_selected:
760  * @self: a #GDataCalendarCalendar
761  * @is_selected: %TRUE to select the calendar, %FALSE otherwise
762  *
763  * Sets the #GDataCalendarCalendar:is-selected property to @is_selected.
764  *
765  * Since: 0.2.0
766  */
767 void
gdata_calendar_calendar_set_is_selected(GDataCalendarCalendar * self,gboolean is_selected)768 gdata_calendar_calendar_set_is_selected (GDataCalendarCalendar *self, gboolean is_selected)
769 {
770 	g_return_if_fail (GDATA_IS_CALENDAR_CALENDAR (self));
771 	self->priv->is_selected = is_selected;
772 	g_object_notify (G_OBJECT (self), "is-selected");
773 }
774 
775 /**
776  * gdata_calendar_calendar_get_access_level:
777  * @self: a #GDataCalendarCalendar
778  *
779  * Gets the #GDataCalendarCalendar:access-level property.
780  *
781  * Return value: the authenticated user's access level to the calendar, or %NULL
782  */
783 const gchar *
gdata_calendar_calendar_get_access_level(GDataCalendarCalendar * self)784 gdata_calendar_calendar_get_access_level (GDataCalendarCalendar *self)
785 {
786 	g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (self), NULL);
787 	return self->priv->access_level;
788 }
789 
790 /**
791  * gdata_calendar_calendar_get_edited:
792  * @self: a #GDataCalendarCalendar
793  *
794  * Gets the #GDataCalendarCalendar:edited property. If the property is unset, <code class="literal">-1</code> will be returned.
795  *
796  * Return value: the UNIX timestamp for the time the calendar was last edited, or <code class="literal">-1</code>
797  * Deprecated: 0.17.2: Unsupported by the online API any more. There is no
798  *   replacement; this will always return <code class="literal">-1</code>.
799  */
800 gint64
gdata_calendar_calendar_get_edited(GDataCalendarCalendar * self)801 gdata_calendar_calendar_get_edited (GDataCalendarCalendar *self)
802 {
803 	g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (self), -1);
804 	return -1;
805 }
806