libsocialweb: add new backend
authorMarco Barisione <marco@barisione.org>
Mon, 1 Nov 2010 13:58:08 +0000 (13:58 +0000)
committerAlban Crequy <alban.crequy@collabora.co.uk>
Tue, 5 Apr 2011 12:02:38 +0000 (13:02 +0100)
Based on patches from:
 Marco Barisione <marco@barisione.org>
Reworked by:
 Alban Crequy <alban.crequy@collabora.co.uk>

backends/Makefile.am
backends/libsocialweb/Makefile.am [new file with mode: 0644]
backends/libsocialweb/sw-backend-factory.vala [new file with mode: 0644]
backends/libsocialweb/sw-backend.vala [new file with mode: 0644]
backends/libsocialweb/sw-persona-store.vala [new file with mode: 0644]
backends/libsocialweb/sw-persona.vala [new file with mode: 0644]
configure.ac

index e3cd064..ba51ad5 100644 (file)
@@ -3,6 +3,10 @@ SUBDIRS = \
        key-file \
        $(NULL)
 
+if ENABLE_LIBSOCIALWEB
+SUBDIRS += libsocialweb
+endif
+
 if ENABLE_TRACKER
 SUBDIRS += tracker
 endif
@@ -11,6 +15,7 @@ DIST_SUBDIRS = \
        telepathy \
        key-file \
        tracker \
+       libsocialweb \
        $(NULL)
 
 -include $(top_srcdir)/git.mk
diff --git a/backends/libsocialweb/Makefile.am b/backends/libsocialweb/Makefile.am
new file mode 100644 (file)
index 0000000..d0da9a7
--- /dev/null
@@ -0,0 +1,55 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/folks \
+       -include $(CONFIG_HEADER) \
+       -DPACKAGE_DATADIR=\"$(pkgdatadir)\" \
+       -DG_LOG_DOMAIN=\"LibSocialWebBackend\" \
+       $(NULL)
+
+VALAFLAGS += \
+       --vapidir=. \
+       --vapidir=$(top_srcdir)/folks \
+       $(addprefix --pkg ,$(folks_backend_libsocialweb_deps)) \
+       $(NULL)
+
+backenddir = $(BACKEND_DIR)/libsocialweb
+backend_LTLIBRARIES = libfolks-backend-libsocialweb.la
+
+libfolks_backend_libsocialweb_la_SOURCES = \
+       sw-backend.vala \
+       sw-backend-factory.vala \
+       sw-persona-store.vala \
+       sw-persona.vala \
+       $(NULL)
+
+folks_backend_libsocialweb_deps = \
+       folks \
+       gee-1.0 \
+       gio-2.0 \
+       gobject-2.0 \
+       libsocialweb-client \
+       $(NULL)
+
+libfolks_backend_libsocialweb_la_CFLAGS = \
+       $(GIO_CFLAGS) \
+       $(GLIB_CFLAGS) \
+       $(GEE_CFLAGS) \
+       $(SW_CLIENT_CFLAGS) \
+       $(NULL)
+
+libfolks_backend_libsocialweb_la_LIBADD = \
+       $(GIO_LIBS) \
+       $(GLIB_LIBS) \
+       $(GEE_LIBS) \
+       $(top_builddir)/folks/libfolks.la \
+       $(SW_CLIENT_LIBS) \
+       $(NULL)
+
+libfolks_backend_libsocialweb_la_LDFLAGS = -shared -fPIC -module -avoid-version
+
+GITIGNOREFILES = \
+       folks-backend-libsocialweb.vapi \
+       $(libfolks_backend_libsocialweb_la_SOURCES:.vala=.c) \
+       libfolks_backend_libsocialweb_la_vala.stamp \
+       $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/backends/libsocialweb/sw-backend-factory.vala b/backends/libsocialweb/sw-backend-factory.vala
new file mode 100644 (file)
index 0000000..14ede90
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>.
+ * Copyright (C) 2009 Nokia Corporation.
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ *          Travis Reitter <travis.reitter@collabora.co.uk>
+ *          Marco Barisione <marco.barisione@collabora.co.uk>
+ *
+ * This file was originally part of Rygel.
+ */
+
+using Folks;
+using Folks.Backends.Sw;
+
+private BackendFactory backend_factory = null;
+
+/**
+ * The libsocialweb backend module entry point.
+ */
+public void module_init (BackendStore backend_store)
+{
+  backend_factory = new BackendFactory (backend_store);
+}
+
+/**
+ * The libsocialweb backend module exit point.
+ */
+public void module_finalize (BackendStore backend_store)
+{
+  backend_factory = null;
+}
+
+/**
+ * A backend factory to create a single {@link Backend}.
+ */
+public class Folks.Backends.Sw.BackendFactory : Object
+{
+  /**
+   * {@inheritDoc}
+   */
+  public BackendFactory (BackendStore backend_store)
+    {
+      backend_store.add_backend (new Backend ());
+    }
+}
diff --git a/backends/libsocialweb/sw-backend.vala b/backends/libsocialweb/sw-backend.vala
new file mode 100644 (file)
index 0000000..739cb2c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *       Travis Reitter <travis.reitter@collabora.co.uk>
+ *       Marco Barisione <marco.barisione@collabora.co.uk>
+ */
+
+using GLib;
+using Folks;
+using Folks.Backends.Sw;
+using SocialWebClient;
+
+extern const string BACKEND_NAME;
+
+/**
+ * A backend which connects to libsocialweb and creates a {@link PersonaStore}
+ * for each service.
+ */
+public class Folks.Backends.Sw.Backend : Folks.Backend
+{
+  private bool _is_prepared = false;
+  private Client _client;
+  private HashTable<string, PersonaStore> _persona_stores =
+         new HashTable<string, PersonaStore> (str_hash, str_equal);
+
+  /**
+   * {@inheritDoc}
+   */
+  public override string name { get { return BACKEND_NAME; } }
+
+  /**
+   * {@inheritDoc}
+   */
+  public override HashTable<string, PersonaStore> persona_stores
+    {
+      get { return this._persona_stores; }
+    }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public Backend ()
+    {
+    }
+
+  /**
+   * Whether this Backend has been prepared.
+   *
+   * See {@link Folks.Backend.is_prepared}.
+   */
+  public override bool is_prepared
+    {
+      get { return this._is_prepared; }
+    }
+
+  /**
+   * {@inheritDoc}
+   */
+  public override async void prepare () throws GLib.Error
+    {
+      lock (this._is_prepared)
+        {
+          if (!this._is_prepared)
+            {
+              this._client = new Client();
+              this._client.get_services((client, services) =>
+                {
+                  foreach (var service_name in services)
+                    this.add_service (service_name);
+
+                  this._is_prepared = true;
+                  this.notify_property ("is-prepared");
+                });
+            }
+        }
+    }
+
+  /**
+   * {@inheritDoc}
+   */
+  public override async void unprepare () throws GLib.Error
+    {
+      this._persona_stores.foreach ((k, v) =>
+        {
+          PersonaStore store = v;
+          store.removed.disconnect (this.store_removed_cb);
+          this.persona_store_removed (store);
+        });
+
+      this._client = null;
+
+      this._persona_stores.remove_all ();
+      this.notify_property ("persona-stores");
+
+      this._is_prepared = false;
+      this.notify_property ("is-prepared");
+    }
+
+  private void add_service (string service_name)
+    {
+      if (this._persona_stores.lookup (service_name) != null)
+        return;
+
+      var store = new PersonaStore (this._client.get_service (service_name));
+      this._persona_stores.insert (store.id, store);
+      store.removed.connect (this.store_removed_cb);
+      this.persona_store_added (store);
+    }
+
+  private void store_removed_cb (Folks.PersonaStore store)
+    {
+      this.persona_store_removed (store);
+      this._persona_stores.remove (store.id);
+    }
+}
diff --git a/backends/libsocialweb/sw-persona-store.vala b/backends/libsocialweb/sw-persona-store.vala
new file mode 100644 (file)
index 0000000..afeee59
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *       Travis Reitter <travis.reitter@collabora.co.uk>
+ *       Philip Withnall <philip.withnall@collabora.co.uk>
+ *       Marco Barisione <marco.barisione@collabora.co.uk>
+ */
+
+using GLib;
+using Folks;
+using SocialWebClient;
+
+/**
+ * A persona store which is associated with a single libsocialweb service.
+ * It will create {@link Persona}s for each of the contacts known to that
+ * service.
+ */
+public class Folks.Backends.Sw.PersonaStore : Folks.PersonaStore
+{
+  private HashTable<string, Persona> _personas;
+  private bool _is_prepared = false;
+  private ClientService _service;
+  private ClientItemView _item_view;
+
+  /**
+   * The type of persona store this is.
+   *
+   * See {@link Folks.PersonaStore.type_id}.
+   */
+  public override string type_id { get { return "libsocialweb"; } }
+
+  /**
+   * Whether this PersonaStore can add {@link Folks.Persona}s.
+   *
+   * See {@link Folks.PersonaStore.can_add_personas}.
+   *
+   * @since 0.3.1
+   */
+  public override MaybeBool can_add_personas
+    {
+      get { return MaybeBool.FALSE; }
+    }
+
+  /**
+   * Whether this PersonaStore can set the alias of {@link Folks.Persona}s.
+   *
+   * See {@link Folks.PersonaStore.can_alias_personas}.
+   *
+   * @since 0.3.1
+   */
+  public override MaybeBool can_alias_personas
+    {
+      get { return MaybeBool.FALSE; }
+    }
+
+  /**
+   * Whether this PersonaStore can set the groups of {@link Folks.Persona}s.
+   *
+   * See {@link Folks.PersonaStore.can_group_personas}.
+   *
+   * @since 0.3.1
+   */
+  public override MaybeBool can_group_personas
+    {
+      get { return MaybeBool.FALSE; }
+    }
+
+  /**
+   * Whether this PersonaStore can remove {@link Folks.Persona}s.
+   *
+   * See {@link Folks.PersonaStore.can_remove_personas}.
+   *
+   * @since 0.3.1
+   */
+  public override MaybeBool can_remove_personas
+    {
+      get { return MaybeBool.FALSE; }
+    }
+
+  /**
+   * Whether this PersonaStore has been prepared.
+   *
+   * See {@link Folks.PersonaStore.is_prepared}.
+   *
+   * @since 0.3.0
+   */
+  public override bool is_prepared
+    {
+      get { return this._is_prepared; }
+    }
+
+  /**
+   * The {@link Persona}s exposed by this PersonaStore.
+   *
+   * See {@link Folks.PersonaStore.personas}.
+   */
+  public override HashTable<string, Persona> personas
+    {
+      get { return this._personas; }
+    }
+
+  /**
+   * Create a new PersonaStore.
+   *
+   * Create a new persona store to store the {@link Persona}s for the contacts
+   * provided by the `service`.
+   */
+  public PersonaStore (ClientService service)
+    {
+      Object (display_name: service.get_display_name(),
+              id: service.get_name ());
+
+      this.trust_level = PersonaStoreTrust.PARTIAL;
+      this._service = service;
+      this._personas = new HashTable<string, Persona> (str_hash, str_equal);
+    }
+
+  /**
+   * Add a new {@link Persona} to the PersonaStore.
+   *
+   * See {@link Folks.PersonaStore.add_persona_from_details}.
+   */
+  public override async Folks.Persona? add_persona_from_details (
+      HashTable<string, Value?> details) throws Folks.PersonaStoreError
+    {
+      // FIXME: There is no better error for this.
+      throw new PersonaStoreError.UNSUPPORTED_ON_USER (
+          "Personas cannot be added to this store.");
+    }
+
+  /**
+   * Remove a {@link Persona} from the PersonaStore.
+   *
+   * See {@link Folks.PersonaStore.remove_persona}.
+   */
+  public override async void remove_persona (Folks.Persona persona)
+      throws Folks.PersonaStoreError
+    {
+      // FIXME: There is no better error for this.
+      throw new PersonaStoreError.UNSUPPORTED_ON_USER (
+          "Personas cannot be removed from this store.");
+    }
+
+  /**
+   * Prepare the PersonaStore for use.
+   *
+   * See {@link Folks.PersonaStore.prepare}.
+   */
+  public override async void prepare ()
+    {
+      lock (this._is_prepared)
+        {
+          if (!this._is_prepared)
+            {
+              var parameters = new HashTable<weak string, weak string> (
+                  str_hash, str_equal);
+              this._service.query_open_view("people", parameters,
+                  (query, item_view) =>
+                    {
+                      item_view.items_added.connect (this.items_added_cb);
+                      item_view.items_changed.connect (this.items_changed_cb);
+                      item_view.items_removed.connect (this.items_removed_cb);
+
+                      this._item_view = item_view;
+                      this._is_prepared = true;
+                      this.notify_property ("is-prepared");
+
+                      this._item_view.start ();
+                    });
+            }
+        }
+    }
+
+  private void items_added_cb (List<unowned Item> items)
+    {
+      var added_personas = new Queue<Persona> ();
+      foreach (var item in items)
+        {
+          var persona = new Persona(this, item);
+          _personas.insert(persona.iid, persona);
+          added_personas.push_tail(persona);
+        }
+
+      if (added_personas.length > 0)
+        this.personas_changed (added_personas.head, null, null, null, 0);
+    }
+
+  private void items_changed_cb (List<unowned Item> items)
+    {
+      foreach (var item in items)
+        {
+          var persona = _personas.lookup(Persona.get_item_id (item));
+          if (persona != null)
+            persona.update (item);
+        }
+    }
+
+  private void items_removed_cb (List<unowned Item> items)
+    {
+      var removed_personas = new Queue<Persona> ();
+      foreach (var item in items)
+        {
+          var persona = _personas.lookup(Persona.get_item_id (item));
+          if (persona != null)
+            {
+              removed_personas.push_tail(persona);
+              _personas.remove(persona.iid);
+            }
+        }
+
+      if (removed_personas.length > 0)
+        this.personas_changed (null, removed_personas.head, null, null, 0);
+    }
+
+}
diff --git a/backends/libsocialweb/sw-persona.vala b/backends/libsocialweb/sw-persona.vala
new file mode 100644 (file)
index 0000000..909e7fb
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *       Travis Reitter <travis.reitter@collabora.co.uk>
+ *       Marco Barisione <marco.barisione@collabora.co.uk>
+ */
+
+using GLib;
+using Folks;
+using SocialWebClient;
+
+/**
+ * A persona subclass which represents a single libsocialweb contact.
+ */
+internal class Folks.Backends.Sw.Persona : Folks.Persona,
+    Aliasable,
+    AvatarDetails
+{
+  private const string[] _linkable_properties = {};
+
+  /**
+   * The names of the Persona's linkable properties.
+   *
+   * See {@link Folks.Persona.linkable_properties}.
+   */
+  public override string[] linkable_properties
+    {
+      get { return this._linkable_properties; }
+    }
+
+  /**
+   * An avatar for the Persona.
+   *
+   * See {@link Folks.HasAvatar.avatar}.
+   */
+  public File avatar { get; set; }
+
+  /**
+   * An alias for the Persona.
+   *
+   * See {@link Folks.Aliasable.alias}.
+   */
+  public string alias { get; private set; }
+
+  /**
+   * Create a new persona.
+   *
+   * Create a new persona for the {@link PersonaStore} `store`, representing
+   * the libsocialweb contact given by `item`.
+   */
+  public Persona (PersonaStore store, Item item)
+    {
+      var id = get_item_id (item);
+      var uid = this.build_uid ("folks", store.id, id);
+      debug ("Creating new Sw.Persona '%s' for %s UID '%s': %p",
+          uid, store.display_name, id, this);
+      Object (alias: item.get_value ("name"),
+              display_id: id,
+              uid: uid,
+              iid: store.id + ":" + id,
+              store: store,
+              is_user: false);
+      update (item);
+    }
+
+  ~Persona ()
+    {
+      debug ("Destroying Sw.Persona '%s': %p", this.uid, this);
+    }
+
+  public static string? get_item_id (Item item)
+    {
+      return item.get_value ("id");
+    }
+
+  public void update (Item item)
+    {
+      var name = item.get_value ("name");
+      if (name != null && name != alias)
+        alias = name;
+
+      var avatar_path = item.get_value ("icon");
+      if (avatar_path != null)
+        {
+          var avatar_file = File.new_for_path (avatar_path);
+          if (avatar != avatar_file)
+            avatar = avatar_file;
+        }
+    }
+}
index 95b3bc2..5ad65c3 100644 (file)
@@ -84,6 +84,7 @@ VALA_REQUIRED=0.11.6
 VALADOC_REQUIRED=0.2.1
 TRACKER_SPARQL_REQUIRED=0.10
 GCONF2_REQUIRED=2.31
+SW_CLIENT_REQUIRED=0.25
 
 PKG_CHECK_MODULES([GLIB],
                   [glib-2.0 >= $GLIB_REQUIRED
@@ -102,6 +103,13 @@ if test x$enable_tracker_backend = xyes; then
                           [tracker-sparql-0.10 >= $TRACKER_SPARQL_REQUIRED])
 fi
 
+PKG_CHECK_MODULES([SW_CLIENT], [libsocialweb-client >= $SW_CLIENT_REQUIRED])
+VALA_CHECK_PACKAGES([telepathy-glib
+                     dbus-glib-1
+                     gio-2.0
+                     gee-1.0
+                     libsocialweb-client])
+
 #
 # Vala building options -- allows tarball builds without installing Vala
 #
@@ -358,6 +366,7 @@ AC_CONFIG_FILES([
     Makefile
     backends/Makefile
     backends/key-file/Makefile
+    backends/libsocialweb/Makefile
     backends/telepathy/Makefile
     backends/telepathy/lib/Makefile
     backends/tracker/Makefile