1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5/* import-globals-from calItemBase.js */
6
7var EXPORTED_SYMBOLS = ["CalAttendee"];
8
9var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
10var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
11
12Services.scriptloader.loadSubScript("resource:///components/calItemBase.js");
13
14/**
15 * Constructor for `calIAttendee` objects.
16 *
17 * @class
18 * @implements {calIAttendee}
19 * @param {string} [icalString] - Optional iCal string for initializing existing attendees.
20 */
21function CalAttendee(icalString) {
22  this.wrappedJSObject = this;
23  this.mProperties = new Map();
24  if (icalString) {
25    this.icalString = icalString;
26  }
27}
28
29CalAttendee.prototype = {
30  QueryInterface: ChromeUtils.generateQI(["calIAttendee"]),
31  classID: Components.ID("{5c8dcaa3-170c-4a73-8142-d531156f664d}"),
32
33  mImmutable: false,
34  get isMutable() {
35    return !this.mImmutable;
36  },
37
38  modify() {
39    if (this.mImmutable) {
40      throw Components.Exception("", Cr.NS_ERROR_OBJECT_IS_IMMUTABLE);
41    }
42  },
43
44  makeImmutable() {
45    this.mImmutable = true;
46  },
47
48  clone() {
49    let a = new CalAttendee();
50
51    if (this.mIsOrganizer) {
52      a.isOrganizer = true;
53    }
54
55    const allProps = ["id", "commonName", "rsvp", "role", "participationStatus", "userType"];
56    for (let prop of allProps) {
57      a[prop] = this[prop];
58    }
59
60    for (let [key, value] of this.mProperties.entries()) {
61      a.setProperty(key, value);
62    }
63
64    return a;
65  },
66  // XXX enforce legal values for our properties;
67
68  icalAttendeePropMap: [
69    { cal: "rsvp", ics: "RSVP" },
70    { cal: "commonName", ics: "CN" },
71    { cal: "participationStatus", ics: "PARTSTAT" },
72    { cal: "userType", ics: "CUTYPE" },
73    { cal: "role", ics: "ROLE" },
74  ],
75
76  mIsOrganizer: false,
77  get isOrganizer() {
78    return this.mIsOrganizer;
79  },
80  set isOrganizer(bool) {
81    this.mIsOrganizer = bool;
82  },
83
84  // icalatt is a calIcalProperty of type attendee
85  set icalProperty(icalatt) {
86    this.modify();
87    this.id = icalatt.valueAsIcalString;
88    this.mIsOrganizer = icalatt.propertyName == "ORGANIZER";
89
90    let promotedProps = {};
91    for (let prop of this.icalAttendeePropMap) {
92      this[prop.cal] = icalatt.getParameter(prop.ics);
93      // Don't copy these to the property bag.
94      promotedProps[prop.ics] = true;
95    }
96
97    // Reset the property bag for the parameters, it will be re-initialized
98    // from the ical property.
99    this.mProperties = new Map();
100
101    for (let [name, value] of cal.iterate.icalParameter(icalatt)) {
102      if (!promotedProps[name]) {
103        this.setProperty(name, value);
104      }
105    }
106  },
107
108  get icalProperty() {
109    let icssvc = cal.getIcsService();
110    let icalatt;
111    if (this.mIsOrganizer) {
112      icalatt = icssvc.createIcalProperty("ORGANIZER");
113    } else {
114      icalatt = icssvc.createIcalProperty("ATTENDEE");
115    }
116
117    if (!this.id) {
118      throw Components.Exception("", Cr.NS_ERROR_NOT_INITIALIZED);
119    }
120    icalatt.valueAsIcalString = this.id;
121    for (let i = 0; i < this.icalAttendeePropMap.length; i++) {
122      let prop = this.icalAttendeePropMap[i];
123      if (this[prop.cal]) {
124        try {
125          icalatt.setParameter(prop.ics, this[prop.cal]);
126        } catch (e) {
127          if (e.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
128            // Illegal values should be ignored, but we could log them if
129            // the user has enabled logging.
130            cal.LOG("Warning: Invalid attendee parameter value " + prop.ics + "=" + this[prop.cal]);
131          } else {
132            throw e;
133          }
134        }
135      }
136    }
137    for (let [key, value] of this.mProperties.entries()) {
138      try {
139        icalatt.setParameter(key, value);
140      } catch (e) {
141        if (e.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
142          // Illegal values should be ignored, but we could log them if
143          // the user has enabled logging.
144          cal.LOG("Warning: Invalid attendee parameter value " + key + "=" + value);
145        } else {
146          throw e;
147        }
148      }
149    }
150    return icalatt;
151  },
152
153  get icalString() {
154    let comp = this.icalProperty;
155    return comp ? comp.icalString : "";
156  },
157  set icalString(val) {
158    let prop = cal.getIcsService().createIcalPropertyFromString(val);
159    if (prop.propertyName != "ORGANIZER" && prop.propertyName != "ATTENDEE") {
160      throw Components.Exception("", Cr.NS_ERROR_ILLEGAL_VALUE);
161    }
162    this.icalProperty = prop;
163  },
164
165  get properties() {
166    return this.mProperties.entries();
167  },
168
169  // The has/get/set/deleteProperty methods are case-insensitive.
170  getProperty(aName) {
171    return this.mProperties.get(aName.toUpperCase());
172  },
173  setProperty(aName, aValue) {
174    this.modify();
175    if (aValue || !isNaN(parseInt(aValue, 10))) {
176      this.mProperties.set(aName.toUpperCase(), aValue);
177    } else {
178      this.mProperties.delete(aName.toUpperCase());
179    }
180  },
181  deleteProperty(aName) {
182    this.modify();
183    this.mProperties.delete(aName.toUpperCase());
184  },
185
186  mId: null,
187  get id() {
188    return this.mId;
189  },
190  set id(aId) {
191    this.modify();
192    // RFC 1738 para 2.1 says we should be using lowercase mailto: urls
193    // we enforce prepending the mailto prefix for email type ids as migration code bug 1199942
194    this.mId = aId ? cal.email.prependMailTo(aId) : null;
195  },
196
197  toString() {
198    const emailRE = new RegExp("^mailto:", "i");
199    let stringRep = (this.id || "").replace(emailRE, "");
200    let commonName = this.commonName;
201
202    if (commonName) {
203      stringRep = commonName + " <" + stringRep + ">";
204    }
205
206    return stringRep;
207  },
208};
209
210makeMemberAttr(CalAttendee, "mCommonName", "commonName", null);
211makeMemberAttr(CalAttendee, "mRsvp", "rsvp", null);
212makeMemberAttr(CalAttendee, "mRole", "role", null);
213makeMemberAttr(CalAttendee, "mParticipationStatus", "participationStatus", "NEEDS-ACTION");
214makeMemberAttr(CalAttendee, "mUserType", "userType", "INDIVIDUAL");
215