Commit 5fa28ed5 authored by Travis Reitter's avatar Travis Reitter
Browse files

Support vCard-like parameters for IM addresses

Helps: bug#653680 - Change ImDetails.im_addresses to support vCard-like
arbitrary parameters
parent 961e0ff5
......@@ -32,6 +32,8 @@ Bugs fixed:
* Bug 653619 — Add company details
* Bug 653679 — Change PostalAddressDetails.postal_addresses to support
vCard-like arbitrary parameters
* Bug 653680 — Change ImDetails.im_addresses to support vCard-like arbitrary
parameters
API changes:
* Swf.Persona retains and exposes its libsocialweb Contact
......@@ -58,6 +60,7 @@ API changes:
* Add parameters as an optional argument for the FieldDetails constructor
* Use PostalAddressFieldDetails for PostalAddressDetails.postal_addresses
* Remove PostalAddress.types (obsoleted by PostalAddressFieldDetails.parameters)
* Add and use ImFieldDetails for ImDetails.im_addresses
Overview of changes from libfolks 0.5.1 to libfolks 0.5.2
=========================================================
......
......@@ -249,8 +249,8 @@ public class Edsf.PersonaStore : Folks.PersonaStore
else if (k == Folks.PersonaStore.detail_key (
PersonaDetail.IM_ADDRESSES))
{
var im_addresses = (MultiMap<string, string>) v.get_object ();
yield this._set_contact_im_addrs (contact, im_addresses);
var im_fds = (MultiMap<string, ImFieldDetails>) v.get_object ();
yield this._set_contact_im_fds (contact, im_fds);
}
else if (k == Folks.PersonaStore.detail_key (
PersonaDetail.PHONE_NUMBERS))
......@@ -988,16 +988,16 @@ public class Edsf.PersonaStore : Folks.PersonaStore
contact.set (E.Contact.field_id ("name"), contact_name);
}
internal async void _set_im_addrs (Edsf.Persona persona,
MultiMap<string, string> im_addrs)
internal async void _set_im_fds (Edsf.Persona persona,
MultiMap<string, ImFieldDetails> im_fds)
{
if (Utils.multi_map_str_str_equal (persona.im_addresses, im_addrs))
if (Utils.multi_map_str_afd_equal (persona.im_addresses, im_fds))
return;
try
{
E.Contact contact = ((Edsf.Persona) persona).contact;
yield this._set_contact_im_addrs (contact, im_addrs);
yield this._set_contact_im_fds (contact, im_fds);
yield this._addressbook.modify_contact (contact);
}
catch (GLib.Error error)
......@@ -1007,8 +1007,8 @@ public class Edsf.PersonaStore : Folks.PersonaStore
}
/* TODO: this could be smarter & more efficient. */
private async void _set_contact_im_addrs (E.Contact contact,
MultiMap<string, string> im_addrs)
private async void _set_contact_im_fds (E.Contact contact,
MultiMap<string, ImFieldDetails> im_fds)
{
var im_eds_map = Edsf.Persona._get_im_eds_map ();
......@@ -1022,16 +1022,16 @@ public class Edsf.PersonaStore : Folks.PersonaStore
}
}
foreach (var proto in im_addrs.get_keys ())
foreach (var proto in im_fds.get_keys ())
{
var attributes = new GLib.List <E.VCardAttribute>();
var attrib_name = ("X-" + proto).up ();
bool added = false;
foreach (var im in im_addrs.get (proto))
foreach (var im_fd in im_fds.get (proto))
{
var attr_n = new E.VCardAttribute (null, attrib_name);
attr_n.add_value (im);
attr_n.add_value (im_fd.value);
attributes.prepend (attr_n);
added = true;
}
......
......@@ -353,20 +353,22 @@ public class Edsf.Persona : Folks.Persona,
}
}
private HashMultiMap<string, string> _im_addresses =
new HashMultiMap<string, string> ();
private HashMultiMap<string, ImFieldDetails> _im_addresses =
new HashMultiMap<string, ImFieldDetails> (null, null,
(GLib.HashFunc) ImFieldDetails.hash,
(GLib.EqualFunc) ImFieldDetails.equal);
/**
* {@inheritDoc}
*
* @since 0.5.UNRELEASED
*/
public MultiMap<string, string> im_addresses
public MultiMap<string, ImFieldDetails> im_addresses
{
get { return this._im_addresses; }
set
{
((Edsf.PersonaStore) this.store)._set_im_addrs (this, value);
((Edsf.PersonaStore) this.store)._set_im_fds (this, value);
}
}
......@@ -515,10 +517,10 @@ public class Edsf.Persona : Folks.Persona,
{
foreach (var protocol in this._im_addresses.get_keys ())
{
var im_addresses = this._im_addresses.get (protocol);
var im_fds = this._im_addresses.get (protocol);
foreach (string address in im_addresses)
callback (protocol + ":" + address);
foreach (var im_fd in im_fds)
callback (protocol + ":" + im_fd.value);
}
}
else if (prop_name == "local-ids")
......@@ -805,18 +807,19 @@ public class Edsf.Persona : Folks.Persona,
var im_eds_map = this._get_im_eds_map ();
this._im_addresses.clear ();
foreach (var proto in im_eds_map.get_keys ())
foreach (var im_proto in im_eds_map.get_keys ())
{
var addresses = this.contact.get_attributes (
im_eds_map.lookup (proto));
im_eds_map.lookup (im_proto));
foreach (var attr in addresses)
{
try
{
var addr = attr.get_value ();
string address = (owned) ImDetails.normalise_im_address (addr,
proto);
this._im_addresses.set (proto, address);
string normalised_addr =
(owned) ImDetails.normalise_im_address (addr, im_proto);
var im_fd = new ImFieldDetails (normalised_addr);
this._im_addresses.set (im_proto, im_fd);
}
catch (Folks.ImDetailsError e)
{
......
......@@ -306,9 +306,9 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
{
unowned Value? val = details.lookup (Folks.PersonaStore.detail_key (
PersonaDetail.IM_ADDRESSES));
MultiMap<string, string> im_addresses
MultiMap<string, ImFieldDetails> im_addresses
= val != null
? (MultiMap<string, string>) val.get_object ()
? (MultiMap<string, ImFieldDetails>) val.get_object ()
: null;
unowned Value? val2 = details.lookup
(this.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES));
......
......@@ -34,7 +34,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
WebServiceDetails
{
private unowned GLib.KeyFile _key_file;
private HashMultiMap<string, string> _im_addresses;
private HashMultiMap<string, ImFieldDetails> _im_addresses;
private HashMultiMap<string, string> _web_service_addresses;
private string _alias;
private const string[] _linkable_properties =
......@@ -93,7 +93,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
/**
* {@inheritDoc}
*/
public MultiMap<string, string> im_addresses
public MultiMap<string, ImFieldDetails> im_addresses
{
get
{ return this._im_addresses; }
......@@ -116,20 +116,23 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
/* Add the new IM addresses to the key file and build a normalised
* table of them to set as the new property value */
var im_addresses = new HashMultiMap<string, string> ();
var im_addresses = new HashMultiMap<string, ImFieldDetails> (
null, null,
(GLib.HashFunc) ImFieldDetails.hash,
(GLib.EqualFunc) ImFieldDetails.equal);
foreach (var protocol in value.get_keys ())
{
var addresses = value.get (protocol);
var normalised_addresses = new HashSet<string> ();
foreach (string address in addresses)
foreach (var im_fd in addresses)
{
string normalised_address;
try
{
normalised_address = ImDetails.normalise_im_address (
address, protocol);
im_fd.value, protocol);
}
catch (ImDetailsError e)
{
......@@ -141,7 +144,8 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
}
normalised_addresses.add (normalised_address);
im_addresses.set (protocol, normalised_address);
var new_im_fd = new ImFieldDetails (normalised_address);
im_addresses.set (protocol, new_im_fd);
}
string[] addrs = (string[]) normalised_addresses.to_array ();
......@@ -229,7 +233,8 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
id);
this._key_file = key_file;
this._im_addresses = new HashMultiMap<string, string> ();
this._im_addresses = new HashMultiMap<string, ImFieldDetails> (
null, null, ImFieldDetails.hash, (EqualFunc) ImFieldDetails.equal);
this._web_service_addresses = new HashMultiMap<string, string> ();
/* Load the IM addresses from the key file */
......@@ -285,7 +290,8 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
continue;
}
this._im_addresses.set (protocol, address);
var im_fd = new ImFieldDetails (address);
this._im_addresses.set (protocol, im_fd);
}
}
}
......@@ -315,8 +321,8 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
{
var im_addresses = this._im_addresses.get (protocol);
foreach (string address in im_addresses)
callback (protocol + ":" + address);
foreach (var im_fd in im_addresses)
callback (protocol + ":" + im_fd.value);
}
}
else if (prop_name == "web-service-addresses")
......
......@@ -116,8 +116,9 @@ public class Swf.Persona : Folks.Persona,
}
}
private HashMultiMap<string, string> _im_addresses =
new HashMultiMap<string, string> ();
private HashMultiMap<string, ImFieldDetails> _im_addresses =
new HashMultiMap<string, ImFieldDetails> (null, null,
ImFieldDetails.hash, (EqualFunc) ImFieldDetails.equal);
private HashMultiMap<string, string> _web_service_addresses =
new HashMultiMap<string, string> ();
......@@ -125,7 +126,7 @@ public class Swf.Persona : Folks.Persona,
/**
* {@inheritDoc}
*/
public MultiMap<string, string> im_addresses
public MultiMap<string, ImFieldDetails> im_addresses
{
get { return this._im_addresses; }
private set {}
......@@ -239,8 +240,9 @@ public class Swf.Persona : Folks.Persona,
var normalised_addr = (owned) normalise_im_address
((owned) facebook_jid_copy, "jabber");
string im_proto = "jabber";
var im_fd = new ImFieldDetails (normalised_addr);
this._im_addresses.set (im_proto, normalised_addr);
this._im_addresses.set (im_proto, im_fd);
}
catch (ImDetailsError e)
{
......
......@@ -39,7 +39,7 @@ public class Tpf.Persona : Folks.Persona,
private Set<string> _groups_ro;
private bool _is_favourite;
private string _alias;
private HashMultiMap<string, string> _im_addresses;
private HashMultiMap<string, ImFieldDetails> _im_addresses;
private const string[] _linkable_properties = { "im-addresses" };
private const string[] _writeable_properties =
{
......@@ -163,7 +163,7 @@ public class Tpf.Persona : Folks.Persona,
*
* See {@link Folks.ImDetails.im_addresses}.
*/
public MultiMap<string, string> im_addresses
public MultiMap<string, ImFieldDetails> im_addresses
{
get { return this._im_addresses; }
private set {}
......@@ -287,12 +287,17 @@ public class Tpf.Persona : Folks.Persona,
this._is_constructed = true;
/* Set our single IM address */
this._im_addresses = new HashMultiMap<string, string> ();
this._im_addresses = new HashMultiMap<string, ImFieldDetails> (
null, null,
(GLib.HashFunc) ImFieldDetails.hash,
(GLib.EqualFunc) ImFieldDetails.equal);
try
{
this._im_addresses.set (account.get_protocol (),
ImDetails.normalise_im_address (id, account.get_protocol ()));
var im_addr = ImDetails.normalise_im_address (id,
account.get_protocol ());
var im_fd = new ImFieldDetails (im_addr);
this._im_addresses.set (account.get_protocol (), im_fd);
}
catch (ImDetailsError e)
{
......@@ -389,8 +394,12 @@ public class Tpf.Persona : Folks.Persona,
debug ("Creating new Tpf.Persona '%s' from cache: %p", uid, this);
// IM addresses
this._im_addresses = new HashMultiMap<string, string> ();
this._im_addresses.set (protocol, im_address);
this._im_addresses = new HashMultiMap<string, ImFieldDetails> (null, null,
(GLib.HashFunc) ImFieldDetails.hash,
(GLib.EqualFunc) ImFieldDetails.equal);
var im_fd = new ImFieldDetails (im_address);
this._im_addresses.set (protocol, im_fd);
// Groups
this._groups = groups;
......
......@@ -499,14 +499,15 @@ public class Trf.PersonaStore : Folks.PersonaStore
else if (k == Folks.PersonaStore.detail_key (
PersonaDetail.IM_ADDRESSES))
{
var im_addresses = (MultiMap<string, string>) v.get_object ();
var im_addresses =
(MultiMap<string, ImFieldDetails>) v.get_object ();
int im_cnt = 0;
foreach (var proto in im_addresses.get_keys ())
{
var addrs_a = im_addresses.get (proto);
foreach (var addr in addrs_a)
foreach (var im_fd in addrs_a)
{
var im_affl = "_:im_affl%d".printf (im_cnt);
var im = "_:im%d".printf (im_cnt);
......@@ -515,7 +516,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
builder.predicate ("a");
builder.object (Trf.OntologyDefs.NCO_IMADDRESS);
builder.predicate (Trf.OntologyDefs.NCO_IMID);
builder.object_string (addr);
builder.object_string (im_fd.value);
builder.predicate (Trf.OntologyDefs.NCO_IMPROTOCOL);
builder.object_string (proto);
......@@ -2000,21 +2001,17 @@ public class Trf.PersonaStore : Folks.PersonaStore
}
internal async void _set_im_addresses (Folks.Persona persona,
MultiMap<string, string> im_addresses)
MultiMap<string, ImFieldDetails> im_addresses)
{
/* FIXME:
* - this conversion should go away once we've switched to use the
* same data structure for each property that is a list of something.
* See: https://bugzilla.gnome.org/show_bug.cgi?id=646079 */
var ims = new HashSet<FieldDetails> ();
var ims = new HashSet<ImFieldDetails> ();
foreach (var proto in im_addresses.get_keys ())
{
var addrs = im_addresses.get (proto);
foreach (var a in addrs)
foreach (var im_fd in addrs)
{
var fd = new FieldDetails (a);
fd.set_parameter ("proto", proto);
ims.add (fd);
var new_im_fd = new ImFieldDetails (im_fd.value);
new_im_fd.set_parameter ("proto", proto);
ims.add (new_im_fd);
}
}
......@@ -2395,13 +2392,13 @@ public class Trf.PersonaStore : Folks.PersonaStore
break;
case Trf.Attrib.IM_ADDRESSES:
default:
fd = (FieldDetails) p;
fd = (ImFieldDetails) p;
attr = "_:p%d".printf (i);
builder.subject (attr);
builder.predicate ("a");
builder.object (related_attrib);
builder.predicate (related_prop);
builder.object_string (fd.value);
builder.object_string (((ImFieldDetails) fd).value);
if (what == Trf.Attrib.IM_ADDRESSES)
{
......
......@@ -277,13 +277,15 @@ public class Trf.Persona : Folks.Persona,
private HashMap<string, HashMap<string, string>> _tracker_ids_ims =
new HashMap<string, HashMap<string, string>> ();
private HashMultiMap<string, string> _im_addresses =
new HashMultiMap<string, string> ();
private HashMultiMap<string, ImFieldDetails> _im_addresses =
new HashMultiMap<string, ImFieldDetails> (null, null,
(GLib.HashFunc) ImFieldDetails.hash,
(GLib.EqualFunc) ImFieldDetails.equal);
/**
* {@inheritDoc}
*/
public MultiMap<string, string> im_addresses
public MultiMap<string, ImFieldDetails> im_addresses
{
get { return this._im_addresses; }
public set
......@@ -450,10 +452,10 @@ public class Trf.Persona : Folks.Persona,
{
foreach (var protocol in this._im_addresses.get_keys ())
{
var im_addresses = this._im_addresses.get (protocol);
var im_fds = this._im_addresses.get (protocol);
foreach (string address in im_addresses)
callback (protocol + ":" + address);
foreach (var im_fd in im_fds)
callback (protocol + ":" + im_fd.value);
}
}
else if (prop_name == "local-ids")
......@@ -910,8 +912,9 @@ public class Trf.Persona : Folks.Persona,
var account_id_copy = account_id.dup ();
var normalised_addr = (owned) normalise_im_address
((owned) account_id_copy, im_proto);
var im_fd = new ImFieldDetails (normalised_addr);
this._im_addresses.set (im_proto, normalised_addr);
this._im_addresses.set (im_proto, im_fd);
var im_proto_map = new HashMap<string, string> ();
im_proto_map.set (im_proto, account_id);
......@@ -948,8 +951,9 @@ public class Trf.Persona : Folks.Persona,
break;
}
var im_fd = new ImFieldDetails (im_addr);
if (proto != null && im_addr != null &&
this._im_addresses.remove (proto, im_addr))
this._im_addresses.remove (proto, im_fd))
{
this._tracker_ids_ims.unset (tracker_id);
if (notify)
......
......@@ -32,6 +32,57 @@ public errordomain Folks.ImDetailsError
INVALID_IM_ADDRESS
}
/**
* Object representing an IM address value that can have some parameters
* associated with it.
*
* See {@link Folks.AbstractFieldDetails}.
*
* @since UNRELEASED
*/
public class Folks.ImFieldDetails : AbstractFieldDetails<string>
{
/**
* Create a new ImFieldDetails.
*
* @param value the value of the field
* @param parameters initial parameters. See
* {@link AbstractFieldDetails.parameters}. A `null` value is equivalent to an
* empty map of parameters.
*
* @return a new ImFieldDetails
*
* @since UNRELEASED
*/
public ImFieldDetails (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<string> (that);
}
/**
* {@inheritDoc}
*
* @since UNRELEASED
*/
public override uint hash ()
{
return base.hash ();
}
}
/**
* IM addresses exposed by an object implementing {@link PresenceDetails}.
*
......@@ -53,7 +104,7 @@ public interface Folks.ImDetails : Object
*
* @since 0.5.1
*/
public abstract MultiMap<string, string> im_addresses
public abstract MultiMap<string, ImFieldDetails> im_addresses
{
get; set;
}
......
......@@ -63,6 +63,7 @@ public class Folks.IndividualAggregator : Object
private HashTable<string, Individual> _link_map;
private bool _linking_enabled = true;
private bool _is_prepared = false;
private bool _prepare_pending = false;
private Debug _debug;
private string _configured_writeable_store_type_id;
private string _configured_writeable_store_id;
......@@ -341,10 +342,12 @@ public class Folks.IndividualAggregator : Object
lock (this._is_prepared)
{
if (!this._is_prepared)
if (!this._is_prepared && !this._prepare_pending)
{
this._prepare_pending = true;
yield this._backend_store.load_backends ();
this._is_prepared = true;
this._prepare_pending = false;
this.notify_property ("is-prepared");
}
}
......@@ -1149,7 +1152,7 @@ public class Folks.IndividualAggregator : Object
this._configured_writeable_store_type_id);
/* `protocols_addrs_set` will be passed to the new Kf.Persona */
var protocols_addrs_set = new HashMultiMap<string, string> ();
var protocols_addrs_set = new HashMultiMap<string, ImFieldDetails> ();
var web_service_addrs_set = new HashMultiMap<string, string> ();
/* List of local_ids */
......
......@@ -96,7 +96,7 @@ public class Folks.Individual : Object,
/* The number of Personas in this Individual which have
* Persona.is_user == true. Iff this is > 0, Individual.is_user == true. */
private uint _persona_user_count = 0;
private HashMultiMap<string, string> _im_addresses;
private HashMultiMap<string, ImFieldDetails> _im_addresses;
private HashMultiMap<string, string> _web_service_addresses;
private string _nickname = "";
......@@ -497,7 +497,7 @@ public class Folks.Individual : Object,
/**
* {@inheritDoc}
*/
public MultiMap<string, string> im_addresses
public MultiMap<string, ImFieldDetails> im_addresses
{
get { return this._im_addresses; }
private set {}
......@@ -687,7 +687,8 @@ public class Folks.Individual : Object,
debug ("Creating new Individual with %u Personas: %p",
(personas != null ? personas.size : 0), this);
this._im_addresses = new HashMultiMap<string, string> ();
this._im_addresses = new HashMultiMap<string, ImFieldDetails> (
null, null, ImFieldDetails.hash, (EqualFunc) ImFieldDetails.equal);
this._web_service_addresses = new HashMultiMap<string, string> ();
this._persona_set =
new HashSet<Persona> (direct_hash, direct_equal);
......
......@@ -170,11 +170,11 @@ public class AddPersonaTests : Folks.TestCase
details.insert (Folks.PersonaStore.detail_key (PersonaDetail.AVATAR),
(owned) v3);
Value? v4 = Value (typeof (MultiMap<string, string>));
var im_addrs = new HashMultiMap<string, string> ();
im_addrs.set ("jabber", this._im_addr_1);
im_addrs.set ("yahoo", this._im_addr_2);
v4.set_object (im_addrs);
Value? v4 = Value (typeof (MultiMap<string, ImFieldDetails>));
var im_fds = new HashMultiMap<string, ImFieldDetails> ();
im_fds.set ("jabber", new ImFieldDetails (this._im_addr_1));
im_fds.set ("yahoo", new ImFieldDetails (this._im_addr_2));
v4.set_object (im_fds);
details.insert (
Folks.PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES), v4);
......@@ -312,12 +312,12 @@ public class AddPersonaTests : Folks.TestCase
foreach (var proto in i.im_addresses.get_keys ())