1/*
2 * Copyright (C) 2011 Collabora Ltd.
3 * Copyright (C) 2011 Philip Withnall
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 2.1 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors:
19 *       Raul Gutierrez Segales <raul.gutierrez.segales@collabora.co.uk>
20 *       Travis Reitter <travis.reitter@collabora.co.uk>
21 *       Philip Withnall <philip@tecnocode.co.uk>
22 */
23
24using Gee;
25using GLib;
26
27/**
28 * Role a contact has in an organisation.
29 *
30 * This represents the role a {@link Persona} or {@link Individual} has in a
31 * single given organisation, such as a company.
32 *
33 * @since 0.4.0
34 */
35public class Folks.Role : Object
36{
37  private string _organisation_name = "";
38  /**
39   * The name of the organisation in which the role is held.
40   */
41  public string organisation_name
42    {
43      get { return this._organisation_name; }
44      set { this._organisation_name = (value != null ? value : ""); }
45    }
46
47  private string _title = "";
48  /**
49   * The title of the position held.
50   *
51   * For example: “Director, Ministry of Silly Walks”
52   */
53  public string title
54    {
55      get { return this._title; }
56      set { this._title = (value != null ? value : ""); }
57    }
58
59  private string _role = "";
60  /**
61   * The role of the position.
62   *
63   * For example: “Programmer”
64   *
65   * @since 0.6.0
66   */
67  public string role
68    {
69      get { return this._role; }
70      set { this._role = (value != null ? value : ""); }
71    }
72
73  private string _uid = "";
74  /**
75   * The UID that distinguishes this role.
76   */
77  [Version (deprecated = true, deprecated_since = "0.6.5",
78      replacement = "AbstractFieldDetails.id")]
79  public string uid
80    {
81      get { return _uid; }
82      set { _uid = (value != null ? value : ""); }
83    }
84
85  /**
86   * Default constructor.
87   *
88   * @param title title of the position
89   * @param organisation_name organisation where the role is hold
90   * @param uid a Unique ID associated to this Role
91   * @return a new Role
92   *
93   * @since 0.4.0
94   */
95  public Role (string? title = null,
96      string? organisation_name = null, string? uid = null)
97    {
98      Object (uid:                  uid,
99              title:                title,
100              organisation_name:    organisation_name);
101    }
102
103  /**
104   * Whether none of the components is set.
105   *
106   * @return ``true`` if all the components are the empty string, ``false``
107   * otherwise.
108   *
109   * @since 0.6.7
110   */
111  public bool is_empty ()
112    {
113      return this.organisation_name == "" &&
114             this.title == "" &&
115             this.role == "";
116    }
117
118  /**
119   * Compare if two roles are equal. Roles are equal if their titles and
120   * organisation names are equal.
121   *
122   * @param a a role to compare
123   * @param b another role to compare
124   * @return ``true`` if the roles are equal, ``false`` otherwise
125   */
126  public static bool equal (Role a, Role b)
127    {
128      return (a.title == b.title) &&
129          (a.role == b.role) &&
130          (a.organisation_name == b.organisation_name);
131    }
132
133  /**
134   * Hash function for the class. Suitable for use as a hash table key.
135   *
136   * @param r a role to hash
137   * @return hash value for the role instance
138   */
139  public static uint hash (Role r)
140    {
141      return r.organisation_name.hash () ^ r.title.hash () ^ r.role.hash ();
142    }
143
144  /**
145   * Formatted version of this role.
146   *
147   * @since 0.4.0
148   */
149  public string to_string ()
150    {
151      var str = _("Title: %s, Organisation: %s, Role: %s");
152      return str.printf (this.title, this.organisation_name, this.role);
153    }
154}
155
156/**
157 * Object representing details of a contact in an organisation which can have
158 * some parameters associated with it.
159 *
160 * See {@link Folks.AbstractFieldDetails}.
161 *
162 * @since 0.6.0
163 */
164public class Folks.RoleFieldDetails : AbstractFieldDetails<Role>
165{
166  private string _id = "";
167  /**
168   * {@inheritDoc}
169   */
170  public override string id
171    {
172      get { return this._id; }
173      set
174        {
175          this._id = (value != null ? value : "");
176
177          /* Keep the Role.uid sync'd from our id */
178          if (this._id != this.value.uid)
179            this.value.uid = this._id;
180        }
181    }
182
183  /**
184   * Create a new RoleFieldDetails.
185   *
186   * @param value the non-empty {@link Role} of the field
187   * @param parameters initial parameters. See
188   * {@link AbstractFieldDetails.parameters}. A ``null`` value is equivalent to an
189   * empty map of parameters.
190   *
191   * @return a new RoleFieldDetails
192   *
193   * @since 0.6.0
194   */
195  public RoleFieldDetails (Role value,
196      MultiMap<string, string>? parameters = null)
197    {
198      if (value.is_empty ())
199        {
200          warning ("Empty role passed to RoleFieldDetails.");
201        }
202
203      /* We keep id and value.uid synchronised in both directions. */
204      Object (value: value,
205              parameters: parameters,
206              id: value.uid);
207    }
208
209  construct
210    {
211      /* Keep the Role.uid sync'd to our id */
212      this.value.notify["uid"].connect ((s, p) =>
213        {
214          if (this.id != this.value.uid)
215            this.id = this.value.uid;
216        });
217    }
218
219  /**
220   * {@inheritDoc}
221   *
222   * @since 0.6.0
223   */
224  public override bool equal (AbstractFieldDetails<Role> that)
225    {
226      var _that_fd = that as RoleFieldDetails;
227      if (_that_fd == null)
228        return false;
229      RoleFieldDetails that_fd = (!) _that_fd;
230
231      if (!base.parameters_equal (that))
232        return false;
233
234      return Role.equal (this.value, that_fd.value);
235    }
236
237  /**
238   * {@inheritDoc}
239   *
240   * @since 0.6.0
241   */
242  public override uint hash ()
243    {
244      return str_hash (this.value.to_string ());
245    }
246}
247
248/**
249 * This interfaces represents the list of roles a {@link Persona} and
250 * {@link Individual} might have.
251 *
252 * @since 0.4.0
253 */
254public interface Folks.RoleDetails : Object
255{
256  /**
257   * The roles of the contact.
258   *
259   * @since 0.6.0
260   */
261  public abstract Set<RoleFieldDetails> roles { get; set; }
262
263  /**
264   * Change the contact's roles.
265   *
266   * It's preferred to call this rather than setting {@link RoleDetails.roles}
267   * directly, as this method gives error notification and will only return once
268   * the roles have been written to the relevant backing store (or the
269   * operation's failed).
270   *
271   * @param roles the set of roles
272   * @throws PropertyError if setting the roles failed
273   * @since 0.6.2
274   */
275  public virtual async void change_roles (Set<RoleFieldDetails> roles)
276      throws PropertyError
277    {
278      /* Default implementation. */
279      throw new PropertyError.NOT_WRITEABLE (
280          _("Roles are not writeable on this contact."));
281    }
282}
283