2
* Copyright (C) 2011 Collabora Ltd.
4
* This library is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser General Public License as published by
6
* the Free Software Foundation, either version 2.1 of the License, or
7
* (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public License
15
* along with this library. If not, see <http://www.gnu.org/licenses/>.
18
* Travis Reitter <travis.reitter@collabora.co.uk>
19
* Philip Withnall <philip.withnall@collabora.co.uk>
20
* Marco Barisione <marco.barisione@collabora.co.uk>
21
* Raul Gutierrez Segales <raul.gutierrez.segales@collabora.co.uk>
30
extern const string BACKEND_NAME;
32
internal enum Trf.Fields
57
internal enum Trf.AfflInfoFields
71
AFFL_ADDRESS_LOCATION,
72
AFFL_EXTENDED_ADDRESS,
83
internal enum Trf.PostalAddressFields
98
internal enum Trf.UrlsFields
106
internal enum Trf.RoleFields
113
internal enum Trf.IMFields
121
internal enum Trf.PhoneFields
127
internal enum Trf.EmailFields
133
internal enum Trf.TagFields
138
private enum Trf.Attrib
147
private const char _REMOVE_ALL_ATTRIBS = 0x01;
148
private const char _REMOVE_PHONES = 0x02;
149
private const char _REMOVE_POSTALS = 0x04;
150
private const char _REMOVE_IM_ADDRS = 0x08;
151
private const char _REMOVE_EMAILS = 0x10;
155
* It will create {@link Persona}s for each contacts on the main addressbook.
157
public class Trf.PersonaStore : Folks.PersonaStore
159
private const string _LOCAL_ID_PROPERTY_NAME = "folks-linking-ids";
160
private const string _WSD_PROPERTY_NAME = "folks-linking-ws-addrs";
161
private const string _OBJECT_NAME = "org.freedesktop.Tracker1";
162
private const string _OBJECT_IFACE = "org.freedesktop.Tracker1.Resources";
163
private const string _OBJECT_PATH = "/org/freedesktop/Tracker1/Resources";
164
private HashMap<string, Persona> _personas;
165
private Map<string, Persona> _personas_ro;
166
private bool _is_prepared = false;
167
private static const int _default_timeout = 100;
168
private Resources _resources_object;
169
private Tracker.Sparql.Connection _connection;
170
private static Gee.TreeMap<string, string> _urn_prefix = null;
171
private static Gee.TreeMap<string, int> _prefix_tracker_id = null;
172
private static const string _INITIAL_QUERY =
174
"tracker:id(?_contact) " +
175
"nco:fullname(?_contact) " +
176
"nco:nameFamily(?_contact) " +
177
"nco:nameGiven(?_contact) " +
178
"nco:nameAdditional(?_contact) " +
179
"nco:nameHonorificPrefix(?_contact) " +
180
"nco:nameHonorificSuffix(?_contact) " +
181
"nco:nickname(?_contact) " +
182
"nco:birthDate(?_contact) " +
183
"nie:url(nco:photo(?_contact)) " +
185
/* keep synced with Trf.IMFields */
188
" fn:concat(tracker:id(?affl),'\t'," +
189
" tracker:coalesce(nco:imProtocol(?a),''), " +
190
"'\t', tracker:coalesce(nco:imID(?a),''), '\t'," +
191
" tracker:coalesce(nco:imNickname(?a),'')), '\\n') " +
192
"WHERE { ?_contact nco:hasAffiliation ?affl. " +
193
" ?affl nco:hasIMAddress ?a } ) " +
195
/* keep synced with Trf.PhoneFields */
198
" (fn:concat(tracker:id(?affl),'\t', " +
199
" nco:phoneNumber(?aff_number)), " +
201
"WHERE { ?_contact nco:hasAffiliation ?affl . " +
202
" ?affl nco:hasPhoneNumber ?aff_number } ) " +
204
/* keep synced with Trf.EmailFields */
207
" (fn:concat(tracker:id(?affl), '\t', " +
208
" nco:emailAddress(?emailaddress)), " +
210
"WHERE { ?_contact nco:hasAffiliation ?affl . " +
211
" ?affl nco:hasEmailAddress ?emailaddress }) " +
213
/* keep synced with Trf.UrlsFields */
216
" (fn:concat(tracker:id(?affl), '\t'," +
217
" tracker:coalesce(nco:blogUrl(?affl),'')," +
219
" tracker:coalesce(nco:websiteUrl(?affl),'')" +
221
" tracker:coalesce(nco:url(?affl),''))," +
223
"WHERE { ?_contact nco:hasAffiliation ?affl } )" +
225
/* keep synced with Trf.TagFields */
227
"GROUP_CONCAT(tracker:id(?_tag), " +
229
"WHERE { ?_contact nao:hasTag " +
234
/* keep synced with Trf.RoleFields */
237
" (fn:concat(tracker:id(?affl), '\t', " +
238
" tracker:coalesce(nco:role(?affl),''), '\t', " +
239
" tracker:coalesce(nco:department(?affl),'')), " +
241
"WHERE { ?_contact nco:hasAffiliation " +
244
"nco:note(?_contact) " +
245
"tracker:id(nco:gender(?_contact)) " +
247
/* keep synced with Trf.PostalAddressFields*/
250
" (fn:concat(tracker:id(?affl), '\t', " +
251
" tracker:coalesce(nco:pobox(?postal),'')" +
253
" tracker:coalesce(nco:district(?postal),'')" +
255
" tracker:coalesce(nco:county(?postal),'')" +
257
" tracker:coalesce(nco:locality(?postal),'')" +
259
" tracker:coalesce(nco:postalcode(?postal),'')" +
261
" tracker:coalesce(nco:streetAddress(?postal)" +
263
" tracker:coalesce(nco:addressLocation(?postal)" +
265
" tracker:coalesce(nco:extendedAddress(?postal)" +
267
" tracker:coalesce(nco:country(?postal),'')" +
269
" tracker:coalesce(nco:region(?postal),'')), " +
271
"WHERE { ?_contact nco:hasAffiliation " +
272
"?affl . ?affl nco:hasPostalAddress ?postal }) " +
274
/* Linking between Trf.Personas */
279
"WHERE { ?_contact nao:hasProperty ?prop . " +
280
" ?prop nao:propertyName ?prop_name . " +
281
" ?prop nao:propertyValue ?prop_value . " +
282
" FILTER (?prop_name = 'folks-linking-ids') } ) " +
284
"{ ?_contact a nco:PersonContact . %s } " +
285
"ORDER BY tracker:id(?_contact) ";
289
* The type of persona store this is.
291
* See {@link Folks.PersonaStore.type_id}.
293
public override string type_id { get { return BACKEND_NAME; } }
296
* Whether this PersonaStore can add {@link Folks.Persona}s.
298
* See {@link Folks.PersonaStore.can_add_personas}.
302
public override MaybeBool can_add_personas
304
get { return MaybeBool.TRUE; }
308
* Whether this PersonaStore can set the alias of {@link Folks.Persona}s.
310
* See {@link Folks.PersonaStore.can_alias_personas}.
314
public override MaybeBool can_alias_personas
316
get { return MaybeBool.FALSE; }
320
* Whether this PersonaStore can set the groups of {@link Folks.Persona}s.
322
* See {@link Folks.PersonaStore.can_group_personas}.
326
public override MaybeBool can_group_personas
328
get { return MaybeBool.FALSE; }
332
* Whether this PersonaStore can remove {@link Folks.Persona}s.
334
* See {@link Folks.PersonaStore.can_remove_personas}.
338
public override MaybeBool can_remove_personas
340
get { return MaybeBool.TRUE; }
344
* Whether this PersonaStore has been prepared.
346
* See {@link Folks.PersonaStore.is_prepared}.
350
public override bool is_prepared
352
get { return this._is_prepared; }
356
* The {@link Persona}s exposed by this PersonaStore.
358
* See {@link Folks.PersonaStore.personas}.
360
public override Map<string, Persona> personas
362
get { return this._personas_ro; }
366
* Create a new PersonaStore.
368
* Create a new persona store to store the {@link Persona}s for the contacts
370
public PersonaStore ()
372
Object (id: BACKEND_NAME, display_name: BACKEND_NAME);
373
this._personas = new HashMap<string, Persona> ();
374
this._personas_ro = this._personas.read_only_view;
375
debug ("Initial query : \n%s\n", this._INITIAL_QUERY);
379
* Add a new {@link Persona} to the PersonaStore.
381
* Accepted keys for `details` are:
382
* - PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES)
383
* - PersonaStore.detail_key (PersonaDetail.ALIAS)
384
* - PersonaStore.detail_key (PersonaDetail.FULL_NAME)
385
* - PersonaStore.detail_key (PersonaDetail.IS_FAVOURITE)
386
* - PersonaStore.detail_key (PersonaDetail.STRUCTURED_NAME)
387
* - PersonaStore.detail_key (PersonaDetail.AVATAR)
388
* - PersonaStore.detail_key (PersonaDetail.BIRTHDAY)
389
* - PersonaStore.detail_key (PersonaDetail.GENDER)
390
* - PersonaStore.detail_key (PersonaDetail.EMAIL_ADDRESSES)
391
* - PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES)
392
* - PersonaStore.detail_key (PersonaDetail.NOTES)
393
* - PersonaStore.detail_key (PersonaDetail.PHONE_NUMBERS)
394
* - PersonaStore.detail_key (PersonaDetail.POSTAL_ADDRESSES)
395
* - PersonaStore.detail_key (PersonaDetail.ROLES)
396
* - PersonaStore.detail_key (PersonaDetail.URL)
397
* - PersonaStore.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES)
399
* See {@link Folks.PersonaStore.add_persona_from_details}.
401
public override async Folks.Persona? add_persona_from_details (
402
HashTable<string, Value?> details) throws Folks.PersonaStoreError
404
var builder = new Tracker.Sparql.Builder.update ();
405
builder.insert_open (null);
406
builder.subject ("_:p");
407
builder.predicate ("a");
408
builder.object ("nco:PersonContact");
410
foreach (var k in details.get_keys ())
412
Value? v = details.lookup (k);
413
if (k == Folks.PersonaStore.detail_key (PersonaDetail.ALIAS))
415
builder.subject ("_:p");
416
builder.predicate (Trf.OntologyDefs.NCO_NICKNAME);
417
builder.object_string (v.get_string ());
419
else if (k == Folks.PersonaStore.detail_key (
420
PersonaDetail.FULL_NAME))
422
builder.subject ("_:p");
423
builder.predicate (Trf.OntologyDefs.NCO_FULLNAME);
424
builder.object_string (v.get_string ());
426
else if (k == Folks.PersonaStore.detail_key (
427
PersonaDetail.STRUCTURED_NAME))
429
StructuredName sname = (StructuredName) v.get_object ();
430
builder.subject ("_:p");
431
builder.predicate (Trf.OntologyDefs.NCO_FAMILY);
432
builder.object_string (sname.family_name);
433
builder.predicate (Trf.OntologyDefs.NCO_GIVEN);
434
builder.object_string (sname.given_name);
435
builder.predicate (Trf.OntologyDefs.NCO_ADDITIONAL);
436
builder.object_string (sname.additional_names);
437
builder.predicate (Trf.OntologyDefs.NCO_SUFFIX);
438
builder.object_string (sname.suffixes);
439
builder.predicate (Trf.OntologyDefs.NCO_PREFIX);
440
builder.object_string (sname.prefixes);
442
else if (k == Folks.PersonaStore.detail_key (
443
PersonaDetail.IS_FAVOURITE))
445
if (v.get_boolean ())
447
builder.subject ("_:p");
448
builder.predicate (Trf.OntologyDefs.NAO_TAG);
449
builder.object (Trf.OntologyDefs.NAO_FAVORITE);
452
else if (k == Folks.PersonaStore.detail_key (PersonaDetail.AVATAR))
454
var avatar = (File) v.get_object ();
455
builder.subject ("_:photo");
456
builder.predicate ("a");
457
builder.object ("nfo:Image, nie:DataObject");
458
builder.predicate (Trf.OntologyDefs.NIE_URL);
459
builder.object_string (avatar.get_uri ());
460
builder.subject ("_:p");
461
builder.predicate (Trf.OntologyDefs.NCO_PHOTO);
462
builder.object ("_:photo");
464
else if (k == Folks.PersonaStore.detail_key (PersonaDetail.BIRTHDAY))
466
var birthday = (DateTime) v.get_boxed ();
467
builder.subject ("_:p");
468
builder.predicate (Trf.OntologyDefs.NCO_BIRTHDAY);
470
birthday.to_timeval (out tv);
471
builder.object_string (tv.to_iso8601 ());
473
else if (k == Folks.PersonaStore.detail_key (PersonaDetail.GENDER))
475
var gender = (Gender) v.get_enum ();
476
if (gender != Gender.UNSPECIFIED)
478
builder.subject ("_:p");
479
builder.predicate (Trf.OntologyDefs.NCO_GENDER);
480
if (gender == Gender.MALE)
481
builder.object (Trf.OntologyDefs.NCO_MALE);
483
builder.object (Trf.OntologyDefs.NCO_FEMALE);
486
else if (k == Folks.PersonaStore.detail_key (
487
PersonaDetail.EMAIL_ADDRESSES))
489
Set<FieldDetails> email_addresses =
490
(Set<FieldDetails>) v.get_object ();
491
yield this._build_update_query_set (builder, email_addresses,
492
"_:p", Trf.Attrib.EMAILS);
494
else if (k == Folks.PersonaStore.detail_key (
495
PersonaDetail.IM_ADDRESSES))
497
var im_addresses = (MultiMap<string, string>) v.get_object ();
500
foreach (var proto in im_addresses.get_keys ())
502
var addrs_a = im_addresses.get (proto);
504
foreach (var addr in addrs_a)
506
var im_affl = "_:im_affl%d".printf (im_cnt);
507
var im = "_:im%d".printf (im_cnt);
509
builder.subject (im);
510
builder.predicate ("a");
511
builder.object (Trf.OntologyDefs.NCO_IMADDRESS);
512
builder.predicate (Trf.OntologyDefs.NCO_IMID);
513
builder.object_string (addr);
514
builder.predicate (Trf.OntologyDefs.NCO_IMPROTOCOL);
515
builder.object_string (proto);
517
builder.subject (im_affl);
518
builder.predicate ("a");
519
builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
520
builder.predicate (Trf.OntologyDefs.NCO_HAS_IMADDRESS);
523
builder.subject ("_:p");
524
builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
525
builder.object (im_affl);
531
else if (k == Folks.PersonaStore.detail_key (PersonaDetail.NOTES))
533
var notes = (Gee.HashSet<Note>) v.get_object ();
534
foreach (var n in notes)
536
builder.subject ("_:p");
537
builder.predicate (Trf.OntologyDefs.NCO_NOTE);
538
builder.object_string (n.content);
541
else if (k == Folks.PersonaStore.detail_key (
542
PersonaDetail.PHONE_NUMBERS))
544
Set<FieldDetails> phone_numbers =
545
(Set<FieldDetails>) v.get_object ();
546
yield this._build_update_query_set (builder, phone_numbers,
547
"_:p", Trf.Attrib.PHONES);
549
else if (k == Folks.PersonaStore.detail_key (PersonaDetail.ROLES))
551
var roles = (Gee.HashSet<Role>) v.get_object ();
554
foreach (var r in roles)
556
var role_affl = "_:role_affl%d".printf (roles_cnt);
558
builder.subject (role_affl);
559
builder.predicate ("a");
560
builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
561
builder.predicate (Trf.OntologyDefs.NCO_ROLE);
562
builder.object_string (r.title);
563
builder.predicate (Trf.OntologyDefs.NCO_ORG);
564
builder.object_string (r.organisation_name);
566
builder.subject ("_:p");
567
builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
568
builder.object (role_affl);
573
else if (k == Folks.PersonaStore.detail_key (
574
PersonaDetail.POSTAL_ADDRESSES))
576
Set<PostalAddress> postal_addresses =
577
(Set<PostalAddress>) v.get_object ();
580
foreach (var pa in postal_addresses)
582
var postal_affl = "_:postal_affl%d".printf (postal_cnt);
583
var postal = "_:postal%d".printf (postal_cnt);
584
builder.subject (postal);
585
builder.predicate ("a");
586
builder.object (Trf.OntologyDefs.NCO_POSTAL_ADDRESS);
587
builder.predicate (Trf.OntologyDefs.NCO_POBOX);
588
builder.object_string (pa.po_box);
589
builder.predicate (Trf.OntologyDefs.NCO_LOCALITY);
590
builder.object_string (pa.locality);
591
builder.predicate (Trf.OntologyDefs.NCO_POSTALCODE);
592
builder.object_string (pa.postal_code);
593
builder.predicate (Trf.OntologyDefs.NCO_STREET_ADDRESS);
594
builder.object_string (pa.street);
595
builder.predicate (Trf.OntologyDefs.NCO_EXTENDED_ADDRESS);
596
builder.object_string (pa.extension);
597
builder.predicate (Trf.OntologyDefs.NCO_COUNTRY);
598
builder.object_string (pa.country);
599
builder.predicate (Trf.OntologyDefs.NCO_REGION);
600
builder.object_string (pa.region);
602
builder.subject (postal_affl);
603
builder.predicate ("a");
604
builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
605
builder.predicate (Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS);
606
builder.object (postal);
608
builder.subject ("_:p");
609
builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
610
builder.object (postal_affl);
615
else if (k == Folks.PersonaStore.detail_key (PersonaDetail.URLS))
617
Set<FieldDetails> urls = (Set<FieldDetails>) v.get_object ();
620
foreach (var u in urls)
622
var url_affl = "_:url_affl%d".printf (url_cnt);
624
builder.subject (url_affl);
625
builder.predicate ("a");
626
builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
627
builder.predicate (Trf.OntologyDefs.NCO_URL);
628
builder.object_string (u.value);
630
builder.subject ("_:p");
631
builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
632
builder.object (url_affl);
637
else if (k == Folks.PersonaStore.detail_key (PersonaDetail.LOCAL_IDS))
639
var local_ids = (Gee.HashSet<string>) v.get_object ();
640
string ids = Trf.PersonaStore.serialize_local_ids (local_ids);
642
builder.subject ("_:folks_ids");
643
builder.predicate ("a");
644
builder.object (Trf.OntologyDefs.NAO_PROPERTY);
645
builder.predicate (Trf.OntologyDefs.NAO_PROPERTY_NAME);
646
builder.object_string (this._LOCAL_ID_PROPERTY_NAME);
647
builder.predicate (Trf.OntologyDefs.NAO_PROPERTY_VALUE);
648
builder.object_string (ids);
650
builder.subject ("_:p");
651
builder.predicate (Trf.OntologyDefs.NAO_HAS_PROPERTY);
652
builder.object ("_:folks_ids");
655
Folks.PersonaStore.detail_key (
656
PersonaDetail.WEB_SERVICE_ADDRESSES))
658
MultiMap<string, string> ws_obj =
659
(MultiMap<string, string>) v.get_object ();
661
var ws_addrs = Trf.PersonaStore.serialize_web_services (ws_obj);
663
builder.subject ("_:folks_ws_ids");
664
builder.predicate ("a");
665
builder.object (Trf.OntologyDefs.NAO_PROPERTY);
666
builder.predicate (Trf.OntologyDefs.NAO_PROPERTY_NAME);
667
builder.object_string (this._WSD_PROPERTY_NAME);
668
builder.predicate (Trf.OntologyDefs.NAO_PROPERTY_VALUE);
669
builder.object_string (ws_addrs);
671
builder.subject ("_:p");
672
builder.predicate (Trf.OntologyDefs.NAO_HAS_PROPERTY);
673
builder.object ("_:folks_ws_ids");
677
throw new PersonaStoreError.INVALID_ARGUMENT (
678
/* Translators: the first parameter identifies the
679
* persona store and the second the unknown key
680
* that was received with the details params. */
681
_("Unrecognized paramter %s by the %s PersonaStore:\n')"),
685
builder.insert_close ();
687
Trf.Persona ret = null;
688
lock (this._personas)
690
string? contact_urn = yield this._insert_persona (builder.result,
692
if (contact_urn != null)
694
string filter = " FILTER(?_contact = <%s>) ".printf (contact_urn);
695
string query = this._INITIAL_QUERY.printf (filter);
696
var ret_personas = yield this._do_add_contacts (query);
698
/* Return the first persona we find in the set */
699
foreach (var p in ret_personas)
707
debug ("Failed to inserting the new persona into Tracker.");
715
* Returns "service1:addr1,addr2;service2:addr3,.."
719
public static string serialize_web_services (
720
MultiMap<string, string> ws_obj)
724
foreach (var service in ws_obj.get_keys ())
731
str += service + ":";
733
var addrs = ws_obj.get (service);
735
foreach (var addr in addrs)
751
* Transforms "service1:addr1,addr2;service2:addr3,.." to
752
* ---> HashMultiMap<string, string>
756
public static MultiMap<string, string> unserialize_web_services
759
var ret = new HashMultiMap<string, string> ();
761
var services = ws_addrs.split (";");
762
foreach (var service_line in services)
764
var service_t = service_line.split (":");
765
var service_name = service_t[0];
766
var addrs = service_t[1].split (",");
768
foreach (var a in addrs)
770
ret.set (service_name, a);
778
* Transform HashSet<string> to "id1,id2,.."
782
public static string serialize_local_ids (Set<string> local_ids)
786
foreach (var id in local_ids)
799
* Transform from id1,id2,.. to HashSet<string>
803
public static Set<string> unserialize_local_ids (string local_ids)
805
var ids = new HashSet<string> ();
809
string[] ids_a = local_ids.split (",");
810
foreach (var id in ids_a)
820
* Remove a {@link Persona} from the PersonaStore.
822
* See {@link Folks.PersonaStore.remove_persona}.
825
public override async void remove_persona (Folks.Persona persona)
826
throws Folks.PersonaStoreError
828
var urn = yield this._remove_attributes_from_persona (persona,
829
_REMOVE_ALL_ATTRIBS);
831
/* Finally: remove literal properties */
832
var q = " DELETE { " +
838
yield this._tracker_update (q.printf (urn, urn), "remove_persona");
841
private async string _remove_attributes_from_persona (Folks.Persona persona,
844
var urn = yield this._urn_from_persona (persona);
845
yield this._remove_attributes (urn, remove_flag);
849
private async void _build_update_query_set (
850
Tracker.Sparql.Builder builder,
851
Set<FieldDetails> properties,
855
string? affl_var = null;
856
string? obj_var = null;
857
unowned string? related_attrib = null;
858
unowned string? related_prop = null;
859
unowned string? related_connection = null;
863
case Trf.Attrib.PHONES:
864
related_attrib = Trf.OntologyDefs.NCO_PHONE;
865
related_prop = Trf.OntologyDefs.NCO_PHONE_PROP;
866
related_connection = Trf.OntologyDefs.NCO_HAS_PHONE;
867
affl_var = "_:phone_affl%d";
868
obj_var = "_:phone%d";
870
case Trf.Attrib.EMAILS:
871
related_attrib = Trf.OntologyDefs.NCO_EMAIL;
872
related_prop = Trf.OntologyDefs.NCO_EMAIL_PROP;
873
related_connection = Trf.OntologyDefs.NCO_HAS_EMAIL;
874
affl_var = "_:email_affl%d";
875
obj_var = "_:email%d";
880
foreach (var p in properties)
882
var affl = affl_var.printf (cnt);
883
var obj = yield this._urn_from_property (
884
related_attrib, related_prop, p.value);
888
obj = obj_var.printf (cnt);
889
builder.subject (obj);
890
builder.predicate ("a");
891
builder.object (related_attrib);
892
builder.predicate (related_prop);
893
builder.object_string (p.value);
896
builder.subject (affl);
897
builder.predicate ("a");
898
builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
899
builder.predicate (related_connection);
900
builder.object (obj);
902
builder.subject (contact_var);
903
builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
904
builder.object (affl);
911
* Garbage collecting related resources:
912
* - for each related resource we (recursively)
913
* check to if the deleted nco:Person
914
* is the only one holding a link, if so we
915
* remove the resource.
917
private async void _remove_attributes (string urn, char remove_flag)
919
Gee.HashSet<string> affiliations =
920
yield this._affiliations_from_persona (urn);
922
foreach (var affl in affiliations)
924
bool got_attrib = false;
926
if ((remove_flag & _REMOVE_ALL_ATTRIBS) ==
927
_REMOVE_ALL_ATTRIBS ||
928
(remove_flag & _REMOVE_PHONES) == _REMOVE_PHONES)
930
Gee.HashSet<string> phones =
931
yield this._phones_from_affiliation (affl);
933
foreach (var phone in phones)
936
yield this._delete_resource (phone);
940
if ((remove_flag & _REMOVE_ALL_ATTRIBS) ==
941
_REMOVE_ALL_ATTRIBS ||
942
(remove_flag & _REMOVE_POSTALS) == _REMOVE_POSTALS)
944
Gee.HashSet<string> postals =
945
yield this._postals_from_affiliation (affl);
946
foreach (var postal in postals)
949
yield this._delete_resource (postal);
953
if ((remove_flag & _REMOVE_ALL_ATTRIBS) ==
954
_REMOVE_ALL_ATTRIBS ||
955
(remove_flag & _REMOVE_IM_ADDRS) == _REMOVE_IM_ADDRS)
957
Gee.HashSet<string> im_addrs =
958
yield this._imaddrs_from_affiliation (affl);
959
foreach (var im_addr in im_addrs)
962
yield this._delete_resource (im_addr);
966
if ((remove_flag & _REMOVE_ALL_ATTRIBS) ==
967
_REMOVE_ALL_ATTRIBS ||
968
(remove_flag & _REMOVE_EMAILS) == _REMOVE_EMAILS)
970
Gee.HashSet<string> emails =
971
yield this._emails_from_affiliation (affl);
972
foreach (var email in emails)
975
yield yield this._delete_resource (email);
980
(remove_flag & _REMOVE_ALL_ATTRIBS) == _REMOVE_ALL_ATTRIBS)
981
yield this._delete_resource (affl);
986
* Prepare the PersonaStore for use.
988
* TODO: we should throw different errors dependening on what went wrong
989
* when we were trying to setup the PersonaStore.
991
* See {@link Folks.PersonaStore.prepare}.
993
public override async void prepare () throws GLib.Error
995
lock (this._is_prepared)
997
if (!this._is_prepared)
1002
yield Tracker.Sparql.Connection.get_async ();
1004
yield this._build_predicates_table ();
1005
yield this._do_add_contacts (this._INITIAL_QUERY.printf (""));
1007
/* Don't add a match rule for all signals from Tracker but
1008
* only for GraphUpdated with the specific class we need. We
1009
* don't want to be woken up for irrelevent updates on the
1012
this._resources_object = yield GLib.Bus.get_proxy<Resources> (
1016
DBusProxyFlags.DO_NOT_CONNECT_SIGNALS |
1017
DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
1018
this._resources_object.g_connection.signal_subscribe
1019
(this._OBJECT_NAME, this._OBJECT_IFACE,
1020
"GraphUpdated", this._OBJECT_PATH,
1021
Trf.OntologyDefs.PERSON_CLASS, GLib.DBusSignalFlags.NONE,
1022
this._graph_updated_cb);
1024
this._is_prepared = true;
1025
this.notify_property ("is-prepared");
1027
catch (GLib.IOError e1)
1029
warning ("Could not connect to D-Bus service: %s",
1031
throw new PersonaStoreError.INVALID_ARGUMENT (e1.message);
1033
catch (Tracker.Sparql.Error e2)
1035
warning ("Error fetching SPARQL connection handler: %s",
1037
throw new PersonaStoreError.INVALID_ARGUMENT (e2.message);
1039
catch (GLib.DBusError e3)
1041
warning ("Could not connect to D-Bus service: %s",
1043
throw new PersonaStoreError.INVALID_ARGUMENT (e3.message);
1049
public int get_favorite_id ()
1051
return this._prefix_tracker_id.get
1052
(Trf.OntologyDefs.NAO_FAVORITE);
1055
public int get_gender_male_id ()
1057
return this._prefix_tracker_id.get
1058
(Trf.OntologyDefs.NCO_MALE);
1061
public int get_gender_female_id ()
1063
return this._prefix_tracker_id.get
1064
(Trf.OntologyDefs.NCO_FEMALE);
1067
private async void _build_predicates_table ()
1069
if (this._prefix_tracker_id != null)
1074
yield this._build_urn_prefix_table ();
1076
this._prefix_tracker_id = new Gee.TreeMap<string, int> ();
1078
string query = "SELECT ";
1079
foreach (var urn_t in this._urn_prefix.keys)
1081
query += " tracker:id(" + urn_t + ")";
1083
query += " WHERE {} ";
1087
Sparql.Cursor cursor = yield this._connection.query_async (query);
1089
while (cursor.next ())
1092
foreach (var urn in this._urn_prefix.keys)
1094
var tracker_id = (int) cursor.get_integer (i);
1095
var prefix = this._urn_prefix.get (urn).dup ();
1096
this._prefix_tracker_id.set (prefix, tracker_id);
1101
catch (Tracker.Sparql.Error e1)
1103
warning ("Couldn't build predicates table: %s %s", query, e1.message);
1105
catch (GLib.Error e2)
1107
warning ("Couldn't build predicates table: %s %s", query, e2.message);
1111
private async void _build_urn_prefix_table ()
1113
if (this._urn_prefix != null)
1117
this._urn_prefix = new Gee.TreeMap<string, string> ();
1118
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#fullname>",
1119
Trf.OntologyDefs.NCO_FULLNAME);
1120
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#nameFamily>",
1121
Trf.OntologyDefs.NCO_FAMILY);
1122
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#nameGiven>",
1123
Trf.OntologyDefs.NCO_GIVEN);
1124
this._urn_prefix.set (
1125
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#nameAdditional>",
1126
Trf.OntologyDefs.NCO_ADDITIONAL);
1127
this._urn_prefix.set (
1128
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#nameHonorificSuffix>",
1129
Trf.OntologyDefs.NCO_SUFFIX);
1130
this._urn_prefix.set (
1131
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#nameHonorificPrefix>",
1132
Trf.OntologyDefs.NCO_PREFIX);
1133
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#nickname>",
1134
Trf.OntologyDefs.NCO_NICKNAME);
1135
this._urn_prefix.set (
1136
Trf.OntologyDefs.RDF_URL_PREFIX + "22-rdf-syntax-ns#type>",
1137
Trf.OntologyDefs.RDF_TYPE);
1138
this._urn_prefix.set (
1139
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#PersonContact>",
1140
Trf.OntologyDefs.NCO_PERSON);
1141
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#websiteUrl>",
1142
Trf.OntologyDefs.NCO_WEBSITE);
1143
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#blogUrl>",
1144
Trf.OntologyDefs.NCO_BLOG);
1145
this._urn_prefix.set (
1146
Trf.OntologyDefs.NAO_URL_PREFIX + "nao#predefined-tag-favorite>",
1147
Trf.OntologyDefs.NAO_FAVORITE);
1148
this._urn_prefix.set (Trf.OntologyDefs.NAO_URL_PREFIX + "nao#hasTag>",
1149
Trf.OntologyDefs.NAO_TAG);
1150
this._urn_prefix.set (
1151
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#hasEmailAddress>",
1152
Trf.OntologyDefs.NCO_HAS_EMAIL);
1153
this._urn_prefix.set (
1154
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#hasPhoneNumber>",
1155
Trf.OntologyDefs.NCO_HAS_PHONE);
1156
this._urn_prefix.set (
1157
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#hasAffiliation>",
1158
Trf.OntologyDefs.NCO_HAS_AFFILIATION);
1159
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#birthDate>",
1160
Trf.OntologyDefs.NCO_BIRTHDAY);
1161
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#note>",
1162
Trf.OntologyDefs.NCO_NOTE);
1163
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#gender>",
1164
Trf.OntologyDefs.NCO_GENDER);
1165
this._urn_prefix.set (
1166
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#gender-male>",
1167
Trf.OntologyDefs.NCO_MALE);
1168
this._urn_prefix.set (
1169
Trf.OntologyDefs.NCO_URL_PREFIX + "nco#gender-female>",
1170
Trf.OntologyDefs.NCO_FEMALE);
1171
this._urn_prefix.set (Trf.OntologyDefs.NCO_URL_PREFIX + "nco#photo>",
1172
Trf.OntologyDefs.NCO_PHOTO);
1173
this._urn_prefix.set (
1174
Trf.OntologyDefs.NAO_URL_PREFIX + "nao#hasProperty>",
1175
Trf.OntologyDefs.NAO_PROPERTY);
1178
private void _graph_updated_cb (DBusConnection connection,
1179
string sender_name, string object_path, string interface_name,
1180
string signal_name, Variant parameters)
1182
string class_name = "";
1183
VariantIter iter_del = null;
1184
VariantIter iter_ins = null;
1186
parameters.get("(sa(iiii)a(iiii))", &class_name, &iter_del, &iter_ins);
1188
if (class_name != Trf.OntologyDefs.PERSON_CLASS)
1193
this._handle_events ((owned) iter_del, (owned) iter_ins);
1196
private async void _handle_events
1197
(owned VariantIter iter_del, owned VariantIter iter_ins)
1199
yield this._handle_delete_events ((owned) iter_del);
1200
yield this._handle_insert_events ((owned) iter_ins);
1203
private async void _handle_delete_events (owned VariantIter iter_del)
1205
var removed_personas = new HashSet<Persona> ();
1207
this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_PERSON);
1208
var rdf_type_id = this._prefix_tracker_id.get (Trf.OntologyDefs.RDF_TYPE);
1211
while (iter_del.next
1212
("(iiii)", &e.graph_id, &e.subject_id, &e.pred_id, &e.object_id))
1214
var p_id = Trf.Persona.build_iid (this.id, e.subject_id.to_string ());
1215
if (e.pred_id == rdf_type_id &&
1216
e.object_id == nco_person_id)
1218
lock (this._personas)
1220
var removed_p = this._personas.get (p_id);
1221
if (removed_p != null)
1223
removed_personas.add (removed_p);
1224
_personas.unset (removed_p.iid);
1230
var persona = this._personas.get (p_id);
1231
if (persona != null)
1233
yield this._do_update (persona, e, false);
1238
if (removed_personas.size > 0)
1240
this._emit_personas_changed (null, removed_personas);
1244
private async void _handle_insert_events (owned VariantIter iter_ins)
1246
var added_personas = new HashSet<Persona> ();
1249
while (iter_ins.next
1250
("(iiii)", &e.graph_id, &e.subject_id, &e.pred_id, &e.object_id))
1252
var subject_tracker_id = e.subject_id.to_string ();
1253
var p_id = Trf.Persona.build_iid (this.id, subject_tracker_id);
1254
Trf.Persona persona;
1255
lock (this._personas)
1257
persona = this._personas.get (p_id);
1258
if (persona == null)
1260
persona = new Trf.Persona (this, subject_tracker_id);
1261
this._personas.set (persona.iid, persona);
1262
added_personas.add (persona);
1265
yield this._do_update (persona, e);
1268
if (added_personas.size > 0)
1270
this._emit_personas_changed (added_personas, null);
1274
private async HashSet<Persona> _do_add_contacts (string query)
1276
var added_personas = new HashSet<Persona> ();
1279
Sparql.Cursor cursor = yield this._connection.query_async (query);
1281
while (cursor.next ())
1283
int tracker_id = (int) cursor.get_integer (Trf.Fields.TRACKER_ID);
1284
var p_id = Trf.Persona.build_iid (this.id, tracker_id.to_string ());
1285
if (this._personas.get (p_id) == null)
1287
var persona = new Trf.Persona (this,
1288
tracker_id.to_string (), cursor);
1289
this._personas.set (persona.iid, persona);
1290
added_personas.add (persona);
1294
if (added_personas.size > 0)
1296
this._emit_personas_changed (added_personas, null);
1298
} catch (GLib.Error e) {
1299
warning ("Couldn't perform queries: %s %s", query, e.message);
1302
return added_personas;
1305
private async void _do_update (Persona p, Event e, bool adding = true)
1308
this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_FULLNAME))
1310
string fullname = "";
1314
yield this._get_property (e.subject_id,
1315
Trf.OntologyDefs.NCO_FULLNAME);
1317
p._update_full_name (fullname);
1319
else if (e.pred_id ==
1320
this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_NICKNAME))
1326
yield this._get_property (
1327
e.subject_id, Trf.OntologyDefs.NCO_NICKNAME);
1329
p._update_alias (alias);
1331
else if (e.pred_id ==
1332
this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_FAMILY))
1334
string family_name = "";
1337
family_name = yield this._get_property (e.subject_id,
1338
Trf.OntologyDefs.NCO_FAMILY);
1340
p._update_family_name (family_name);
1342
else if (e.pred_id ==
1343
this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_GIVEN))
1345
string given_name = "";
1348
given_name = yield this._get_property (
1349
e.subject_id, Trf.OntologyDefs.NCO_GIVEN);
1351
p._update_given_name (given_name);
1353
else if (e.pred_id ==
1354
this._prefix_tracker_id.get (Trf.OntologyDefs.NCO_ADDITIONAL))
1356
string additional_name = "";
1359
additional_name = yield this._get_property
1360
(e.subject_id, Trf.OntologyDefs.NCO_ADDITIONAL);
1362
p._update_additional_names (additional_name);
1364
else if (e.pred_id == this._prefix_tracker_id.get
1365
(Trf.OntologyDefs.NCO_SUFFIX))
1367
string suffix_name = "";
1370
suffix_name = yield this._get_property
1371
(e.subject_id, Trf.OntologyDefs.NCO_SUFFIX);
1373
p._update_suffixes (suffix_name);
1375
else if (e.pred_id == this._prefix_tracker_id.get
1376
(Trf.OntologyDefs.NCO_PREFIX))
1378
string prefix_name = "";
1381
prefix_name = yield this._get_property
1382
(e.subject_id, Trf.OntologyDefs.NCO_PREFIX);
1384
p._update_prefixes (prefix_name);
1386
else if (e.pred_id == this._prefix_tracker_id.get
1387
(Trf.OntologyDefs.NAO_TAG))
1389
if (e.object_id == this.get_favorite_id ())
1393
p._set_favourite (true);
1397
p._set_favourite (false);
1401
else if (e.pred_id == this._prefix_tracker_id.get
1402
(Trf.OntologyDefs.NCO_HAS_EMAIL))
1406
var email = yield this._get_property (
1408
Trf.OntologyDefs.NCO_EMAIL_PROP,
1409
Trf.OntologyDefs.NCO_EMAIL);
1410
p._add_email (email, e.object_id.to_string ());
1414
p._remove_email (e.object_id.to_string ());
1417
else if (e.pred_id == this._prefix_tracker_id.get
1418
(Trf.OntologyDefs.NCO_HAS_PHONE))
1422
var phone = yield this._get_property (
1423
e.object_id, Trf.OntologyDefs.NCO_PHONE_PROP,
1424
Trf.OntologyDefs.NCO_PHONE);
1425
p._add_phone (phone, e.object_id.to_string ());
1429
p._remove_phone (e.object_id.to_string ());
1432
else if (e.pred_id == this._prefix_tracker_id.get
1433
(Trf.OntologyDefs.NCO_HAS_AFFILIATION))
1438
yield this._get_affl_info (e.subject_id.to_string (),
1439
e.object_id.to_string ());
1441
debug ("affl_info : %s", affl_info.to_string ());
1443
if (affl_info.im_tracker_id != null)
1445
p._update_nickname (affl_info.im_nickname);
1446
if (affl_info.im_proto != null)
1447
p._add_im_address (affl_info.affl_tracker_id,
1448
affl_info.im_proto, affl_info.im_account_id);
1451
if (affl_info.affl_tracker_id != null)
1453
if (affl_info.title != null ||
1454
affl_info.org != null)
1456
p._add_role (affl_info.affl_tracker_id, affl_info.title,
1461
if (affl_info.postal_address != null)
1462
p._add_postal_address (affl_info.postal_address);
1464
if (affl_info.phone != null)
1465
p._add_phone (affl_info.phone, e.object_id.to_string ());
1467
if (affl_info.email != null)
1468
p._add_email (affl_info.email, e.object_id.to_string ());
1470
if (affl_info.website != null)
1471
p._add_url (affl_info.website,
1472
affl_info.affl_tracker_id, "website");
1474
if (affl_info.blog != null)
1475
p._add_url (affl_info.blog,
1476
affl_info.affl_tracker_id, "blog");
1478
if (affl_info.url != null)
1479
p._add_url (affl_info.url,
1480
affl_info.affl_tracker_id, "url");
1484
p._remove_im_address (e.object_id.to_string ());
1485
p._remove_role (e.object_id.to_string ());
1486
p._remove_postal_address (e.object_id.to_string ());
1487
p._remove_phone (e.object_id.to_string ());
1488
p._remove_email (e.object_id.to_string ());
1489
p._remove_url (e.object_id.to_string ());
1492
else if (e.pred_id == this._prefix_tracker_id.get
1493
(Trf.OntologyDefs.NCO_BIRTHDAY))
1498
bday = yield this._get_property (
1499
e.subject_id, Trf.OntologyDefs.NCO_BIRTHDAY);
1501
p._set_birthday (bday);
1503
else if (e.pred_id == this._prefix_tracker_id.get
1504
(Trf.OntologyDefs.NCO_NOTE))
1509
note = yield this._get_property (
1510
e.subject_id, Trf.OntologyDefs.NCO_NOTE);
1514
else if (e.pred_id == this._prefix_tracker_id.get
1515
(Trf.OntologyDefs.NCO_GENDER))
1519
p._set_gender (e.object_id);
1526
else if (e.pred_id == this._prefix_tracker_id.get
1527
(Trf.OntologyDefs.NCO_PHOTO))
1529
string avatar_url = "";
1532
avatar_url = yield this._get_property (e.object_id,
1533
Trf.OntologyDefs.NIE_URL, Trf.OntologyDefs.NFO_IMAGE);
1535
p._set_avatar (avatar_url);
1537
else if (e.pred_id == this._prefix_tracker_id.get
1538
(Trf.OntologyDefs.NAO_PROPERTY))
1541
* nao:Properties shouldn't be abused since we have to reset
1542
* them all when there is a DELETE. Plus, the Tracker devs
1543
* say nao:Property is by nature slow. */
1546
string[] prop_info = yield this._get_nao_property_by_prop_id (
1548
if (prop_info[0] == this._LOCAL_ID_PROPERTY_NAME)
1550
p._set_local_ids (prop_info[1]);
1552
else if (prop_info[0] == this._WSD_PROPERTY_NAME)
1554
p._set_web_service_addrs (prop_info[1]);
1559
string local_ids = yield this._get_nao_property_by_person_id (
1561
this._LOCAL_ID_PROPERTY_NAME);
1562
string ws_addrs = yield this._get_nao_property_by_person_id (
1564
this._WSD_PROPERTY_NAME);
1566
p._set_local_ids (local_ids);
1567
p._set_web_service_addrs (ws_addrs);
1572
private async string _get_property
1573
(int subject_tracker_id, string property,
1574
string subject_type = Trf.OntologyDefs.NCO_PERSON)
1576
const string query_template =
1577
"SELECT ?property WHERE" +
1580
" . FILTER(tracker:id(?p) = %d ) }";
1582
string query = query_template.printf (subject_type,
1583
property, subject_tracker_id);
1584
return yield this._single_value_query (query);
1587
private async string _get_nao_property_by_person_id (int nco_person_id,
1590
const string query_t = "SELECT " +
1593
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
1594
Trf.OntologyDefs.NAO_HAS_PROPERTY + " ?prop . " +
1595
" ?prop " + Trf.OntologyDefs.NAO_PROPERTY_NAME + " ?prop_name . " +
1596
" ?prop " + Trf.OntologyDefs.NAO_PROPERTY_VALUE + " ?prop_value . " +
1597
" FILTER (tracker:id(?p) = %d && ?prop_name = '%s') } ";
1599
string query = query_t.printf(nco_person_id, prop_name);
1600
return yield this._single_value_query (query);
1603
private async string[] _get_nao_property_by_prop_id (int nao_prop_id)
1605
const string query_t = "SELECT " +
1606
" fn:concat(?prop_name, '\t', ?prop_value)" +
1608
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
1609
Trf.OntologyDefs.NAO_HAS_PROPERTY + " ?prop . " +
1610
" ?prop " + Trf.OntologyDefs.NAO_PROPERTY_NAME + " ?prop_name . " +
1611
" ?prop " + Trf.OntologyDefs.NAO_PROPERTY_VALUE + " ?prop_value . " +
1612
" FILTER (tracker:id(?prop) = %d) } ";
1614
string query = query_t.printf(nao_prop_id);
1615
var ret = yield this._single_value_query (query);
1616
return ret.split ("\t");
1620
* This should be kept in sync with Trf.AfflInfoFields
1622
private async Trf.AfflInfo _get_affl_info (
1623
string person_id, string affiliation_id)
1625
Trf.AfflInfo affl_info = new Trf.AfflInfo ();
1626
const string query_template =
1629
Trf.OntologyDefs.NCO_IMPROTOCOL + "(?i) " +
1630
Trf.OntologyDefs.NCO_IMID + "(?i) " +
1632
Trf.OntologyDefs.NCO_ROLE + "(?a) " +
1633
Trf.OntologyDefs.NCO_ORG + "(?a) " +
1634
Trf.OntologyDefs.NCO_POBOX + "(?postal) " +
1635
Trf.OntologyDefs.NCO_DISTRICT + "(?postal) " +
1636
Trf.OntologyDefs.NCO_COUNTY + "(?postal) " +
1637
Trf.OntologyDefs.NCO_LOCALITY + "(?postal) " +
1638
Trf.OntologyDefs.NCO_POSTALCODE + "(?postal) " +
1639
Trf.OntologyDefs.NCO_STREET_ADDRESS + "(?postal) " +
1640
Trf.OntologyDefs.NCO_ADDRESS_LOCATION + "(?postal) " +
1641
Trf.OntologyDefs.NCO_EXTENDED_ADDRESS + "(?postal) " +
1642
Trf.OntologyDefs.NCO_COUNTRY + "(?postal) " +
1643
Trf.OntologyDefs.NCO_REGION + "(?postal) " +
1644
Trf.OntologyDefs.NCO_EMAIL_PROP + "(?e) " +
1645
Trf.OntologyDefs.NCO_PHONE_PROP + "(?number) " +
1646
Trf.OntologyDefs.NCO_WEBSITE + "(?a) " +
1647
Trf.OntologyDefs.NCO_BLOG + "(?a) " +
1648
Trf.OntologyDefs.NCO_URL + "(?a) " +
1649
Trf.OntologyDefs.NCO_IM_NICKNAME + "(?i) " +
1651
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
1652
Trf.OntologyDefs.NCO_HAS_AFFILIATION + " ?a . " +
1653
" OPTIONAL { ?a " + Trf.OntologyDefs.NCO_HAS_IMADDRESS + " ?i } . " +
1654
" OPTIONAL { ?a " + Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS +
1656
" OPTIONAL { ?a " + Trf.OntologyDefs.NCO_HAS_EMAIL + " ?e } . " +
1657
" OPTIONAL { ?a " + Trf.OntologyDefs.NCO_HAS_PHONE + " ?number } " +
1658
" FILTER(tracker:id(?p) = %s" +
1659
" && tracker:id(?a) = %s" +
1662
string query = query_template.printf (person_id, affiliation_id);
1664
debug ("_get_affl_info: %s", query);
1668
Sparql.Cursor cursor = yield this._connection.query_async (query);
1669
while (yield cursor.next_async ())
1671
affl_info.im_tracker_id = cursor.get_string
1672
(Trf.AfflInfoFields.IM_TRACKER_ID).dup ();
1673
affl_info.im_proto = cursor.get_string
1674
(Trf.AfflInfoFields.IM_PROTOCOL).dup ();
1675
affl_info.im_account_id = cursor.get_string
1676
(Trf.AfflInfoFields.IM_ACCOUNT_ID).dup ();
1677
affl_info.im_nickname = cursor.get_string
1678
(Trf.AfflInfoFields.IM_NICKNAME).dup ();
1680
affl_info.affl_tracker_id = cursor.get_string
1681
(Trf.AfflInfoFields.AFFL_TRACKER_ID).dup ();
1682
affl_info.title = cursor.get_string
1683
(Trf.AfflInfoFields.AFFL_ROLE).dup ();
1684
affl_info.org = cursor.get_string
1685
(Trf.AfflInfoFields.AFFL_ORG).dup ();
1687
var po_box = cursor.get_string
1688
(Trf.AfflInfoFields.AFFL_POBOX).dup ();
1689
var extension = cursor.get_string
1690
(Trf.AfflInfoFields.AFFL_EXTENDED_ADDRESS).dup ();
1691
var street = cursor.get_string
1692
(Trf.AfflInfoFields.AFFL_STREET_ADDRESS).dup ();
1693
var locality = cursor.get_string
1694
(Trf.AfflInfoFields.AFFL_LOCALITY).dup ();
1695
var region = cursor.get_string
1696
(Trf.AfflInfoFields.AFFL_REGION).dup ();
1697
var postal_code = cursor.get_string
1698
(Trf.AfflInfoFields.AFFL_POSTALCODE).dup ();
1699
var country = cursor.get_string
1700
(Trf.AfflInfoFields.AFFL_COUNTRY).dup ();
1702
var types = new HashSet<string> ();
1704
affl_info.postal_address = new Folks.PostalAddress (
1705
po_box, extension, street, locality, region, postal_code,
1706
country, null, types, affl_info.affl_tracker_id);
1708
affl_info.email = cursor.get_string
1709
(Trf.AfflInfoFields.AFFL_EMAIL).dup ();
1710
affl_info.phone = cursor.get_string
1711
(Trf.AfflInfoFields.AFFL_PHONE).dup ();
1713
affl_info.website = cursor.get_string
1714
(Trf.AfflInfoFields.AFFL_WEBSITE).dup ();
1715
affl_info.blog = cursor.get_string
1716
(Trf.AfflInfoFields.AFFL_BLOG).dup ();
1717
affl_info.url = cursor.get_string
1718
(Trf.AfflInfoFields.AFFL_URL).dup ();
1721
catch (Tracker.Sparql.Error e1)
1723
warning ("Couldn't fetch affiliation info: %s %s",
1726
catch (GLib.Error e2)
1728
warning ("Couldn't fetch affiliation info: %s %s",
1735
private async string? _insert_persona (string query, string persona_var)
1737
GLib.Variant variant;
1738
string contact_urn = null;
1742
debug ("_insert_persona: %s", query);
1743
variant = yield this._connection.update_blank_async (query);
1745
VariantIter iter1, iter2, iter3;
1746
string anon_var = null;
1747
iter1 = variant.iterator ();
1749
while (iter1.next ("aa{ss}", out iter2))
1754
while (iter2.next ("a{ss}", out iter3))
1759
while (iter3.next ("{ss}", out anon_var, out contact_urn))
1761
/* The dictionary mapping blank node names to
1762
* IRIs doesn't have a fixed order so we need
1763
* check for the anon var corresponding to
1764
* nco:PersonContact.
1766
if (anon_var == persona_var)
1772
catch (GLib.Error e)
1775
warning ("Couldn't insert nco:PersonContact: %s", e.message);
1781
private async string _single_value_query (string query)
1783
Gee.HashSet<string> rows = yield this._multi_value_query (query);
1784
foreach (var r in rows)
1791
private async Gee.HashSet<string> _multi_value_query (string query)
1793
Gee.HashSet<string> ret = new Gee.HashSet<string> ();
1795
debug ("[_multi_value_query] %s", query);
1799
Sparql.Cursor cursor = yield this._connection.query_async (query);
1800
while (cursor.next ())
1802
var prop = cursor.get_string (0);
1807
catch (Tracker.Sparql.Error e1)
1809
warning ("Couldn't run query: %s %s", query, e1.message);
1811
catch (GLib.Error e2)
1813
warning ("Couldn't run query: %s %s", query, e2.message);
1819
private async string _urn_from_tracker_id (string tracker_id)
1821
const string query = "SELECT fn:concat('<', tracker:uri(%s), '>') " +
1823
return yield this._single_value_query (query.printf (tracker_id));
1826
internal async void _set_alias (Trf.Persona persona, string alias)
1828
const string query_t = "DELETE { "+
1829
" ?p " + Trf.OntologyDefs.NCO_NICKNAME + " ?n " +
1832
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
1833
Trf.OntologyDefs.NCO_NICKNAME + " ?n . " +
1834
" FILTER(tracker:id(?p) = %s) " +
1837
" ?p " + Trf.OntologyDefs.NCO_NICKNAME + " '%s' " +
1840
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
1841
"FILTER (tracker:id(?p) = %s) " +
1844
string query = query_t.printf (persona.tracker_id (), alias,
1845
persona.tracker_id ());
1847
yield this._tracker_update (query, "change_alias");
1850
internal async void _set_local_ids (Trf.Persona persona,
1851
Set<string> local_ids)
1853
string ids = Trf.PersonaStore.serialize_local_ids (local_ids);
1854
yield this._set_tracker_property (persona,
1855
this._LOCAL_ID_PROPERTY_NAME, ids,
1859
internal async void _set_web_service_addrs (Trf.Persona persona,
1860
MultiMap<string, string> ws_obj)
1862
var ws_addrs = Trf.PersonaStore.serialize_web_services (ws_obj);
1863
yield this._set_tracker_property (persona,
1864
this._WSD_PROPERTY_NAME, ws_addrs,
1865
"_set_web_service_addrs");
1868
private async void _set_tracker_property(Trf.Persona persona,
1869
string prop_name, string prop_value, string callers_name)
1871
const string query_t = "DELETE " +
1872
" { ?p " + Trf.OntologyDefs.NAO_HAS_PROPERTY + " ?prop } " +
1874
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
1875
Trf.OntologyDefs.NAO_HAS_PROPERTY + " ?prop . " +
1876
" ?prop " + Trf.OntologyDefs.NAO_PROPERTY_NAME + " ?prop_name . " +
1877
" FILTER (tracker:id(?p) = %s && ?name = '%s' ) } " +
1879
" _:prop a " + Trf.OntologyDefs.NAO_PROPERTY + " ; " +
1880
Trf.OntologyDefs.NAO_PROPERTY_NAME +
1882
Trf.OntologyDefs.NAO_PROPERTY_VALUE + " '%s' . " +
1883
" ?p " + Trf.OntologyDefs.NAO_HAS_PROPERTY + " _:prop " +
1886
" ?p a nco:PersonContact . " +
1887
"FILTER (tracker:id(?p) = %s) } ";
1889
string query = query_t.printf (persona.tracker_id (), prop_name,
1890
prop_name, prop_value, persona.tracker_id ());
1891
yield this._tracker_update (query, callers_name);
1894
internal async void _set_is_favourite (Folks.Persona persona,
1897
const string ins_q = "INSERT { " +
1898
" ?p " + Trf.OntologyDefs.NAO_TAG + " " +
1899
Trf.OntologyDefs.NAO_FAVORITE +
1902
" ?p a " + Trf.OntologyDefs.NCO_PERSON +
1903
" FILTER (tracker:id(?p) = %s) " +
1905
const string del_q = "DELETE { " +
1906
" ?p " + Trf.OntologyDefs.NAO_TAG + " " +
1907
Trf.OntologyDefs.NAO_FAVORITE + " " +
1910
" ?p a " + Trf.OntologyDefs.NCO_PERSON +
1911
" FILTER (tracker:id(?p) = %s) " +
1917
query = ins_q.printf (((Trf.Persona) persona).tracker_id ());
1921
query = del_q.printf (((Trf.Persona) persona).tracker_id ());
1924
yield this._tracker_update (query, "change_is_favourite");
1927
internal async void _set_emails (Folks.Persona persona,
1928
Set<FieldDetails> emails)
1930
yield this._set_unique_attrib_set (persona, emails,
1934
internal async void _set_phones (Folks.Persona persona,
1935
Set<FieldDetails> phone_numbers)
1937
yield this._set_unique_attrib_set (persona, phone_numbers,
1941
internal async void _set_unique_attrib_set (Folks.Persona persona,
1942
Set<FieldDetails> properties, Trf.Attrib attrib)
1944
string? query_name = null;
1945
var p_id = ((Trf.Persona) persona).tracker_id ();
1946
var builder = new Tracker.Sparql.Builder.update ();
1947
builder.insert_open (null);
1951
case Trf.Attrib.PHONES:
1952
query_name = "_set_phones";
1953
yield this._remove_attributes_from_persona (persona,
1955
yield this._build_update_query_set (builder, properties,
1956
"?contact", Trf.Attrib.PHONES);
1958
case Trf.Attrib.EMAILS:
1959
query_name = "_set_emailss";
1960
yield this._remove_attributes_from_persona (persona,
1962
yield this._build_update_query_set (builder, properties,
1963
"?contact", Trf.Attrib.EMAILS);
1966
builder.insert_close ();
1967
builder.where_open ();
1968
builder.subject ("?contact");
1969
builder.predicate ("a");
1970
builder.object (Trf.OntologyDefs.NCO_PERSON);
1971
string filter = " FILTER(tracker:id(?contact) = %s) ".printf (p_id);
1972
builder.append (filter);
1973
builder.where_close ();
1975
yield this._tracker_update (builder.result, query_name);
1978
internal async void _set_urls (Folks.Persona persona,
1979
Set<FieldDetails> urls)
1981
yield this._set_attrib_set (persona, urls,
1985
internal async void _set_im_addresses (Folks.Persona persona,
1986
MultiMap<string, string> im_addresses)
1989
* - this conversion should go away once we've switched to use the
1990
* same data structure for each property that is a list of something.
1991
* See: https://bugzilla.gnome.org/show_bug.cgi?id=646079 */
1992
var ims = new HashSet<FieldDetails> ();
1993
foreach (var proto in im_addresses.get_keys ())
1995
var addrs = im_addresses.get (proto);
1996
foreach (var a in addrs)
1998
var fd = new FieldDetails (a);
1999
fd.set_parameter ("proto", proto);
2004
yield this._set_attrib_set (persona, ims, Trf.Attrib.IM_ADDRESSES);
2007
internal async void _set_postal_addresses (Folks.Persona persona,
2008
Set<PostalAddress> postal_addresses)
2010
yield this._set_attrib_set (persona, postal_addresses,
2011
Trf.Attrib.POSTAL_ADDRESSES);
2014
internal async void _set_roles (Folks.Persona persona,
2017
const string del_t = "DELETE { " +
2018
" ?p " + Trf.OntologyDefs.NCO_HAS_AFFILIATION + " ?a " +
2021
" ?p a " + Trf.OntologyDefs.NCO_PERSON + "; " +
2022
" " + Trf.OntologyDefs.NCO_HAS_AFFILIATION + " ?a . " +
2023
" OPTIONAL { ?a " + Trf.OntologyDefs.NCO_ORG + " ?o } . " +
2024
" OPTIONAL { ?a " + Trf.OntologyDefs.NCO_ROLE + " ?r } . " +
2025
" FILTER(tracker:id(?p) = %s) " +
2028
var p_id = ((Trf.Persona) persona).tracker_id ();
2029
string del_q = del_t.printf (p_id);
2031
var builder = new Tracker.Sparql.Builder.update ();
2032
builder.insert_open (null);
2035
foreach (var r in roles)
2037
string affl = "_:a%d".printf (i);
2039
builder.subject (affl);
2040
builder.predicate ("a");
2041
builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
2042
builder.predicate (Trf.OntologyDefs.NCO_ROLE);
2043
builder.object_string (r.title);
2044
builder.predicate (Trf.OntologyDefs.NCO_ORG);
2045
builder.object_string (r.organisation_name);
2046
builder.subject ("?contact");
2047
builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
2048
builder.object (affl);
2051
builder.insert_close ();
2052
builder.where_open ();
2053
builder.subject ("?contact");
2054
builder.predicate ("a");
2055
builder.object (Trf.OntologyDefs.NCO_PERSON);
2056
string filter = " FILTER(tracker:id(?contact) = %s) ".printf (p_id);
2057
builder.append (filter);
2058
builder.where_close ();
2060
yield this._tracker_update (del_q + builder.result, "_set_roles");
2063
internal async void _set_notes (Folks.Persona persona,
2066
const string del_t = "DELETE { " +
2067
"?p " + Trf.OntologyDefs.NCO_NOTE + " ?n " +
2070
" ?p a nco:PersonContact ; " +
2071
Trf.OntologyDefs.NCO_NOTE + " ?n . " +
2072
" FILTER(tracker:id(?p) = %s)" +
2075
var p_id = ((Trf.Persona) persona).tracker_id ();
2076
string del_q = del_t.printf (p_id);
2078
var builder = new Tracker.Sparql.Builder.update ();
2079
builder.insert_open (null);
2081
foreach (var n in notes)
2083
builder.subject ("?contact");
2084
builder.predicate (Trf.OntologyDefs.NCO_NOTE);
2085
builder.object_string (n.content);
2088
builder.insert_close ();
2089
builder.where_open ();
2090
builder.subject ("?contact");
2091
builder.predicate ("a");
2092
builder.object (Trf.OntologyDefs.NCO_PERSON);
2093
string filter = " FILTER(tracker:id(?contact) = %s) ".printf (p_id);
2094
builder.append (filter);
2095
builder.where_close ();
2097
yield this._tracker_update (del_q + builder.result, "_set_notes");
2100
internal async void _set_birthday (Folks.Persona persona,
2101
owned DateTime bday)
2103
const string q_t = "DELETE { " +
2104
" ?p " + Trf.OntologyDefs.NCO_BIRTHDAY + " ?b " +
2107
" ?p a " + Trf.OntologyDefs.NCO_PERSON + "; " +
2108
Trf.OntologyDefs.NCO_BIRTHDAY + " ?b . " +
2109
" FILTER (tracker:id(?p) = %s ) " +
2112
" ?p " + Trf.OntologyDefs.NCO_BIRTHDAY + " '%s' " +
2115
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
2116
" FILTER (tracker:id(?p) = %s) " +
2119
var p_id = ((Trf.Persona) persona).tracker_id ();
2121
bday.to_timeval (out tv);
2122
string query = q_t.printf (p_id, tv.to_iso8601 (), p_id);
2124
yield this._tracker_update (query, "_set_birthday");
2127
internal async void _set_gender (Folks.Persona persona,
2128
owned Gender gender)
2130
const string del_t = "DELETE { " +
2131
" ?p " + Trf.OntologyDefs.NCO_GENDER + " ?g " +
2134
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
2135
Trf.OntologyDefs.NCO_GENDER + " ?g . " +
2136
" FILTER (tracker:id(?p) = %s) " +
2138
const string ins_t = "INSERT { " +
2139
" ?p " + Trf.OntologyDefs.NCO_GENDER + " %s " +
2142
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
2143
" FILTER (tracker:id(?p) = %s) " +
2146
var p_id = ((Trf.Persona) persona).tracker_id ();
2149
if (gender == Gender.UNSPECIFIED)
2151
query = del_t.printf (p_id);
2157
if (gender == Gender.MALE)
2158
gender_urn = Trf.OntologyDefs.NCO_URL_PREFIX + "nco#gender-male>";
2160
gender_urn = Trf.OntologyDefs.NCO_URL_PREFIX + "nco#gender-female>";
2162
query = del_t.printf (p_id) + ins_t.printf (gender_urn, p_id);
2165
yield this._tracker_update (query, "_set_gender");
2168
internal async void _set_avatar (Folks.Persona persona,
2171
const string query_d = "DELETE {" +
2172
" ?c " + Trf.OntologyDefs.NCO_PHOTO + " ?p " +
2175
" ?c a " + Trf.OntologyDefs.NCO_PERSON + " ; " +
2176
Trf.OntologyDefs.NCO_PHOTO + " ?p . " +
2177
" FILTER(tracker:id(?c) = %s) " +
2180
const string query_i = "INSERT { " +
2181
" _:i a " + Trf.OntologyDefs.NFO_IMAGE + ", " +
2182
Trf.OntologyDefs.NIE_DATAOBJECT + " ; " +
2183
Trf.OntologyDefs.NIE_URL + " '%s' . " +
2184
" ?c " + Trf.OntologyDefs.NCO_PHOTO + " _:i " +
2187
" ?c a nco:PersonContact . " +
2188
" FILTER(tracker:id(?c) = %s) " +
2191
var p_id = ((Trf.Persona) persona).tracker_id ();
2193
var image_urn = yield this._get_property (int.parse (p_id),
2194
Trf.OntologyDefs.NCO_PHOTO);
2195
if (image_urn != "")
2196
this._delete_resource ("<%s>".printf (image_urn));
2198
string query = query_d.printf (p_id);
2201
query += query_i.printf (avatar.get_uri (), p_id);
2203
yield this._tracker_update (query, "_set_avatar");
2206
internal async void _set_structured_name (Folks.Persona persona,
2207
StructuredName sname)
2209
const string query_t = "DELETE { " +
2210
" ?p " + Trf.OntologyDefs.NCO_FAMILY + " ?family . " +
2211
" ?p " + Trf.OntologyDefs.NCO_GIVEN + " ?given . " +
2212
" ?p " + Trf.OntologyDefs.NCO_ADDITIONAL + " ?adi . " +
2213
" ?p " + Trf.OntologyDefs.NCO_PREFIX + " ?prefix . " +
2214
" ?p " + Trf.OntologyDefs.NCO_SUFFIX + " ?suffix " +
2217
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
2218
" OPTIONAL { ?p " + Trf.OntologyDefs.NCO_FAMILY + " ?family } . " +
2219
" OPTIONAL { ?p " + Trf.OntologyDefs.NCO_GIVEN + " ?given } . " +
2220
" OPTIONAL { ?p " + Trf.OntologyDefs.NCO_ADDITIONAL + " ?adi } . " +
2221
" OPTIONAL { ?p " + Trf.OntologyDefs.NCO_PREFIX + " ?prefix } . " +
2222
" OPTIONAL { ?p " + Trf.OntologyDefs.NCO_SUFFIX + " ?suffix } . " +
2223
" FILTER (tracker:id(?p) = %s) " +
2226
" ?p " + Trf.OntologyDefs.NCO_FAMILY + " '%s'; " +
2227
" " + Trf.OntologyDefs.NCO_GIVEN + " '%s'; " +
2228
" " + Trf.OntologyDefs.NCO_ADDITIONAL + " '%s'; " +
2229
" " + Trf.OntologyDefs.NCO_PREFIX + " '%s'; " +
2230
" " + Trf.OntologyDefs.NCO_SUFFIX + " '%s' " +
2233
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
2234
" FILTER (tracker:id(?p) = %s) " +
2237
var p_id = ((Trf.Persona) persona).tracker_id ();
2238
string query = query_t.printf (p_id, sname.family_name, sname.given_name,
2239
sname.additional_names, sname.prefixes, sname.suffixes, p_id);
2240
yield this._tracker_update (query, "_set_structured_name");
2243
internal async void _set_full_name (Folks.Persona persona,
2246
const string query_t = "DELETE { " +
2247
" ?p " + Trf.OntologyDefs.NCO_FULLNAME + " ?fn " +
2250
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
2251
" OPTIONAL { ?p " + Trf.OntologyDefs.NCO_FULLNAME + " ?fn } . " +
2252
" FILTER (tracker:id(?p) = %s) " +
2255
" ?p " + Trf.OntologyDefs.NCO_FULLNAME + " '%s' " +
2258
" ?p a " + Trf.OntologyDefs.NCO_PERSON + " . " +
2259
" FILTER (tracker:id(?p) = %s) " +
2262
var p_id = ((Trf.Persona) persona).tracker_id ();
2263
string query = query_t.printf (p_id, full_name, p_id);
2264
yield this._tracker_update (query, "_set_full_name");
2268
* - first we nuke old attribs
2269
* - we create new affls with the new attribs
2271
private async void _set_attrib_set (Folks.Persona persona,
2272
Set<Object> attribs, Trf.Attrib what)
2274
var p_id = ((Trf.Persona) persona).tracker_id ();
2276
unowned string? related_attrib = null;
2277
unowned string? related_prop = null;
2278
unowned string? related_prop_2 = null;
2279
unowned string? related_connection = null;
2283
case Trf.Attrib.IM_ADDRESSES:
2284
related_attrib = Trf.OntologyDefs.NCO_IMADDRESS;
2285
related_prop = Trf.OntologyDefs.NCO_IMID;
2286
related_prop_2 = Trf.OntologyDefs.NCO_IMPROTOCOL;
2287
related_connection = Trf.OntologyDefs.NCO_HAS_IMADDRESS;
2288
yield this._remove_attributes_from_persona (persona,
2291
case Trf.Attrib.POSTAL_ADDRESSES:
2292
related_attrib = Trf.OntologyDefs.NCO_POSTAL_ADDRESS;
2293
related_connection = Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS;
2294
yield this._remove_attributes_from_persona (persona,
2297
case Trf.Attrib.URLS:
2298
related_attrib = Trf.OntologyDefs.NCO_URL;
2299
related_connection = Trf.OntologyDefs.NCO_URL;
2303
var builder = new Tracker.Sparql.Builder.update ();
2304
builder.insert_open (null);
2306
foreach (var p in attribs)
2308
FieldDetails fd = null;
2309
PostalAddress pa = null;
2311
string affl = "_:a%d".printf (i);
2316
case Trf.Attrib.POSTAL_ADDRESSES:
2317
pa = (PostalAddress) p;
2318
attr = "_:p%d".printf (i);
2319
builder.subject (attr);
2320
builder.predicate ("a");
2321
builder.object (related_attrib);
2322
builder.predicate (Trf.OntologyDefs.NCO_POBOX);
2323
builder.object_string (pa.po_box);
2324
builder.predicate (Trf.OntologyDefs.NCO_LOCALITY);
2325
builder.object_string (pa.locality);
2326
builder.predicate (Trf.OntologyDefs.NCO_POSTALCODE);
2327
builder.object_string (pa.postal_code);
2328
builder.predicate (Trf.OntologyDefs.NCO_STREET_ADDRESS);
2329
builder.object_string (pa.street);
2330
builder.predicate (Trf.OntologyDefs.NCO_EXTENDED_ADDRESS);
2331
builder.object_string (pa.extension);
2332
builder.predicate (Trf.OntologyDefs.NCO_COUNTRY);
2333
builder.object_string (pa.country);
2334
builder.predicate (Trf.OntologyDefs.NCO_REGION);
2335
builder.object_string (pa.region);
2337
case Trf.Attrib.URLS:
2338
fd = (FieldDetails) p;
2339
var type_p = fd.get_parameter_values ("type");
2340
if (type_p.contains ("blog"))
2342
related_connection = Trf.OntologyDefs.NCO_BLOG;
2344
else if (type_p.contains ("website"))
2346
related_connection = Trf.OntologyDefs.NCO_WEBSITE;
2348
attr = "'%s'".printf (fd.value);
2350
case Trf.Attrib.IM_ADDRESSES:
2352
fd = (FieldDetails) p;
2353
attr = "_:p%d".printf (i);
2354
builder.subject (attr);
2355
builder.predicate ("a");
2356
builder.object (related_attrib);
2357
builder.predicate (related_prop);
2358
builder.object_string (fd.value);
2360
if (what == Trf.Attrib.IM_ADDRESSES)
2362
builder.predicate (related_prop_2);
2364
fd.get_parameter_values ("proto").to_array ();
2365
builder.object_string (im_params[0]);
2371
builder.subject (affl);
2372
builder.predicate ("a");
2373
builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
2374
builder.predicate (related_connection);
2375
builder.object (attr);
2376
builder.subject ("?contact");
2377
builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
2378
builder.object (affl);
2382
builder.insert_close ();
2383
builder.where_open ();
2384
builder.subject ("?contact");
2385
builder.predicate ("a");
2386
builder.object (Trf.OntologyDefs.NCO_PERSON);
2387
string filter = " FILTER(tracker:id(?contact) = %s) ".printf (p_id);
2388
builder.append (filter);
2389
builder.where_close ();
2391
yield this._tracker_update (builder.result, "set_attrib");
2394
private async bool _tracker_update (string query, string caller)
2398
debug ("%s: %s", caller, query);
2402
yield this._connection.update_async (query);
2405
catch (Tracker.Sparql.Error e1)
2407
warning ("[%s] SPARQL syntax error: %s. Query: %s",
2408
caller, e1.message, query);
2410
catch (GLib.IOError e2)
2412
warning ("[%s] IO error: %s",
2413
caller, e2.message);
2415
catch (GLib.DBusError e3)
2417
warning ("[%s] DBus error: %s",
2418
caller, e3.message);
2424
private async Gee.HashSet<string> _affiliations_from_persona (string urn)
2426
return yield this._linked_resources (urn, Trf.OntologyDefs.NCO_PERSON,
2427
Trf.OntologyDefs.NCO_HAS_AFFILIATION);
2430
private async Gee.HashSet<string> _phones_from_affiliation (string affl)
2432
return yield this._linked_resources (affl,
2433
Trf.OntologyDefs.NCO_AFFILIATION,
2434
Trf.OntologyDefs.NCO_HAS_PHONE);
2437
private async Gee.HashSet<string> _postals_from_affiliation (string affl)
2439
return yield this._linked_resources (affl,
2440
Trf.OntologyDefs.NCO_AFFILIATION,
2441
Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS);
2444
private async Gee.HashSet<string> _imaddrs_from_affiliation (string affl)
2446
return yield this._linked_resources (affl,
2447
Trf.OntologyDefs.NCO_AFFILIATION,
2448
Trf.OntologyDefs.NCO_HAS_IMADDRESS);
2451
private async Gee.HashSet<string> _emails_from_affiliation (string affl)
2453
return yield this._linked_resources (affl,
2454
Trf.OntologyDefs.NCO_AFFILIATION,
2455
Trf.OntologyDefs.NCO_HAS_EMAIL);
2459
* Retrieve the list of linked resources of a given subject
2461
* @param resource the urn of the resource in <urn> format
2462
* @return number of resources linking to this resource
2464
private async int _resource_usage_count (string resource)
2466
const string query_t = "SELECT " +
2469
" %s a rdfs:Resource . " +
2472
var query = query_t.printf (resource, resource);
2473
var result = yield this._single_value_query (query);
2474
return int.parse (result);
2480
* We asume that the caller is holding a link to the resource,
2481
* so if _resource_usage_count () == 1 it means no one else
2482
* (beside the caller) is linking to the resource.
2484
* This means that _delete_resource shold be called before
2485
* removing the resources that hold a link to it (which also
2486
* makes sense from the signaling perspective).
2488
private async bool _delete_resource (string resource_urn,
2489
bool check_count = true)
2491
bool deleted = false;
2492
var query_t = " DELETE { " +
2493
" %s a rdfs:Resource " +
2496
" %s a rdfs:Resource " +
2499
var query = query_t.printf (resource_urn, resource_urn);
2502
int count = yield this._resource_usage_count (resource_urn);
2505
deleted = yield this._tracker_update (query, "_delete_resource");
2510
deleted = yield this._tracker_update (query, "_delete_resource");
2517
* Retrieve the list of linked resources of a given subject
2519
* @param urn the urn of the subject in <urn> format
2520
* @param subject_type i.e: nco:Person, nco:Affiliation, etc
2521
* @param linking_predicate i.e.: nco:hasAffiliation
2522
* @return a list of linked resources (in <urn> format)
2524
private async Gee.HashSet<string> _linked_resources (string urn,
2525
string subject_type, string linking_predicate)
2527
string query_t = "SELECT " +
2528
" fn:concat('<',?linkedr,'>') " +
2534
var query = query_t.printf (urn, subject_type, linking_predicate);
2535
return yield this._multi_value_query (query);
2538
private async string _urn_from_persona (Folks.Persona persona)
2540
var id = ((Trf.Persona) persona).tracker_id ();
2541
return yield this._urn_from_tracker_id (id);
2545
* Helper method to figure out if a constrained property
2548
private async string _urn_from_property (string class_name,
2549
string property_name,
2550
string property_value)
2552
const string query_template = "SELECT " +
2553
" fn:concat('<', ?o, '>') " +
2556
" %s ?prop_val . " +
2557
"FILTER (?prop_val = '%s') " +
2560
string query = query_template.printf (class_name,
2561
property_name, property_value);
2562
return yield this._single_value_query (query);