persona-store.vala 20.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * Copyright (C) 2010 Collabora Ltd.
 *
 * This library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors:
 *       Travis Reitter <travis.reitter@collabora.co.uk>
 */

using GLib;
22
using Gee;
23

24
25
26
/**
 * Trust level for a {@link PersonaStore}'s {@link Persona}s for linking
 * purposes.
27
28
 *
 * @since 0.1.13
29
30
31
32
33
34
35
36
37
 */
public enum Folks.PersonaStoreTrust
{
  /**
   * The {@link Persona}s aren't trusted at all, and cannot be linked.
   *
   * This should be used for {@link PersonaStore}s where even the
   * {@link Persona} UID could be maliciously edited to corrupt {@link Persona}
   * links, or where the UID changes regularly.
38
39
   *
   * @since 0.1.13
40
41
42
43
44
45
46
47
48
49
   */
  NONE,

  /**
   * Only the {@link Persona.uid} property is trusted for linking.
   *
   * In practice, this means that {@link Persona}s from this
   * {@link PersonaStore} will not contribute towards the linking process, but
   * can be linked together by their UIDs using data from {@link Persona}s from
   * a fully-trusted {@link PersonaStore}.
50
51
   *
   * @since 0.1.13
52
53
54
55
56
57
58
59
60
   */
  PARTIAL,

  /**
   * Every property in {@link Persona.linkable_properties} is trusted.
   *
   * This should only be used for user-controlled {@link PersonaStore}s, as if a
   * remote store is compromised, malicious changes could be made to its data
   * which corrupt the user's {@link Persona} links.
61
62
   *
   * @since 0.1.13
63
64
65
   */
  FULL
}
66
67
68
/**
 * Errors from {@link PersonaStore}s.
 */
69
70
public errordomain Folks.PersonaStoreError
{
71
72
73
  /**
   * An argument to the method was invalid.
   */
74
  INVALID_ARGUMENT,
75
76
77
78

  /**
   * Creation of a {@link Persona} failed.
   */
79
  CREATE_FAILED,
80
81
82
83
84
85
86
87

  /**
   * Such an operation may not be performed on a {@link Persona} with
   * {@link Persona.is_user} set to `true`.
   *
   * @since 0.3.0
   */
  UNSUPPORTED_ON_USER,
88
89
90
91
92
93
94

  /**
   * The {@link PersonaStore} was offline (ie, this is a temporary failure).
   *
   * @since 0.3.0
   */
  STORE_OFFLINE,
95
96
97
98
99
100
101

  /**
   * The {@link PersonaStore} doesn't support write operations.
   *
   * @since 0.3.4
   */
  READ_ONLY,
102
103
104
105

  /**
   * The operation was denied due to not having sufficient permissions.
   *
Travis Reitter's avatar
Travis Reitter committed
106
   * @since 0.6.0
107
108
109
110
111
112
113
114
   */
  PERMISSION_DENIED,

  /**
   * Removal of a {@link Persona} failed. This is a generic error which is used
   * if no other error code (such as, e.g.,
   * {@link PersonaStoreError.PERMISSION_DENIED}) is applicable.
   *
Travis Reitter's avatar
Travis Reitter committed
115
   * @since 0.6.0
116
117
   */
  REMOVE_FAILED,
118
119
120
121
122

  /**
   * Such an operation may only be performed on a {@link Persona} with
   * {@link Persona.is_user} set to `true`.
   *
Travis Reitter's avatar
Travis Reitter committed
123
   * @since 0.6.4
124
125
   */
  UNSUPPORTED_ON_NON_USER,
126
127
}

128
129
130
131
/**
 * Definition of the available fields to be looked up with
 * {@link PersonaStore.detail_key}.
 *
Travis Reitter's avatar
Travis Reitter committed
132
 * @since 0.5.0
133
 */
134
135
/* NOTE: Must be kept in sync with
 * {@link Folks.PersonaStore._PERSONA_DETAIL}. */
136
137
public enum Folks.PersonaDetail
{
138
139
140
  /**
   * Invalid field for use in error returns.
   *
Raul Gutierrez Segales's avatar
Raul Gutierrez Segales committed
141
   * @since 0.6.2
142
143
144
   */
  INVALID = -1,

145
146
147
148
149
  /**
   * Field for {@link AliasDetails.alias}.
   *
   * @since 0.5.0
   */
150
  ALIAS = 0,
151
152
153
154
155
156

  /**
   * Field for {@link AvatarDetails.avatar}.
   *
   * @since 0.5.0
   */
157
  AVATAR,
158
159
160
161
162
163

  /**
   * Field for {@link BirthdayDetails.birthday}.
   *
   * @since 0.5.0
   */
164
  BIRTHDAY,
165
166
167
168
169
170

  /**
   * Field for {@link EmailDetails.email_addresses}.
   *
   * @since 0.5.0
   */
171
  EMAIL_ADDRESSES,
172
173
174
175
176
177

  /**
   * Field for {@link NameDetails.full_name}.
   *
   * @since 0.5.0
   */
178
  FULL_NAME,
179
180
181
182
183
184

  /**
   * Field for {@link GenderDetails.gender}.
   *
   * @since 0.5.0
   */
185
  GENDER,
186
187
188
189
190
191

  /**
   * Field for {@link ImDetails.im_addresses}.
   *
   * @since 0.5.0
   */
192
  IM_ADDRESSES,
193
194
195
196
197
198

  /**
   * Field for {@link FavouriteDetails.is_favourite}.
   *
   * @since 0.5.0
   */
199
  IS_FAVOURITE,
200
201
202
203
204
205

  /**
   * Field for {@link LocalIdDetails.local_ids}.
   *
   * @since 0.5.0
   */
206
  LOCAL_IDS,
207
208
209
210
211
212

  /**
   * Field for {@link NameDetails.nickname}.
   *
   * @since 0.5.0
   */
213
  NICKNAME,
214
215
216
217
218
219

  /**
   * Field for {@link NoteDetails.notes}.
   *
   * @since 0.5.0
   */
220
  NOTES,
221
222
223
224
225
226

  /**
   * Field for {@link PhoneDetails.phone_numbers}.
   *
   * @since 0.5.0
   */
227
  PHONE_NUMBERS,
228
229
230
231
232
233

  /**
   * Field for {@link PostalAddressDetails.postal_addresses}.
   *
   * @since 0.5.0
   */
234
  POSTAL_ADDRESSES,
235
236
237
238
239
240

  /**
   * Field for {@link RoleDetails.roles}.
   *
   * @since 0.5.0
   */
241
  ROLES,
242
243
244
245
246
247

  /**
   * Field for {@link NameDetails.structured_name}.
   *
   * @since 0.5.0
   */
248
  STRUCTURED_NAME,
249
250
251
252
253
254

  /**
   * Field for {@link UrlDetails.urls}.
   *
   * @since 0.5.0
   */
255
  URLS,
256
257
258
259
260
261

  /**
   * Field for {@link WebServiceDetails.web_service_addresses}.
   *
   * @since 0.5.0
   */
262
263
264
265
266
  WEB_SERVICE_ADDRESSES,

  /**
   * Field for {@link GroupDetails.groups}.
   *
Raul Gutierrez Segales's avatar
Raul Gutierrez Segales committed
267
   * @since 0.6.2
268
   */
269
270
271
272
273
  GROUPS,

  /**
   * Field for {@link InteractionDetails.im_interaction_count}.
   *
Travis Reitter's avatar
Travis Reitter committed
274
   * @since 0.7.1
275
276
277
278
279
280
   */
  IM_INTERACTION_COUNT,

  /**
   * Field for {@link InteractionDetails.last_im_interaction_datetime}.
   *
Travis Reitter's avatar
Travis Reitter committed
281
   * @since 0.7.1
282
283
284
285
286
287
   */
  LAST_IM_INTERACTION_DATETIME,

  /**
   * Field for {@link InteractionDetails.call_interaction_count}.
   *
Travis Reitter's avatar
Travis Reitter committed
288
   * @since 0.7.1
289
290
291
292
293
294
   */
  CALL_INTERACTION_COUNT,

  /**
   * Field for {@link InteractionDetails.last_call_interaction_datetime}.
   *
Travis Reitter's avatar
Travis Reitter committed
295
   * @since 0.7.1
296
   */
297
298
299
300
301
  LAST_CALL_INTERACTION_DATETIME,

  /**
   * Field for {@link AntiLinkable.anti_links}.
   *
Philip Withnall's avatar
Philip Withnall committed
302
   * @since 0.7.3
303
304
   */
  ANTI_LINKS,
305
306
}

307
308
/**
 * A store for {@link Persona}s.
309
310
311
312
313
314
 *
 * After creating a PersonaStore instance, you must connect to the
 * {@link PersonaStore.personas_changed} signal, //then// call
 * {@link PersonaStore.prepare}, otherwise a race condition may occur between
 * emission of {@link PersonaStore.personas_changed} and your code connecting to
 * it.
315
 */
316
public abstract class Folks.PersonaStore : Object
317
{
318
319
320
321
322
323
324
325
326
327
  construct
    {
      debug ("Constructing PersonaStore ‘%s’ (%p)", this.id, this);
    }

  ~PersonaStore ()
    {
      debug ("Destroying PersonaStore ‘%s’ (%p)", this.id, this);
    }

328
  /**
329
330
   * The following list of properties are the basic keys
   * that each PersonaStore with write capabilities should
331
   * support for {@link PersonaStore.add_persona_from_details}.
332
333
334
335
336
   *
   * Note that these aren't the only valid keys; backends are
   * allowed to support keys beyond the ones defined here
   * which might be specific to the backend in question.
   *
337
   * NOTE: MUST be kept in sync with {@link Folks.PersonaDetail}.
338
   *
Travis Reitter's avatar
Travis Reitter committed
339
   * @since 0.5.0
340
341
342
343
344
345
   */
  private static const string _PERSONA_DETAIL[] = {
    "alias",
    "avatar",
    "birthday",
    "email-addresses",
346
347
    "full-name",
    "gender",
348
    "im-addresses",
349
    "is-favourite",
350
    "local-ids",
351
    "nickname",
352
353
354
355
    "notes",
    "phone-numbers",
    "postal-addresses",
    "roles",
356
357
    "structured-name",
    "urls",
358
    "web-service-addresses",
359
360
361
362
    "groups",
    "im-interaction-count",
    "last-im-interaction-datetime",
    "call-interaction-count",
363
364
    "last-call-interaction-datetime",
    "anti-links"
365
366
367
368
  };

  /**
   * Returns the key corresponding to @detail, for use in
369
   * the details param of {@link PersonaStore.add_persona_from_details}.
370
371
   *
   * @param detail the {@link PersonaDetail} to lookup
372
   * @return the corresponding property name, or `null` if `detail` is invalid
373
   *
Travis Reitter's avatar
Travis Reitter committed
374
   * @since 0.5.0
375
   */
376
  public static unowned string? detail_key (Folks.PersonaDetail detail)
377
    {
378
379
380
381
382
383
      if (detail == PersonaDetail.INVALID ||
          detail >= PersonaStore._PERSONA_DETAIL.length)
        {
          return null;
        }

384
      return PersonaStore._PERSONA_DETAIL[detail];
385
386
    }

Philip Withnall's avatar
Philip Withnall committed
387
  /**
388
389
   * Emitted when one or more {@link Persona}s are added to or removed from
   * the store.
390
   *
391
392
393
   * This will not be emitted until after {@link PersonaStore.prepare} has been
   * called.
   *
394
395
   * @param added a set of {@link Persona}s which have been removed
   * @param removed a set of {@link Persona}s which have been removed
396
397
398
   * @param message a string message from the backend, if any
   * @param actor the {@link Persona} who made the change, if known
   * @param reason the reason for the change
399
   *
Travis Reitter's avatar
Travis Reitter committed
400
   * @since 0.5.1
401
   */
402
403
  public signal void personas_changed (Set<Persona> added,
      Set<Persona> removed,
404
405
      string? message,
      Persona? actor,
406
      GroupDetails.ChangeReason reason);
Travis Reitter's avatar
Travis Reitter committed
407

408
409
410
411
412
413
414
415
416
417
418
  /* Emit the personas-changed signal, turning null parameters into empty sets
   * and only passing a read-only view to the signal handlers. */
  protected void _emit_personas_changed (Set<Persona>? added,
      Set<Persona>? removed,
      string? message = null,
      Persona? actor = null,
      GroupDetails.ChangeReason reason = GroupDetails.ChangeReason.NONE)
    {
      var _added = added;
      var _removed = removed;

Philip Withnall's avatar
Philip Withnall committed
419
420
      if ((added == null || ((!) added).size == 0) &&
          (removed == null || ((!) removed).size == 0))
421
422
423
424
425
426
427
428
429
430
431
432
433
        {
          /* Don't bother signalling if nothing's changed */
          return;
        }
      else if (added == null)
        {
          _added = new HashSet<Persona> ();
        }
      else if (removed == null)
        {
          _removed = new HashSet<Persona> ();
        }

434
435
436
      Internal.profiling_point ("emitting PersonaStore::personas-changed " +
          "(ID: %s)", this.id);

Philip Withnall's avatar
Philip Withnall committed
437
438
439
      // We've now guaranteed that both _added and _removed are non-null.
      this.personas_changed (((!) _added).read_only_view,
          ((!) _removed).read_only_view, message, actor, reason);
440
441
    }

442
443
444
445
446
  /**
   * Emitted when the backing store for this PersonaStore has been removed.
   *
   * At this point, the PersonaStore and all its {@link Persona}s are invalid,
   * so any client referencing it should unreference it.
447
448
449
   *
   * This will not be emitted until after {@link PersonaStore.prepare} has been
   * called.
450
   */
Travis Reitter's avatar
Travis Reitter committed
451
  public abstract signal void removed ();
452

453
454
455
456
  /**
   * The type of PersonaStore this is.
   *
   * This is the same for all PersonaStores provided by a given {@link Backend}.
457
458
459
   *
   * This is guaranteed to always be available; even before
   * {@link PersonaStore.prepare} is called.
460
   */
461
462
463
  public abstract string type_id
    {
      /* Note: the type_id must not contain colons because the primary writeable
464
       * store is configured, either via GSettings or the FOLKS_PRIMARY_STORE
465
466
467
       * env variable, with a string of the form 'type_id:store_id'. */
      get;
    }
468

469
470
471
472
473
474
475
476
477
478
479
480
  /**
   * The human-readable, service-specific name used to represent the
   * PersonaStore to the user.
   *
   * For example: `foo@@xmpp.example.org`.
   *
   * This should be used whenever the user needs to be presented with a
   * familiar, service-specific name. For instance, in a prompt for the user to
   * select a specific IM account from which to initiate a chat.
   *
   * This is not guaranteed to be unique even within this PersonaStore's
   * {@link Backend}.
481
482
   *
   * @since 0.1.13
483
   */
484
  public string display_name { get; construct; }
485

486
487
488
489
490
491
492
  /**
   * The instance identifier for this PersonaStore.
   *
   * Since each {@link Backend} can provide multiple different PersonaStores
   * for different accounts or servers (for example), they each need an ID
   * which is unique within the backend.
   */
493
  public string id { get; construct; }
494
495
496

  /**
   * The {@link Persona}s exposed by this PersonaStore.
497
   *
Travis Reitter's avatar
Travis Reitter committed
498
   * @since 0.5.1
499
   */
500
  public abstract Map<string, Persona> personas { get; }
501

502
503
504
505
506
507
508
  /**
   * Whether this {@link PersonaStore} can add {@link Persona}s.
   *
   * @since 0.3.1
   */
  public abstract MaybeBool can_add_personas { get; default = MaybeBool.UNSET; }

509
510
511
512
513
  /**
   * Whether this {@link PersonaStore} can set the alias of {@link Persona}s.
   *
   * @since 0.3.1
   */
Travis Reitter's avatar
Travis Reitter committed
514
  [Deprecated (since = "0.6.3.1",
515
      replacement = "PersonaStore.always_writeable_properties")]
516
517
518
519
520
521
  public abstract MaybeBool can_alias_personas
    {
      get;
      default = MaybeBool.UNSET;
    }

522
523
524
525
526
  /**
   * Whether this {@link PersonaStore} can set the groups of {@link Persona}s.
   *
   * @since 0.3.1
   */
Travis Reitter's avatar
Travis Reitter committed
527
  [Deprecated (since = "0.6.3.1",
528
      replacement = "PersonaStore.always_writeable_properties")]
529
530
531
532
533
534
  public abstract MaybeBool can_group_personas
    {
      get;
      default = MaybeBool.UNSET;
    }

535
536
537
538
539
540
541
542
543
544
545
  /**
   * Whether this {@link PersonaStore} can remove {@link Persona}s.
   *
   * @since 0.3.1
   */
  public abstract MaybeBool can_remove_personas
    {
      get;
      default = MaybeBool.UNSET;
    }

546
547
548
549
550
551
552
553
  /**
   * Whether {@link PersonaStore.prepare} has successfully completed for this
   * store.
   *
   * @since 0.3.0
   */
  public abstract bool is_prepared { get; default = false; }

554
555
556
557
558
559
560
561
562
  /**
   * Whether the store has reached a quiescent state. This will happen at some
   * point after {@link PersonaStore.prepare} has successfully completed for the
   * store. A store is in a quiescent state when all the {@link Persona}s that
   * it originally knows about have been loaded.
   *
   * It's guaranteed that this property's value will only ever change after
   * {@link IndividualAggregator.is_prepared} has changed to `true`.
   *
Raul Gutierrez Segales's avatar
Raul Gutierrez Segales committed
563
   * @since 0.6.2
564
565
566
   */
  public abstract bool is_quiescent { get; default = false; }

567
568
569
   /**
   * Whether the PersonaStore is writeable.
   *
570
571
572
573
574
575
576
577
   * Only if a PersonaStore is writeable will its {@link Persona}s be updated by
   * changes to the {@link Individual}s containing them, and those changes then
   * be written out to the relevant backing store.
   *
   * If this property is `false`, it doesn't mean that {@link Persona}s in this
   * persona store aren't writeable at all. If their properties are updated
   * through the {@link Persona}, rather than through the {@link Individual}
   * containing that persona, changes may be propagated to the backing store.
578
579
580
   *
   * PersonaStores must not set this property themselves; it will be set as
   * appropriate by the {@link IndividualAggregator}.
581
582
   *
   * @since 0.1.13
583
   */
Philip Withnall's avatar
Philip Withnall committed
584
  [Deprecated (since = "0.6.3",
585
      replacement = "PersonaStore.is_primary_store")]
586
587
  public bool is_writeable { get; set; default = false; }

588
589
  private PersonaStoreTrust _trust_level = PersonaStoreTrust.NONE;

590
591
592
593
594
595
596
597
  /**
   * The trust level of the PersonaStore for linking.
   *
   * Each {@link PersonaStore} is assigned a trust level by the
   * IndividualAggregator, designating whether to trust the properties of its
   * {@link Persona}s for linking to produce {@link Individual}s.
   *
   * @see PersonaStoreTrust
598
   * @since 0.1.13
599
600
601
   */
  public PersonaStoreTrust trust_level
    {
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
      get
        { 
          return this._trust_level; 
        }
      
      set 
        {
          if (value > trust_level)
            {
              this._trust_level = value;
              this.notify_property ("trust-level");
            }
          else
            {
              debug ("Unable to lower Persona Store trust_level");
            }
        }
619
620
    }

621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
  /**
   * The names of the properties of the {@link Persona}s in this store which are
   * always writeable.
   *
   * If a property name is in this list, setting the property on a persona
   * should result in the updated value being stored in the backend's permanent
   * storage (unless it gets rejected due to being invalid, or a different error
   * occurs).
   *
   * This property value is guaranteed to be constant for a given persona store,
   * but may vary between persona stores in the same backend. It's guaranteed
   * that this will always be a subset of the value of
   * {@link Persona.writeable_properties} for the personas in this persona
   * store.
   *
Raul Gutierrez Segales's avatar
Raul Gutierrez Segales committed
636
   * @since 0.6.2
637
638
639
   */
  public abstract string[] always_writeable_properties { get; }

640
641
642
643
644
645
646
647
648
649
650
651
  /**
   * Prepare the PersonaStore for use.
   *
   * This connects the PersonaStore to whichever backend-specific services it
   * requires to be able to provide {@link Persona}s. This should be called
   * //after// connecting to the {@link PersonaStore.personas_changed} signal,
   * or a race condition could occur, with the signal being emitted before your
   * code has connected to it, and {@link Persona}s getting "lost" as a result.
   *
   * This is normally handled transparently by the {@link IndividualAggregator}.
   *
   * If this function throws an error, the PersonaStore will not be functional.
652
   *
653
654
   * This function is guaranteed to be idempotent (since version 0.3.0).
   *
655
656
657
658
659
660
   * Concurrent calls to this function from different threads will block until
   * preparation has completed. However, concurrent calls to this function from
   * a single thread might not, i.e. the first call will block but subsequent
   * calls might return before the first one. (Though they will be safe in every
   * other respect.)
   *
661
662
663
   * @throws GLib.Error if preparing the backend-specific services failed — this
   * will be a backend-specific error
   *
664
   * @since 0.1.11
665
666
667
   */
  public abstract async void prepare () throws GLib.Error;

668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
  /**
   * Flush any pending changes to the PersonaStore's backing store.
   *
   * PersonaStores may (transparently) implement caching or I/O queueing which
   * means that changes to their {@link Persona}s may not be immediately written
   * to the PersonaStore's backing store. Calling this function will force all
   * pending changes to be flushed to the backing store.
   *
   * This must not be called before {@link PersonaStore.prepare}.
   *
   * @since 0.1.17
   */
  public virtual async void flush ()
    {
      /* Default implementation doesn't have to do anything */
    }

685
686
687
688
  /**
   * Add a new {@link Persona} to the PersonaStore.
   *
   * The {@link Persona} will be created by the PersonaStore backend from the
689
   * key-value pairs given in `details`.
690
   *
691
692
693
694
695
   * All additions through this function will later be emitted through the
   * personas-changed signal to be notified of the new {@link Persona}. The
   * return value is purely for convenience, since it can be complicated to
   * correlate the provided details with the final Persona.
   *
696
   * If the store is offline (or {@link PersonaStore.prepare} hasn't yet been
Philip Withnall's avatar
Philip Withnall committed
697
   * called successfully), this function will throw
698
699
700
701
   * {@link PersonaStoreError.STORE_OFFLINE}. It's the responsibility of the
   * caller to cache details and re-try this function if it wishes to make
   * offline adds work.
   *
702
   * If the details are not recognised or are invalid,
703
704
705
   * {@link PersonaStoreError.INVALID_ARGUMENT} will be thrown. A default set
   * of possible details are defined by {@link Folks.PersonaDetail} but backends
   * can either support a subset or superset of the suggested defaults.
706
   *
707
708
   * If a {@link Persona} with the given details already exists in the store, no
   * error will be thrown and this function will return `null`.
709
   *
710
711
   * @param details a key-value map of details to use in creating the new
   * {@link Persona}
712
713
714
715
716
   *
   * @return the new {@link Persona} or `null` if the corresponding Persona
   * already existed. If non-`null`, the new {@link Persona} will also be
   * amongst the {@link Persona}(s) in a future emission of
   * {@link PersonaStore.personas_changed}.
717
   * @throws PersonaStoreError if adding the persona failed
718
   */
719
  public abstract async Persona? add_persona_from_details (
720
      HashTable<string, Value?> details) throws Folks.PersonaStoreError;
721
722
723
724

  /**
   * Remove a {@link Persona} from the PersonaStore.
   *
725
726
727
728
729
   * It isn't guaranteed that the Persona will actually be removed by the time
   * this asynchronous function finishes. The successful removal of the Persona
   * will be signalled through emission of
   * {@link PersonaStore.personas_changed}.
   *
730
   * If the store is offline (or {@link PersonaStore.prepare} hasn't yet been
Philip Withnall's avatar
Philip Withnall committed
731
732
733
734
735
   * called successfully), this function will throw
   * {@link PersonaStoreError.STORE_OFFLINE}. It's the responsibility of the
   * caller to cache details and re-try this function if it wishes to make
   * offline removals work.
   *
736
   * @param persona the {@link Persona} to remove
737
738
   * @throws PersonaStoreError if removing the persona failed
   *
739
   * @since 0.1.11
740
   */
741
742
  public abstract async void remove_persona (Persona persona)
      throws Folks.PersonaStoreError;
743
744
745
746
747

  /**
   * Whether this {@link PersonaStore} is the primary store which is
   * to be used for linking {@link Persona}s and such.
   *
Philip Withnall's avatar
Philip Withnall committed
748
   * @since 0.6.3
749
750
   */
  public bool is_primary_store { get; internal set; default = false; }
751
752
753
754
755
756

  /**
   * Whether this {@link PersonaStore} has been marked as the default
   * store (in its backend) by the user. I.e.: a PersonaStore for the e-d-s
   * backend would set this to true if it represents the default address book.
   *
Philip Withnall's avatar
Philip Withnall committed
757
   * @since 0.6.3
758
759
   */
  public bool is_user_set_default { get; internal set; default = false; }
760
}