[folks/wip/arbitrary-field-interface: 8/9] core: Add an ExtendedInfo interface for storing arbitrary fields



commit 31018a77990ee1ecdf696fd679cb83d711524b23
Author: Rodrigo Moya <rodrigo gnome-db org>
Date:   Tue Jan 20 12:07:28 2015 +0000

    core: Add an ExtendedInfo interface for storing arbitrary fields
    
    This adds a new interface which allows clients to store arbitrary fields
    in backends. No backends currently implement it, but that will change.
    
    See the documentation for reasoning about when it’s appropriate to use
    this interface.
    
    New API:
     • ExtendedInfo interface
     • ExtendedFieldDetails class
     • Individual now implements ExtendedInfo
    
    https://bugzilla.gnome.org/show_bug.cgi?id=641211

 folks/Makefile.am        |    1 +
 folks/extended-info.vala |  151 ++++++++++++++++++++++++++++++++++++++++++++++
 folks/individual.vala    |  119 ++++++++++++++++++++++++++++++++++++-
 folks/persona-store.vala |   10 +++-
 po/POTFILES.in           |    1 +
 po/POTFILES.skip         |    1 +
 6 files changed, 281 insertions(+), 2 deletions(-)
---
diff --git a/folks/Makefile.am b/folks/Makefile.am
index f5d0b25..86e91ab 100644
--- a/folks/Makefile.am
+++ b/folks/Makefile.am
@@ -78,6 +78,7 @@ libfolks_la_SOURCES = \
        backend-store.vala \
        birthday-details.vala \
        email-details.vala \
+       extended-info.vala \
        favourite-details.vala \
        folks-namespace.vala \
        gender-details.vala \
diff --git a/folks/extended-info.vala b/folks/extended-info.vala
new file mode 100644
index 0000000..0d81805
--- /dev/null
+++ b/folks/extended-info.vala
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2013, 2015 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:
+ *       Rodrigo Moya <rodrigo gnome org>
+ *       Philip Withnall <philip withnall collabora co uk>
+ */
+
+using GLib;
+using Gee;
+
+/**
+ * Object representing an arbitrary field that can have some parameters
+ * associated with it. This is intended to be as general-purpose as, for
+ * example, a vCard property. See the documentation for
+ * { link Folks.ExtendedInfo} for information on when using this object is
+ * appropriate.
+ *
+ * See { link Folks.AbstractFieldDetails} for details on common parameter names
+ * and values.
+ *
+ * @since UNRELEASED
+ */
+public class Folks.ExtendedFieldDetails : AbstractFieldDetails<string>
+{
+  /**
+   * Create a new ExtendedFieldDetails.
+   *
+   * @param value the value of the field, which may be the empty string
+   * @param parameters initial parameters. See
+   * { link AbstractFieldDetails.parameters}. A ``null`` value is equivalent to
+   * an empty map of parameters.
+   *
+   * @return a new ExtendedFieldDetails
+   *
+   * @since UNRELEASED
+   */
+  public ExtendedFieldDetails (string value,
+      MultiMap<string, string>? parameters = null)
+    {
+      this.value = value;
+      if (parameters != null)
+          this.parameters = (!) parameters;
+    }
+
+  /**
+   * { inheritDoc}
+   *
+   * @since UNRELEASED
+   */
+  public override bool equal (AbstractFieldDetails<string> that)
+    {
+      return base.equal (that);
+    }
+
+  /**
+   * { inheritDoc}
+   *
+   * @since UNRELEASED
+   */
+  public override uint hash ()
+    {
+      return base.hash ();
+    }
+}
+
+/**
+ * Arbitrary field interface.
+ *
+ * This interface allows clients to store arbitrary fields for contacts in
+ * backends that support it.
+ *
+ * This interface should be used for application-specific data, in which case
+ * the application should use the vCard approach to prefixing non-standard
+ * property names: `X-[APPLICATION NAME]-*’. Note that this is a global
+ * namespace, shared between all consumers of the backend’s data, so please
+ * namespace application-specific data with the application’s name.
+ *
+ * This interface should not be used for more general-purpose data which could
+ * be better represented with a type-safe interface implemented in libfolks.
+ * It must not be used for data which is already represented with a type-safe
+ * interface in libfolks.
+ *
+ * A good example of data which could be stored on this interface is an e-mail
+ * application’s setting of whether a content prefers to receive HTML or
+ * plaintext e-mail.
+ *
+ * A good example of data which should not be stored on this interface is a
+ * contact’s anniversary. That should be added in a separate interface in
+ * libfolks.
+ *
+ * @since UNRELEASED
+ */
+public interface Folks.ExtendedInfo : Object
+{
+  /**
+   * Retrieve the value for an arbitrary field.
+   *
+   * @return The value of the extended field, which may be empty, or `null` if
+   *   the field is not set
+   *
+   * @since UNRELEASED
+   */
+  public abstract ExtendedFieldDetails? get_extended_field (string name);
+
+  /**
+   * Change the value of an arbitrary field.
+   *
+   * @param name name of the arbitrary field to change value
+   * @param value new value for the arbitrary field
+   * @throws PropertyError if setting the value failed
+   *
+   * @since UNRELEASED
+   */
+  public virtual async void change_extended_field (
+      string name, ExtendedFieldDetails value) throws PropertyError
+    {
+      /* Default implementation */
+      throw new PropertyError.NOT_WRITEABLE (
+          _("Extended fields are not writeable on this contact."));
+    }
+
+  /**
+   * Remove an arbitrary field.
+   *
+   * @param name name of the arbitrary field to remove
+   * @throws PropertyError if removing the property failed
+   *
+   * @since UNRELEASED
+   */
+  public virtual async void remove_extended_field (string name)
+      throws PropertyError
+    {
+      /* Default implementation */
+      throw new PropertyError.NOT_WRITEABLE (
+          _("Extended fields are not writeable on this contact."));
+    }
+}
diff --git a/folks/individual.vala b/folks/individual.vala
index d090cbd..0681e54 100644
--- a/folks/individual.vala
+++ b/folks/individual.vala
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 Collabora Ltd.
+ * Copyright (C) 2010, 2015 Collabora Ltd.
  * Copyright (C) 2011, 2013 Philip Withnall
  *
  * This library is free software: you can redistribute it and/or modify
@@ -86,6 +86,7 @@ public class Folks.Individual : Object,
     AvatarDetails,
     BirthdayDetails,
     EmailDetails,
+    ExtendedInfo,
     FavouriteDetails,
     GenderDetails,
     GroupDetails,
@@ -984,6 +985,122 @@ public class Folks.Individual : Object,
     }
 
   /**
+   * { inheritDoc}
+   *
+   * @since UNRELEASED
+   */
+  public ExtendedFieldDetails? get_extended_field (string name)
+    {
+      debug ("Getting extended field '%s' on '%s'…", name, this.id);
+
+      /* Try to get it from the writeable Personas which have "extended-info"
+       * as a writeable property. */
+      foreach (var p in this._persona_set)
+        {
+          if ("extended-info" in p.writeable_properties)
+            {
+              var e = p as ExtendedInfo;
+              return e.get_extended_field (name);
+            }
+        }
+
+      return null;
+    }
+
+  /**
+   * { inheritDoc}
+   *
+   * @since UNRELEASED
+   */
+  public async void change_extended_field (
+      string name, ExtendedFieldDetails value) throws PropertyError
+    {
+      debug ("Setting extended field '%s' on '%s'…", name, this.id);
+
+      PropertyError? persona_error = null;
+      var prop_changed = false;
+
+      /* Try to write it to only the writeable Personas which have "extended-info"
+       * as a writeable property. */
+      foreach (var p in this._persona_set)
+        {
+          if ("extended-info" in p.writeable_properties)
+            {
+              var e = p as ExtendedInfo;
+              try
+                {
+                  yield e.change_extended_field (name, value);
+                  debug ("    written to writeable persona '%s'", p.uid);
+                  prop_changed = true;
+                }
+              catch (PropertyError e)
+                {
+                  /* Store the first error so we can throw it if setting the
+                   * extended field fails on every other persona. */
+                  if (persona_error == null)
+                    {
+                      persona_error = e;
+                    }
+                }
+            }
+        }
+
+      /* Failure? Changing the property failed on every suitable persona found
+       * (and potentially zero suitable personas were found). */
+      if (prop_changed == false)
+        {
+          if (persona_error == null)
+            {
+              persona_error = new PropertyError.NOT_WRITEABLE (
+                  _("Failed to change property ‘%s’: No suitable personas were found."),
+                  "extended-info");
+            }
+
+          throw persona_error;
+        }
+    }
+
+  /**
+   * { inheritDoc}
+   *
+   * @since UNRELEASED
+   */
+  public async void remove_extended_field (string name) throws PropertyError
+    {
+      debug ("Removing extended field '%s' on '%s'…", name, this.id);
+
+      PropertyError? persona_error = null;
+
+      /* Try to remove it from all writeable Personas. */
+      foreach (var p in this._persona_set)
+        {
+          if ("extended-info" in p.writeable_properties)
+            {
+              var e = p as ExtendedInfo;
+              try
+                {
+                  yield e.remove_extended_field (name);
+                  debug ("    removed from writeable persona '%s'", p.uid);
+                }
+              catch (PropertyError e)
+                {
+                  /* Store the first error so we can throw it later. */
+                  if (persona_error == null)
+                    {
+                      persona_error = e;
+                    }
+                }
+            }
+        }
+
+      /* Failure? */
+      if (persona_error != null)
+        {
+          throw persona_error;
+        }
+    }
+
+  /**
    * The set of { link Persona}s encapsulated by this Individual.
    *
    * There must always be at least one Persona in this set.
diff --git a/folks/persona-store.vala b/folks/persona-store.vala
index 400e3d7..bf437f1 100644
--- a/folks/persona-store.vala
+++ b/folks/persona-store.vala
@@ -312,6 +312,13 @@ public enum Folks.PersonaDetail
    * @since 0.7.3
    */
   ANTI_LINKS,
+
+  /**
+   * Field for { link ExtendedFieldDetails}.
+   *
+   * @since UNRELEASED
+   */
+  EXTENDED_INFO,
 }
 
 /**
@@ -372,7 +379,8 @@ public abstract class Folks.PersonaStore : Object
     "last-im-interaction-datetime",
     "call-interaction-count",
     "last-call-interaction-datetime",
-    "anti-links"
+    "anti-links",
+    "extended-info"
   };
 
   /**
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8d8e71a..17ab450 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -17,6 +17,7 @@ folks/avatar-details.vala
 folks/backend-store.vala
 folks/birthday-details.vala
 folks/email-details.vala
+folks/extended-info.vala
 folks/favourite-details.vala
 folks/gender-details.vala
 folks/group-details.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index d7a74b0..cb7fa2a 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -16,6 +16,7 @@ folks/avatar-details.c
 folks/backend-store.c
 folks/birthday-details.c
 folks/email-details.c
+folks/extended-info.c
 folks/favourite-details.c
 folks/gender-details.c
 folks/group-details.c


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