[evolution-data-server] calendar file backend: support removing parent event with CALOBJ_MOD_THIS



commit ba88feadc788ab9a2961afd6a3575d7079928c32
Author: Patrick Ohly <patrick ohly intel com>
Date:   Wed May 11 16:59:51 2011 +0200

    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.

 calendar/backends/file/e-cal-backend-file.c |  136 +++++++++++++++++---------
 1 files changed, 89 insertions(+), 47 deletions(-)
---
diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c
index 9f522d3..eb22a4c 100644
--- a/calendar/backends/file/e-cal-backend-file.c
+++ b/calendar/backends/file/e-cal-backend-file.c
@@ -2405,47 +2405,94 @@ e_cal_backend_file_modify_object (ECalBackendSync *backend, EDataCal *cal, GCanc
 	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 *
@@ -2506,8 +2553,6 @@ e_cal_backend_file_remove_object (ECalBackendSync *backend, EDataCal *cal, GCanc
 	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);
@@ -2516,20 +2561,16 @@ e_cal_backend_file_remove_object (ECalBackendSync *backend, EDataCal *cal, GCanc
 		*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));
@@ -2578,6 +2619,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;
 
@@ -2585,7 +2627,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;
 
@@ -2602,11 +2644,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);
 
@@ -2847,7 +2889,7 @@ e_cal_backend_file_receive_objects (ECalBackendSync *backend, EDataCal *cal, GCa
 				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);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]