summaryrefslogtreecommitdiffstats
path: root/comm/calendar/base/content/dialogs/calendar-summary-dialog.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/calendar/base/content/dialogs/calendar-summary-dialog.js')
-rw-r--r--comm/calendar/base/content/dialogs/calendar-summary-dialog.js381
1 files changed, 381 insertions, 0 deletions
diff --git a/comm/calendar/base/content/dialogs/calendar-summary-dialog.js b/comm/calendar/base/content/dialogs/calendar-summary-dialog.js
new file mode 100644
index 0000000000..0f70375814
--- /dev/null
+++ b/comm/calendar/base/content/dialogs/calendar-summary-dialog.js
@@ -0,0 +1,381 @@
+/* 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/. */
+
+/* exported reply */
+
+/* global MozElements */
+
+/* import-globals-from calendar-dialog-utils.js */
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+var { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs");
+
+ChromeUtils.defineESModuleGetters(this, {
+ SelectionUtils: "resource://gre/modules/SelectionUtils.sys.mjs",
+});
+
+XPCOMUtils.defineLazyGetter(this, "gStatusNotification", () => {
+ return new MozElements.NotificationBox(async element => {
+ let box = document.getElementById("status-notifications");
+ // Fix window size after the notification animation is done.
+ box.addEventListener(
+ "transitionend",
+ () => {
+ window.sizeToContent();
+ },
+ { once: true }
+ );
+ box.append(element);
+ });
+});
+
+window.addEventListener("load", onLoad);
+window.addEventListener("unload", onUnload);
+
+/**
+ * Sets up the summary dialog, setting all needed fields on the dialog from the
+ * item received in the window arguments.
+ */
+async function onLoad() {
+ let args = window.arguments[0];
+ let item = args.calendarEvent;
+ item = item.clone(); // use an own copy of the passed item
+ window.calendarItem = item;
+ window.isInvitation = args.isInvitation;
+ let dialog = document.querySelector("dialog");
+
+ document.title = item.title;
+
+ // set the dialog-id to enable the right CSS to be used.
+ if (item.isEvent()) {
+ setDialogId(dialog, "calendar-event-summary-dialog");
+ } else if (item.isTodo()) {
+ setDialogId(dialog, "calendar-task-summary-dialog");
+ }
+
+ // Start setting up the item summary custom element.
+ let itemSummary = document.getElementById("calendar-item-summary");
+ itemSummary.item = item;
+
+ window.readOnly = itemSummary.readOnly;
+ let calendar = itemSummary.calendar;
+
+ if (!window.readOnly) {
+ let attendee = cal.itip.getInvitedAttendee(item, calendar);
+ if (attendee) {
+ // if this is an unresponded invitation, preset our default alarm values:
+ if (!item.getAlarms().length && attendee.participationStatus == "NEEDS-ACTION") {
+ cal.alarms.setDefaultValues(item);
+ }
+
+ window.attendee = attendee.clone();
+ // Since we don't have API to update an attendee in place, remove
+ // and add again. Also, this is needed if the attendee doesn't exist
+ // (i.e REPLY on a mailing list)
+ item.removeAttendee(attendee);
+ item.addAttendee(window.attendee);
+
+ window.responseMode = "USER";
+ }
+ }
+
+ // Finish setting up the item summary custom element.
+ itemSummary.updateItemDetails();
+
+ updateToolbar();
+ updateDialogButtons(item);
+
+ if (typeof window.ToolbarIconColor !== "undefined") {
+ window.ToolbarIconColor.init();
+ }
+
+ await document.l10n.translateRoots();
+ window.sizeToContent();
+ window.focus();
+ opener.setCursor("auto");
+}
+
+function onUnload() {
+ if (typeof window.ToolbarIconColor !== "undefined") {
+ window.ToolbarIconColor.uninit();
+ }
+}
+
+/**
+ * Updates the user's participation status (PARTSTAT from see RFC5545), and
+ * send a notification if requested. Then close the dialog.
+ *
+ * @param {string} aResponseMode - a literal of one of the response modes defined
+ * in calIItipItem (like 'NONE')
+ * @param {string} aPartStat - participation status; a PARTSTAT value
+ */
+function reply(aResponseMode, aPartStat) {
+ // Set participation status.
+ if (window.attendee) {
+ let aclEntry = window.calendarItem.calendar.aclEntry;
+ if (aclEntry) {
+ let userAddresses = aclEntry.getUserAddresses();
+ if (
+ userAddresses.length > 0 &&
+ !cal.email.attendeeMatchesAddresses(window.attendee, userAddresses)
+ ) {
+ window.attendee.setProperty("SENT-BY", "mailto:" + userAddresses[0]);
+ }
+ }
+ window.attendee.participationStatus = aPartStat;
+ updateToolbar();
+ }
+
+ // Send notification and close window.
+ saveAndClose(aResponseMode);
+}
+
+/**
+ * Stores the event in the calendar, sends a notification if requested and
+ * closes the dialog.
+ *
+ * @param {string} aResponseMode - a literal of one of the response modes defined
+ * in calIItipItem (like 'NONE')
+ */
+function saveAndClose(aResponseMode) {
+ window.responseMode = aResponseMode;
+ document.querySelector("dialog").acceptDialog();
+}
+
+function updateToolbar() {
+ if (window.readOnly || window.isInvitation !== true) {
+ document.getElementById("summary-toolbox").hidden = true;
+ return;
+ }
+
+ let replyButtons = document.getElementsByAttribute("type", "menu-button");
+ for (let element of replyButtons) {
+ element.removeAttribute("hidden");
+ if (window.attendee) {
+ // we disable the control which represents the current partstat
+ let status = window.attendee.participationStatus || "NEEDS-ACTION";
+ if (element.getAttribute("value") == status) {
+ element.setAttribute("disabled", "true");
+ } else {
+ element.removeAttribute("disabled");
+ }
+ }
+ }
+
+ if (window.attendee) {
+ // we display a notification about the users partstat
+ let partStat = window.attendee.participationStatus || "NEEDS-ACTION";
+ let type = window.calendarItem.isEvent() ? "event" : "task";
+
+ let msgStr = {
+ ACCEPTED: type + "Accepted",
+ COMPLETED: "taskCompleted",
+ DECLINED: type + "Declined",
+ DELEGATED: type + "Delegated",
+ TENTATIVE: type + "Tentative",
+ };
+ // this needs to be noted differently to get accepted the '-' in the key
+ msgStr["NEEDS-ACTION"] = type + "NeedsAction";
+ msgStr["IN-PROGRESS"] = "taskInProgress";
+
+ let msg = cal.l10n.getString("calendar-event-dialog", msgStr[partStat]);
+
+ gStatusNotification.appendNotification(
+ "statusNotification",
+ {
+ label: msg,
+ priority: gStatusNotification.PRIORITY_INFO_MEDIUM,
+ },
+ null
+ );
+ } else {
+ gStatusNotification.removeAllNotifications();
+ }
+}
+
+/**
+ * Copy the text content of the given link node to the clipboard.
+ *
+ * @param {string} labelNode - The label node inside an html:a element.
+ */
+function locationCopyLink(labelNode) {
+ let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
+ clipboard.copyString(labelNode.parentNode.getAttribute("href"));
+}
+
+/**
+ * This configures the dialog buttons depending on the writable status
+ * of the item and whether it recurs or not:
+ * 1) The calendar is read-only - The buttons stay hidden.
+ * 2) The item is an invitation - The buttons stay hidden.
+ * 3) The item is recurring - Show an edit menu with occurrence options.
+ * 4) Otherwise - Show the single edit button.
+ *
+ * @param {calIItemBase} item
+ */
+function updateDialogButtons(item) {
+ let editButton = document.getElementById("calendar-summary-dialog-edit-button");
+ let isRecurring = item.parentItem !== item;
+ if (window.readOnly === true) {
+ // This enables pressing the "enter" key to close the dialog.
+ editButton.focus();
+ } else if (window.isInvitation === true) {
+ document.addEventListener("dialogaccept", onInvitationDialogAccept);
+ } else if (isRecurring) {
+ // Show the edit button menu for repeating events.
+ let menuButton = document.getElementById("calendar-summary-dialog-edit-menu-button");
+ menuButton.hidden = false;
+
+ // Pressing the "enter" key will display the occurrence menu.
+ document.getElementById("calendar-summary-dialog-edit-menu-button").focus();
+ document.addEventListener("dialogaccept", evt => {
+ evt.preventDefault();
+ });
+ } else {
+ // Show the single edit button for non-repeating events.
+ document.addEventListener("dialogaccept", () => {
+ useEditDialog(item);
+ });
+ editButton.hidden = false;
+ }
+ // Show the custom dialog footer when the event is editable.
+ if (window.readOnly !== true && window.isInvitation !== true) {
+ let footer = document.getElementById("calendar-summary-dialog-custom-button-footer");
+ footer.hidden = false;
+ }
+}
+
+/**
+ * Saves any changed information to the item.
+ */
+function onInvitationDialogAccept() {
+ // let's make sure we have a response mode defined
+ let resp = window.responseMode || "USER";
+ let respMode = { responseMode: Ci.calIItipItem[resp] };
+
+ let args = window.arguments[0];
+ let oldItem = args.calendarEvent;
+ let newItem = window.calendarItem;
+ let calendar = newItem.calendar;
+ saveReminder(newItem, calendar, document.querySelector(".item-alarm"));
+ adaptScheduleAgent(newItem);
+ args.onOk(newItem, calendar, oldItem, null, respMode);
+ window.calendarItem = newItem;
+}
+
+/**
+ * Invokes the editing dialog for the current item occurrence.
+ */
+function onEditThisOccurrence() {
+ useEditDialog(window.calendarItem);
+}
+
+/**
+ * Invokes the editing dialog for all occurrences of the current item.
+ */
+function onEditAllOccurrences() {
+ useEditDialog(window.calendarItem.parentItem);
+}
+
+/**
+ * Switch to the "modify" mode dialog so the user can make changes to the event.
+ *
+ * @param {calIItemBase} item
+ */
+function useEditDialog(item) {
+ window.addEventListener("unload", () => {
+ window.opener.modifyEventWithDialog(item, false);
+ });
+ window.close();
+}
+
+/**
+ * Initializes the context menu used for the attendees area.
+ *
+ * @param {Event} event
+ */
+function onAttendeeContextMenu(event) {
+ let copyMenu = document.getElementById("attendee-popup-copy-menu");
+ let item = window.arguments[0].calendarEvent;
+
+ let attId =
+ event.target.getAttribute("attendeeid") || event.target.parentNode.getAttribute("attendeeid");
+ let attendee = item.getAttendees().find(att => att.id == attId);
+
+ if (!attendee) {
+ copyMenu.hidden = true;
+ return;
+ }
+
+ let id = attendee.toString();
+ let idMenuItem = document.getElementById("attendee-popup-copy-menu-id");
+ idMenuItem.setAttribute("label", id);
+ idMenuItem.hidden = false;
+
+ let name = attendee.commonName;
+ let nameMenuItem = document.getElementById("attendee-popup-copy-menu-common-name");
+ if (name && name != id) {
+ nameMenuItem.setAttribute("label", name);
+ nameMenuItem.hidden = false;
+ } else {
+ nameMenuItem.hidden = true;
+ }
+
+ copyMenu.hidden = false;
+}
+
+/**
+ * Initializes the context menu used for the event description area in the
+ * event summary.
+ *
+ * @param {Event} event
+ */
+function openDescriptionContextMenu(event) {
+ const popup = document.getElementById("description-popup");
+ const link = event.target.closest("a") ? event.target.closest("a").getAttribute("href") : null;
+ const linkText = event.target.closest("a") ? event.target.closest("a").text : null;
+ const copyLinkTextMenuItem = document.getElementById("description-context-menu-copy-link-text");
+ const copyLinkLocationMenuItem = document.getElementById(
+ "description-context-menu-copy-link-location"
+ );
+ const selectionCollapsed = SelectionUtils.getSelectionDetails(window).docSelectionIsCollapsed;
+
+ // Hide copy command if there is no text selected.
+ popup.querySelector('[command="cmd_copy"]').hidden = selectionCollapsed;
+
+ copyLinkLocationMenuItem.hidden = !link;
+ copyLinkTextMenuItem.hidden = !link;
+ popup.querySelector("#calendar-summary-description-context-menuseparator").hidden =
+ selectionCollapsed && !link;
+ copyLinkTextMenuItem.setAttribute("text", linkText);
+
+ popup.openPopupAtScreen(event.screenX, event.screenY, true, event);
+ event.preventDefault();
+}
+
+/**
+ * Copies the link text in a calender event description
+ * @param {Event} event
+ */
+async function copyLinkTextToClipboard(event) {
+ return navigator.clipboard.writeText(event.target.getAttribute("text"));
+}
+
+/**
+ * Copies the label value of a menuitem to the clipboard.
+ */
+async function copyLabelToClipboard(event) {
+ return navigator.clipboard.writeText(event.target.getAttribute("label"));
+}
+
+/**
+ * Brings up the compose window to send an e-mail to all attendees.
+ */
+function sendMailToAttendees() {
+ let item = window.arguments[0].calendarEvent;
+ let toList = cal.email.createRecipientList(item.getAttendees());
+ let emailSubject = cal.l10n.getString("calendar-event-dialog", "emailSubjectReply", [item.title]);
+ let identity = item.calendar.getProperty("imip.identity");
+ cal.email.sendTo(toList, emailSubject, null, identity);
+}