summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2011-05-11 16:59:51 +0200
committerPatrick Ohly <patrick.ohly@intel.com>2011-06-07 11:39:51 +0200
commit9867e52e7372a830ec4056adb7ea6df848a83481 (patch)
treec999809194c212fd8311276299b8eea62a9c4497
parent246fb4ca8d6836f97468c4a61e7a535ab771b805 (diff)
downloadevolution-data-server-9867e52e7372a830ec4056adb7ea6df848a83481.tar.gz
calendar file backend: support removing parent event with CALOBJ_MOD_THIS
It was possible to create a meeting series with just a detached event (RECURRENCE-ID set) by importing a meeting invitation for that single recurrence. It was not possible to arrive at that same state after adding the parent event (the one with the RRULE) because e_cal_remove_object_with_mod() removed all instances for CALOBJ_MOD_THIS and empty rid. This contradicts the intended semantic of e_cal_remove_object_with_mod(): "By using a combination of the @uid, @rid and @mod arguments, you can remove specific instances. If what you want is to remove all instances, use e_cal_remove_object instead." This patch implements the desired semantic: - e_cal_backend_file_remove_object(CALOBJ_MOD_THIS) now always calls remove_instance(). - remove_instance() was extended to also work for the parent event. - That call removes the complete object if nothing is left after removing the instance. This case must be handled by the caller. The return value is the original object (if it still exists) and NULL if not. - Because the uid pointer into the object may become invalid as part of the removal, a more permanent pointer has to be provided by the caller. (cherry picked from commit ba88feadc788ab9a2961afd6a3575d7079928c32)
-rw-r--r--calendar/backends/file/e-cal-backend-file.c134
1 files changed, 88 insertions, 46 deletions
diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c
index 7aff3f65c..6925bc53d 100644
--- a/calendar/backends/file/e-cal-backend-file.c
+++ b/calendar/backends/file/e-cal-backend-file.c
@@ -2623,47 +2623,94 @@ e_cal_backend_file_modify_object (ECalBackendSync *backend, EDataCal *cal, const
g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
}
-static void
-remove_instance (ECalBackendFile *cbfile, ECalBackendFileObject *obj_data, const gchar *rid)
+/**
+ * Remove one and only one instance. The object may be empty
+ * afterwards, in which case it will be removed completely.
+ *
+ * @uid pointer to UID which must remain valid even if the object gets
+ * removed
+ * @rid NULL, "", or non-empty string when manipulating a specific recurrence;
+ * also must remain valid
+ * @return modified object or NULL if it got removed
+ */
+static ECalBackendFileObject *
+remove_instance (ECalBackendFile *cbfile, ECalBackendFileObject *obj_data, const gchar *uid, const gchar *rid)
{
gchar *hash_rid;
ECalComponent *comp;
struct icaltimetype current;
- if (!rid || !*rid)
- return;
+ /* only check for non-NULL below, empty string is detected here */
+ if (rid && !*rid)
+ rid = NULL;
- if (g_hash_table_lookup_extended (obj_data->recurrences, rid, (gpointer *)&hash_rid, (gpointer *)&comp)) {
- /* remove the component from our data */
+ if (rid) {
+ /* remove recurrence */
+ if (g_hash_table_lookup_extended (obj_data->recurrences, rid,
+ (gpointer *)&hash_rid, (gpointer *)&comp)) {
+ /* remove the component from our data */
+ icalcomponent_remove_component (cbfile->priv->icalcomp,
+ e_cal_component_get_icalcomponent (comp));
+ cbfile->priv->comp = g_list_remove (cbfile->priv->comp, comp);
+ obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, comp);
+ g_hash_table_remove (obj_data->recurrences, rid);
+ } else {
+ /* not an error, only add EXDATE */
+ }
+ /* component empty? */
+ if (!obj_data->full_object) {
+ if (!obj_data->recurrences_list) {
+ /* empty now, remove it */
+ remove_component (cbfile, uid, obj_data);
+ return NULL;
+ } else {
+ return obj_data;
+ }
+ }
+ /* remove the main component from our data before modifying it */
icalcomponent_remove_component (cbfile->priv->icalcomp,
- e_cal_component_get_icalcomponent (comp));
- cbfile->priv->comp = g_list_remove (cbfile->priv->comp, comp);
- obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, comp);
- g_hash_table_remove (obj_data->recurrences, rid);
- }
+ e_cal_component_get_icalcomponent (obj_data->full_object));
+ cbfile->priv->comp = g_list_remove (cbfile->priv->comp, obj_data->full_object);
- if (!obj_data->full_object)
- return;
+ /* add EXDATE or EXRULE to parent */
+ e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
+ icaltime_from_string (rid), CALOBJ_MOD_THIS);
- /* remove the component from our data, temporarily */
- icalcomponent_remove_component (cbfile->priv->icalcomp,
- e_cal_component_get_icalcomponent (obj_data->full_object));
- cbfile->priv->comp = g_list_remove (cbfile->priv->comp, obj_data->full_object);
+ /* Since we are only removing one instance of recurrence
+ event, update the last modified time on the component */
+ current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+ e_cal_component_set_last_modified (obj_data->full_object, &current);
+
+ /* add the modified object to the beginning of the list,
+ so that it's always before any detached instance we
+ might have */
+ icalcomponent_add_component (cbfile->priv->icalcomp,
+ e_cal_component_get_icalcomponent (obj_data->full_object));
+ cbfile->priv->comp = g_list_prepend (cbfile->priv->comp, obj_data->full_object);
+ } else {
+ /* remove the main component from our data before deleting it */
+ if (!remove_component_from_intervaltree (cbfile, obj_data->full_object)) {
+ /* return without changing anything */
+ g_message (G_STRLOC " Could not remove component from interval tree!");
+ return obj_data;
+ }
+ icalcomponent_remove_component (cbfile->priv->icalcomp,
+ e_cal_component_get_icalcomponent (obj_data->full_object));
+ cbfile->priv->comp = g_list_remove (cbfile->priv->comp, obj_data->full_object);
- e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
- icaltime_from_string (rid), CALOBJ_MOD_THIS);
+ /* remove parent */
+ g_object_unref (obj_data->full_object);
+ obj_data->full_object = NULL;
- /* Since we are only removing one instance of recurrence
- event, update the last modified time on the component */
- current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
- e_cal_component_set_last_modified (obj_data->full_object, &current);
+ /* component may be empty now, check that */
+ if (!obj_data->recurrences_list) {
+ remove_component (cbfile, uid, obj_data);
+ return NULL;
+ }
+ }
- /* add the modified object to the beginning of the list,
- so that it's always before any detached instance we
- might have */
- icalcomponent_add_component (cbfile->priv->icalcomp,
- e_cal_component_get_icalcomponent (obj_data->full_object));
- cbfile->priv->comp = g_list_prepend (cbfile->priv->comp, obj_data->full_object);
+ /* component still exists in a modified form */
+ return obj_data;
}
static gchar *
@@ -2724,8 +2771,6 @@ e_cal_backend_file_remove_object (ECalBackendSync *backend, EDataCal *cal,
if (rid && *rid)
recur_id = rid;
- comp = obj_data->full_object;
-
switch (mod) {
case CALOBJ_MOD_ALL :
*old_object = get_object_string_from_fileobject (obj_data, recur_id);
@@ -2734,20 +2779,16 @@ e_cal_backend_file_remove_object (ECalBackendSync *backend, EDataCal *cal,
*object = NULL;
break;
case CALOBJ_MOD_THIS :
- if (!recur_id) {
- *old_object = get_object_string_from_fileobject (obj_data, recur_id);
- remove_component (cbfile, uid, obj_data);
- *object = NULL;
- } else {
- *old_object = get_object_string_from_fileobject (obj_data, recur_id);
+ *old_object = get_object_string_from_fileobject (obj_data, recur_id);
- remove_instance (cbfile, obj_data, recur_id);
- if (comp)
- *object = e_cal_component_get_as_string (comp);
- }
+ obj_data = remove_instance (cbfile, obj_data, uid, recur_id);
+ if (obj_data && obj_data->full_object)
+ *object = e_cal_component_get_as_string (obj_data->full_object);
break;
case CALOBJ_MOD_THISANDPRIOR :
case CALOBJ_MOD_THISANDFUTURE :
+ comp = obj_data->full_object;
+
if (!recur_id || !*recur_id) {
g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
g_propagate_error (error, EDC_ERROR (ObjectNotFound));
@@ -2796,6 +2837,7 @@ cancel_received_object (ECalBackendFile *cbfile, icalcomponent *icalcomp, gchar
ECalBackendFilePrivate *priv;
gchar *rid;
ECalComponent *comp;
+ const gchar *uid = icalcomponent_get_uid (icalcomp);
priv = cbfile->priv;
@@ -2803,7 +2845,7 @@ cancel_received_object (ECalBackendFile *cbfile, icalcomponent *icalcomp, gchar
*new_object = NULL;
/* Find the old version of the component. */
- obj_data = g_hash_table_lookup (priv->comp_uid_hash, icalcomponent_get_uid (icalcomp));
+ obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
if (!obj_data)
return FALSE;
@@ -2820,11 +2862,11 @@ cancel_received_object (ECalBackendFile *cbfile, icalcomponent *icalcomp, gchar
/* new_object is kept NULL if not removing the instance */
rid = e_cal_component_get_recurid_as_string (comp);
if (rid && *rid) {
- remove_instance (cbfile, obj_data, rid);
- if (obj_data->full_object)
+ obj_data = remove_instance (cbfile, obj_data, uid, rid);
+ if (obj_data && obj_data->full_object)
*new_object = e_cal_component_get_as_string (obj_data->full_object);
} else
- remove_component (cbfile, icalcomponent_get_uid (icalcomp), obj_data);
+ remove_component (cbfile, uid, obj_data);
g_free (rid);
@@ -3065,7 +3107,7 @@ e_cal_backend_file_receive_objects (ECalBackendSync *backend, EDataCal *cal, con
if (obj_data->full_object)
old_object = e_cal_component_get_as_string (obj_data->full_object);
if (rid)
- remove_instance (cbfile, obj_data, rid);
+ remove_instance (cbfile, obj_data, uid, rid);
else
remove_component (cbfile, uid, obj_data);