[folks] linking: add interface for linkable web service contact UIDs



commit 4a775a4a5092558ed28a8cd92a341e55beffbec2
Author: Alban Crequy <alban crequy collabora co uk>
Date:   Tue Mar 29 19:13:19 2011 +0100

    linking: add interface for linkable web service contact UIDs
    
    Bug: https://bugzilla.gnome.org/show_bug.cgi?id=644867

 backends/key-file/kf-persona-store.vala    |   18 +++++-
 backends/key-file/kf-persona.vala          |   94 +++++++++++++++++++++++++++-
 backends/libsocialweb/lib/swf-persona.vala |   37 +++++++++--
 folks/Makefile.am                          |    1 +
 folks/individual-aggregator.vala           |   68 ++++++++++++++------
 folks/individual.vala                      |   55 ++++++++++++++++-
 folks/persona-store.vala                   |    2 +
 folks/web-service-details.vala             |   49 ++++++++++++++
 8 files changed, 293 insertions(+), 31 deletions(-)
---
diff --git a/backends/key-file/kf-persona-store.vala b/backends/key-file/kf-persona-store.vala
index d4c69dc..e3660ad 100644
--- a/backends/key-file/kf-persona-store.vala
+++ b/backends/key-file/kf-persona-store.vala
@@ -20,6 +20,7 @@
  */
 
 using GLib;
+using Gee;
 using Folks;
 using Folks.Backends.Kf;
 
@@ -290,6 +291,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
    *
    * Accepted keys for `details` are:
    * - PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES)
+   * - PersonaStore.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES)
    *
    * See { link Folks.PersonaStore.add_persona_from_details}.
    */
@@ -300,8 +302,15 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
             PersonaDetail.IM_ADDRESSES));
       unowned HashTable<string, LinkedHashSet<string>> im_addresses =
           (HashTable<string, LinkedHashSet<string>>) val.get_boxed ();
-
-      if (im_addresses == null || im_addresses.size () == 0)
+      val = details.lookup (this.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES));
+      unowned HashMap<string, LinkedHashSet<string>> web_service_addresses =
+          (HashMap<string, LinkedHashSet<string>>) val.get_object ();
+      uint im_addresses_size = (im_addresses == null)
+          ? 0 : im_addresses.size ();
+      uint web_service_addresses_size = (web_service_addresses == null)
+          ? 0 : web_service_addresses.size;
+
+      if (im_addresses_size + web_service_addresses_size == 0)
         {
           throw new PersonaStoreError.INVALID_ARGUMENT (
               /* Translators: the first two parameters are identifiers for the
@@ -325,11 +334,14 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
         }
       while (this._key_file.has_group (persona_id) == true);
 
-      /* Create a new persona and set its im-addresses property to update the
+      /* Create a new persona and set its addresses property to update the
        * key file */
       Persona persona = new Kf.Persona (this._key_file, persona_id, this);
       this._personas.insert (persona.iid, persona);
-      persona.im_addresses = im_addresses;
+      if (im_addresses_size > 0)
+        persona.im_addresses = im_addresses;
+      if (web_service_addresses_size > 0)
+        persona.web_service_addresses = web_service_addresses;
 
       /* FIXME: GroupDetails.ChangeReason is not the right enum to use here */
       GLib.List<Persona> personas = new GLib.List<Persona> ();
diff --git a/backends/key-file/kf-persona.vala b/backends/key-file/kf-persona.vala
index 5bd9177..bc78b7e 100644
--- a/backends/key-file/kf-persona.vala
+++ b/backends/key-file/kf-persona.vala
@@ -37,8 +37,13 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
    * GenericArray<string> here rather than just string[], as null-terminated
    * arrays aren't supported as generic types. */
   private HashTable<string, LinkedHashSet<string>> _im_addresses;
+  private HashMap<string, LinkedHashSet<string>> _web_service_addresses;
   private string _alias;
-  private const string[] _linkable_properties = { "im-addresses" };
+  private const string[] _linkable_properties =
+    {
+      "im-addresses",
+      "web-service-addresses"
+    };
 
   /**
    * { inheritDoc}
@@ -143,6 +148,56 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
     }
 
   /**
+   * { inheritDoc}
+   */
+  public HashMap<string, LinkedHashSet<string>> web_service_addresses
+    {
+      get
+        { return this._web_service_addresses; }
+
+      set
+        {
+          /* Remove the current web service addresses from the key file */
+          foreach (var web_service in this._web_service_addresses.keys)
+            {
+              try
+                {
+                  this._key_file.remove_key (this.display_id, "web-service." + web_service);
+                }
+              catch (KeyFileError e)
+                {
+                  /* Ignore the error, since it's just a group or key not found
+                   * error. */
+                }
+            }
+
+          /* Add the new web service addresses to the key file and build a
+           * table of them to set as the new property value */
+          HashMap<string, LinkedHashSet<string>> web_service_addresses =
+              new HashMap<string, LinkedHashSet<string>> (str_hash, str_equal);
+
+          foreach (var entry in value.entries)
+            {
+              unowned string web_service = (string) entry.key;
+              unowned LinkedHashSet<string?> addresses =
+                  (LinkedHashSet<string?>) entry.value;
+
+              string[] addrs = (string[]) addresses.to_array ();
+              addrs.length = addresses.size;
+
+              this._key_file.set_string_list (this.display_id,
+                  "web-service." + web_service, addrs);
+              web_service_addresses.set (web_service, addresses);
+            }
+
+          this._web_service_addresses = web_service_addresses;
+
+          /* Get the PersonaStore to save the key file */
+          ((Kf.PersonaStore) this.store).save_key_file.begin ();
+        }
+    }
+
+  /**
    * Create a new persona.
    *
    * Create a new persona for the { link PersonaStore} `store`, representing
@@ -165,6 +220,9 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
       this._key_file = key_file;
       this._im_addresses = new HashTable<string, LinkedHashSet<string>> (
           str_hash, str_equal);
+      this._web_service_addresses
+          = new HashMap<string, LinkedHashSet<string>> (str_hash,
+              str_equal);
 
       /* Load the IM addresses from the key file */
       try
@@ -181,6 +239,26 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
                   continue;
                 }
 
+              /* Web service addresses */
+              var decomposed_key = key.split(".", 2);
+              if (decomposed_key.length == 2 &&
+                  decomposed_key[0] == "web-service")
+                {
+                  unowned string web_service = decomposed_key[1];
+                  var web_service_addresses = this._key_file.get_string_list (
+                      this.display_id, web_service);
+    
+                  var address_set = new LinkedHashSet<string> ();
+    
+                  foreach (var web_service_address in web_service_addresses)
+                    {
+                      address_set.add (web_service_address);
+                    }
+    
+                  this._web_service_addresses.set (web_service, address_set);
+                  continue;
+                }
+
               /* IM addresses */
               unowned string protocol = key;
               var im_addresses = this._key_file.get_string_list (
@@ -202,7 +280,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
                       warning (e.message);
                       continue;
                     }
-                    address_set.add (address);
+                  address_set.add (address);
                 }
 
               this._im_addresses.insert (protocol, address_set);
@@ -240,6 +318,18 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
                   callback (protocol + ":" + address);
             });
         }
+      else if (prop_name == "web-service-addresses")
+        {
+          foreach (var entry in this.web_service_addresses.entries)
+            {
+              unowned string web_service = (string) entry.key;
+              unowned LinkedHashSet<string> web_service_addresses =
+                  (LinkedHashSet<string>) entry.value;
+
+              foreach (string address in web_service_addresses)
+                  callback (web_service + ":" + address);
+            }
+        }
       else
         {
           /* Chain up */
diff --git a/backends/libsocialweb/lib/swf-persona.vala b/backends/libsocialweb/lib/swf-persona.vala
index 0244e46..270f097 100644
--- a/backends/libsocialweb/lib/swf-persona.vala
+++ b/backends/libsocialweb/lib/swf-persona.vala
@@ -20,6 +20,7 @@
  */
 
 using GLib;
+using Gee;
 using Folks;
 using SocialWebClient;
 
@@ -31,9 +32,14 @@ public class Swf.Persona : Folks.Persona,
     GenderDetails,
     ImDetails,
     NameDetails,
-    UrlDetails
+    UrlDetails,
+    WebServiceDetails
 {
-  private const string[] _linkable_properties = {};
+  private const string[] _linkable_properties =
+    {
+      "im-addresses",
+      "web-service-addresses"
+    };
 
   /**
    * The names of the Persona's linkable properties.
@@ -73,16 +79,16 @@ public class Swf.Persona : Folks.Persona,
    */
   public Gender gender { get; private set; }
 
-  private List<FieldDetails> _urls;
+  private GLib.List<FieldDetails> _urls;
   /**
    * { inheritDoc}
    */
-  public List<FieldDetails> urls
+  public GLib.List<FieldDetails> urls
     {
       get { return this._urls; }
       private set
         {
-          this._urls = new List<FieldDetails> ();
+          this._urls = new GLib.List<FieldDetails> ();
           foreach (unowned FieldDetails ps in value)
             this._urls.prepend (ps);
           this._urls.reverse ();
@@ -91,6 +97,10 @@ public class Swf.Persona : Folks.Persona,
 
   private HashTable<string, LinkedHashSet<string>> _im_addresses =
       new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+
+  private HashMap<string, LinkedHashSet<string>> _web_service_addresses =
+      new HashMap<string, LinkedHashSet<string>> (str_hash, str_equal);
+
   /**
    * { inheritDoc}
    */
@@ -101,6 +111,15 @@ public class Swf.Persona : Folks.Persona,
     }
 
   /**
+   * { inheritDoc}
+   */
+  public HashMap<string, LinkedHashSet<string>> web_service_addresses
+    {
+      get { return this._web_service_addresses; }
+      private set {}
+    }
+
+  /**
    * Build the Facebook JID.
    *
    * @param store_id the { link PersonaStore.id}
@@ -158,6 +177,7 @@ public class Swf.Persona : Folks.Persona,
   public Persona (PersonaStore store, Contact contact)
     {
       var id = get_contact_id (contact);
+      var service = contact.service.dup();
       var uid = this.build_uid (BACKEND_NAME, store.id, id);
       var iid = this._build_iid (store.id, id);
 
@@ -192,6 +212,11 @@ public class Swf.Persona : Folks.Persona,
             }
         }
 
+      var web_service_address_array = new LinkedHashSet<string> ();
+      web_service_address_array.add (id);
+      this._web_service_addresses.set ((owned) service,
+          (owned) web_service_address_array);
+
       update (contact);
     }
 
@@ -233,7 +258,7 @@ public class Swf.Persona : Folks.Persona,
       if (this.full_name != full_name)
         this.full_name = full_name;
 
-      var urls = new List<FieldDetails> ();
+      var urls = new GLib.List<FieldDetails> ();
 
       var website = contact.get_value ("url");
       if (website != null)
diff --git a/folks/Makefile.am b/folks/Makefile.am
index 4013118..6e8fb89 100644
--- a/folks/Makefile.am
+++ b/folks/Makefile.am
@@ -22,6 +22,7 @@ libfolks_la_SOURCES = \
 	field-details.vala \
 	gender-details.vala \
 	group-details.vala \
+	web-service-details.vala \
 	im-details.vala \
 	name-details.vala \
 	note-details.vala \
diff --git a/folks/individual-aggregator.vala b/folks/individual-aggregator.vala
index c1f3ebf..e3c0332 100644
--- a/folks/individual-aggregator.vala
+++ b/folks/individual-aggregator.vala
@@ -909,36 +909,66 @@ public class Folks.IndividualAggregator : Object
       /* `protocols_addrs_set` will be passed to the new Kf.Persona */
       var protocols_addrs_set =
           new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+      var web_service_addrs_set =
+          new HashMap<string, LinkedHashSet<string>> (str_hash, str_equal);
 
       foreach (var persona in personas)
         {
-          if (!(persona is ImDetails))
-            continue;
-
-          ((ImDetails) persona).im_addresses.foreach ((k, v) =>
+          if (persona is ImDetails)
             {
-              unowned string protocol = (string) k;
-              unowned LinkedHashSet<string> addresses =
-                (LinkedHashSet<string>) v;
-
-              var address_set = protocols_addrs_set.lookup (protocol);
-
-              if (address_set == null)
+              ((ImDetails) persona).im_addresses.foreach ((k, v) =>
                 {
-                  address_set = new LinkedHashSet<string> ();
+                  unowned string protocol = (string) k;
+                  unowned LinkedHashSet<string> addresses =
+                    (LinkedHashSet<string>) v;
+    
+                  var address_set = protocols_addrs_set.lookup (protocol);
+    
+                  if (address_set == null)
+                    {
+                      address_set = new LinkedHashSet<string> ();
+    
+                      protocols_addrs_set.insert (protocol, address_set);
+                    }
+    
+                  address_set.add_all (addresses);
+                });
+            }
 
-                  protocols_addrs_set.insert (protocol, address_set);
+          if (persona is WebServiceDetails)
+            {
+              foreach (var entry in
+                  ((WebServiceDetails) persona).web_service_addresses.entries)
+                {
+                  unowned string web_service = (string) entry.key;
+                  unowned LinkedHashSet<string> addresses =
+                    (LinkedHashSet<string>) entry.value;
+    
+                  var address_set = web_service_addrs_set.get (web_service);
+    
+                  if (address_set == null)
+                    {
+                      address_set = new LinkedHashSet<string> ();
+    
+                      web_service_addrs_set.set (web_service, address_set);
+                    }
+    
+                  address_set.add_all (addresses);
                 }
-
-              address_set.add_all (addresses);
-            });
+            }
         }
 
-      var addresses_value = Value (typeof (HashTable));
-      addresses_value.set_boxed (protocols_addrs_set);
+      var im_addresses_value = Value (typeof (HashTable));
+      im_addresses_value.set_boxed (protocols_addrs_set);
+
+      var web_service_addresses_value = Value (typeof (HashMap));
+      web_service_addresses_value.set_object (web_service_addrs_set);
 
       var details = new HashTable<string, Value?> (str_hash, str_equal);
-      details.insert ("im-addresses", addresses_value);
+      details.insert (PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES),
+          im_addresses_value);
+      details.insert (PersonaStore.detail_key
+          (PersonaDetail.WEB_SERVICE_ADDRESSES), web_service_addresses_value);
 
       yield this.add_persona_from_details (null, this._writeable_store,
           details);
diff --git a/folks/individual.vala b/folks/individual.vala
index 39e3067..6f60940 100644
--- a/folks/individual.vala
+++ b/folks/individual.vala
@@ -77,7 +77,8 @@ public class Folks.Individual : Object,
     PhoneDetails,
     PostalAddressDetails,
     RoleDetails,
-    UrlDetails
+    UrlDetails,
+    WebServiceDetails
 {
   private bool _is_favourite;
   private string _alias;
@@ -98,6 +99,7 @@ public class Folks.Individual : Object,
    * Persona.is_user == true. Iff this is > 0, Individual.is_user == true. */
   private uint _persona_user_count = 0;
   private HashTable<string, LinkedHashSet<string>> _im_addresses;
+  private HashMap<string, LinkedHashSet<string>> _web_service_addresses;
 
   /**
    * The trust level of the Individual.
@@ -410,6 +412,15 @@ public class Folks.Individual : Object,
     }
 
   /**
+   * { inheritDoc}
+   */
+  public HashMap<string, LinkedHashSet<string>> web_service_addresses
+    {
+      get { return this._web_service_addresses; }
+      private set {}
+    }
+
+  /**
    * The set of { link Persona}s encapsulated by this Individual.
    *
    * Changing the set of personas may cause updates to the aggregated properties
@@ -544,6 +555,11 @@ public class Folks.Individual : Object,
       this._update_im_addresses ();
     }
 
+  private void _notify_web_service_addresses_cb (Object obj, ParamSpec ps)
+    {
+      this._update_web_service_addresses ();
+    }
+
   private void _notify_is_favourite_cb (Object obj, ParamSpec ps)
     {
       this._update_is_favourite ();
@@ -564,6 +580,8 @@ public class Folks.Individual : Object,
     {
       this._im_addresses =
           new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+      this._web_service_addresses =
+          new HashMap<string, LinkedHashSet<string>> (str_hash, str_equal);
       this._persona_set = new HashSet<Persona> (null, null);
       this._stores = new HashMap<PersonaStore, uint> (null, null);
       this._gender = Gender.UNSPECIFIED;
@@ -638,6 +656,7 @@ public class Folks.Individual : Object,
       this._update_alias ();
       this._update_trust_level ();
       this._update_im_addresses ();
+      this._update_web_service_addresses ();
       this._update_structured_name ();
       this._update_full_name ();
       this._update_nickname ();
@@ -914,6 +933,36 @@ public class Folks.Individual : Object,
       this.notify_property ("im-addresses");
     }
 
+  private void _update_web_service_addresses ()
+    {
+      /* populate the web service addresses as the union of our Personas' addresses */
+      foreach (var persona in this.personas)
+        {
+          if (persona is WebServiceDetails)
+            {
+              var web_service_details = (WebServiceDetails) persona;
+              foreach (var entry in
+                  web_service_details.web_service_addresses.entries)
+                {
+                  var cur_web_service = (string) entry.key;
+                  var cur_addresses = (LinkedHashSet<string>) entry.value;
+                  var web_service_set = this._web_service_addresses.get
+                                            (cur_web_service);
+
+                  if (web_service_set == null)
+                    {
+                      web_service_set = new LinkedHashSet<string> ();
+                      this._web_service_addresses.set (cur_web_service,
+                                                       web_service_set);
+                    }
+
+                  web_service_set.add_all (cur_addresses);
+                }
+            }
+        }
+      this.notify_property ("web-service-addresses");
+    }
+
   private void _connect_to_persona (Persona persona)
     {
       persona.notify["alias"].connect (this._notify_alias_cb);
@@ -921,6 +970,8 @@ public class Folks.Individual : Object,
       persona.notify["presence-message"].connect (this._notify_presence_cb);
       persona.notify["presence-type"].connect (this._notify_presence_cb);
       persona.notify["im-addresses"].connect (this._notify_im_addresses_cb);
+      persona.notify["web-service-addresses"].connect
+              (this._notify_web_service_addresses_cb);
       persona.notify["is-favourite"].connect (this._notify_is_favourite_cb);
       persona.notify["structured-name"].connect (
           this._notify_structured_name_cb);
@@ -1021,6 +1072,8 @@ public class Folks.Individual : Object,
       persona.notify["presence-type"].disconnect (this._notify_presence_cb);
       persona.notify["im-addresses"].disconnect (
           this._notify_im_addresses_cb);
+      persona.notify["web-service-addresses"].disconnect (
+          this._notify_web_service_addresses_cb);
       persona.notify["is-favourite"].disconnect (
           this._notify_is_favourite_cb);
       persona.notify["structured-name"].disconnect (
diff --git a/folks/persona-store.vala b/folks/persona-store.vala
index 38e68f9..bc392e6 100644
--- a/folks/persona-store.vala
+++ b/folks/persona-store.vala
@@ -117,6 +117,7 @@ public enum Folks.PersonaDetail
   GENDER,
   EMAIL_ADDRESSES,
   IM_ADDRESSES,
+  WEB_SERVICE_ADDRESSES,
   NOTES,
   PHONE_NUMBERS,
   POSTAL_ADDRESSES,
@@ -158,6 +159,7 @@ public abstract class Folks.PersonaStore : Object
     "gender",
     "email-addresses",
     "im-addresses",
+    "web-service-addresses",
     "notes",
     "phone-numbers",
     "postal-addresses",
diff --git a/folks/web-service-details.vala b/folks/web-service-details.vala
new file mode 100644
index 0000000..3afe19f
--- /dev/null
+++ b/folks/web-service-details.vala
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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:
+ *       Alban Crequy <alban crequy collabora co uk>
+ */
+
+using Gee;
+
+/**
+ * web service addresses exposed by an object implementing
+ * { link PresenceDetails}.
+ *
+ * @since UNRELEASED
+ */
+public interface Folks.WebServiceDetails : Object
+{
+  /**
+   * A mapping of web service to an ordered set of web service addresses.
+   *
+   * Each mapping is from an arbitrary web service identifier to a set of web
+   * service addresses for the contact, listed in preference order.
+   * The most-preferred web service address for each web service comes first
+   * in that web service's list.
+   *
+   * Web service addresses are guaranteed to be unique per web service, but
+   * not necessarily unique amongst all web services.
+   *
+   * @since UNRELEASED
+   */
+  public abstract Gee.HashMap<string, LinkedHashSet<string>>
+      web_service_addresses
+    {
+      get; set;
+    }
+}



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