summaryrefslogtreecommitdiffstats
path: root/comm/calendar/base/content/dialogs/calendar-invitations-dialog.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/calendar/base/content/dialogs/calendar-invitations-dialog.js')
-rw-r--r--comm/calendar/base/content/dialogs/calendar-invitations-dialog.js310
1 files changed, 310 insertions, 0 deletions
diff --git a/comm/calendar/base/content/dialogs/calendar-invitations-dialog.js b/comm/calendar/base/content/dialogs/calendar-invitations-dialog.js
new file mode 100644
index 0000000000..871ffef276
--- /dev/null
+++ b/comm/calendar/base/content/dialogs/calendar-invitations-dialog.js
@@ -0,0 +1,310 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* globals MozXULElement, MozElements */ // From calendar-invitations-dialog.xhtml.
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+// Wrap in a block to prevent leaking to window scope.
+{
+ class MozCalendarInvitationsRichlistitem extends MozElements.MozRichlistitem {
+ constructor() {
+ super();
+
+ this.mCalendarItem = null;
+ this.mInitialParticipationStatus = null;
+ this.mParticipationStatus = null;
+ this.calInvitationsProps = Services.strings.createBundle(
+ "chrome://calendar/locale/calendar-invitations-dialog.properties"
+ );
+ }
+
+ getString(propName) {
+ return this.calInvitationsProps.GetStringFromName(propName);
+ }
+
+ connectedCallback() {
+ if (this.delayConnectedCallback() || this.hasChildNodes()) {
+ return;
+ }
+
+ this.setAttribute("is", "calendar-invitations-richlistitem");
+ this.classList.add("calendar-invitations-richlistitem");
+
+ this.appendChild(
+ MozXULElement.parseXULToFragment(
+ `
+ <hbox align="start" flex="1">
+ <!-- Note: The wrapper div is only here because the XUL box does not
+ - properly crop img elements with CSS object-fit and
+ - object-position. Should be removed when converting the parent
+ - element to HTML. -->
+ <html:div>
+ <html:img class="calendar-invitations-richlistitem-icon"
+ src="chrome://calendar/skin/shared/calendar-invitations-dialog-list-images.png" />
+ </html:div>
+ <vbox flex="1">
+ <label class="calendar-invitations-richlistitem-title" crop="end"/>
+ <label class="calendar-invitations-richlistitem-date" crop="end"/>
+ <label class="calendar-invitations-richlistitem-recurrence" crop="end"/>
+ <label class="calendar-invitations-richlistitem-location" crop="end"/>
+ <label class="calendar-invitations-richlistitem-organizer" crop="end"/>
+ <label class="calendar-invitations-richlistitem-attendee" crop="end"/>
+ <label class="calendar-invitations-richlistitem-spacer" value="" hidden="true"/>
+ </vbox>
+ <vbox>
+ <button group="${this.getAttribute("itemId")}"
+ type="radio"
+ class="calendar-invitations-richlistitem-accept-button
+ calendar-invitations-richlistitem-button"
+ label="&calendar.invitations.list.accept.button.label;"
+ oncommand="accept();"/>
+ <button group="${this.getAttribute("itemId")}"
+ type="radio"
+ class="calendar-invitations-richlistitem-decline-button
+ calendar-invitations-richlistitem-button"
+ label="&calendar.invitations.list.decline.button.label;"
+ oncommand="decline();"/>
+ </vbox>
+ </hbox>
+ `,
+ ["chrome://calendar/locale/calendar-invitations-dialog.dtd"]
+ )
+ );
+ }
+
+ set calendarItem(val) {
+ this.setCalendarItem(val);
+ }
+
+ get calendarItem() {
+ return this.mCalendarItem;
+ }
+
+ set initialParticipationStatus(val) {
+ this.mInitialParticipationStatus = val;
+ }
+
+ get initialParticipationStatus() {
+ return this.mInitialParticipationStatus;
+ }
+
+ set participationStatus(val) {
+ this.mParticipationStatus = val;
+ let icon = this.querySelector(".calendar-invitations-richlistitem-icon");
+ // Status attribute changes the image region in CSS.
+ icon.setAttribute("status", val);
+ document.l10n.setAttributes(
+ icon,
+ `calendar-invitation-current-participation-status-icon-${val.toLowerCase()}`
+ );
+ }
+
+ get participationStatus() {
+ return this.mParticipationStatus;
+ }
+
+ setCalendarItem(item) {
+ this.mCalendarItem = item;
+ this.mInitialParticipationStatus = this.getCalendarItemParticipationStatus(item);
+ this.participationStatus = this.mInitialParticipationStatus;
+
+ let titleLabel = this.querySelector(".calendar-invitations-richlistitem-title");
+ titleLabel.setAttribute("value", item.title);
+
+ let dateLabel = this.querySelector(".calendar-invitations-richlistitem-date");
+ let dateString = cal.dtz.formatter.formatItemInterval(item);
+ if (item.startDate.isDate) {
+ dateString += ", " + this.getString("allday-event");
+ }
+ dateLabel.setAttribute("value", dateString);
+
+ let recurrenceLabel = this.querySelector(".calendar-invitations-richlistitem-recurrence");
+ if (item.recurrenceInfo) {
+ recurrenceLabel.setAttribute("value", this.getString("recurrent-event"));
+ } else {
+ recurrenceLabel.setAttribute("hidden", "true");
+ let spacer = this.querySelector(".calendar-invitations-richlistitem-spacer");
+ spacer.removeAttribute("hidden");
+ }
+
+ let locationLabel = this.querySelector(".calendar-invitations-richlistitem-location");
+ let locationProperty = item.getProperty("LOCATION") || this.getString("none");
+ let locationString = this.calInvitationsProps.formatStringFromName("location", [
+ locationProperty,
+ ]);
+
+ locationLabel.setAttribute("value", locationString);
+
+ let organizerLabel = this.querySelector(".calendar-invitations-richlistitem-organizer");
+ let org = item.organizer;
+ let organizerProperty = "";
+ if (org) {
+ if (org.commonName && org.commonName.length > 0) {
+ organizerProperty = org.commonName;
+ } else if (org.id) {
+ organizerProperty = org.id.replace(/^mailto:/i, "");
+ }
+ }
+ let organizerString = this.calInvitationsProps.formatStringFromName("organizer", [
+ organizerProperty,
+ ]);
+ organizerLabel.setAttribute("value", organizerString);
+
+ let attendeeLabel = this.querySelector(".calendar-invitations-richlistitem-attendee");
+ let att = cal.itip.getInvitedAttendee(item);
+ let attendeeProperty = "";
+ if (att) {
+ if (att.commonName && att.commonName.length > 0) {
+ attendeeProperty = att.commonName;
+ } else if (att.id) {
+ attendeeProperty = att.id.replace(/^mailto:/i, "");
+ }
+ }
+ let attendeeString = this.calInvitationsProps.formatStringFromName("attendee", [
+ attendeeProperty,
+ ]);
+ attendeeLabel.setAttribute("value", attendeeString);
+ Array.from(this.querySelectorAll("button")).map(button =>
+ button.setAttribute("group", item.hashId)
+ );
+ }
+
+ getCalendarItemParticipationStatus(item) {
+ let att = cal.itip.getInvitedAttendee(item);
+ return att ? att.participationStatus : null;
+ }
+
+ setCalendarItemParticipationStatus(item, status) {
+ if (item.calendar?.supportsScheduling) {
+ let att = item.calendar.getSchedulingSupport().getInvitedAttendee(item);
+ if (att) {
+ let att_ = att.clone();
+ att_.participationStatus = status;
+
+ // Update attendee
+ item.removeAttendee(att);
+ item.addAttendee(att_);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ accept() {
+ this.participationStatus = "ACCEPTED";
+ }
+
+ decline() {
+ this.participationStatus = "DECLINED";
+ }
+ }
+ customElements.define("calendar-invitations-richlistitem", MozCalendarInvitationsRichlistitem, {
+ extends: "richlistitem",
+ });
+}
+
+window.addEventListener("DOMContentLoaded", onLoad);
+window.addEventListener("unload", onUnload);
+
+/**
+ * Sets up the invitations dialog from the window arguments, retrieves the
+ * invitations from the invitations manager.
+ */
+async function onLoad() {
+ let title = document.title;
+ let updatingBox = document.getElementById("updating-box");
+ updatingBox.removeAttribute("hidden");
+ opener.setCursor("auto");
+
+ let { invitationsManager } = window.arguments[0];
+ let items = await cal.iterate.mapStream(invitationsManager.getInvitations(), chunk => {
+ document.title = title + " (" + chunk.length + ")";
+ let updatingBox = document.getElementById("updating-box");
+ updatingBox.setAttribute("hidden", "true");
+ let richListBox = document.getElementById("invitations-listbox");
+ for (let item of chunk) {
+ let newNode = document.createXULElement("richlistitem", {
+ is: "calendar-invitations-richlistitem",
+ });
+ richListBox.appendChild(newNode);
+ newNode.calendarItem = item;
+ }
+ });
+
+ invitationsManager.toggleInvitationsPanel(items);
+ updatingBox.setAttribute("hidden", "true");
+
+ let richListBox = document.getElementById("invitations-listbox");
+ if (richListBox.getRowCount() > 0) {
+ richListBox.selectedIndex = 0;
+ } else {
+ let noInvitationsBox = document.getElementById("noinvitations-box");
+ noInvitationsBox.removeAttribute("hidden");
+ }
+}
+
+/**
+ * Cleans up the invitations dialog, cancels pending requests.
+ */
+async function onUnload() {
+ let args = window.arguments[0];
+ return args.invitationsManager.cancelPendingRequests();
+}
+
+/**
+ * Handler function to be called when the accept button is pressed.
+ */
+document.addEventListener("dialogaccept", async () => {
+ let args = window.arguments[0];
+ fillJobQueue(args.queue);
+ await args.invitationsManager.processJobQueue(args.queue);
+ args.finishedCallBack();
+});
+
+/**
+ * Handler function to be called when the cancel button is pressed.
+ */
+document.addEventListener("dialogcancel", () => {
+ let args = window.arguments[0];
+ args.finishedCallBack();
+});
+
+/**
+ * Fills the job queue from the invitations-listbox's items. The job queue
+ * contains objects for all items that have a modified participation status.
+ *
+ * @param queue The queue to fill.
+ */
+function fillJobQueue(queue) {
+ let richListBox = document.getElementById("invitations-listbox");
+ let rowCount = richListBox.getRowCount();
+ for (let i = 0; i < rowCount; i++) {
+ let richListItem = richListBox.getItemAtIndex(i);
+ let newStatus = richListItem.participationStatus;
+ let oldStatus = richListItem.initialParticipationStatus;
+ if (newStatus != oldStatus) {
+ let actionString = "modify";
+ let oldCalendarItem = richListItem.calendarItem;
+ let newCalendarItem = oldCalendarItem.clone();
+
+ // set default alarm on unresponded items that have not been declined:
+ if (
+ !newCalendarItem.getAlarms().length &&
+ oldStatus == "NEEDS-ACTION" &&
+ newStatus != "DECLINED"
+ ) {
+ cal.alarms.setDefaultValues(newCalendarItem);
+ }
+
+ richListItem.setCalendarItemParticipationStatus(newCalendarItem, newStatus);
+ let job = {
+ action: actionString,
+ oldItem: oldCalendarItem,
+ newItem: newCalendarItem,
+ };
+ queue.push(job);
+ }
+ }
+}