[evolution-data-server/wip/offline-cache] [ECalMetaBackend] Implement refresh with offline changes storing
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/wip/offline-cache] [ECalMetaBackend] Implement refresh with offline changes storing
- Date: Fri, 3 Mar 2017 13:03:07 +0000 (UTC)
commit c2e29a6ddbe1ebc11b4608305348f08b6362b451
Author: Milan Crha <mcrha redhat com>
Date: Fri Mar 3 14:01:33 2017 +0100
[ECalMetaBackend] Implement refresh with offline changes storing
src/calendar/libedata-cal/e-cal-cache.c | 131 ++++++++++++
src/calendar/libedata-cal/e-cal-cache.h | 37 ++++
src/calendar/libedata-cal/e-cal-meta-backend.c | 270 +++++++++++++++++++-----
src/libebackend/e-cache.h | 11 +
4 files changed, 396 insertions(+), 53 deletions(-)
---
diff --git a/src/calendar/libedata-cal/e-cal-cache.c b/src/calendar/libedata-cal/e-cal-cache.c
index b51f848..1c7e45f 100644
--- a/src/calendar/libedata-cal/e-cal-cache.c
+++ b/src/calendar/libedata-cal/e-cal-cache.c
@@ -88,9 +88,89 @@ G_DEFINE_TYPE_WITH_CODE (ECalCache, e_cal_cache, E_TYPE_CACHE,
G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)
G_IMPLEMENT_INTERFACE (E_TYPE_TIMEZONE_CACHE, ecc_timezone_cache_init))
+G_DEFINE_BOXED_TYPE (ECalCacheOfflineChange, e_cal_cache_offline_change, e_cal_cache_offline_change_copy,
e_cal_cache_offline_change_free)
G_DEFINE_BOXED_TYPE (ECalCacheSearchData, e_cal_cache_search_data, e_cal_cache_search_data_copy,
e_cal_cache_search_data_free)
/**
+ * e_cal_cache_offline_change_new:
+ * @uid: a unique component identifier
+ * @rid: (nullable): a Recurrence-ID of the component
+ * @revision: (nullable): a revision of the component
+ * @object: (nullable): component itself
+ * @state: an #EOfflineState
+ *
+ * Creates a new #ECalCacheOfflineChange with the offline @state
+ * information for the given @uid.
+ *
+ * Returns: (transfer full): A new #ECalCacheOfflineChange. Free it with
+ * e_cal_cache_offline_change_free() when no longer needed.
+ *
+ * Since: 3.26
+ **/
+ECalCacheOfflineChange *
+e_cal_cache_offline_change_new (const gchar *uid,
+ const gchar *rid,
+ const gchar *revision,
+ const gchar *object,
+ EOfflineState state)
+{
+ ECalCacheOfflineChange *change;
+
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ change = g_new0 (ECalCacheOfflineChange, 1);
+ change->uid = g_strdup (uid);
+ change->rid = g_strdup (rid);
+ change->revision = g_strdup (revision);
+ change->object = g_strdup (object);
+ change->state = state;
+
+ return change;
+}
+
+/**
+ * e_cal_cache_offline_change_copy:
+ * @change: (nullable): a source #ECalCacheOfflineChange to copy, or %NULL
+ *
+ * Returns: (transfer full): Copy of the given @change. Free it with
+ * e_cal_cache_offline_change_free() when no longer needed.
+ * If the @change is %NULL, then returns %NULL as well.
+ *
+ * Since: 3.26
+ **/
+ECalCacheOfflineChange *
+e_cal_cache_offline_change_copy (const ECalCacheOfflineChange *change)
+{
+ if (!change)
+ return NULL;
+
+ return e_cal_cache_offline_change_new (change->uid, change->rid, change->revision, change->object,
change->state);
+}
+
+/**
+ * e_cal_cache_offline_change_free:
+ * @change: (nullable): an #ECalCacheOfflineChange
+ *
+ * Frees the @change structure, previously allocated with e_cal_cache_offline_change_new()
+ * or e_cal_cache_offline_change_copy().
+ *
+ * Since: 3.26
+ **/
+void
+e_cal_cache_offline_change_free (gpointer change)
+{
+ ECalCacheOfflineChange *chng = change;
+
+ if (chng) {
+ g_free (chng->uid);
+ g_free (chng->rid);
+ g_free (chng->revision);
+ g_free (chng->object);
+ g_free (chng);
+ }
+}
+
+/**
* e_cal_cache_search_data_new:
* @uid: a component UID; cannot be %NULL
* @rid: (nullable): a component Recurrence-ID; can be %NULL
@@ -2672,6 +2752,57 @@ e_cal_cache_search_with_callback (ECalCache *cal_cache,
}
/**
+ * e_cal_cache_get_offline_changes:
+ * @cal_cache: an #ECalCache
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * The same as e_cache_get_offline_changes(), only splits the saved UID
+ * into UID and RID and saved the data into #ECalCacheOfflineChange structure.
+ *
+ * Returns: (transfer full) (element-type ECalCacheOfflineChange): A newly allocated list of all
+ * offline changes. Free it with g_slist_free_full (slist, e_cal_cache_offline_change_free);
+ * when no longer needed.
+ *
+ * Since: 3.26
+ **/
+GSList *
+e_cal_cache_get_offline_changes (ECalCache *cal_cache,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GSList *changes, *link;
+
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), NULL);
+
+ changes = e_cache_get_offline_changes (E_CACHE (cal_cache), cancellable, error);
+
+ for (link = changes; link; link = g_slist_next (link)) {
+ ECacheOfflineChange *cache_change = link->data;
+ ECalCacheOfflineChange *cal_change;
+ gchar *uid = NULL, *rid = NULL;
+
+ if (!cache_change || !ecc_decode_id_sql (cache_change->uid, &uid, &rid)) {
+ g_warn_if_reached ();
+
+ e_cache_offline_change_free (cache_change);
+ link->data = NULL;
+
+ continue;
+ }
+
+ cal_change = e_cal_cache_offline_change_new (uid, rid, cache_change->revision,
cache_change->object, cache_change->state);
+ link->data = cal_change;
+
+ e_cache_offline_change_free (cache_change);
+ g_free (uid);
+ g_free (rid);
+ }
+
+ return changes;
+}
+
+/**
* e_cal_cache_put_timezone:
* @cal_cache: an #ECalCache
* @zone: an icaltimezone to put
diff --git a/src/calendar/libedata-cal/e-cal-cache.h b/src/calendar/libedata-cal/e-cal-cache.h
index 4f80a79..9dd1476 100644
--- a/src/calendar/libedata-cal/e-cal-cache.h
+++ b/src/calendar/libedata-cal/e-cal-cache.h
@@ -51,6 +51,40 @@ typedef struct _ECalCacheClass ECalCacheClass;
typedef struct _ECalCachePrivate ECalCachePrivate;
/**
+ * ECalCacheOfflineChange:
+ * @uid: UID of the component
+ * @rid: Recurrence-ID of the component
+ * @revision: stored revision of the component
+ * @object: the component itself, as iCalalendar string
+ * @state: an #EOfflineState of the component
+ *
+ * Holds the information about offline change for one component.
+ *
+ * Since: 3.26
+ **/
+typedef struct {
+ gchar *uid;
+ gchar *rid;
+ gchar *revision;
+ gchar *object;
+ EOfflineState state;
+} ECalCacheOfflineChange;
+
+#define E_TYPE_CAL_CACHE_OFFLINE_CHANGE (e_cal_cache_offline_change_get_type ())
+
+GType e_cal_cache_offline_change_get_type
+ (void) G_GNUC_CONST;
+ECalCacheOfflineChange *
+ e_cal_cache_offline_change_new (const gchar *uid,
+ const gchar *rid,
+ const gchar *revision,
+ const gchar *object,
+ EOfflineState state);
+ECalCacheOfflineChange *
+ e_cal_cache_offline_change_copy (const ECalCacheOfflineChange *change);
+void e_cal_cache_offline_change_free (/* ECalCacheOfflineChange */ gpointer change);
+
+/**
* ECalCacheSearchData:
* @uid: the UID of this component
* @rid: (nullable): the Recurrence-ID of this component
@@ -258,6 +292,9 @@ gboolean e_cal_cache_search_with_callback
gpointer user_data,
GCancellable *cancellable,
GError **error);
+GSList * e_cal_cache_get_offline_changes (ECalCache *cal_cache,
+ GCancellable *cancellable,
+ GError **error);
gboolean e_cal_cache_put_timezone (ECalCache *cal_cache,
const icaltimezone *zone,
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index 3f46426..067628e 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -43,6 +43,9 @@
#include "e-cal-backend-util.h"
#include "e-cal-meta-backend.h"
+#define ECMB_KEY_SYNC_TAG "sync-tag"
+#define ECMB_KEY_DID_CONNECT "did-connect"
+
#define LOCAL_PREFIX "file://"
struct _ECalMetaBackendPrivate {
@@ -71,6 +74,21 @@ G_DEFINE_BOXED_TYPE (ECalMetaBackendInfo, e_cal_meta_backend_info, e_cal_meta_ba
static void ecmb_schedule_refresh (ECalMetaBackend *meta_backend);
static void ecmb_schedule_source_changed (ECalMetaBackend *meta_backend);
static void ecmb_schedule_go_offline (ECalMetaBackend *meta_backend);
+static gboolean ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
+ ECalCache *cal_cache,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
+ ECalCache *cal_cache,
+ gboolean overwrite_existing,
+ EConflictResolution conflict_resolution,
+ const GSList *in_instances,
+ const gchar *extra,
+ const gchar *orig_uid,
+ gboolean *requires_put,
+ GCancellable *cancellable,
+ GError **error);
/**
* e_cal_cache_search_data_new:
@@ -350,6 +368,83 @@ ecmb_start_view_thread_func (ECalBackend *cal_backend,
}
}
+static gboolean
+ecmb_upload_local_changes_sync (ECalMetaBackend *meta_backend,
+ ECalCache *cal_cache,
+ EConflictResolution conflict_resolution,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GSList *offline_changes, *link;
+ GHashTable *covered_uids;
+ ECache *cache;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), FALSE);
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
+
+ cache = E_CACHE (cal_cache);
+ covered_uids = g_hash_table_new (g_str_hash, g_str_equal);
+
+ offline_changes = e_cal_cache_get_offline_changes (cal_cache, cancellable, error);
+ for (link = offline_changes; link && success; link = g_slist_next (link)) {
+ ECalCacheOfflineChange *change = link->data;
+ gchar *extra = NULL;
+
+ success = !g_cancellable_set_error_if_cancelled (cancellable, error);
+ if (!success)
+ break;
+
+ if (!change || g_hash_table_contains (covered_uids, change->uid))
+ continue;
+
+ g_hash_table_insert (covered_uids, change->uid, NULL);
+
+ if (!e_cal_cache_get_component_extra (cal_cache, change->uid, NULL, &extra, cancellable,
NULL))
+ extra = NULL;
+
+ if (change->state == E_OFFLINE_STATE_LOCALLY_CREATED ||
+ change->state == E_OFFLINE_STATE_LOCALLY_MODIFIED) {
+ GSList *instances = NULL;
+
+ success = e_cal_cache_get_components_by_uid (cal_cache, change->uid, &instances,
cancellable, error);
+ if (success) {
+ success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache,
+ change->state == E_OFFLINE_STATE_LOCALLY_MODIFIED,
+ conflict_resolution, instances, extra, change->uid, NULL,
cancellable, error);
+ }
+
+ g_slist_free_full (instances, g_object_unref);
+ } else if (change->state == E_OFFLINE_STATE_LOCALLY_DELETED) {
+ GError *local_error = NULL;
+
+ success = e_cal_meta_backend_remove_component_sync (meta_backend, conflict_resolution,
+ change->uid, extra, cancellable, &local_error);
+
+ if (!success) {
+ if (g_error_matches (local_error, E_DATA_CAL_ERROR, ObjectNotFound)) {
+ g_clear_error (&local_error);
+ success = TRUE;
+ } else if (local_error) {
+ g_propagate_error (error, local_error);
+ }
+ }
+ } else {
+ g_warn_if_reached ();
+ }
+
+ g_free (extra);
+ }
+
+ g_slist_free_full (offline_changes, e_cal_cache_offline_change_free);
+ g_hash_table_destroy (covered_uids);
+
+ if (success)
+ success = e_cache_clear_offline_changes (cache, cancellable, error);
+
+ return success;
+}
+
static void
ecmb_refresh_thread_func (ECalBackend *cal_backend,
gpointer user_data,
@@ -357,6 +452,9 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
GError **error)
{
ECalMetaBackend *meta_backend;
+ ECalCache *cal_cache;
+ ECacheOfflineFlag offline_flag = E_CACHE_IS_ONLINE;
+ gboolean success, repeat = TRUE;
g_return_if_fail (E_IS_CAL_META_BACKEND (cal_backend));
@@ -365,8 +463,104 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
meta_backend = E_CAL_META_BACKEND (cal_backend);
- if (!e_backend_get_online (E_BACKEND (meta_backend)))
+ if (!e_backend_get_online (E_BACKEND (meta_backend)) ||
+ !e_cal_meta_backend_connect_sync (meta_backend, cancellable, NULL)) {
+ /* Ignore connection errors here */
return;
+ }
+
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+ if (!cal_cache) {
+ return;
+ }
+
+ success = ecmb_upload_local_changes_sync (meta_backend, cal_cache, E_CONFLICT_RESOLUTION_KEEP_LOCAL,
cancellable, error);
+
+ while (repeat && success &&
+ !g_cancellable_set_error_if_cancelled (cancellable, error)) {
+ GSList *created_objects = NULL, *modified_objects = NULL, *removed_objects = NULL, *link;
+ gchar *last_sync_tag, *new_sync_tag = NULL;
+
+ repeat = FALSE;
+
+ last_sync_tag = e_cache_dup_key (E_CACHE (cal_cache), ECMB_KEY_SYNC_TAG, NULL);
+ if (last_sync_tag && !*last_sync_tag) {
+ g_free (last_sync_tag);
+ last_sync_tag = NULL;
+ }
+
+ success = e_cal_meta_backend_get_changes_sync (meta_backend, last_sync_tag, &new_sync_tag,
&repeat,
+ &created_objects, &modified_objects, &removed_objects, cancellable, error);
+
+ if (success) {
+ GHashTable *covered_uids;
+
+ covered_uids = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* Removed objects first */
+ for (link = removed_objects; link && success; link = g_slist_next (link)) {
+ ECalMetaBackendInfo *nfo = link->data;
+ ECalComponentId id;
+
+ if (!nfo) {
+ g_warn_if_reached ();
+ continue;
+ }
+
+ id.uid = nfo->uid;
+ id.rid = nfo->rid;
+
+ success = e_cal_cache_remove_component (cal_cache, id.uid, id.rid,
offline_flag, cancellable, error);
+ if (success)
+ e_cal_backend_notify_component_removed (cal_backend, &id, NULL, NULL);
+ }
+
+ /* Then modified objects */
+ for (link = modified_objects; link && success; link = g_slist_next (link)) {
+ ECalMetaBackendInfo *nfo = link->data;
+
+ if (!nfo || !nfo->uid) {
+ g_warn_if_reached ();
+ continue;
+ }
+
+ if (g_hash_table_contains (covered_uids, nfo->uid))
+ continue;
+
+ g_hash_table_insert (covered_uids, nfo->uid, NULL);
+
+ success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache,
nfo->uid, cancellable, error);
+ }
+
+ g_hash_table_remove_all (covered_uids);
+
+ /* Finally created objects */
+ for (link = created_objects; link && success; link = g_slist_next (link)) {
+ ECalMetaBackendInfo *nfo = link->data;
+
+ if (!nfo || !nfo->uid) {
+ g_warn_if_reached ();
+ continue;
+ }
+
+ success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache,
nfo->uid, cancellable, error);
+ }
+
+ g_hash_table_destroy (covered_uids);
+ }
+
+ if (success) {
+ e_cache_set_key (E_CACHE (cal_cache), ECMB_KEY_SYNC_TAG, new_sync_tag ? new_sync_tag
: "", NULL);
+ }
+
+ g_slist_free_full (created_objects, e_cal_meta_backend_info_free);
+ g_slist_free_full (modified_objects, e_cal_meta_backend_info_free);
+ g_slist_free_full (removed_objects, e_cal_meta_backend_info_free);
+ g_free (last_sync_tag);
+ g_free (new_sync_tag);
+ }
+
+ g_object_unref (cal_cache);
}
static void
@@ -713,15 +907,18 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
static gboolean
ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
+ ECalCache *cal_cache,
gboolean overwrite_existing,
EConflictResolution conflict_resolution,
const GSList *in_instances,
const gchar *extra,
- gchar **out_new_uid,
+ const gchar *orig_uid,
+ gboolean *out_requires_put,
GCancellable *cancellable,
GError **error)
{
GSList *link, *instances = NULL;
+ gchar *new_uid = NULL;
gboolean has_attachments = FALSE, success = TRUE;
for (link = (GSList *) in_instances; link && !has_attachments; link = g_slist_next (link)) {
@@ -748,7 +945,19 @@ ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
}
success = success && e_cal_meta_backend_save_component_sync (meta_backend, overwrite_existing,
conflict_resolution,
- instances ? instances : in_instances, extra, out_new_uid, cancellable, error);
+ instances ? instances : in_instances, extra, &new_uid, cancellable, error);
+
+ if (success && new_uid) {
+ success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, new_uid, cancellable,
error);
+
+ if (success && g_strcmp0 (new_uid, orig_uid) != 0)
+ success = ecmb_maybe_remove_from_cache (meta_backend, cal_cache, E_CACHE_IS_ONLINE,
orig_uid, cancellable, error);
+
+ g_free (new_uid);
+
+ if (out_requires_put)
+ *out_requires_put = FALSE;
+ }
g_slist_free_full (instances, g_object_unref);
@@ -1084,33 +1293,15 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
if (*offline_flag == E_CACHE_IS_ONLINE) {
GSList *instances;
- gchar *new_uid = NULL;
instances = g_slist_prepend (NULL, comp);
- if (!ecmb_save_component_wrapper_sync (meta_backend, FALSE, conflict_resolution, instances,
NULL, &new_uid, cancellable, error)) {
+ if (!ecmb_save_component_wrapper_sync (meta_backend, cal_cache, FALSE, conflict_resolution,
instances, NULL, uid, &requires_put, cancellable, error)) {
g_slist_free (instances);
return FALSE;
}
g_slist_free (instances);
-
- if (new_uid) {
- if (!ecmb_load_component_wrapper_sync (meta_backend, cal_cache, new_uid, cancellable,
error)) {
- g_free (new_uid);
- return FALSE;
- }
-
- if (g_strcmp0 (new_uid, uid) != 0 &&
- !ecmb_maybe_remove_from_cache (meta_backend, cal_cache, *offline_flag, uid,
cancellable, error)) {
- g_free (new_uid);
- return FALSE;
- }
-
- g_free (new_uid);
-
- requires_put = FALSE;
- }
}
if (requires_put) {
@@ -1384,21 +1575,8 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
}
if (success && *offline_flag == E_CACHE_IS_ONLINE) {
- gchar *new_uid = NULL;
-
- success = ecmb_save_component_wrapper_sync (meta_backend, TRUE, conflict_resolution,
- instances, extra, &new_uid, cancellable, error);
-
- if (success && new_uid) {
- success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, new_uid,
cancellable, error);
-
- if (success && g_strcmp0 (new_uid, id->uid) != 0)
- success = ecmb_maybe_remove_from_cache (meta_backend, cal_cache, *offline_flag,
id->uid, cancellable, error);
-
- g_free (new_uid);
-
- requires_put = FALSE;
- }
+ success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache, TRUE,
conflict_resolution,
+ instances, extra, id->uid, &requires_put, cancellable, error);
}
if (success && requires_put)
@@ -1688,26 +1866,12 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
}
if (*offline_flag == E_CACHE_IS_ONLINE) {
- gchar *new_uid = NULL;
-
- success = ecmb_save_component_wrapper_sync (meta_backend, TRUE,
conflict_resolution,
- instances, extra, &new_uid, cancellable, error);
-
- if (success && new_uid) {
- success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache,
new_uid, cancellable, error);
-
- if (success && g_strcmp0 (new_uid, uid) != 0)
- success = ecmb_maybe_remove_from_cache (meta_backend, cal_cache,
*offline_flag, uid, cancellable, error);
-
- g_free (new_uid);
-
- requires_put = FALSE;
- }
+ success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache, TRUE,
conflict_resolution,
+ instances, extra, uid, &requires_put, cancellable, error);
}
if (success && requires_put)
success = ecmb_put_instances (meta_backend, cal_cache, uid, *offline_flag,
instances, extra, cancellable, error);
-
}
g_free (extra);
diff --git a/src/libebackend/e-cache.h b/src/libebackend/e-cache.h
index 90ce333..ca10faf 100644
--- a/src/libebackend/e-cache.h
+++ b/src/libebackend/e-cache.h
@@ -123,6 +123,17 @@ guint e_cache_column_values_get_size (ECacheColumnValues *other_columns);
void e_cache_column_values_init_iter (ECacheColumnValues *other_columns,
GHashTableIter *iter);
+/**
+ * ECacheOfflineChange:
+ * @uid: UID of the object
+ * @revision: stored revision of the object
+ * @object: the object itself
+ * @state: an #EOfflineState of the object
+ *
+ * Holds the information about offline change for one object.
+ *
+ * Since: 3.26
+ **/
typedef struct {
gchar *uid;
gchar *revision;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]