[gnome-todo/wip/gbsneto/plugins] eds: stub out eds plugins



commit f12442296c7b53dc247633a06e4984f07e75ae1b
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Thu Dec 24 00:20:20 2015 -0200

    eds: stub out eds plugins
    
    WARNING: this is non-compilable code

 Makefile.am                      |    6 +-
 common.am                        |    8 +
 configure.ac                     |    9 +
 plugins/Makefile.am              |    3 +
 plugins/eds/Makefile.am          |   46 ++
 plugins/eds/edit-pane.ui         |  274 +++++++++++
 plugins/eds/eds.gresource.xml    |    6 +
 plugins/eds/eds.plugin.in        |   13 +
 plugins/eds/gtd-plugin-eds.c     |  343 ++++++++++++++
 plugins/eds/gtd-plugin-eds.h     |   35 ++
 plugins/eds/gtd-provider-eds.c   |  958 ++++++++++++++++++++++++++++++++++++++
 plugins/eds/gtd-provider-eds.h   |   67 +++
 plugins/eds/gtd-provider-local.c |  239 ++++++++++
 plugins/eds/gtd-provider-local.h |   40 ++
 plugins/eds/gtd-task-list-eds.c  |  139 ++++++
 plugins/eds/gtd-task-list-eds.h  |   42 ++
 src/gtd-manager.c                |  151 ++-----
 17 files changed, 2254 insertions(+), 125 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 6bd056d..9851c4b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,9 @@
+include $(top_srcdir)/common.am
+
 ACLOCAL_AMFLAGS = --install -I m4 ${ACLOCAL_FLAGS}
 
-SUBDIRS = src data po
-DIST_SUBDIRS = src data po
+SUBDIRS = src data plugins po
+DIST_SUBDIRS = src data plugins po
 
 INTLTOOL_FILES = \
        intltool-extract.in \
diff --git a/common.am b/common.am
new file mode 100644
index 0000000..ce557d1
--- /dev/null
+++ b/common.am
@@ -0,0 +1,8 @@
+plugindir = @GNOME_TODO_PLUGIN_DIR@
+
+GNOME_TODO_PLUGIN_CFLAGS = \
+       $(GNOME_TODO_CFLAGS)
+
+
+GNOME_TODO_PLUGIN_WARN_CFLAGS = \
+       $(GNOME_TODO_WARN_CFLAGS)
diff --git a/configure.ac b/configure.ac
index ce570cb..c9a5e7f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,12 +74,21 @@ PKG_CHECK_MODULES(GNOME_TODO,
 
 APPSTREAM_XML
 
+
+dnl ================================================================
+dnl Plugins
+dnl ================================================================
+GNOME_TODO_ADD_PLUGIN([eds], [Evolution-Data-Server], [yes])
+
+AC_SUBST([GNOME_TODO_PLUGIN_DIR], [${libdir}])
+
 AC_CONFIG_FILES([
       Makefile
       src/Makefile
       data/Makefile
       data/icons/Makefile
       data/org.gnome.Todo.desktop.in
+      plugins/Makefile
       po/Makefile.in
 ])
 
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
new file mode 100644
index 0000000..da699ff
--- /dev/null
+++ b/plugins/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = eds
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/plugins/eds/Makefile.am b/plugins/eds/Makefile.am
new file mode 100644
index 0000000..5310323
--- /dev/null
+++ b/plugins/eds/Makefile.am
@@ -0,0 +1,46 @@
+include $(top_srcdir)/common.am
+
+plugindir = $(datadir)/gnome-todo/plugins
+
+plugin_LTLIBRARIES = libeds.la
+plugin_DATA = eds.plugin
+
+BUILT_SOURCES = \
+       gtd-plugin-eds-resources.c
+
+libeds_la_SOURCES = \
+       gtd-plugin-eds.c \
+       gtd-plugin-eds.h \
+       gtd-provider-eds.c \
+       gtd-provider-eds.h \
+       gtd-provider-goa.c \
+       gtd-provider-goa.h \
+       gtd-provider-local.c \
+       gtd-provider-local.h \
+       gtd-task-list-eds.c \
+       gtd-task-list-eds.h \
+       $(BUILT_SOURCES)
+
+libeds_la_CFLAGS = \
+       $(GNOME_TODO_PLUGIN_CFLAGS) \
+       $(GNOME_TODO_PLUGIN_WARN_CFLAGS) \
+       -DGOA_API_IS_SUBJECT_TO_CHANGE \
+       -DG_LOG_DOMAIN='"EDS"'
+
+libeds_la_LIBADD = \
+       $(GNOME_TODO_LIBS)
+
+libeds_la_LDFLAGS = \
+       -Wl -module \
+       $(GNOME_TODO_WARN_LDFLAGS)
+
+# generate binary-bundle resources
+resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies \
+                --sourcedir=$(top_srcdir)/plugins/eds $(top_srcdir)/plugins/eds/eds.gresource.xml)
+
+gtd-plugin-eds-resources.c: eds.gresource.xml $(resource_files)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(top_srcdir)/plugins/eds 
--generate-source $<
+
+
+
+EXTRA_DIST = eds.plugin.in
diff --git a/plugins/eds/edit-pane.ui b/plugins/eds/edit-pane.ui
new file mode 100644
index 0000000..50f039b
--- /dev/null
+++ b/plugins/eds/edit-pane.ui
@@ -0,0 +1,274 @@
+<interface>
+  <object class="GtkGrid" id="eds_edit_grid">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="margin_top">6</property>
+    <property name="margin_bottom">12</property>
+    <property name="vexpand">True</property>
+    <property name="row_spacing">6</property>
+    <property name="column_spacing">12</property>
+    <child>
+      <object class="GtkButton" id="close_button">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="receives_default">False</property>
+        <property name="margin_start">12</property>
+        <property name="margin_end">12</property>
+        <property name="relief">none</property>
+        <signal name="clicked" handler="gtd_edit_pane__close_button_clicked" object="GtdEditPane" 
swapped="no"/>
+        <child>
+          <object class="GtkImage" id="close_button_image">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="icon_name">window-close-symbolic</property>
+          </object>
+        </child>
+        <style>
+          <class name="image-button"/>
+          <class name="flat"/>
+        </style>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="top_attach">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="details_label">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="margin_start">12</property>
+        <property name="margin_end">12</property>
+        <property name="hexpand">True</property>
+        <property name="label" translatable="yes">Details</property>
+        <property name="xalign">0</property>
+        <attributes>
+          <attribute name="weight" value="bold"/>
+        </attributes>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkSeparator" id="separator">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">1</property>
+        <property name="width">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkGrid" id="grid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="vexpand">True</property>
+        <property name="border_width">12</property>
+        <property name="row_spacing">12</property>
+        <child>
+          <object class="GtkLabel" id="notes_dim_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Notes</property>
+            <property name="xalign">0</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="notes_scrolled_window">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="shadow_type">in</property>
+            <property name="min_content_width">325</property>
+            <property name="min_content_height">225</property>
+            <child>
+              <object class="GtkTextView" id="notes_textview">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="accepts_tab">False</property>
+                <property name="left-margin">6</property>
+                <property name="right-margin">6</property>
+                <property name="pixels-above-lines">6</property>
+                <property name="wrap-mode">word-char</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="due_date_dim_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Due Date</property>
+            <property name="xalign">0</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkMenuButton" id="date_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="popover">date_popover</property>
+            <child>
+              <object class="GtkBox" id="date_button_box">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="date_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkImage" id="date_button_image">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">pan-down-symbolic</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">3</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="priority_dim_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Priority</property>
+            <property name="xalign">0</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">4</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkComboBoxText" id="priority_combo">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="active">0</property>
+            <items>
+              <item translatable="yes" context="taskpriority">None</item>
+              <item translatable="yes">Low</item>
+              <item translatable="yes">Medium</item>
+              <item translatable="yes">High</item>
+            </items>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">5</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="remove_button">
+            <property name="label" translatable="yes">Delete</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="valign">end</property>
+            <property name="vexpand">True</property>
+            <signal name="clicked" handler="gtd_edit_pane__delete_button_clicked" object="GtdEditPane" 
swapped="no" />
+            <style>
+              <class name="destructive-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">6</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">2</property>
+        <property name="width">2</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkPopover" id="date_popover">
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <property name="position">bottom</property>
+    <child>
+      <object class="GtkBox" id="date_popover_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkCalendar" id="calendar">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="show_week_numbers">True</property>
+            <signal name="day-selected" handler="gtd_edit_pane__date_selected" object="GtdEditPane" 
swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="no_date_button">
+            <property name="label" translatable="yes" context="taskdate">None</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">False</property>
+            <signal name="clicked" handler="gtd_edit_pane__no_date_button_clicked" object="GtdEditPane" 
swapped="no" />
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/eds/eds.gresource.xml b/plugins/eds/eds.gresource.xml
new file mode 100644
index 0000000..5cc98dd
--- /dev/null
+++ b/plugins/eds/eds.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/todo">
+    <file compressed="true"  alias="ui/eds/edit-pane.ui">edit-pane.ui</file>
+  </gresource>
+</gresources>
diff --git a/plugins/eds/eds.plugin.in b/plugins/eds/eds.plugin.in
new file mode 100644
index 0000000..3861a59
--- /dev/null
+++ b/plugins/eds/eds.plugin.in
@@ -0,0 +1,13 @@
+[Plugin]
+Name = Core
+Module = eds
+Description = Evolution-data-server plugin for GNOME To Do
+Version = @VERSION@
+Authors = Georges Basile Stavracas Neto <gbsneto gnome org>
+Copyright = Copyleft © The To Do maintainers
+Website = https://wiki.gnome.org/Apps/Todo
+Builin = true
+Hidden = true
+License = GPL
+Loader = C
+Depends =
diff --git a/plugins/eds/gtd-plugin-eds.c b/plugins/eds/gtd-plugin-eds.c
new file mode 100644
index 0000000..fd92484
--- /dev/null
+++ b/plugins/eds/gtd-plugin-eds.c
@@ -0,0 +1,343 @@
+/* gtd-plugin-eds.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtd-plugin-eds.h"
+#include "gtd-provider-local.h"
+
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gnome-todo/gnome-todo.h>
+
+/**
+ * The #GtdPluginEds is a class that loads all the
+ * essential providers of GNOME To Do.
+ *
+ * It basically loads #ESourceRegistry which provides
+ * #GtdProviderLocal. Immediately after that, it loads
+ * #GoaClient which provides one #GtdProviderGoa per
+ * supported account.
+ *
+ * The currently supported Online Accounts are Google,
+ * ownCloud and Microsoft Exchange ones.
+ */
+
+struct _GtdPluginEds
+{
+  PeasExtensionBaseClass  parent;
+
+  /* Providers */
+  GList                  *providers;
+};
+
+enum
+{
+  PROP_0,
+  PROP_OBJECT,
+  PROP_PROVIDERS,
+  LAST_PROP
+};
+
+const gchar *supported_accounts[] = {
+  "exchange",
+  "google",
+  "owncloud",
+  NULL
+};
+
+static void          gtd_activatable_iface_init                  (GtdActivatableInterface  *iface);
+
+static void          peas_activatable_iface_init                 (PeasActivatableInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GtdPluginEds, gtd_plugin_eds, PEAS_TYPE_EXTENSION_BASE,
+                                0,
+                                G_IMPLEMENT_INTERFACE_DYNAMIC (PEAS_TYPE_ACTIVATABLE,
+                                                               peas_activatable_iface_init)
+                                G_IMPLEMENT_INTERFACE_DYNAMIC (GTD_TYPE_ACTIVATABLE,
+                                                               gtd_activatable_iface_init))
+
+/*
+ * PeasActivatable interface implementation
+ */
+static void
+gtd_plugin_eds_activate (PeasActivatable *activatable)
+{
+  g_message ("activate");
+}
+
+static void
+gtd_plugin_eds_deactivate (PeasActivatable *activatable)
+{
+  g_message ("deactivate");
+}
+
+static void
+peas_activatable_iface_init (PeasActivatableInterface *iface)
+{
+  iface->activate = gtd_plugin_eds_activate;
+  iface->deactivate = gtd_plugin_eds_deactivate;
+}
+
+/*
+ * GtdActivatable interface implementation
+ */
+static GList*
+gtd_plugin_eds_get_providers (GtdActivatable *activatable)
+{
+  GtdPluginEds *plugin = GTD_PLUGIN_EDS (activatable);
+
+  return plugin->providers;
+}
+
+static void
+gtd_activatable_iface_init (GtdActivatableInterface *iface)
+{
+  iface->get_providers = gtd_plugin_eds_get_providers;
+}
+
+
+/*
+ * Init
+ */
+
+static void
+gtd_plugin_eds_goa_client_finish_cb (GObject      *client,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+  GtdPluginEds *self;
+  GoaClient *goa_client;
+  GError *error;
+
+  self = GTD_PLUGIN_EDS (user_data);
+  error = NULL;
+
+  goa_client = goa_client_new_finish (result, &error);
+
+  if (!error)
+    {
+      GList *accounts;
+      GList *l;
+      gchar *default_location;
+
+      /* Load each supported GoaAccount into a GtdStorage */
+      accounts = goa_client_get_accounts (goa_client);
+      default_location = g_settings_get_string (priv->settings, "storage-location");
+
+      for (l = accounts; l != NULL; l = l->next)
+        {
+          GoaObject *object;
+          GoaAccount *account;
+
+          object = l->data;
+          account = goa_object_peek_account (object);
+
+          if (g_strv_contains (supported_accounts, goa_account_get_provider_type (account)))
+            {
+              GtdStorage *storage;
+
+              /* Create the new GOA storage */
+              storage = gtd_storage_goa_new (object);
+              gtd_storage_set_enabled (storage, !goa_account_get_calendar_disabled (account));
+              gtd_storage_set_is_default (storage, g_strcmp0 (gtd_storage_get_id (storage), 
default_location) == 0);
+
+              g_assert (gtd_storage_goa_get_account (GTD_STORAGE_GOA (storage)) == account);
+
+              gtd_manager__setup_url (GTD_MANAGER (user_data), GTD_STORAGE_GOA (storage));
+
+              priv->storage_locations = g_list_insert_sorted (priv->storage_locations,
+                                                              storage,
+                                                              (GCompareFunc) gtd_storage_compare);
+
+              g_signal_emit (user_data, signals[STORAGE_ADDED], 0, storage);
+            }
+        }
+
+      /* Connect GoaClient signals */
+      g_signal_connect (priv->goa_client,
+                        "account-added",
+                        G_CALLBACK (gtd_manager__goa_account_added_cb),
+                        user_data);
+
+      g_signal_connect (priv->goa_client,
+                        "account-changed",
+                        G_CALLBACK (gtd_manager__goa_account_changed_cb),
+                        user_data);
+
+      g_signal_connect (priv->goa_client,
+                        "account-removed",
+                        G_CALLBACK (gtd_manager__goa_account_removed_cb),
+                        user_data);
+
+      g_list_free_full (accounts, g_object_unref);
+      g_free (default_location);
+    }
+  else
+    {
+      g_warning ("%s: %s: %s",
+                 G_STRFUNC,
+                 _("Error loading GNOME Online Accounts"),
+                 error->message);
+
+      emit_show_error_message (GTD_MANAGER (user_data),
+                               _("Error loading GNOME Online Accounts"),
+                               error->message);
+
+      g_clear_error (&error);
+    }
+
+  g_object_notify (user_data, "goa-client-ready");
+  g_object_notify (user_data, "goa-client");
+}
+
+
+
+static void
+gtd_plugin_eds_source_registry_finish_cb (GObject      *source_object,
+                                          GAsyncResult *result,
+                                          gpointer      user_data)
+{
+  GtdPluginEds *self = GTD_PLUGIN_EDS (user_data);
+  GtdProviderLocal *provider;
+  ESourceRegistry *registry;
+  GError *error = NULL;
+
+  registry = e_source_registry_new_finish (result, &error);
+
+  /* Abort on error */
+  if (error)
+    {
+      g_warning ("%s: %s",
+                 _("Error loading Evolution-Data-Server backend"),
+                 error->message);
+
+      g_clear_error (&error);
+      return;
+    }
+
+  /* Load the local provider */
+  provider = gtd_provider_local_new (registry);
+  g_signal_emit_by_name (self, "provider-added", provider);
+
+  self->providers = g_list_append (self->providers, provider);
+
+  g_object_notify (G_OBJECT (self), "providers");
+
+  /* We only start loading Goa accounts after
+   * ESourceRegistry is get, since it'd be way
+   * too hard to synchronize these two asynchronous
+   * calls.
+   */
+  /* online accounts */
+  goa_client_new (NULL,
+                  (GAsyncReadyCallback) gtd_plugin_eds_goa_client_finish_cb,
+                  self);
+}
+
+static void
+gtd_plugin_eds_finalize (GObject *object)
+{
+  GtdPluginEds *self = (GtdPluginEds *)object;
+
+  g_list_free_full (self->providers, g_object_unref);
+
+  G_OBJECT_CLASS (gtd_plugin_eds_parent_class)->finalize (object);
+}
+
+static void
+gtd_plugin_eds_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GtdPluginEds *self = GTD_PLUGIN_EDS (object);
+
+  switch (prop_id)
+    {
+    case PROP_OBJECT:
+      g_value_set_object (value, NULL);
+      break;
+
+    case PROP_PROVIDERS:
+      g_value_set_pointer (value, self->providers);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_plugin_eds_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_OBJECT:
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_plugin_eds_class_init (GtdPluginEdsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtd_plugin_eds_finalize;
+  object_class->get_property = gtd_plugin_eds_get_property;
+  object_class->set_property = gtd_plugin_eds_set_property;
+
+  g_object_class_override_property (object_class,
+                                    PROP_OBJECT,
+                                    "object");
+
+  g_object_class_override_property (object_class,
+                                    PROP_PROVIDERS,
+                                    "providers");
+}
+
+static void
+gtd_plugin_eds_init (GtdPluginEds *self)
+{
+  g_message ("%s", G_STRFUNC);
+
+  /* load the source registry */
+  e_source_registry_new (NULL,
+                         (GAsyncReadyCallback) gtd_plugin_eds_source_registry_finish_cb,
+                         self);
+}
+
+/* Empty class_finalize method */
+static void
+gtd_plugin_eds_class_finalize (GtdPluginEdsClass *klass)
+{
+}
+
+G_MODULE_EXPORT void
+peas_register_types (PeasObjectModule *module)
+{
+  gtd_plugin_eds_register_type (G_TYPE_MODULE (module));
+
+  peas_object_module_register_extension_type (module,
+                                              GTD_TYPE_ACTIVATABLE,
+                                              GTD_TYPE_PLUGIN_EDS);
+}
diff --git a/plugins/eds/gtd-plugin-eds.h b/plugins/eds/gtd-plugin-eds.h
new file mode 100644
index 0000000..24ef1d0
--- /dev/null
+++ b/plugins/eds/gtd-plugin-eds.h
@@ -0,0 +1,35 @@
+/* gtd-eds-plugin.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTD_EDS_PLUGIN_H
+#define GTD_EDS_PLUGIN_H
+
+#include <glib.h>
+#include <gnome-todo/gtd-activatable.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_PLUGIN_EDS (gtd_plugin_eds_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdPluginEds, gtd_plugin_eds, GTD, PLUGIN_EDS, PeasExtensionBase)
+
+G_MODULE_EXPORT void  peas_register_types                        (PeasObjectModule   *module);
+
+G_END_DECLS
+
+#endif /* GTD_EDS_PLUGIN_H */
diff --git a/plugins/eds/gtd-provider-eds.c b/plugins/eds/gtd-provider-eds.c
new file mode 100644
index 0000000..5331b80
--- /dev/null
+++ b/plugins/eds/gtd-provider-eds.c
@@ -0,0 +1,958 @@
+/* gtd-provider-eds.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtd-provider-eds.h"
+
+#include <glib/gi18n.h>
+
+/**
+ * #GtdProviderEds is the base class of #GtdProviderLocal
+ * and #GtdProviderGoa. It provides the common functionality
+ * shared between these two providers.
+ *
+ * The subclasses basically have to implement GtdProviderEds->should_load_source
+ * which decides whether a given #ESource should be loaded (and added to the
+ * sources list) or not. #GtdProviderLocal for example would filter out
+ * sources whose backend is not "local".
+ */
+
+typedef struct
+{
+  GList                *task_lists;
+
+  ESourceRegistry      *source_registry;
+  ECredentialsPrompter *credentials_prompter;
+
+  GHashTable           *clients;
+  gint                  loaded_sources;
+} GtdProviderEdsPrivate;
+
+/* Auxiliary struct for asyncronous task operations */
+typedef struct _TaskData
+{
+  GtdProviderEds     *provider;
+  gpointer           *data;
+} TaskData;
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtdProviderEds, gtd_provider_eds, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_REGISTRY,
+  N_PROPS
+};
+
+static TaskData*
+task_data_new (GtdProviderEds *provider,
+               gpointer       *data)
+{
+  TaskData *tdata;
+
+  tdata = g_new0 (TaskData, 1);
+  tdata->provider = provider;
+  tdata->data = data;
+
+  return tdata;
+}
+
+
+static void
+gtd_provider_eds_fill_task_list (GObject      *client,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  GtdTaskList *list;
+  TaskData *data = user_data;
+  GSList *component_list;
+  GError *error = NULL;
+
+  g_return_if_fail (GTD_IS_PROVIDER_EDS (data->provider));
+
+  list = GTD_TASK_LIST (data->data);
+
+  e_cal_client_get_object_list_as_comps_finish (E_CAL_CLIENT (client),
+                                                result,
+                                                &component_list,
+                                                &error);
+
+  gtd_object_set_ready (GTD_OBJECT (data->data), TRUE);
+  g_free (data);
+
+  if (!error)
+    {
+      GSList *l;
+
+      for (l = component_list; l != NULL; l = l->next)
+        {
+          GtdTask *task;
+
+          task = gtd_task_new (l->data);
+          gtd_task_set_list (task, list);
+
+          gtd_task_list_save_task (list, task);
+        }
+
+      e_cal_client_free_ecalcomp_slist (component_list);
+    }
+  else
+    {
+      g_warning ("%s: %s: %s",
+                 G_STRFUNC,
+                 _("Error fetching tasks from list"),
+                 error->message);
+/*
+      emit_show_error_message (GTD_MANAGER (user_data),
+                               _("Error fetching tasks from list"),
+                               error->message);
+*/
+      g_error_free (error);
+      return;
+    }
+}
+
+static void
+gtd_provider_eds_on_client_connected (GObject      *source_object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+  GtdProviderEdsPrivate *priv;
+  GtdProviderEds *self;
+  ECalClient *client;
+  ESource *source;
+  GError *error = NULL;
+
+  self = GTD_PROVIDER_EDS (user_data);
+  priv = gtd_provider_eds_get_instance_private (self);
+  source = e_client_get_source (E_CLIENT (source_object));
+  client = E_CAL_CLIENT (e_cal_client_connect_finish (result, &error));
+
+  /* Update ready flag */
+  priv->loaded_sources--;
+  gtd_object_set_ready (GTD_OBJECT (user_data), priv->loaded_sources <= 0);
+
+  if (!error)
+    {
+      GtdTaskList *list;
+      TaskData *data;
+      ESource *parent;
+
+      /* parent source's display name is list's origin */
+      parent = e_source_registry_ref_source (priv->source_registry, e_source_get_parent (source));
+
+      /* creates a new task list */
+      list = gtd_task_list_new (source, e_source_get_display_name (parent));
+
+      /* it's not ready until we fetch the list of tasks from client */
+      gtd_object_set_ready (GTD_OBJECT (list), FALSE);
+
+      /* async data */
+      data = task_data_new (user_data, (gpointer) list);
+
+      /* asyncronously fetch the task list */
+      e_cal_client_get_object_list_as_comps (client,
+                                             "contains? \"any\" \"\"",
+                                             NULL,
+                                             gtd_provider_eds_fill_task_list,
+                                             data);
+
+      priv->task_lists = g_list_append (priv->task_lists, list);
+
+      g_object_set_data (G_OBJECT (source), "task-list", list);
+      g_hash_table_insert (priv->clients, source, client);
+
+      /* Emit LIST_ADDED signal */
+      g_signal_emit_by_name (self, "list-added", list);
+
+      g_object_unref (parent);
+
+      g_debug ("%s: %s (%s)",
+               G_STRFUNC,
+               _("Task list source successfully connected"),
+               e_source_get_display_name (source));
+    }
+  else
+    {
+      g_debug ("%s: %s (%s): %s",
+               G_STRFUNC,
+               _("Failed to connect to task list source"),
+               e_source_get_uid (source),
+               error->message);
+
+/*
+      emit_show_error_message (gtd_manager_get_default (),
+                               _("Failed to connect to task list source"),
+                               error->message);
+*/
+
+      g_error_free (error);
+      return;
+    }
+
+}
+
+static void
+gtd_provider_eds_load_source (GtdProviderEds *provider,
+                              ESource        *source)
+{
+  GtdProviderEdsPrivate *priv;
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+
+  if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST) &&
+      !g_hash_table_lookup (priv->clients, source) &&
+      GTD_PROVIDER_EDS_CLASS (G_OBJECT_GET_CLASS (provider))->should_load_source (provider, source))
+    {
+      g_message ("%s: loading local list '%s'",
+                 G_STRFUNC,
+                 e_source_get_display_name (source));
+
+      e_cal_client_connect (source,
+                            E_CAL_CLIENT_SOURCE_TYPE_TASKS,
+                            10, /* seconds to wait */
+                            NULL,
+                            gtd_provider_eds_on_client_connected,
+                            provider);
+    }
+  else
+    {
+      g_warning ("%s: %s %s (%s)",
+                 G_STRFUNC,
+                 _("Skipping already loaded task list "),
+                 e_source_get_display_name (source),
+                 e_source_get_uid (source));
+    }
+}
+
+static void
+gtd_provider_eds_remove_source (GtdProviderEds *provider,
+                                ESource        *source)
+{
+  GtdProviderEdsPrivate *priv;
+  GtdTaskList *list;
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+  list = g_object_get_data (G_OBJECT (source), "task-list");
+
+  g_hash_table_remove (priv->clients, source);
+
+  /* Since all subclasses will have this signal given that they
+   * are all GtdProvider implementations, it's not that bad
+   * to let it stay here.
+   */
+  g_signal_emit_by_name (provider, "list-removed", list);
+}
+
+
+
+
+/************************
+ * Credentials prompter *
+ ************************/
+
+static void
+gtd_manager__invoke_authentication (GObject      *source_object,
+                                    GAsyncResult *result,
+                                    gpointer      user_data)
+{
+  ESource *source = E_SOURCE (source_object);
+  GError *error = NULL;
+  gboolean canceled;
+
+  e_source_invoke_authenticate_finish (source,
+                                       result,
+                                       &error);
+
+  canceled = g_error_matches (error,
+                                   G_IO_ERROR,
+                                   G_IO_ERROR_CANCELLED);
+
+  if (!canceled)
+    {
+      g_warning ("%s: %s (%s): %s",
+                 G_STRFUNC,
+                 _("Failed to prompt for credentials"),
+                 e_source_get_uid (source),
+                 error->message);
+    }
+
+  g_clear_error (&error);
+}
+
+static void
+gtd_provider_local_credentials_prompt_done (GObject      *source_object,
+                                            GAsyncResult *result,
+                                            gpointer      user_data)
+{
+  ETrustPromptResponse response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+  ESource *source = E_SOURCE (source_object);
+  GError *error = NULL;
+
+  e_trust_prompt_run_for_source_finish (source, result, &response, &error);
+
+  if (error)
+    {
+      g_warning ("%s: %s '%s': %s",
+                 G_STRFUNC,
+                 _("Failed to prompt for credentials for"),
+                 e_source_get_display_name (source),
+                 error->message);
+
+    }
+  else if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT || response == 
E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY)
+    {
+      /* Use NULL credentials to reuse those from the last time. */
+      e_source_invoke_authenticate (source,
+                                    NULL,
+                                    NULL /* cancellable */,
+                                    gtd_manager__invoke_authentication,
+                                    NULL);
+    }
+
+  g_clear_error (&error);
+}
+
+static void
+gtd_provider_eds_credentials_required (ESourceRegistry          *registry,
+                                       ESource                  *source,
+                                       ESourceCredentialsReason  reason,
+                                       const gchar              *certificate_pem,
+                                       GTlsCertificateFlags      certificate_errors,
+                                       const GError             *error,
+                                       gpointer                  user_data)
+{
+  GtdProviderEdsPrivate *priv;
+  GtdProviderEds *self;
+
+  g_return_if_fail (GTD_IS_PROVIDER_EDS (user_data));
+
+  self = GTD_PROVIDER_EDS (user_data);
+  priv = gtd_provider_eds_get_instance_private (self);
+
+  if (e_credentials_prompter_get_auto_prompt_disabled_for (priv->credentials_prompter, source))
+    return;
+
+  if (reason == E_SOURCE_CREDENTIALS_REASON_SSL_FAILED)
+    {
+      e_trust_prompt_run_for_source (e_credentials_prompter_get_dialog_parent (priv->credentials_prompter),
+                                     source,
+                                     certificate_pem,
+                                     certificate_errors,
+                                     error ? error->message : NULL,
+                                     TRUE, // allow saving sources
+                                     NULL, // we won't cancel the operation
+                                     gtd_provider_local_credentials_prompt_done,
+                                     NULL);
+    }
+  else if (error && reason == E_SOURCE_CREDENTIALS_REASON_ERROR)
+    {
+      g_warning ("%s: %s '%s': %s",
+                 G_STRFUNC,
+                 _("Authentication failure"),
+                 e_source_get_display_name (source),
+                 error->message);
+    }
+}
+
+
+static void
+gtd_provider_eds_finalize (GObject *object)
+{
+  GtdProviderEds *self = (GtdProviderEds *)object;
+  GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (self);
+
+  g_clear_pointer (&priv->clients, g_hash_table_destroy);
+  g_clear_object (&priv->credentials_prompter);
+  g_clear_object (&priv->source_registry);
+
+  G_OBJECT_CLASS (gtd_provider_eds_parent_class)->finalize (object);
+}
+
+static void
+gtd_provider_eds_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  GtdProviderEds *self = GTD_PROVIDER_EDS (object);
+  GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_REGISTRY:
+      g_value_set_object (value, priv->source_registry);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_provider_eds_set_registry (GtdProviderEds  *provider,
+                               ESourceRegistry *registry)
+{
+  GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (provider);
+  GList *sources;
+  GList *l;
+  GError *error = NULL;
+
+  g_set_object (&priv->source_registry, registry);
+
+  priv->credentials_prompter = e_credentials_prompter_new (priv->source_registry);
+
+  if (error != NULL)
+    {
+      g_warning ("%s: %s", _("Error loading task manager"), error->message);
+      g_error_free (error);
+      return;
+    }
+
+  /* First of all, disable authentication dialog for non-tasklists sources */
+  sources = e_source_registry_list_sources (priv->source_registry, NULL);
+
+  for (l = sources; l != NULL; l = g_list_next (l))
+    {
+      ESource *source = E_SOURCE (l->data);
+
+      /* Mark for skip also currently disabled sources */
+      e_credentials_prompter_set_auto_prompt_disabled_for (priv->credentials_prompter,
+                                                           source,
+                                                           !e_source_has_extension (source, 
E_SOURCE_EXTENSION_TASK_LIST));
+    }
+
+  g_list_free_full (sources, g_object_unref);
+
+  /* Load task list sources */
+  sources = e_source_registry_list_sources (priv->source_registry, E_SOURCE_EXTENSION_TASK_LIST);
+
+  /* While load_sources > 0, Gtd::ready = FALSE */
+  priv->loaded_sources = g_list_length (sources);
+
+  gtd_object_set_ready (GTD_OBJECT (provider), priv->loaded_sources == 0);
+
+  g_debug ("%s: number of sources to load: %d",
+           G_STRFUNC,
+           priv->loaded_sources);
+
+  for (l = sources; l != NULL; l = l->next)
+    gtd_provider_eds_load_source (provider, l->data);
+
+  g_list_free_full (sources, g_object_unref);
+
+  /* listen to the signals, so new sources don't slip by */
+  g_signal_connect_swapped (priv->source_registry,
+                            "source-added",
+                            G_CALLBACK (gtd_provider_eds_load_source),
+                            provider);
+
+  g_signal_connect_swapped (priv->source_registry,
+                            "source-removed",
+                            G_CALLBACK (gtd_provider_eds_remove_source),
+                            provider);
+
+  g_signal_connect (priv->source_registry,
+                    "credentials-required",
+                    G_CALLBACK (gtd_provider_eds_credentials_required),
+                    provider);
+
+  e_credentials_prompter_process_awaiting_credentials (priv->credentials_prompter);
+}
+
+
+static void
+gtd_provider_eds_create_task_finished (GObject      *client,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+  TaskData *data = user_data;
+  gchar *new_uid = NULL;
+  GError *error = NULL;
+
+  e_cal_client_create_object_finish (E_CAL_CLIENT (client),
+                                     result,
+                                     &new_uid,
+                                     &error);
+
+  gtd_object_set_ready (GTD_OBJECT (data->data), TRUE);
+
+  if (error)
+    {
+      g_warning ("%s: %s: %s",
+                 G_STRFUNC,
+                 _("Error creating task"),
+                 error->message);
+
+/*
+      emit_show_error_message (GTD_MANAGER (user_data),
+                               _("Error creating task"),
+                               error->message);
+*/
+
+      g_error_free (error);
+      g_free (data);
+      return;
+    }
+  else
+    {
+      /*
+       * In the case the task UID changes because of creation proccess,
+       * reapply it to the task.
+       */
+      if (new_uid)
+        {
+          gtd_object_set_uid (GTD_OBJECT (data->data), new_uid);
+          g_free (new_uid);
+        }
+
+      g_free (data);
+    }
+}
+
+static void
+gtd_provider_eds_update_task_finished (GObject      *client,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+  TaskData *data = user_data;
+  GtdTask *task;
+  GError *error = NULL;
+
+  task = GTD_TASK (data->data);
+  e_cal_client_modify_object_finish (E_CAL_CLIENT (client),
+                                     result,
+                                     &error);
+
+  if (error)
+    {
+      g_warning ("%s: %s: %s",
+                 G_STRFUNC,
+                 _("Error updating task"),
+                 error->message);
+
+/*
+      emit_show_error_message (data->manager,
+                               _("Error updating task"),
+                               error->message);
+*/
+
+      g_error_free (error);
+    }
+
+  g_free (data);
+
+  gtd_object_set_ready (GTD_OBJECT (data->data), TRUE);
+}
+
+static void
+gtd_provider_eds_remove_task_finished (GObject      *client,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+  TaskData *data = user_data;
+  GError *error = NULL;
+
+  e_cal_client_remove_object_finish (E_CAL_CLIENT (client),
+                                     result,
+                                     &error);
+
+  gtd_object_set_ready (GTD_OBJECT (data->data), TRUE);
+
+  g_object_unref ((GtdTask*) data->data);
+  g_free (data);
+
+  if (error)
+    {
+      g_warning ("%s: %s: %s",
+                 G_STRFUNC,
+                 _("Error removing task"),
+                 error->message);
+/*
+      emit_show_error_message (GTD_MANAGER (user_data),
+                               _("Error removing task"),
+                               error->message);
+*/
+      g_error_free (error);
+      return;
+    }
+}
+
+static void
+gtd_provider_eds_remote_create_source_finished (GObject      *source,
+                                                GAsyncResult *result,
+                                                gpointer      user_data)
+{
+  GError *error;
+
+  error = NULL;
+
+  e_source_remote_create_finish (E_SOURCE (source),
+                                 result,
+                                 &error);
+
+  if (error)
+    {
+      g_warning ("%s: %s: %s",
+                 G_STRFUNC,
+                 _("Error creating task list"),
+                 error->message);
+/*
+      emit_show_error_message (GTD_MANAGER (user_data),
+                               _("Error creating task list"),
+                               error->message);
+*/
+      g_clear_error (&error);
+    }
+}
+
+static void
+task_list_removal_finished (GtdManager  *manager,
+                            ESource     *source,
+                            GError     **error)
+{
+  gtd_object_set_ready (GTD_OBJECT (manager), TRUE);
+
+  if (*error)
+    {
+      g_warning ("%s: %s: %s",
+                 G_STRFUNC,
+                 _("Error removing task list"),
+                 (*error)->message);
+/*
+      emit_show_error_message (manager,
+                               _("Error removing task list"),
+                               (*error)->message);
+*/
+      g_clear_error (error);
+    }
+}
+
+
+static void
+gtd_provider_eds_remote_delete_finished (GObject      *source,
+                                         GAsyncResult *result,
+                                         gpointer      user_data)
+{
+  GError *error = NULL;
+
+  e_source_remote_delete_finish (E_SOURCE (source),
+                                 result,
+                                 &error);
+
+  task_list_removal_finished (GTD_MANAGER (user_data),
+                              E_SOURCE (source),
+                              &error);
+}
+
+static void
+gtd_provider_eds_remove_source_finished (GObject      *source,
+                                         GAsyncResult *result,
+                                         gpointer      user_data)
+{
+  GError *error = NULL;
+
+  e_source_remove_finish (E_SOURCE (source),
+                          result,
+                          &error);
+
+  task_list_removal_finished (GTD_MANAGER (user_data),
+                              E_SOURCE (source),
+                              &error);
+}
+
+static void
+gtd_provider_eds_commit_source_finished (GObject      *registry,
+                                         GAsyncResult *result,
+                                         gpointer      user_data)
+{
+  GError *error = NULL;
+
+  g_return_if_fail (GTD_IS_MANAGER (user_data));
+
+  gtd_object_set_ready (GTD_OBJECT (user_data), TRUE);
+  e_source_registry_commit_source_finish (E_SOURCE_REGISTRY (registry),
+                                          result,
+                                          &error);
+
+  if (error)
+    {
+      g_warning ("%s: %s: %s",
+                 G_STRFUNC,
+                 _("Error saving task list"),
+                 error->message);
+/*
+      emit_show_error_message (GTD_MANAGER (user_data),
+                               _("Error saving task list"),
+                               error->message);
+*/
+      g_error_free (error);
+      return;
+    }
+}
+
+static void
+gtd_provider_eds_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  GtdProviderEds *self = GTD_PROVIDER_EDS (object);
+
+  switch (prop_id)
+    {
+    case PROP_REGISTRY:
+      gtd_provider_eds_set_registry (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_provider_eds_class_init (GtdProviderEdsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtd_provider_eds_finalize;
+  object_class->get_property = gtd_provider_eds_get_property;
+  object_class->set_property = gtd_provider_eds_set_property;
+
+  g_object_class_install_property (object_class,
+                                   PROP_REGISTRY,
+                                   g_param_spec_object ("registry",
+                                                        "Source registry",
+                                                        "The EDS source registry object",
+                                                        E_TYPE_SOURCE_REGISTRY,
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | 
G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gtd_provider_eds_init (GtdProviderEds *self)
+{
+  GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (self);
+
+  /* hash table */
+  priv->clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
+                                         (GEqualFunc) e_source_equal,
+                                         g_object_unref,
+                                         g_object_unref);
+}
+
+GtdProviderEds*
+gtd_provider_eds_new (ESourceRegistry *registry)
+{
+  return g_object_new (GTD_TYPE_PROVIDER_EDS,
+                       "registry", registry,
+                       NULL);
+}
+
+ESourceRegistry*
+gtd_provider_eds_get_registry (GtdProviderEds *provider)
+{
+  GtdProviderEdsPrivate *priv;
+
+  g_return_val_if_fail (GTD_IS_PROVIDER_EDS (provider), NULL);
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+
+  return priv->source_registry;
+}
+
+void
+gtd_provider_eds_create_task (GtdProviderEds *provider,
+                              GtdTask        *task)
+{
+  GtdProviderEdsPrivate *priv;
+  ECalComponent *component;
+  ECalClient *client;
+  TaskData *data;
+  ESource *source;
+
+  g_return_if_fail (GTD_IS_TASK (task));
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+  source = gtd_task_list_get_source (gtd_task_get_list (task));
+  client = g_hash_table_lookup (priv->clients, source);
+  component = gtd_task_get_component (task);
+
+  /* Temporary data for async operation */
+  data = task_data_new (provider, (gpointer) task);
+
+  /* The task is not ready until we finish the operation */
+  gtd_object_set_ready (GTD_OBJECT (task), FALSE);
+
+  e_cal_client_create_object (client,
+                              e_cal_component_get_icalcomponent (component),
+                              NULL, // We won't cancel the operation
+                              (GAsyncReadyCallback) gtd_provider_eds_create_task_finished,
+                              data);
+}
+
+void
+gtd_provider_eds_update_task (GtdProviderEds *provider,
+                              GtdTask        *task)
+{
+  GtdProviderEdsPrivate *priv;
+  ECalComponent *component;
+  ECalClient *client;
+  TaskData *data;
+  ESource *source;
+
+  g_return_if_fail (GTD_IS_TASK (task));
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+  source = gtd_task_list_get_source (gtd_task_get_list (task));
+  client = g_hash_table_lookup (priv->clients, source);
+  component = gtd_task_get_component (task);
+
+  /* Temporary data for async operation */
+  data = task_data_new (provider, (gpointer) task);
+
+  /* The task is not ready until we finish the operation */
+  gtd_object_set_ready (GTD_OBJECT (task), FALSE);
+
+  e_cal_client_modify_object (client,
+                              e_cal_component_get_icalcomponent (component),
+                              E_CAL_OBJ_MOD_THIS,
+                              NULL, // We won't cancel the operation
+                              (GAsyncReadyCallback) gtd_provider_eds_update_task_finished,
+                              data);
+}
+
+void
+gtd_provider_eds_remove_task (GtdProviderEds *provider,
+                              GtdTask        *task)
+{
+
+  GtdProviderEdsPrivate *priv;
+  ECalComponent *component;
+  ECalComponentId *id;
+  ECalClient *client;
+  TaskData *data;
+  ESource *source;
+
+  g_return_if_fail (GTD_IS_TASK (task));
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+  source = gtd_task_list_get_source (gtd_task_get_list (task));
+  client = g_hash_table_lookup (priv->clients, source);
+  component = gtd_task_get_component (task);
+  id = e_cal_component_get_id (component);
+
+  /* Temporary data for async operation */
+  data = task_data_new (provider, (gpointer) task);
+
+  /* The task is not ready until we finish the operation */
+  gtd_object_set_ready (GTD_OBJECT (task), FALSE);
+
+  e_cal_client_remove_object (client,
+                              id->uid,
+                              id->rid,
+                              E_CAL_OBJ_MOD_THIS,
+                              NULL, // We won't cancel the operation
+                              (GAsyncReadyCallback) gtd_provider_eds_remove_task_finished,
+                              data);
+
+  e_cal_component_free_id (id);
+}
+
+void
+gtd_provider_eds_create_task_list (GtdProviderEds *provider,
+                                   GtdTaskList    *list)
+{
+  GtdProviderEdsPrivate *priv;
+  ESource *source;
+  ESource *parent;
+
+  g_return_if_fail (GTD_IS_TASK_LIST (list));
+  g_return_if_fail (gtd_task_list_get_source (list));
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+  source = gtd_task_list_get_source (list);
+  parent = e_source_registry_ref_source (priv->source_registry, e_source_get_parent (source));
+
+  e_source_remote_create (parent,
+                          source,
+                          NULL,
+                          (GAsyncReadyCallback) gtd_provider_eds_remote_create_source_finished,
+                          provider);
+
+  g_object_unref (parent);
+}
+
+void
+gtd_provider_eds_update_task_list (GtdProviderEds *provider,
+                                   GtdTaskList    *list)
+{
+
+  GtdProviderEdsPrivate *priv;
+  ESource *source;
+
+  g_return_if_fail (GTD_IS_TASK_LIST (list));
+  g_return_if_fail (gtd_task_list_get_source (list));
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+  source = gtd_task_list_get_source (list);
+
+  gtd_object_set_ready (GTD_OBJECT (provider), FALSE);
+  e_source_registry_commit_source (priv->source_registry,
+                                   source,
+                                   NULL,
+                                   (GAsyncReadyCallback) gtd_provider_eds_commit_source_finished,
+                                   provider);
+}
+
+void
+gtd_provider_eds_remove_task_list (GtdProviderEds *provider,
+                                   GtdTaskList    *list)
+{
+  GtdProviderEdsPrivate *priv;
+  ESource *source;
+
+  g_return_if_fail (GTD_IS_TASK_LIST (list));
+  g_return_if_fail (gtd_task_list_get_source (list));
+
+  priv = gtd_provider_eds_get_instance_private (provider);
+  source = gtd_task_list_get_source (list);
+
+  gtd_object_set_ready (GTD_OBJECT (provider), FALSE);
+
+  if (e_source_get_remote_deletable (source))
+    {
+      e_source_remote_delete (source,
+                              NULL,
+                              (GAsyncReadyCallback) gtd_provider_eds_remote_delete_finished,
+                              provider);
+    }
+  else
+    {
+      e_source_remove (source,
+                       NULL,
+                       (GAsyncReadyCallback) gtd_provider_eds_remove_source_finished,
+                       provider);
+    }
+}
+
+GList*
+gtd_provider_eds_get_task_lists (GtdProviderEds *provider)
+{
+  GtdProviderEdsPrivate *priv = gtd_provider_eds_get_instance_private (provider);
+
+  return priv->task_lists;
+}
diff --git a/plugins/eds/gtd-provider-eds.h b/plugins/eds/gtd-provider-eds.h
new file mode 100644
index 0000000..2b53751
--- /dev/null
+++ b/plugins/eds/gtd-provider-eds.h
@@ -0,0 +1,67 @@
+/* gtd-provider-eds.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTD_PROVIDER_EDS_H
+#define GTD_PROVIDER_EDS_H
+
+#include <glib.h>
+#include <gnome-todo/gnome-todo.h>
+#include <libecal/libecal.h>
+#include <libedataserverui/libedataserverui.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_PROVIDER_EDS (gtd_provider_eds_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (GtdProviderEds, gtd_provider_eds, GTD, PROVIDER_EDS, GtdObject)
+
+struct _GtdProviderEdsClass
+{
+  GtdObjectClass parent;
+
+  gboolean           (*should_load_source)                       (GtdProviderEds     *provider,
+                                                                  ESource            *source);
+};
+
+GtdProviderEds*      gtd_provider_eds_new                        (ESourceRegistry    *registry);
+
+ESourceRegistry*     gtd_provider_eds_get_registry               (GtdProviderEds     *local);
+
+void                 gtd_provider_eds_create_task                (GtdProviderEds     *provider,
+                                                                  GtdTask            *task);
+
+void                 gtd_provider_eds_update_task                (GtdProviderEds     *provider,
+                                                                  GtdTask            *task);
+
+void                 gtd_provider_eds_remove_task                (GtdProviderEds     *provider,
+                                                                  GtdTask            *task);
+
+void                 gtd_provider_eds_create_task_list           (GtdProviderEds     *provider,
+                                                                  GtdTaskList        *list);
+
+void                 gtd_provider_eds_update_task_list           (GtdProviderEds     *provider,
+                                                                  GtdTaskList        *list);
+
+void                 gtd_provider_eds_remove_task_list           (GtdProviderEds     *provider,
+                                                                  GtdTaskList        *list);
+
+GList*               gtd_provider_eds_get_task_lists             (GtdProviderEds     *provider);
+
+G_END_DECLS
+
+#endif /* GTD_PROVIDER_EDS_H */
diff --git a/plugins/eds/gtd-provider-local.c b/plugins/eds/gtd-provider-local.c
new file mode 100644
index 0000000..a751477
--- /dev/null
+++ b/plugins/eds/gtd-provider-local.c
@@ -0,0 +1,239 @@
+/* gtd-provider-local.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtd-provider-local.h"
+
+#include <glib/gi18n.h>
+
+struct _GtdProviderLocal
+{
+  GtdObject               parent;
+
+  GIcon                  *icon;
+  GList                  *tasklists;
+};
+
+static void          gtd_provider_iface_init                     (GtdProviderInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtdProviderLocal, gtd_provider_local, GTD_TYPE_PROVIDER_EDS,
+                         G_IMPLEMENT_INTERFACE (GTD_TYPE_PROVIDER,
+                                                gtd_provider_iface_init))
+
+enum {
+  PROP_0,
+  PROP_ENABLED,
+  PROP_ID,
+  PROP_NAME,
+  PROP_DESCRIPTION,
+  LAST_PROP
+};
+
+/*
+ * GtdProviderInterface implementation
+ */
+static const gchar*
+gtd_provider_local_get_id (GtdProvider *provider)
+{
+  return "local";
+}
+
+static const gchar*
+gtd_provider_local_get_name (GtdProvider *provider)
+{
+  return _("Local");
+}
+
+static const gchar*
+gtd_provider_local_get_description (GtdProvider *provider)
+{
+  return _("On This Computer");
+}
+
+
+static gboolean
+gtd_provider_local_get_enabled (GtdProvider *provider)
+{
+  return TRUE;
+}
+
+static const GIcon*
+gtd_provider_local_get_icon_for_list (GtdProvider *provider,
+                                      GtdTaskList *list)
+{
+  GtdProviderLocal *self;
+
+  self = GTD_PROVIDER_LOCAL (provider);
+
+  return self->icon;
+}
+
+static const GtkWidget*
+gtd_provider_local_get_edit_panel (GtdProvider *provider)
+{
+  return NULL;
+}
+
+static void
+gtd_provider_local_create_task (GtdProvider *provider,
+                                GtdTask     *task)
+{
+  gtd_provider_eds_create_task (GTD_PROVIDER_EDS (provider), task);
+}
+
+static void
+gtd_provider_local_update_task (GtdProvider *provider,
+                                GtdTask     *task)
+{
+  gtd_provider_eds_update_task (GTD_PROVIDER_EDS (provider), task);
+}
+
+static void
+gtd_provider_local_remove_task (GtdProvider *provider,
+                                GtdTask     *task)
+{
+  gtd_provider_eds_remove_task (GTD_PROVIDER_EDS (provider), task);
+}
+
+static void
+gtd_provider_local_create_task_list (GtdProvider *provider,
+                                     GtdTaskList *list)
+{
+  gtd_provider_eds_create_task_list (GTD_PROVIDER_EDS (provider), list);
+}
+
+static void
+gtd_provider_local_update_task_list (GtdProvider *provider,
+                                     GtdTaskList *list)
+{
+  gtd_provider_eds_update_task_list (GTD_PROVIDER_EDS (provider), list);
+}
+
+static void
+gtd_provider_local_remove_task_list (GtdProvider *provider,
+                                     GtdTaskList *list)
+{
+  gtd_provider_eds_remove_task_list (GTD_PROVIDER_EDS (provider), list);
+
+  g_signal_emit_by_name (provider, "list-removed", list);
+}
+
+static GList*
+gtd_provider_local_get_task_lists (GtdProvider *provider)
+{
+  return gtd_provider_eds_get_task_lists (GTD_PROVIDER_EDS (provider));
+}
+
+static void
+gtd_provider_iface_init (GtdProviderInterface *iface)
+{
+  iface->get_id = gtd_provider_local_get_id;
+  iface->get_name = gtd_provider_local_get_name;
+  iface->get_description = gtd_provider_local_get_description;
+  iface->get_enabled = gtd_provider_local_get_enabled;
+  iface->get_icon_for_list = gtd_provider_local_get_icon_for_list;
+  iface->get_edit_panel = gtd_provider_local_get_edit_panel;
+  iface->create_task = gtd_provider_local_create_task;
+  iface->update_task = gtd_provider_local_update_task;
+  iface->remove_task = gtd_provider_local_remove_task;
+  iface->create_task_list = gtd_provider_local_create_task_list;
+  iface->update_task_list = gtd_provider_local_update_task_list;
+  iface->get_task_lists = gtd_provider_local_get_task_lists;
+}
+
+GtdProviderLocal*
+gtd_provider_local_new (ESourceRegistry *registry)
+{
+  return g_object_new (GTD_TYPE_PROVIDER_LOCAL,
+                       "registry", registry,
+                       NULL);
+}
+
+static void
+gtd_provider_local_finalize (GObject *object)
+{
+  GtdProviderLocal *self = (GtdProviderLocal *)object;
+
+  g_clear_object (&self->icon);
+
+  G_OBJECT_CLASS (gtd_provider_local_parent_class)->finalize (object);
+}
+
+static void
+gtd_provider_local_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GtdProvider *provider = GTD_PROVIDER (object);
+
+  switch (prop_id)
+    {
+    case PROP_DESCRIPTION:
+      g_value_set_string (value, gtd_provider_local_get_description (provider));
+      break;
+
+    case PROP_ENABLED:
+      g_value_set_boolean (value, gtd_provider_local_get_enabled (provider));
+      break;
+
+    case PROP_ID:
+      g_value_set_string (value, gtd_provider_local_get_id (provider));
+      break;
+
+    case PROP_NAME:
+      g_value_set_string (value, gtd_provider_local_get_name (provider));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static gboolean
+gtd_provider_local_should_load_source (GtdProviderEds *provider,
+                                       ESource        *source)
+{
+  if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+    return g_strcmp0 (e_source_get_parent (source), "local-stub") == 0;
+
+  return FALSE;
+}
+
+static void
+gtd_provider_local_class_init (GtdProviderLocalClass *klass)
+{
+  GtdProviderEdsClass *eds_class = GTD_PROVIDER_EDS_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  eds_class->should_load_source = gtd_provider_local_should_load_source;
+
+  object_class->finalize = gtd_provider_local_finalize;
+  object_class->get_property = gtd_provider_local_get_property;
+
+  g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
+  g_object_class_override_property (object_class, PROP_ENABLED, "enabled");
+  g_object_class_override_property (object_class, PROP_ID, "id");
+  g_object_class_override_property (object_class, PROP_NAME, "name");
+}
+
+static void
+gtd_provider_local_init (GtdProviderLocal *self)
+{
+  /* icon */
+  self->icon = G_ICON (g_themed_icon_new_with_default_fallbacks ("computer-symbolic"));
+}
diff --git a/plugins/eds/gtd-provider-local.h b/plugins/eds/gtd-provider-local.h
new file mode 100644
index 0000000..fab47d8
--- /dev/null
+++ b/plugins/eds/gtd-provider-local.h
@@ -0,0 +1,40 @@
+/* gtd-provider-local.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTD_PROVIDER_LOCAL_H
+#define GTD_PROVIDER_LOCAL_H
+
+#include "gtd-provider-eds.h"
+
+#include <glib.h>
+
+#include <gnome-todo/gnome-todo.h>
+#include <libecal/libecal.h>
+#include <libedataserverui/libedataserverui.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_PROVIDER_LOCAL (gtd_provider_local_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdProviderLocal, gtd_provider_local, GTD, PROVIDER_LOCAL, GtdProviderEds)
+
+GtdProviderLocal*    gtd_provider_local_new                      (ESourceRegistry    *source_registry);
+
+G_END_DECLS
+
+#endif /* GTD_PROVIDER_LOCAL_H */
diff --git a/plugins/eds/gtd-task-list-eds.c b/plugins/eds/gtd-task-list-eds.c
new file mode 100644
index 0000000..520d3ae
--- /dev/null
+++ b/plugins/eds/gtd-task-list-eds.c
@@ -0,0 +1,139 @@
+/* gtd-task-list-eds.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtd-task-list-eds.h"
+
+struct _GtdTaskListEds
+{
+  ESource            *source;
+};
+
+G_DEFINE_TYPE (GtdTaskListEds, gtd_task_list_eds, GTD_TYPE_TASK_LIST)
+
+enum {
+  PROP_0,
+  PROP_SOURCE,
+  N_PROPS
+};
+
+static void
+gtd_task_list_eds_finalize (GObject *object)
+{
+  GtdTaskListEds *self = GTD_TASK_LIST_EDS (object);
+
+  g_clear_object (&self->source);
+
+  G_OBJECT_CLASS (gtd_task_list_eds_parent_class)->finalize (object);
+}
+
+static void
+gtd_task_list_eds_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  GtdTaskListEds *self = GTD_TASK_LIST_EDS (object);
+
+  switch (prop_id)
+    {
+    case PROP_SOURCE:
+      g_value_set_object (value, self->source);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_task_list_eds_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GtdTaskListEds *self = GTD_TASK_LIST_EDS (object);
+
+  switch (prop_id)
+    {
+    case PROP_SOURCE:
+      gtd_task_list_eds_set_source (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_task_list_eds_class_init (GtdTaskListEdsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtd_task_list_eds_finalize;
+  object_class->get_property = gtd_task_list_eds_get_property;
+  object_class->set_property = gtd_task_list_eds_set_property;
+
+  /**
+   * GtdTaskListEds::source:
+   *
+   * The #ESource of this #GtdTaskListEds
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_SOURCE,
+                                   g_param_spec_object ("source",
+                                                        "ESource of this list",
+                                                        "The ESource of this list",
+                                                        E_TYPE_SOURCE,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+gtd_task_list_eds_init (GtdTaskListEds *self)
+{
+}
+
+GtdTaskListEds*
+gtd_task_list_eds_new (GtdProvider *provider,
+                       ESource     *source)
+{
+  return g_object_new (GTD_TYPE_TASK_LIST_EDS,
+                       "provider", provider,
+                       "source", source,
+                       NULL);
+}
+
+ESource*
+gtd_task_list_eds_get_source (GtdTaskListEds *list)
+{
+  g_return_val_if_fail (GTD_IS_TASK_LIST_EDS (list), NULL);
+
+  return list->source;
+}
+
+void
+gtd_task_list_eds_set_source (GtdTaskListEds *list,
+                              ESource        *source)
+{
+  g_return_if_fail (GTD_IS_TASK_LIST_EDS (list));
+
+  if (g_set_object (&list->source, source))
+    {
+      // TODO: bind color & name
+      g_object_notify (G_OBJECT (list), "source");
+    }
+}
diff --git a/plugins/eds/gtd-task-list-eds.h b/plugins/eds/gtd-task-list-eds.h
new file mode 100644
index 0000000..2579f37
--- /dev/null
+++ b/plugins/eds/gtd-task-list-eds.h
@@ -0,0 +1,42 @@
+/* gtd-task-list-eds.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTD_TASK_LIST_EDS_H
+#define GTD_TASK_LIST_EDS_H
+
+#include <glib-object.h>
+#include <gnome-todo/gnome-todo.h>
+#include <libecal/libecal.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_TASK_LIST_EDS (gtd_task_list_eds_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdTaskListEds, gtd_task_list_eds, GTD, TASK_LIST_EDS, GtdTaskList)
+
+GtdTaskListEds*      gtd_task_list_eds_new                       (GtdProvider        *provider,
+                                                                  ESource            *source);
+
+ESource*             gtd_task_list_eds_get_source                (GtdTaskListEds     *list);
+
+void                 gtd_task_list_eds_set_source                (GtdTaskListEds     *list,
+                                                                  ESource            *source);
+
+G_END_DECLS
+
+#endif /* GTD_TASK_LIST_EDS_H */
diff --git a/src/gtd-manager.c b/src/gtd-manager.c
index 3a543fb..d92cefa 100644
--- a/src/gtd-manager.c
+++ b/src/gtd-manager.c
@@ -17,6 +17,7 @@
  */
 
 #include "gtd-manager.h"
+#include "gtd-plugin-manager.h"
 #include "gtd-storage-goa.h"
 #include "gtd-storage-local.h"
 #include "gtd-storage.h"
@@ -29,30 +30,14 @@
 
 typedef struct
 {
-  GHashTable            *clients;
-  GList                 *task_lists;
-  ECredentialsPrompter  *credentials_prompter;
-  ESourceRegistry       *source_registry;
-
   /*
    * Today & Scheduled lists
    */
   GtdTaskList           *today_tasks_list;
   GtdTaskList           *scheduled_tasks_list;
 
-  /* Online accounts */
-  GoaClient             *goa_client;
-  gboolean               goa_client_ready;
-  GList                 *storage_locations;
-
   GSettings             *settings;
-
-  /*
-   * Small flag that contains the number of sources
-   * that still have to be loaded. When this number
-   * reaches 0, all sources are loaded.
-   */
-  gint                   load_sources;
+  GtdPluginManager      *plugin_manager;
 } GtdManagerPrivate;
 
 struct _GtdManager
@@ -1321,6 +1306,8 @@ gtd_manager_init (GtdManager *self)
   /* fixed task lists */
   self->priv->scheduled_tasks_list = g_object_new (GTD_TYPE_TASK_LIST, NULL);
   self->priv->today_tasks_list = g_object_new (GTD_TYPE_TASK_LIST, NULL);
+
+  self->priv->plugin_manager = gtd_plugin_manager_new ();
 }
 
 /**
@@ -1359,30 +1346,16 @@ void
 gtd_manager_create_task (GtdManager *manager,
                          GtdTask    *task)
 {
-  GtdManagerPrivate *priv = GTD_MANAGER (manager)->priv;
-  ECalComponent *component;
-  ECalClient *client;
-  TaskData *data;
-  ESource *source;
+  GtdTaskList *list;
+  GtdProvider *provider;
 
   g_return_if_fail (GTD_IS_MANAGER (manager));
   g_return_if_fail (GTD_IS_TASK (task));
 
-  source = gtd_task_list_get_source (gtd_task_get_list (task));
-  client = g_hash_table_lookup (priv->clients, source);
-  component = gtd_task_get_component (task);
+  list = gtd_task_get_list (task);
+  provider = gtd_task_list_get_provider (list);
 
-  /* Temporary data for async operation */
-  data = task_data_new (manager, (gpointer) task);
-
-  /* The task is not ready until we finish the operation */
-  gtd_object_set_ready (GTD_OBJECT (task), FALSE);
-
-  e_cal_client_create_object (client,
-                              e_cal_component_get_icalcomponent (component),
-                              NULL, // We won't cancel the operation
-                              (GAsyncReadyCallback) gtd_manager__create_task_finished,
-                              data);
+  gtd_provider_create_task (provider, task);
 }
 
 /**
@@ -1398,36 +1371,16 @@ void
 gtd_manager_remove_task (GtdManager *manager,
                          GtdTask    *task)
 {
-  GtdManagerPrivate *priv = GTD_MANAGER (manager)->priv;
-  ECalComponent *component;
-  ECalComponentId *id;
-  ECalClient *client;
-  TaskData *data;
-  ESource *source;
+  GtdTaskList *list;
+  GtdProvider *provider;
 
   g_return_if_fail (GTD_IS_MANAGER (manager));
   g_return_if_fail (GTD_IS_TASK (task));
 
-  source = gtd_task_list_get_source (gtd_task_get_list (task));
-  client = g_hash_table_lookup (priv->clients, source);
-  component = gtd_task_get_component (task);
-  id = e_cal_component_get_id (component);
-
-  /* Temporary data for async operation */
-  data = task_data_new (manager, (gpointer) task);
-
-  /* The task is not ready until we finish the operation */
-  gtd_object_set_ready (GTD_OBJECT (task), FALSE);
-
-  e_cal_client_remove_object (client,
-                              id->uid,
-                              id->rid,
-                              E_CAL_OBJ_MOD_THIS,
-                              NULL, // We won't cancel the operation
-                              (GAsyncReadyCallback) gtd_manager__remove_task_finished,
-                              data);
+  list = gtd_task_get_list (task);
+  provider = gtd_task_list_get_provider (list);
 
-  e_cal_component_free_id (id);
+  gtd_provider_remove_task (provider, task);
 }
 
 /**
@@ -1443,31 +1396,16 @@ void
 gtd_manager_update_task (GtdManager *manager,
                          GtdTask    *task)
 {
-  GtdManagerPrivate *priv = GTD_MANAGER (manager)->priv;
-  ECalComponent *component;
-  ECalClient *client;
-  TaskData *data;
-  ESource *source;
+  GtdTaskList *list;
+  GtdProvider *provider;
 
   g_return_if_fail (GTD_IS_MANAGER (manager));
   g_return_if_fail (GTD_IS_TASK (task));
 
-  source = gtd_task_list_get_source (gtd_task_get_list (task));
-  client = g_hash_table_lookup (priv->clients, source);
-  component = gtd_task_get_component (task);
-
-  /* Temporary data for async operation */
-  data = task_data_new (manager, (gpointer) task);
-
-  /* The task is not ready until we finish the operation */
-  gtd_object_set_ready (GTD_OBJECT (task), FALSE);
+  list = gtd_task_get_list (task);
+  provider = gtd_task_list_get_provider (list);
 
-  e_cal_client_modify_object (client,
-                              e_cal_component_get_icalcomponent (component),
-                              E_CAL_OBJ_MOD_THIS,
-                              NULL, // We won't cancel the operation
-                              (GAsyncReadyCallback) gtd_manager__update_task_finished,
-                              data);
+  gtd_provider_update_task (provider, task);
 }
 
 /**
@@ -1482,25 +1420,14 @@ void
 gtd_manager_create_task_list (GtdManager  *manager,
                               GtdTaskList *list)
 {
-  GtdManagerPrivate *priv;
-  ESource *source;
-  ESource *parent;
+  GtdProvider *provider;
 
   g_return_if_fail (GTD_IS_MANAGER (manager));
   g_return_if_fail (GTD_IS_TASK_LIST (list));
-  g_return_if_fail (gtd_task_list_get_source (list));
-
-  priv = manager->priv;
-  source = gtd_task_list_get_source (list);
-  parent = e_source_registry_ref_source (priv->source_registry, e_source_get_parent (source));
 
-  e_source_remote_create (parent,
-                          source,
-                          NULL,
-                          (GAsyncReadyCallback) gtd_manager__remote_create_source_finished,
-                          manager);
+  provider = gtd_task_list_get_provider (list);
 
-  g_object_unref (parent);
+  gtd_provider_create_task_list (provider, list);
 }
 
 /**
@@ -1516,30 +1443,14 @@ void
 gtd_manager_remove_task_list (GtdManager  *manager,
                               GtdTaskList *list)
 {
-  ESource *source;
+  GtdProvider *provider;
 
   g_return_if_fail (GTD_IS_MANAGER (manager));
   g_return_if_fail (GTD_IS_TASK_LIST (list));
-  g_return_if_fail (gtd_task_list_get_source (list));
-
-  source = gtd_task_list_get_source (list);
 
-  gtd_object_set_ready (GTD_OBJECT (manager), FALSE);
+  provider = gtd_task_list_get_provider (list);
 
-  if (e_source_get_remote_deletable (source))
-    {
-      e_source_remote_delete (source,
-                              NULL,
-                              (GAsyncReadyCallback) gtd_manager__remote_delete_finished,
-                              manager);
-    }
-  else
-    {
-      e_source_remove (source,
-                       NULL,
-                       (GAsyncReadyCallback) gtd_manager__remove_source_finished,
-                       manager);
-    }
+  gtd_provider_remove_task_list (provider, list);
 
   g_signal_emit (manager,
                  signals[LIST_REMOVED],
@@ -1560,20 +1471,14 @@ void
 gtd_manager_save_task_list (GtdManager  *manager,
                             GtdTaskList *list)
 {
-  ESource *source;
+  GtdProvider *provider;
 
   g_return_if_fail (GTD_IS_MANAGER (manager));
   g_return_if_fail (GTD_IS_TASK_LIST (list));
-  g_return_if_fail (gtd_task_list_get_source (list));
 
-  source = gtd_task_list_get_source (list);
+  provider = gtd_task_list_get_provider (list);
 
-  gtd_object_set_ready (GTD_OBJECT (manager), FALSE);
-  e_source_registry_commit_source (manager->priv->source_registry,
-                                   source,
-                                   NULL,
-                                   (GAsyncReadyCallback) gtd_manager__commit_source_finished,
-                                   manager);
+  gtd_provider_update_task_list (provider, list);
 }
 
 /**


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