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