/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* * GData Client * Copyright (C) Philip Withnall 2009, 2010, 2014, 2015 * * GData Client is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * GData Client is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GData Client. If not, see . */ /** * SECTION:gdata-calendar-event * @short_description: GData Calendar event object * @stability: Stable * @include: gdata/services/calendar/gdata-calendar-event.h * * #GDataCalendarEvent is a subclass of #GDataEntry to represent an event on a calendar from Google Calendar. * * For more details of Google Calendar's GData API, see the * * online documentation. * * * Adding a New Event to the Default Calendar * * GDataCalendarService *service; * GDataCalendarEvent *event, *new_event; * GDataGDWhere *where; * GDataGDWho *who; * GDataGDWhen *when; * GTimeVal current_time; * GError *error = NULL; * * /* Create a service */ * service = create_calendar_service (); * * /* Create the new event */ * event = gdata_calendar_event_new (NULL); * * gdata_entry_set_title (GDATA_ENTRY (event), "Event Title"); * gdata_entry_set_content (GDATA_ENTRY (event), "Event description. This should be a few sentences long."); * gdata_calendar_event_set_status (event, GDATA_GD_EVENT_STATUS_CONFIRMED); * * where = gdata_gd_where_new (NULL, "Description of the location", NULL); * gdata_calendar_event_add_place (event, where); * g_object_unref (where); * * who = gdata_gd_who_new (GDATA_GD_WHO_EVENT_ORGANIZER, "John Smith", "john.smith@gmail.com"); * gdata_calendar_event_add_person (event, who); * g_object_unref (who); * * g_get_current_time (¤t_time); * when = gdata_gd_when_new (current_time.tv_sec, current_time.tv_sec + 3600, FALSE); * gdata_calendar_event_add_time (event, when); * g_object_unref (when); * * /* Insert the event in the calendar */ * new_event = gdata_calendar_service_insert_event (service, event, NULL, &error); * * g_object_unref (event); * g_object_unref (service); * * if (error != NULL) { * g_error ("Error inserting event: %s", error->message); * g_error_free (error); * return NULL; * } * * /* Do something with the new_event here, such as return it to the user or store its ID for later usage */ * * g_object_unref (new_event); * * */ #include #include #include #include #include "gdata-calendar-event.h" #include "gdata-private.h" #include "gdata-service.h" #include "gdata-parser.h" #include "gdata-types.h" #include "gdata-comparable.h" static GObject *gdata_calendar_event_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params); static void gdata_calendar_event_dispose (GObject *object); static void gdata_calendar_event_finalize (GObject *object); static void gdata_calendar_event_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void gdata_calendar_event_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void get_json (GDataParsable *parsable, JsonBuilder *builder); static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error); static gboolean post_parse_json (GDataParsable *parsable, gpointer user_data, GError **error); static const gchar *get_content_type (void); struct _GDataCalendarEventPrivate { gint64 edited; gchar *status; gchar *visibility; gchar *transparency; gchar *uid; gint64 sequence; GList *times; /* GDataGDWhen */ gboolean guests_can_modify; gboolean guests_can_invite_others; gboolean guests_can_see_guests; gboolean anyone_can_add_self; GList *people; /* GDataGDWho */ GList *places; /* GDataGDWhere */ gchar *recurrence; gchar *original_event_id; gchar *original_event_uri; gchar *organiser_email; /* owned */ /* Parsing state. */ struct { gint64 start_time; gint64 end_time; gboolean seen_start; gboolean seen_end; gboolean start_is_date; gboolean end_is_date; } parser; }; enum { PROP_EDITED = 1, PROP_STATUS, PROP_VISIBILITY, PROP_TRANSPARENCY, PROP_UID, PROP_SEQUENCE, PROP_GUESTS_CAN_MODIFY, PROP_GUESTS_CAN_INVITE_OTHERS, PROP_GUESTS_CAN_SEE_GUESTS, PROP_ANYONE_CAN_ADD_SELF, PROP_RECURRENCE, PROP_ORIGINAL_EVENT_ID, PROP_ORIGINAL_EVENT_URI }; G_DEFINE_TYPE (GDataCalendarEvent, gdata_calendar_event, GDATA_TYPE_ENTRY) static void gdata_calendar_event_class_init (GDataCalendarEventClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass); GDataEntryClass *entry_class = GDATA_ENTRY_CLASS (klass); g_type_class_add_private (klass, sizeof (GDataCalendarEventPrivate)); gobject_class->constructor = gdata_calendar_event_constructor; gobject_class->get_property = gdata_calendar_event_get_property; gobject_class->set_property = gdata_calendar_event_set_property; gobject_class->dispose = gdata_calendar_event_dispose; gobject_class->finalize = gdata_calendar_event_finalize; parsable_class->parse_json = parse_json; parsable_class->post_parse_json = post_parse_json; parsable_class->get_json = get_json; parsable_class->get_content_type = get_content_type; entry_class->kind_term = "calendar#event"; /** * GDataCalendarEvent:edited: * * The last time the event was edited. If the event has not been edited yet, the content indicates the time it was created. * * For more information, see the * Atom Publishing Protocol specification. */ g_object_class_install_property (gobject_class, PROP_EDITED, g_param_spec_int64 ("edited", "Edited", "The last time the event was edited.", -1, G_MAXINT64, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:status: * * The scheduling status of the event. For example: %GDATA_GD_EVENT_STATUS_CANCELED or %GDATA_GD_EVENT_STATUS_CONFIRMED. * * For more information, see the * GData specification. * * Since: 0.2.0 */ g_object_class_install_property (gobject_class, PROP_STATUS, g_param_spec_string ("status", "Status", "The scheduling status of the event.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:visibility: * * The event's visibility to calendar users. For example: %GDATA_GD_EVENT_VISIBILITY_PUBLIC or %GDATA_GD_EVENT_VISIBILITY_DEFAULT. * * For more information, see the * GData specification. */ g_object_class_install_property (gobject_class, PROP_VISIBILITY, g_param_spec_string ("visibility", "Visibility", "The event's visibility to calendar users.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:transparency: * * How the event is marked as consuming time on a calendar. For example: %GDATA_GD_EVENT_TRANSPARENCY_OPAQUE or * %GDATA_GD_EVENT_TRANSPARENCY_TRANSPARENT. * * For more information, see the * GData specification. */ g_object_class_install_property (gobject_class, PROP_TRANSPARENCY, g_param_spec_string ("transparency", "Transparency", "How the event is marked as consuming time on a calendar.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:uid: * * The globally unique identifier (UID) of the event as defined in Section 4.8.4.7 of RFC 2445. */ g_object_class_install_property (gobject_class, PROP_UID, g_param_spec_string ("uid", "UID", "The globally unique identifier (UID) of the event.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:sequence: * * The revision sequence number of the event as defined in Section 4.8.7.4 of RFC 2445. */ g_object_class_install_property (gobject_class, PROP_SEQUENCE, g_param_spec_uint ("sequence", "Sequence", "The revision sequence number of the event.", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:guests-can-modify: * * Indicates whether attendees may modify the original event, so that changes are visible to organizers and other attendees. * Otherwise, any changes made by attendees will be restricted to that attendee's calendar. * * For more information, see the * * GData specification. */ g_object_class_install_property (gobject_class, PROP_GUESTS_CAN_MODIFY, g_param_spec_boolean ("guests-can-modify", "Guests can modify", "Indicates whether attendees may modify the original event.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:guests-can-invite-others: * * Indicates whether attendees may invite others to the event. * * For more information, see the GData specification. */ g_object_class_install_property (gobject_class, PROP_GUESTS_CAN_INVITE_OTHERS, g_param_spec_boolean ("guests-can-invite-others", "Guests can invite others", "Indicates whether attendees may invite others.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:guests-can-see-guests: * * Indicates whether attendees can see other people invited to the event. * * For more information, see the * * GData specification. */ g_object_class_install_property (gobject_class, PROP_GUESTS_CAN_SEE_GUESTS, g_param_spec_boolean ("guests-can-see-guests", "Guests can see guests", "Indicates whether attendees can see other people invited.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:anyone-can-add-self: * * Indicates whether anyone can invite themselves to the event, by adding themselves to the attendee list. */ g_object_class_install_property (gobject_class, PROP_ANYONE_CAN_ADD_SELF, g_param_spec_boolean ("anyone-can-add-self", "Anyone can add self", "Indicates whether anyone can invite themselves to the event.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:recurrence: * * Represents the dates and times when a recurring event takes place. The returned string is in iCal format, as a list of properties. * * For more information, see the * GData specification. * * Note: gdata_calendar_event_add_time() and gdata_calendar_event_set_recurrence() are mutually * exclusive. See the documentation for gdata_calendar_event_add_time() for details. * * Since: 0.3.0 */ g_object_class_install_property (gobject_class, PROP_RECURRENCE, g_param_spec_string ("recurrence", "Recurrence", "Represents the dates and times when a recurring event takes place.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:original-event-id: * * The event ID for the original event, if this event is an exception to a recurring event. * * Since: 0.3.0 */ g_object_class_install_property (gobject_class, PROP_ORIGINAL_EVENT_ID, g_param_spec_string ("original-event-id", "Original event ID", "The event ID for the original event.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * GDataCalendarEvent:original-event-uri: * * The event URI for the original event, if this event is an exception to a recurring event. * * Since: 0.3.0 */ g_object_class_install_property (gobject_class, PROP_ORIGINAL_EVENT_URI, g_param_spec_string ("original-event-uri", "Original event URI", "The event URI for the original event.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } static void gdata_calendar_event_init (GDataCalendarEvent *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_CALENDAR_EVENT, GDataCalendarEventPrivate); self->priv->edited = -1; } static GObject * gdata_calendar_event_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params) { GObject *object; /* Chain up to the parent class */ object = G_OBJECT_CLASS (gdata_calendar_event_parent_class)->constructor (type, n_construct_params, construct_params); if (_gdata_parsable_is_constructed_from_xml (GDATA_PARSABLE (object)) == FALSE) { GDataCalendarEventPrivate *priv = GDATA_CALENDAR_EVENT (object)->priv; GTimeVal time_val; /* Set the edited property to the current time (creation time). We don't do this in *_init() since that would cause * setting it from parse_xml() to fail (duplicate element). */ g_get_current_time (&time_val); priv->edited = time_val.tv_sec; } return object; } static void gdata_calendar_event_dispose (GObject *object) { GDataCalendarEventPrivate *priv = GDATA_CALENDAR_EVENT (object)->priv; if (priv->times != NULL) { g_list_foreach (priv->times, (GFunc) g_object_unref, NULL); g_list_free (priv->times); } priv->times = NULL; if (priv->people != NULL) { g_list_foreach (priv->people, (GFunc) g_object_unref, NULL); g_list_free (priv->people); } priv->people = NULL; if (priv->places != NULL) { g_list_foreach (priv->places, (GFunc) g_object_unref, NULL); g_list_free (priv->places); } priv->places = NULL; /* Chain up to the parent class */ G_OBJECT_CLASS (gdata_calendar_event_parent_class)->dispose (object); } static void gdata_calendar_event_finalize (GObject *object) { GDataCalendarEventPrivate *priv = GDATA_CALENDAR_EVENT (object)->priv; g_free (priv->status); g_free (priv->visibility); g_free (priv->transparency); g_free (priv->uid); g_free (priv->recurrence); g_free (priv->original_event_id); g_free (priv->original_event_uri); g_free (priv->organiser_email); /* Chain up to the parent class */ G_OBJECT_CLASS (gdata_calendar_event_parent_class)->finalize (object); } static void gdata_calendar_event_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GDataCalendarEventPrivate *priv = GDATA_CALENDAR_EVENT (object)->priv; switch (property_id) { case PROP_EDITED: g_value_set_int64 (value, priv->edited); break; case PROP_STATUS: g_value_set_string (value, priv->status); break; case PROP_VISIBILITY: g_value_set_string (value, priv->visibility); break; case PROP_TRANSPARENCY: g_value_set_string (value, priv->transparency); break; case PROP_UID: g_value_set_string (value, priv->uid); break; case PROP_SEQUENCE: g_value_set_uint (value, CLAMP (priv->sequence, 0, G_MAXUINT)); break; case PROP_GUESTS_CAN_MODIFY: g_value_set_boolean (value, priv->guests_can_modify); break; case PROP_GUESTS_CAN_INVITE_OTHERS: g_value_set_boolean (value, priv->guests_can_invite_others); break; case PROP_GUESTS_CAN_SEE_GUESTS: g_value_set_boolean (value, priv->guests_can_see_guests); break; case PROP_ANYONE_CAN_ADD_SELF: g_value_set_boolean (value, priv->anyone_can_add_self); break; case PROP_RECURRENCE: g_value_set_string (value, priv->recurrence); break; case PROP_ORIGINAL_EVENT_ID: g_value_set_string (value, priv->original_event_id); break; case PROP_ORIGINAL_EVENT_URI: g_value_set_string (value, priv->original_event_uri); break; default: /* We don't have any other property... */ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gdata_calendar_event_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GDataCalendarEvent *self = GDATA_CALENDAR_EVENT (object); switch (property_id) { case PROP_STATUS: gdata_calendar_event_set_status (self, g_value_get_string (value)); break; case PROP_VISIBILITY: gdata_calendar_event_set_visibility (self, g_value_get_string (value)); break; case PROP_TRANSPARENCY: gdata_calendar_event_set_transparency (self, g_value_get_string (value)); break; case PROP_UID: gdata_calendar_event_set_uid (self, g_value_get_string (value)); break; case PROP_SEQUENCE: gdata_calendar_event_set_sequence (self, g_value_get_uint (value)); break; case PROP_GUESTS_CAN_MODIFY: gdata_calendar_event_set_guests_can_modify (self, g_value_get_boolean (value)); break; case PROP_GUESTS_CAN_INVITE_OTHERS: gdata_calendar_event_set_guests_can_invite_others (self, g_value_get_boolean (value)); break; case PROP_GUESTS_CAN_SEE_GUESTS: gdata_calendar_event_set_guests_can_see_guests (self, g_value_get_boolean (value)); break; case PROP_ANYONE_CAN_ADD_SELF: gdata_calendar_event_set_anyone_can_add_self (self, g_value_get_boolean (value)); break; case PROP_RECURRENCE: gdata_calendar_event_set_recurrence (self, g_value_get_string (value)); break; default: /* We don't have any other property... */ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gboolean date_object_from_json (JsonReader *reader, const gchar *member_name, GDataParserOptions options, gint64 *date_time_output, gboolean *is_date_output, gboolean *success, GError **error) { gint64 date_time; gboolean is_date = FALSE; gboolean found_member = FALSE; /* Check if there’s such an element */ if (g_strcmp0 (json_reader_get_member_name (reader), member_name) != 0) { return FALSE; } /* Check that it’s an object. */ if (!json_reader_is_object (reader)) { const GError *child_error; /* Manufacture an error. */ json_reader_read_member (reader, "dateTime"); child_error = json_reader_get_error (reader); g_assert (child_error != NULL); *success = gdata_parser_error_from_json_error (reader, child_error, error); json_reader_end_member (reader); return TRUE; } /* Try to parse either the dateTime or date member. */ if (json_reader_read_member (reader, "dateTime")) { const gchar *date_string; const GError *child_error; GTimeVal time_val; date_string = json_reader_get_string_value (reader); child_error = json_reader_get_error (reader); if (child_error != NULL) { *success = gdata_parser_error_from_json_error (reader, child_error, error); json_reader_end_member (reader); return TRUE; } if (!g_time_val_from_iso8601 (date_string, &time_val)) { *success = gdata_parser_error_not_iso8601_format_json (reader, date_string, error); json_reader_end_member (reader); return TRUE; } date_time = time_val.tv_sec; is_date = FALSE; found_member = TRUE; } json_reader_end_member (reader); if (json_reader_read_member (reader, "date")) { const gchar *date_string; const GError *child_error; date_string = json_reader_get_string_value (reader); child_error = json_reader_get_error (reader); if (child_error != NULL) { *success = gdata_parser_error_from_json_error (reader, child_error, error); json_reader_end_member (reader); return TRUE; } if (!gdata_parser_int64_from_date (date_string, &date_time)) { *success = gdata_parser_error_not_iso8601_format_json (reader, date_string, error); json_reader_end_member (reader); return TRUE; } is_date = TRUE; found_member = TRUE; } json_reader_end_member (reader); /* Ignore timeZone; it should be specified in dateTime. */ if (!found_member) { *success = gdata_parser_error_required_json_content_missing (reader, error); return TRUE; } *date_time_output = date_time; *is_date_output = is_date; *success = TRUE; return TRUE; } /* Convert between v2 and v3 versions of various enum values. v2 uses a URI * style with a constant prefix; v3 simply drops this prefix, and changes the * spelling of ‘canceled’ to ‘cancelled’. */ #define V2_PREFIX "http://schemas.google.com/g/2005#event." static gchar * add_v2_prefix (const gchar *in) { return g_strconcat (V2_PREFIX, in, NULL); } static const gchar * strip_v2_prefix (const gchar *uri) { /* Convert to v3 format. */ if (g_str_has_prefix (uri, V2_PREFIX)) { return uri + strlen (V2_PREFIX); } else { return uri; } } static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error) { gboolean success; GDataCalendarEvent *self = GDATA_CALENDAR_EVENT (parsable); GDataCalendarEventPrivate *priv = self->priv; /* FIXME: Currently unsupported: * - htmlLink * - colorId * - endTimeUnspecified * - originalStartTime * - attendeesOmitted * - extendedProperties * - hangoutLink * - gadget * - privateCopy * - locked * - reminders * - source */ if (g_strcmp0 (json_reader_get_member_name (reader), "start") == 0) { self->priv->parser.seen_start = TRUE; } else if (g_strcmp0 (json_reader_get_member_name (reader), "end") == 0) { self->priv->parser.seen_end = TRUE; } if (gdata_parser_string_from_json_member (reader, "recurringEventId", P_DEFAULT, &self->priv->original_event_id, &success, error) || gdata_parser_boolean_from_json_member (reader, "guestsCanModify", P_DEFAULT, &self->priv->guests_can_modify, &success, error) || gdata_parser_boolean_from_json_member (reader, "guestsCanInviteOthers", P_DEFAULT, &self->priv->guests_can_invite_others, &success, error) || gdata_parser_boolean_from_json_member (reader, "guestsCanSeeOtherGuests", P_DEFAULT, &self->priv->guests_can_see_guests, &success, error) || gdata_parser_boolean_from_json_member (reader, "anyoneCanAddSelf", P_DEFAULT, &self->priv->anyone_can_add_self, &success, error) || gdata_parser_string_from_json_member (reader, "iCalUID", P_DEFAULT, &self->priv->uid, &success, error) || gdata_parser_int_from_json_member (reader, "sequence", P_DEFAULT, &self->priv->sequence, &success, error) || gdata_parser_int64_time_from_json_member (reader, "updated", P_DEFAULT, &self->priv->edited, &success, error) || date_object_from_json (reader, "start", P_DEFAULT, &self->priv->parser.start_time, &self->priv->parser.start_is_date, &success, error) || date_object_from_json (reader, "end", P_DEFAULT, &self->priv->parser.end_time, &self->priv->parser.end_is_date, &success, error)) { if (success) { if (self->priv->edited != -1) { _gdata_entry_set_updated (GDATA_ENTRY (parsable), self->priv->edited); } if (self->priv->original_event_id != NULL) { g_free (self->priv->original_event_uri); self->priv->original_event_uri = g_strconcat ("https://www.googleapis.com/calendar/v3/events/", self->priv->original_event_id, NULL); } if (self->priv->parser.seen_start && self->priv->parser.seen_end) { GDataGDWhen *when; when = gdata_gd_when_new (self->priv->parser.start_time, self->priv->parser.end_time, self->priv->parser.start_is_date || self->priv->parser.end_is_date); self->priv->times = g_list_prepend (self->priv->times, when); /* transfer ownership */ self->priv->parser.seen_start = FALSE; self->priv->parser.seen_end = FALSE; } } return success; } else if (g_strcmp0 (json_reader_get_member_name (reader), "transparency") == 0) { gchar *transparency = NULL; /* owned */ g_assert (gdata_parser_string_from_json_member (reader, "transparency", P_DEFAULT, &transparency, &success, error)); if (success) { priv->transparency = add_v2_prefix (transparency); } g_free (transparency); return success; } else if (g_strcmp0 (json_reader_get_member_name (reader), "visibility") == 0) { gchar *visibility = NULL; /* owned */ g_assert (gdata_parser_string_from_json_member (reader, "visibility", P_DEFAULT, &visibility, &success, error)); if (success) { priv->visibility = add_v2_prefix (visibility); } g_free (visibility); return success; } else if (g_strcmp0 (json_reader_get_member_name (reader), "status") == 0) { gchar *status = NULL; /* owned */ g_assert (gdata_parser_string_from_json_member (reader, "status", P_DEFAULT, &status, &success, error)); if (success) { if (g_strcmp0 (status, "cancelled") == 0) { /* Those damned British Englishes. */ priv->status = add_v2_prefix ("canceled"); } else { priv->status = add_v2_prefix (status); } } g_free (status); return success; } else if (g_strcmp0 (json_reader_get_member_name (reader), "summary") == 0) { const gchar *summary; const GError *child_error = NULL; summary = json_reader_get_string_value (reader); child_error = json_reader_get_error (reader); if (child_error != NULL) { gdata_parser_error_from_json_error (reader, child_error, error); return FALSE; } gdata_entry_set_title (GDATA_ENTRY (parsable), summary); } else if (g_strcmp0 (json_reader_get_member_name (reader), "description") == 0) { const gchar *description; const GError *child_error = NULL; description = json_reader_get_string_value (reader); child_error = json_reader_get_error (reader); if (child_error != NULL) { gdata_parser_error_from_json_error (reader, child_error, error); return FALSE; } gdata_entry_set_content (GDATA_ENTRY (parsable), description); } else if (g_strcmp0 (json_reader_get_member_name (reader), "location") == 0) { const gchar *location; GDataGDWhere *where = NULL; /* owned */ const GError *child_error = NULL; location = json_reader_get_string_value (reader); child_error = json_reader_get_error (reader); if (child_error != NULL) { gdata_parser_error_from_json_error (reader, child_error, error); return FALSE; } where = gdata_gd_where_new (GDATA_GD_WHERE_EVENT, location, NULL); priv->places = g_list_prepend (priv->places, where); /* transfer ownership */ } else if (g_strcmp0 (json_reader_get_member_name (reader), "created") == 0) { gint64 created; g_assert (gdata_parser_int64_time_from_json_member (reader, "created", P_DEFAULT, &created, &success, error)); if (success) { _gdata_entry_set_published (GDATA_ENTRY (parsable), created); } return success; } else if (g_strcmp0 (json_reader_get_member_name (reader), "recurrence") == 0) { guint i, j; GString *recurrence = NULL; /* owned */ /* In the JSON API, the recurrence is given as an array of * strings, each giving an RFC 2445 property such as RRULE, * EXRULE, RDATE or EXDATE. Concatenate them all to form a * recurrence string as used in v2 of the API. */ if (self->priv->recurrence != NULL) { return gdata_parser_error_duplicate_json_element (reader, error); } recurrence = g_string_new (""); for (i = 0, j = json_reader_count_elements (reader); i < j; i++) { const gchar *line; const GError *child_error; json_reader_read_element (reader, i); line = json_reader_get_string_value (reader); child_error = json_reader_get_error (reader); if (child_error != NULL) { gdata_parser_error_from_json_error (reader, child_error, error); json_reader_end_element (reader); return FALSE; } g_string_append (recurrence, line); g_string_append (recurrence, "\n"); json_reader_end_element (reader); } g_assert (self->priv->recurrence == NULL); self->priv->recurrence = g_string_free (recurrence, FALSE); return TRUE; } else if (g_strcmp0 (json_reader_get_member_name (reader), "attendees") == 0) { guint i, j; if (priv->people != NULL) { return gdata_parser_error_duplicate_json_element (reader, error); } for (i = 0, j = json_reader_count_elements (reader); i < j; i++) { GDataGDWho *who = NULL; /* owned */ const gchar *email_address, *value_string; const gchar *relation_type; gboolean is_organizer, is_resource; const GError *child_error; json_reader_read_element (reader, i); json_reader_read_member (reader, "responseStatus"); child_error = json_reader_get_error (reader); if (child_error != NULL) { gdata_parser_error_from_json_error (reader, child_error, error); json_reader_end_member (reader); return FALSE; } json_reader_end_member (reader); json_reader_read_member (reader, "email"); email_address = json_reader_get_string_value (reader); json_reader_end_member (reader); json_reader_read_member (reader, "displayName"); value_string = json_reader_get_string_value (reader); json_reader_end_member (reader); json_reader_read_member (reader, "organizer"); is_organizer = json_reader_get_boolean_value (reader); json_reader_end_member (reader); json_reader_read_member (reader, "resource"); is_resource = json_reader_get_boolean_value (reader); json_reader_end_member (reader); /* FIXME: Currently unsupported: * - id * - self * - optional (writeble) * - responseStatus (writeble) * - comment (writeble) * - additionalGuests (writeble) */ if (is_organizer) { relation_type = GDATA_GD_WHO_EVENT_ORGANIZER; } else if (!is_resource) { relation_type = GDATA_GD_WHO_EVENT_ATTENDEE; } else { /* FIXME: Add support for resources. */ relation_type = NULL; } who = gdata_gd_who_new (relation_type, value_string, email_address); priv->people = g_list_prepend (priv->people, who); /* transfer ownership */ json_reader_end_element (reader); } } else if (g_strcmp0 (json_reader_get_member_name (reader), "organizer") == 0) { /* This actually gives the parent calendar. Optional. */ g_clear_pointer (&priv->organiser_email, g_free); if (json_reader_read_member (reader, "email")) priv->organiser_email = g_strdup (json_reader_get_string_value (reader)); json_reader_end_member (reader); return TRUE; } else if (g_strcmp0 (json_reader_get_member_name (reader), "creator") == 0) { /* These are read-only and already handled as part of * ‘attendees’, so ignore them. */ return TRUE; } else { return GDATA_PARSABLE_CLASS (gdata_calendar_event_parent_class)->parse_json (parsable, reader, user_data, error); } return TRUE; } static gboolean post_parse_json (GDataParsable *parsable, gpointer user_data, GError **error) { GDataLink *_link = NULL; /* owned */ const gchar *id, *calendar_id; gchar *uri = NULL; /* owned */ GDataCalendarEventPrivate *priv; priv = GDATA_CALENDAR_EVENT (parsable)->priv; /* Set the self link, which is needed for gdata_service_delete_entry(). * Unfortunately, it needs the event ID _and_ the calendar ID — which * is perversely only available as the organiser e-mail address. */ id = gdata_entry_get_id (GDATA_ENTRY (parsable)); calendar_id = priv->organiser_email; if (id == NULL || calendar_id == NULL) { return TRUE; } uri = g_strconcat ("https://www.googleapis.com/calendar/v3/calendars/", calendar_id, "/events/", id, NULL); _link = gdata_link_new (uri, GDATA_LINK_SELF); gdata_entry_add_link (GDATA_ENTRY (parsable), _link); g_object_unref (_link); g_free (uri); return TRUE; } static void get_json (GDataParsable *parsable, JsonBuilder *builder) { GList *l; const gchar *id, *etag, *title, *description; GDataGDWho *organiser_who = NULL; /* unowned */ GDataCalendarEventPrivate *priv = GDATA_CALENDAR_EVENT (parsable)->priv; /* FIXME: Support: * - colorId * - attendeesOmitted * - extendedProperties * - gadget * - reminders * - source */ id = gdata_entry_get_id (GDATA_ENTRY (parsable)); if (id != NULL) { json_builder_set_member_name (builder, "id"); json_builder_add_string_value (builder, id); } json_builder_set_member_name (builder, "kind"); json_builder_add_string_value (builder, "calendar#event"); /* Add the ETag, if available. */ etag = gdata_entry_get_etag (GDATA_ENTRY (parsable)); if (etag != NULL) { json_builder_set_member_name (builder, "etag"); json_builder_add_string_value (builder, etag); } /* Calendar labels titles as ‘summary’. */ title = gdata_entry_get_title (GDATA_ENTRY (parsable)); if (title != NULL) { json_builder_set_member_name (builder, "summary"); json_builder_add_string_value (builder, title); } description = gdata_entry_get_content (GDATA_ENTRY (parsable)); if (description != NULL) { json_builder_set_member_name (builder, "description"); json_builder_add_string_value (builder, description); } /* Add all the calendar-specific JSON */ json_builder_set_member_name (builder, "anyoneCanAddSelf"); json_builder_add_boolean_value (builder, priv->anyone_can_add_self); json_builder_set_member_name (builder, "guestsCanInviteOthers"); json_builder_add_boolean_value (builder, priv->guests_can_invite_others); json_builder_set_member_name (builder, "guestsCanModify"); json_builder_add_boolean_value (builder, priv->guests_can_modify); json_builder_set_member_name (builder, "guestsCanSeeOtherGuests"); json_builder_add_boolean_value (builder, priv->guests_can_see_guests); if (priv->transparency != NULL) { json_builder_set_member_name (builder, "transparency"); json_builder_add_string_value (builder, strip_v2_prefix (priv->transparency)); } if (priv->visibility != NULL) { json_builder_set_member_name (builder, "visibility"); json_builder_add_string_value (builder, strip_v2_prefix (priv->visibility)); } if (priv->uid != NULL) { json_builder_set_member_name (builder, "iCalUID"); json_builder_add_string_value (builder, priv->uid); } if (priv->sequence > 0) { json_builder_set_member_name (builder, "sequence"); json_builder_add_int_value (builder, priv->sequence); } if (priv->status != NULL) { const gchar *status; /* Convert to v3 format. */ status = strip_v2_prefix (priv->status); if (g_strcmp0 (status, "canceled") == 0) { status = "cancelled"; } json_builder_set_member_name (builder, "status"); json_builder_add_string_value (builder, status); } if (priv->recurrence != NULL) { gchar **parts; guint i; json_builder_set_member_name (builder, "recurrence"); json_builder_begin_array (builder); parts = g_strsplit (priv->recurrence, "\n", -1); for (i = 0; parts[i] != NULL; i++) { json_builder_add_string_value (builder, parts[i]); } g_strfreev (parts); json_builder_end_array (builder); } if (priv->original_event_id != NULL) { json_builder_set_member_name (builder, "recurringEventId"); json_builder_add_string_value (builder, priv->original_event_id); } /* Times. */ for (l = priv->times; l != NULL; l = l->next) { GDataGDWhen *when; /* unowned */ gchar *val = NULL; /* owned */ const gchar *member_name; gint64 start_time, end_time; when = l->data; /* Start time. */ start_time = gdata_gd_when_get_start_time (when); json_builder_set_member_name (builder, "start"); json_builder_begin_object (builder); if (gdata_gd_when_is_date (when)) { member_name = "date"; val = gdata_parser_date_from_int64 (start_time); } else { member_name = "dateTime"; val = gdata_parser_int64_to_iso8601 (start_time); } json_builder_set_member_name (builder, member_name); json_builder_add_string_value (builder, val); g_free (val); json_builder_set_member_name (builder, "timeZone"); json_builder_add_string_value (builder, "UTC"); json_builder_end_object (builder); /* End time. */ end_time = gdata_gd_when_get_end_time (when); if (end_time > -1) { json_builder_set_member_name (builder, "end"); json_builder_begin_object (builder); if (gdata_gd_when_is_date (when)) { member_name = "date"; val = gdata_parser_date_from_int64 (end_time); } else { member_name = "dateTime"; val = gdata_parser_int64_to_iso8601 (end_time); } json_builder_set_member_name (builder, member_name); json_builder_add_string_value (builder, val); g_free (val); json_builder_set_member_name (builder, "timeZone"); json_builder_add_string_value (builder, "UTC"); json_builder_end_object (builder); } else { json_builder_set_member_name (builder, "endTimeUnspecified"); json_builder_add_boolean_value (builder, TRUE); } /* Only use the first time. :-( * FIXME: There must be a better solution. */ if (l->next != NULL) { g_warning ("Ignoring secondary times; they are no " "longer supported by the server-side API."); break; } } /* Locations. */ for (l = priv->places; l != NULL; l = l->next) { GDataGDWhere *where; /* unowned */ const gchar *location; where = l->data; location = gdata_gd_where_get_value_string (where); json_builder_set_member_name (builder, "location"); json_builder_add_string_value (builder, location); /* Only use the first location. :-( * FIXME: There must be a better solution. */ if (l->next != NULL) { g_warning ("Ignoring secondary locations; they are no " "longer supported by the server-side API."); break; } } /* People. */ json_builder_set_member_name (builder, "attendees"); json_builder_begin_array (builder); for (l = priv->people; l != NULL; l = l->next) { GDataGDWho *who; /* unowned */ const gchar *display_name, *email_address; who = l->data; json_builder_begin_object (builder); display_name = gdata_gd_who_get_value_string (who); if (display_name != NULL) { json_builder_set_member_name (builder, "displayName"); json_builder_add_string_value (builder, display_name); } email_address = gdata_gd_who_get_email_address (who); if (email_address != NULL) { json_builder_set_member_name (builder, "email"); json_builder_add_string_value (builder, email_address); } if (g_strcmp0 (gdata_gd_who_get_relation_type (who), GDATA_GD_WHO_EVENT_ORGANIZER) == 0) { json_builder_set_member_name (builder, "organizer"); json_builder_add_boolean_value (builder, TRUE); organiser_who = who; } json_builder_end_object (builder); } json_builder_end_array (builder); if (organiser_who != NULL) { const gchar *display_name, *email_address; json_builder_set_member_name (builder, "organizer"); json_builder_begin_object (builder); display_name = gdata_gd_who_get_value_string (organiser_who); if (display_name != NULL) { json_builder_set_member_name (builder, "displayName"); json_builder_add_string_value (builder, display_name); } email_address = gdata_gd_who_get_email_address (organiser_who); if (email_address != NULL) { json_builder_set_member_name (builder, "email"); json_builder_add_string_value (builder, email_address); } json_builder_end_object (builder); } } static const gchar * get_content_type (void) { return "application/json"; } /** * gdata_calendar_event_new: * @id: (allow-none): the event's ID, or %NULL * * Creates a new #GDataCalendarEvent with the given ID and default properties. * * Return value: a new #GDataCalendarEvent; unref with g_object_unref() */ GDataCalendarEvent * gdata_calendar_event_new (const gchar *id) { return GDATA_CALENDAR_EVENT (g_object_new (GDATA_TYPE_CALENDAR_EVENT, "id", id, NULL)); } /** * gdata_calendar_event_get_edited: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:edited property. If the property is unset, -1 will be returned. * * Return value: the UNIX timestamp for the time the event was last edited, or -1 */ gint64 gdata_calendar_event_get_edited (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), -1); return self->priv->edited; } /** * gdata_calendar_event_get_status: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:status property. * * Return value: the event status, or %NULL * * Since: 0.2.0 */ const gchar * gdata_calendar_event_get_status (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), NULL); return self->priv->status; } /** * gdata_calendar_event_set_status: * @self: a #GDataCalendarEvent * @status: (allow-none): a new event status, or %NULL * * Sets the #GDataCalendarEvent:status property to the new status, @status. * * Set @status to %NULL to unset the property in the event. * * Since: 0.2.0 */ void gdata_calendar_event_set_status (GDataCalendarEvent *self, const gchar *status) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); g_free (self->priv->status); self->priv->status = g_strdup (status); g_object_notify (G_OBJECT (self), "status"); } /** * gdata_calendar_event_get_visibility: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:visibility property. * * Return value: the event visibility, or %NULL */ const gchar * gdata_calendar_event_get_visibility (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), NULL); return self->priv->visibility; } /** * gdata_calendar_event_set_visibility: * @self: a #GDataCalendarEvent * @visibility: (allow-none): a new event visibility, or %NULL * * Sets the #GDataCalendarEvent:visibility property to the new visibility, @visibility. * * Set @visibility to %NULL to unset the property in the event. */ void gdata_calendar_event_set_visibility (GDataCalendarEvent *self, const gchar *visibility) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); g_free (self->priv->visibility); self->priv->visibility = g_strdup (visibility); g_object_notify (G_OBJECT (self), "visibility"); } /** * gdata_calendar_event_get_transparency: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:transparency property. * * Return value: the event transparency, or %NULL */ const gchar * gdata_calendar_event_get_transparency (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), NULL); return self->priv->transparency; } /** * gdata_calendar_event_set_transparency: * @self: a #GDataCalendarEvent * @transparency: (allow-none): a new event transparency, or %NULL * * Sets the #GDataCalendarEvent:transparency property to the new transparency, @transparency. * * Set @transparency to %NULL to unset the property in the event. */ void gdata_calendar_event_set_transparency (GDataCalendarEvent *self, const gchar *transparency) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); g_free (self->priv->transparency); self->priv->transparency = g_strdup (transparency); g_object_notify (G_OBJECT (self), "transparency"); } /** * gdata_calendar_event_get_uid: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:uid property. * * Return value: the event's UID, or %NULL */ const gchar * gdata_calendar_event_get_uid (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), NULL); return self->priv->uid; } /** * gdata_calendar_event_set_uid: * @self: a #GDataCalendarEvent * @uid: (allow-none): a new event UID, or %NULL * * Sets the #GDataCalendarEvent:uid property to the new UID, @uid. * * Set @uid to %NULL to unset the property in the event. */ void gdata_calendar_event_set_uid (GDataCalendarEvent *self, const gchar *uid) { /* TODO: is modifying this allowed? */ g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); g_free (self->priv->uid); self->priv->uid = g_strdup (uid); g_object_notify (G_OBJECT (self), "uid"); } /** * gdata_calendar_event_get_sequence: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:sequence property. * * Return value: the event's sequence number */ guint gdata_calendar_event_get_sequence (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), 0); return CLAMP (self->priv->sequence, 0, G_MAXUINT); } /** * gdata_calendar_event_set_sequence: * @self: a #GDataCalendarEvent * @sequence: a new sequence number, or 0 * * Sets the #GDataCalendarEvent:sequence property to the new sequence number, @sequence. */ void gdata_calendar_event_set_sequence (GDataCalendarEvent *self, guint sequence) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); self->priv->sequence = sequence; g_object_notify (G_OBJECT (self), "sequence"); } /** * gdata_calendar_event_get_guests_can_modify: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:guests-can-modify property. * * Return value: %TRUE if attendees can modify the original event, %FALSE otherwise */ gboolean gdata_calendar_event_get_guests_can_modify (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), FALSE); return self->priv->guests_can_modify; } /** * gdata_calendar_event_set_guests_can_modify: * @self: a #GDataCalendarEvent * @guests_can_modify: %TRUE if attendees can modify the original event, %FALSE otherwise * * Sets the #GDataCalendarEvent:guests-can-modify property to @guests_can_modify. */ void gdata_calendar_event_set_guests_can_modify (GDataCalendarEvent *self, gboolean guests_can_modify) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); self->priv->guests_can_modify = guests_can_modify; g_object_notify (G_OBJECT (self), "guests-can-modify"); } /** * gdata_calendar_event_get_guests_can_invite_others: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:guests-can-invite-others property. * * Return value: %TRUE if attendees can invite others to the event, %FALSE otherwise */ gboolean gdata_calendar_event_get_guests_can_invite_others (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), FALSE); return self->priv->guests_can_invite_others; } /** * gdata_calendar_event_set_guests_can_invite_others: * @self: a #GDataCalendarEvent * @guests_can_invite_others: %TRUE if attendees can invite others to the event, %FALSE otherwise * * Sets the #GDataCalendarEvent:guests-can-invite-others property to @guests_can_invite_others. */ void gdata_calendar_event_set_guests_can_invite_others (GDataCalendarEvent *self, gboolean guests_can_invite_others) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); self->priv->guests_can_invite_others = guests_can_invite_others; g_object_notify (G_OBJECT (self), "guests-can-invite-others"); } /** * gdata_calendar_event_get_guests_can_see_guests: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:guests-can-see-guests property. * * Return value: %TRUE if attendees can see who's attending the event, %FALSE otherwise */ gboolean gdata_calendar_event_get_guests_can_see_guests (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), FALSE); return self->priv->guests_can_see_guests; } /** * gdata_calendar_event_set_guests_can_see_guests: * @self: a #GDataCalendarEvent * @guests_can_see_guests: %TRUE if attendees can see who's attending the event, %FALSE otherwise * * Sets the #GDataCalendarEvent:guests-can-see-guests property to @guests_can_see_guests. */ void gdata_calendar_event_set_guests_can_see_guests (GDataCalendarEvent *self, gboolean guests_can_see_guests) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); self->priv->guests_can_see_guests = guests_can_see_guests; g_object_notify (G_OBJECT (self), "guests-can-see-guests"); } /** * gdata_calendar_event_get_anyone_can_add_self: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:anyone-can-add-self property. * * Return value: %TRUE if anyone can add themselves as an attendee to the event, %FALSE otherwise */ gboolean gdata_calendar_event_get_anyone_can_add_self (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), FALSE); return self->priv->anyone_can_add_self; } /** * gdata_calendar_event_set_anyone_can_add_self: * @self: a #GDataCalendarEvent * @anyone_can_add_self: %TRUE if anyone can add themselves as an attendee to the event, %FALSE otherwise * * Sets the #GDataCalendarEvent:anyone-can-add-self property to @anyone_can_add_self. */ void gdata_calendar_event_set_anyone_can_add_self (GDataCalendarEvent *self, gboolean anyone_can_add_self) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); self->priv->anyone_can_add_self = anyone_can_add_self; g_object_notify (G_OBJECT (self), "anyone-can-add-self"); } /** * gdata_calendar_event_add_person: * @self: a #GDataCalendarEvent * @who: a #GDataGDWho to add * * Adds the person @who to the event as a guest (attendee, organiser, performer, etc.), and increments its reference count. * * Duplicate people will not be added to the list. */ void gdata_calendar_event_add_person (GDataCalendarEvent *self, GDataGDWho *who) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); g_return_if_fail (GDATA_IS_GD_WHO (who)); if (g_list_find_custom (self->priv->people, who, (GCompareFunc) gdata_comparable_compare) == NULL) self->priv->people = g_list_append (self->priv->people, g_object_ref (who)); } /** * gdata_calendar_event_get_people: * @self: a #GDataCalendarEvent * * Gets a list of the people attending the event. * * Return value: (element-type GData.GDWho) (transfer none): a #GList of #GDataGDWhos, or %NULL * * Since: 0.2.0 */ GList * gdata_calendar_event_get_people (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), NULL); return self->priv->people; } /** * gdata_calendar_event_add_place: * @self: a #GDataCalendarEvent * @where: a #GDataGDWhere to add * * Adds the place @where to the event as a location and increments its reference count. * * Duplicate places will not be added to the list. */ void gdata_calendar_event_add_place (GDataCalendarEvent *self, GDataGDWhere *where) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); g_return_if_fail (GDATA_IS_GD_WHERE (where)); if (g_list_find_custom (self->priv->places, where, (GCompareFunc) gdata_comparable_compare) == NULL) self->priv->places = g_list_append (self->priv->places, g_object_ref (where)); } /** * gdata_calendar_event_get_places: * @self: a #GDataCalendarEvent * * Gets a list of the locations associated with the event. * * Return value: (element-type GData.GDWhere) (transfer none): a #GList of #GDataGDWheres, or %NULL * * Since: 0.2.0 */ GList * gdata_calendar_event_get_places (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), NULL); return self->priv->places; } /** * gdata_calendar_event_add_time: * @self: a #GDataCalendarEvent * @when: a #GDataGDWhen to add * * Adds @when to the event as a time period when the event happens, and increments its reference count. * * Duplicate times will not be added to the list. * * Note: gdata_calendar_event_add_time() and gdata_calendar_event_set_recurrence() are mutually * exclusive, as the server doesn't support positive exceptions to recurrence rules. If recurrences * are required, use gdata_calendar_event_set_recurrence(). Note that this means reminders cannot * be set for the event, as they are only supported by #GDataGDWhen. No checks are performed for * these forbidden conditions, as to do so would break libgdata's API; if both a recurrence is set * and a specific time is added, the server will return an error when the #GDataCalendarEvent is * inserted using gdata_service_insert_entry(). * * Since: 0.2.0 */ void gdata_calendar_event_add_time (GDataCalendarEvent *self, GDataGDWhen *when) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); g_return_if_fail (GDATA_IS_GD_WHEN (when)); if (g_list_find_custom (self->priv->times, when, (GCompareFunc) gdata_comparable_compare) == NULL) self->priv->times = g_list_append (self->priv->times, g_object_ref (when)); } /** * gdata_calendar_event_get_times: * @self: a #GDataCalendarEvent * * Gets a list of the time periods associated with the event. * * Return value: (element-type GData.GDWhen) (transfer none): a #GList of #GDataGDWhens, or %NULL * * Since: 0.2.0 */ GList * gdata_calendar_event_get_times (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), NULL); return self->priv->times; } /** * gdata_calendar_event_get_primary_time: * @self: a #GDataCalendarEvent * @start_time: (out caller-allocates): a #gint64 for the start time, or %NULL * @end_time: (out caller-allocates): a #gint64 for the end time, or %NULL * @when: (out callee-allocates) (transfer none): a #GDataGDWhen for the primary time structure, or %NULL * * Gets the first time period associated with the event, conveniently returning just its start and * end times if required. * * If there are no time periods, or more than one time period, associated with the event, %FALSE will * be returned, and the parameters will remain unmodified. * * Return value: %TRUE if there is only one time period associated with the event, %FALSE otherwise * * Since: 0.2.0 */ gboolean gdata_calendar_event_get_primary_time (GDataCalendarEvent *self, gint64 *start_time, gint64 *end_time, GDataGDWhen **when) { GDataGDWhen *primary_when; g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), FALSE); if (self->priv->times == NULL || self->priv->times->next != NULL) return FALSE; primary_when = GDATA_GD_WHEN (self->priv->times->data); if (start_time != NULL) *start_time = gdata_gd_when_get_start_time (primary_when); if (end_time != NULL) *end_time = gdata_gd_when_get_end_time (primary_when); if (when != NULL) *when = primary_when; return TRUE; } /** * gdata_calendar_event_get_recurrence: * @self: a #GDataCalendarEvent * * Gets the #GDataCalendarEvent:recurrence property. * * Return value: the event recurrence patterns, or %NULL * * Since: 0.3.0 */ const gchar * gdata_calendar_event_get_recurrence (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), NULL); return self->priv->recurrence; } /** * gdata_calendar_event_set_recurrence: * @self: a #GDataCalendarEvent * @recurrence: (allow-none): a new event recurrence, or %NULL * * Sets the #GDataCalendarEvent:recurrence property to the new recurrence, @recurrence. * * Set @recurrence to %NULL to unset the property in the event. * * Note: gdata_calendar_event_add_time() and gdata_calendar_event_set_recurrence() are mutually * exclusive. See the documentation for gdata_calendar_event_add_time() for details. * * Since: 0.3.0 */ void gdata_calendar_event_set_recurrence (GDataCalendarEvent *self, const gchar *recurrence) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); g_free (self->priv->recurrence); self->priv->recurrence = g_strdup (recurrence); g_object_notify (G_OBJECT (self), "recurrence"); } /** * gdata_calendar_event_get_original_event_details: * @self: a #GDataCalendarEvent * @event_id: (out callee-allocates) (transfer full): return location for the original event's ID, or %NULL * @event_uri: (out callee-allocates) (transfer full): return location for the original event's URI, or %NULL * * Gets details of the original event, if this event is an exception to a recurring event. The original * event's ID and the URI of the event's XML are returned in @event_id and @event_uri, respectively. * * If this event is not an exception to a recurring event, @event_id and @event_uri will be set to %NULL. * See gdata_calendar_event_is_exception() to determine more simply whether an event is an exception to a * recurring event. * * If both @event_id and @event_uri are %NULL, this function is a no-op. Otherwise, they should both be * freed with g_free(). * * Since: 0.3.0 */ void gdata_calendar_event_get_original_event_details (GDataCalendarEvent *self, gchar **event_id, gchar **event_uri) { g_return_if_fail (GDATA_IS_CALENDAR_EVENT (self)); if (event_id != NULL) *event_id = g_strdup (self->priv->original_event_id); if (event_uri != NULL) *event_uri = g_strdup (self->priv->original_event_uri); } /** * gdata_calendar_event_is_exception: * @self: a #GDataCalendarEvent * * Determines whether the event is an exception to a recurring event. If it is, details of the original event * can be retrieved using gdata_calendar_event_get_original_event_details(). * * Return value: %TRUE if the event is an exception, %FALSE otherwise * * Since: 0.3.0 */ gboolean gdata_calendar_event_is_exception (GDataCalendarEvent *self) { g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (self), FALSE); return (self->priv->original_event_id != NULL && self->priv->original_event_uri != NULL) ? TRUE : FALSE; }