Fix bug 404900 - Add Accept/Decline to Calendar item's context menu. r=nomisvai
authorPhilipp Kewisch <mozilla@kewis.ch>
Sun, 27 Feb 2011 13:51:00 +0100
changeset 10369 28b7f3e771a1358244c216bbad1f0a2a43d3c56c
parent 10368 c81a17a68ce60b9ce81f2b6d2d8715c1a7fa21d4
child 10370 a67b394ad448997526fc97854752fec9f6a1d3bd
push id6
push userbienvenu@nventure.com
push dateWed, 16 May 2012 22:24:51 +0000
treeherdertry-comm-central@ac395aea3008 [default view] [failures only]
reviewersnomisvai
bugs404900
Fix bug 404900 - Add Accept/Decline to Calendar item's context menu. r=nomisvai Fix bug 404900 - Add Accept/Decline to Calendar item's context menu. r=mschroeder
calendar/base/content/agenda-listbox.js
calendar/base/content/agenda-listbox.xml
calendar/base/content/calendar-common-sets.js
calendar/base/content/calendar-common-sets.xul
calendar/base/content/calendar-item-editing.js
calendar/base/content/calendar-task-tree.js
calendar/base/content/calendar-ui-utils.js
calendar/base/content/today-pane.xul
calendar/base/themes/pinstripe/calendar-views.css
calendar/base/themes/winstripe/calendar-views.css
--- a/calendar/base/content/agenda-listbox.js
+++ b/calendar/base/content/agenda-listbox.js
@@ -598,35 +598,31 @@ function createNewEvent(aEvent) {
         // isDate = true automatically makes the start time be the next full hour.
         var eventStart = agendaListbox.today.start.clone();
         eventStart.isDate = true;
         createEventWithDialog(getSelectedCalendar(), eventStart);
     }
 }
 
 /**
- * Rebuilds the agenda popup menu from the agenda-menu-box, disabling items if a
- * period was selected.
+ * Sets up the context menu for the agenda listbox
  *
- * XXX Why is this needed?
- *
+ * @param popup         The <menupopup> element to set up.
  */
-agendaListbox.buildAgendaPopupMenu =
-function enableAgendaPopupMenu() {
-    var listItem = this.agendaListboxControl.selectedItem;
-    var enabled = this.isEventListItem(listItem);
-    var popup = document.getElementById("agenda-menu");
-    while (popup.hasChildNodes()) {
-        popup.removeChild(popup.firstChild);
+agendaListbox.setupContextMenu =
+function setupContextMenu(popup) {
+    let listItem = this.agendaListboxControl.selectedItem;
+    let enabled = this.isEventListItem(listItem);
+    let menuitems = popup.childNodes;
+    for (let i = 0; i < menuitems.length; i++) {
+        setBooleanAttribute(menuitems[i], "disabled", !enabled);
     }
-    var menuitems = document.getElementById("agenda-menu-box").childNodes;
-    for (var i= 0; i < menuitems.length; i++) {
-        setBooleanAttribute(menuitems[i], "disabled", !enabled);
-        popup.appendChild(menuitems[i].cloneNode(true));
-    }
+
+    let menu = document.getElementById("calendar-today-pane-menu-attendance-menu");
+    setupAttendanceMenu(menu, agendaListbox.getSelectedItems({}));
 }
 
 
 /**
  * Refreshes the agenda listbox. If aStart or aEnd is not passed, the agenda
  * listbox's limiting dates will be used.
  *
  * @param aStart        (optional) The start date for the item query.
--- a/calendar/base/content/agenda-listbox.xml
+++ b/calendar/base/content/agenda-listbox.xml
@@ -53,25 +53,23 @@
       <property name="occurrence" readonly="true">
         <getter><![CDATA[
           return this.mOccurrence;
         ]]></getter>
       </property>
     </implementation>
     <handlers>
       <handler event="click" phase="capturing"><![CDATA[
-        // We only care about button 0 (left click) events
-        if (event.button == 0) { 
-            if (event.detail == 1) {
-                agendaListbox.onSelect(this);
-            } else {
-                document.getElementById('agenda_edit_event_command').doCommand();
-                event.stopPropagation();
-                event.preventDefault();
-            }
+        if (event.detail == 1) {
+            agendaListbox.onSelect(this);
+        } else if (event.button == 0) {
+            // We only care about button 0 doubleclick events
+            document.getElementById('agenda_edit_event_command').doCommand();
+            event.stopPropagation();
+            event.preventDefault();
         }
       ]]></handler>
       <handler event="mouseover"><![CDATA[
          event.stopPropagation();
          onMouseOverItem(event);
       ]]></handler>
     </handlers>
   </binding>
--- a/calendar/base/content/calendar-common-sets.js
+++ b/calendar/base/content/calendar-common-sets.js
@@ -183,23 +183,32 @@ var calendarController = {
             case "calendar_toggle_workdays_only_command":
                 return this.isInMode("calendar") &&
                        currentView().supportsWorkdaysOnly;
             case "calendar_publish_selected_events_command":
                 return this.item_selected;
 
             case "calendar_reload_remote_calendar":
                 return !this.no_network_calendars && !this.offline;
+            case "calendar_attendance_command": {
+                let attendSel = false;
+                if (this.todo_tasktree_focused) {
+                    attendSel = this.writable &&
+                                this.todo_items_invitation &&
+                                this.todo_items_selected &&
+                                this.todo_items_writable;
+                } else {
+                    attendSel = this.item_selected && this.selected_events_invitation;
+                }
 
-            case "calendar_attendance_command":
                 // Small hack, we want to hide instead of disable.
-                let attendSel = this.item_selected && this.selected_events_invitation;
                 setBooleanAttribute("calendar_attendance_command", "hidden", !attendSel);
                 return attendSel;
                 break;
+            }
 
             // The following commands all just need the calendar in foreground,
             // make sure you take care when changing things here.
             case "calendar_view_next_command":
             case "calendar_view_prev_command":
             case "calendar_in_foreground":
                 return this.isCalendarInForeground();
             case "calendar_in_background":
@@ -624,16 +633,37 @@ var calendarController = {
     /**
      * Returns a boolean indicating that tasks are selected.
      */
     get todo_items_selected() {
         var selectedTasks = getSelectedTasks();
         return (selectedTasks.length > 0);
     },
 
+
+    get todo_items_invitation() {
+        let selectedTasks = getSelectedTasks();
+        let selected_tasks_invitation = 0;
+
+        for each (let item in selectedTasks) {
+            if (cal.isInvitation(item)) {
+                selected_tasks_invitation++;
+            } else if (item.organizer) {
+                // If we are the organizer and there are attendees, then
+                // this is likely also an invitation.
+                let calOrgId = item.calendar.getProperty("organizerId");
+                if (item.organizer.id == calOrgId && item.getAttendees({}).length) {
+                    selected_tasks_invitation++;
+                }
+            }
+        }
+
+        return (selectedTasks.length == selected_tasks_invitation);
+    },
+
     /**
      * Returns a boolean indicating that at least one task in the selection is
      * on a calendar that is writable.
      */
     get todo_items_writable() {
         var selectedTasks = getSelectedTasks();
         for each (var task in selectedTasks) {
             if (isCalendarWritable(task.calendar)) {
@@ -825,47 +855,17 @@ function setupContextItemType(event, ite
         event.target.setAttribute("type", "todo");
         adaptModificationMenuItem("calendar-item-context-menu-delete-menuitem", "Task");
     } else {
         event.target.removeAttribute("type");
         adaptModificationMenuItem("calendar-item-context-menu-delete-menuitem", "Item");
     }
 
     let menu = document.getElementById("calendar-item-context-menu-attendance-menu");
-    let allSingle = items.every(function(x) !x.recurrenceId);
-    setElementValue(menu, allSingle ? "single" : "recurring", "itemType");
-
-    // Set up the attendance menu
-    function getInvStat(item) {
-        let attendee = null;
-        if (cal.isInvitation(item)) {
-            attendee = cal.getInvitedAttendee(item);
-        } else if (item.organizer) {
-            let calOrgId = item.calendar.getProperty("organizerId");
-            if (calOrgId == item.organizer.id && item.getAttendees({}).length) {
-                attendee = item.organizer;
-            }
-        }
-        return attendee && attendee.participationStatus;
-    }
-
-    let firstStatusOccurrences = items.length && getInvStat(items[0]);
-    let firstStatusParents = items.length && getInvStat(items[0].parentItem);
-    let sameStatusOccurrences = items.every(function (x) getInvStat(x) == firstStatusOccurrences);
-    let sameStatusParents = items.every(function (x) getInvStat(x.parentItem) == firstStatusParents)
-
-    let occurrenceChildren = menu.getElementsByAttribute("value", firstStatusOccurrences);
-    let parentsChildren = menu.getElementsByAttribute("value", firstStatusParents);
-    if (sameStatusOccurrences && occurrenceChildren[0]) {
-        occurrenceChildren[0].setAttribute("checked", "true");
-    }
-
-    if (sameStatusParents && parentsChildren[1]) {
-        parentsChildren[1].setAttribute("checked", "true");
-    }
+    setupAttendanceMenu(menu, items);
 
     return true;
 }
 
 /**
  * Shows the given date in the current view, if in calendar mode.
  *
  * XXX This function is misplaced, should go to calendar-views.js or a minimonth
--- a/calendar/base/content/calendar-common-sets.xul
+++ b/calendar/base/content/calendar-common-sets.xul
@@ -238,16 +238,17 @@
       </menu>
       <menuseparator id="calendar-menuseparator-before-delete"/>
       <!-- the label and accesskey of the following menuitem is set during runtime,
            and depends on wether the item is a task or an event-->
       <menuitem id="calendar-item-context-menu-delete-menuitem"
                 key="calendar-delete-item-key"
                 observes="calendar_delete_event_command"/>
       <menu id="calendar-item-context-menu-attendance-menu"
+            class="attendance-menu"
             label="&calendar.context.attendance.menu.label;"
             accesskey="&calendar.context.attendance.menu.accesskey;"
             oncommand="setContextPartstat(event.target.value, event.target.getAttribute('scope'), currentView().getSelectedItems({}))"
             observes="calendar_attendance_command">
         <menupopup id="calendar-item-context-menu-attendance-menupopup">
           <label id="calendar-item-context-attendance-thisoccurrence-label"
                  class="calendar-context-heading-label"
                  scope="all-occurrences"
@@ -420,11 +421,68 @@
                     oncommand="tasksToEvents(event)"/>
         </menupopup>
       </menu>
       <menuseparator/>
       <menuitem id="task-context-menu-delete"
                 label="&calendar.context.deletetask.label;"
                 accesskey="&calendar.context.deletetask.accesskey;"
                 observes="calendar_delete_todo_command"/>
+      <menu id="task-context-menu-attendance-menu"
+            class="attendance-menu"
+            label="&calendar.context.attendance.menu.label;"
+            accesskey="&calendar.context.attendance.menu.accesskey;"
+            oncommand="setContextPartstat(event.target.value, event.target.getAttribute('scope'), getSelectedTasks())"
+            observes="calendar_attendance_command">
+        <menupopup id="task-context-menu-attendance-menupopup">
+          <label id="task-context-attendance-thisoccurrence-label"
+                 class="calendar-context-heading-label"
+                 scope="all-occurrences"
+                 value="&calendar.context.attendance.occurrence.label;"/>
+          <menuitem id="task-context-menu-attend-accept-menuitem"
+                    type="radio"
+                    scope="this-occurrence"
+                    name="task-context-attendance"
+                    label="&read.only.accept.label;" value="ACCEPTED"/>
+          <menuitem id="task-context-menu-attend-tentative-menuitem"
+                    type="radio"
+                    scope="this-occurrence"
+                    name="task-context-attendance"
+                    label="&read.only.tentative.label;" value="TENTATIVE"/>
+          <menuitem id="task-context-menu-attend-declined-menuitem"
+                    type="radio"
+                    scope="this-occurrence"
+                    name="task-context-attendance"
+                    label="&read.only.decline.label;" value="DECLINED"/>
+          <menuitem id="task-context-menu-attend-needsaction-menuitem"
+                    type="radio"
+                    scope="this-occurrence"
+                    name="task-context-attendance"
+                    label="&read.only.needs.action.label;" value="NEEDS-ACTION"/>
+          <label id="task-context-attendance-alloccurrence-label"
+                 class="calendar-context-heading-label"
+                 scope="all-occurrences"
+                 value="&calendar.context.attendance.all.label;"/>
+          <menuitem id="task-context-menu-attend-accept-all-menuitem"
+                    type="radio"
+                    scope="all-occurrences"
+                    name="task-context-attendance-all"
+                    label="&read.only.accept.label;" value="ACCEPTED"/>
+          <menuitem id="task-context-menu-attend-tentative-all-menuitem"
+                    type="radio"
+                    scope="all-occurrences"
+                    name="task-context-attendance-all"
+                    label="&read.only.tentative.label;" value="TENTATIVE"/>
+          <menuitem id="task-context-menu-attend-declined-all-menuitem"
+                    type="radio"
+                    scope="all-occurrences"
+                    name="task-context-attendance-all"
+                    label="&read.only.decline.label;" value="DECLINED"/>
+          <menuitem id="task-context-menu-attend-needsaction-all-menuitem"
+                    type="radio"
+                    scope="all-occurrences"
+                    name="task-context-attendance-all"
+                    label="&read.only.needs.action.label;" value="NEEDS-ACTION"/>
+        </menupopup>
+      </menu>
     </menupopup>
   </popupset>
 </overlay>
--- a/calendar/base/content/calendar-item-editing.js
+++ b/calendar/base/content/calendar-item-editing.js
@@ -522,13 +522,13 @@ function setContextPartstat(value, scope
                     newItem.removeAttendee(attendee);
                     newItem.addAttendee(newAttendee);
                 }
 
                 doTransaction('modify', newItem, newItem.calendar, oldItem, null);
             }
         }
     } catch (e) {
-        cal.ERROR("Error settinge partstat: " + e);
+        cal.ERROR("Error setting partstat: " + e);
     } finally {
         endBatchTransaction();
     }
 }
--- a/calendar/base/content/calendar-task-tree.js
+++ b/calendar/base/content/calendar-task-tree.js
@@ -93,36 +93,40 @@ function addCategoryNames(aEvent) {
 /**
  * Change the opening context menu for the selected tasks.
  *
  * @param aEvent    The popupshowing event of the opening menu.
  */
 function changeContextMenuForTask(aEvent) {
     let idnode = document.popupNode.id;
     let sunbird = cal.isSunbird();
+    let items = getSelectedTasks(aEvent);
     document.getElementById("task-context-menu-new").hidden =
         (idnode == "unifinder-todo-tree" && !sunbird);
     document.getElementById("task-context-menu-modify").hidden =
         (idnode == "unifinder-todo-tree" && !sunbird);
     document.getElementById("task-context-menu-new-todaypane").hidden =
         (idnode == "calendar-task-tree" || sunbird);
     document.getElementById("task-context-menu-modify-todaypane").hidden =
         (idnode == "calendar-task-tree" || sunbird);
-    let tasksSelected = (getSelectedTasks(aEvent).length > 0);
+    let tasksSelected = (items.length > 0);
     applyAttributeToMenuChildren(aEvent.target, "disabled", (!tasksSelected));
     if (calendarController.isCommandEnabled("calendar_new_todo_command") &&
         calendarController.isCommandEnabled("calendar_new_todo_todaypane_command")) {
         document.getElementById("calendar_new_todo_command").removeAttribute("disabled");
         document.getElementById("calendar_new_todo_todaypane_command").removeAttribute("disabled");
     } else {
         document.getElementById("calendar_new_todo_command").setAttribute("disabled", "true");
         document.getElementById("calendar_new_todo_todaypane_command").setAttribute("disabled", "true");
     }
 
     changeMenuForTask(aEvent);
+
+    let menu = document.getElementById("task-context-menu-attendance-menu");
+    setupAttendanceMenu(menu, items);
 }
 
 /**
  * Change the opening menu for the selected tasks.
  *
  * @param aEvent    The popupshowing event of the opening menu.
  */
 function changeMenuForTask(aEvent) {
--- a/calendar/base/content/calendar-ui-utils.js
+++ b/calendar/base/content/calendar-ui-utils.js
@@ -685,8 +685,51 @@ function getOtherOrientation(aOrientatio
 function updateSelectedLabel(aElement) {
     if (typeof(aElement) == "string") {
         aElement = document.getElementById(aElement);
     }
     var selectedIndex = aElement.selectedIndex;
     aElement.selectedIndex = -1;
     aElement.selectedIndex = selectedIndex;
 }
+
+/**
+ * Sets up the attendance context menu, based on the given items
+ *
+ * @param aMenu     The DOM Node of the menupopup to set up
+ * @param aItems    The array of items to consider
+ */
+function setupAttendanceMenu(aMenu, aItems) {
+    let allSingle = aItems.every(function(x) !x.recurrenceId);
+    setElementValue(aMenu, allSingle ? "single" : "recurring", "itemType");
+
+    // Set up the attendance menu
+    function getInvStat(item) {
+        let attendee = null;
+        if (cal.isInvitation(item)) {
+            attendee = cal.getInvitedAttendee(item);
+        } else if (item.organizer) {
+            let calOrgId = item.calendar.getProperty("organizerId");
+            if (calOrgId == item.organizer.id && item.getAttendees({}).length) {
+                attendee = item.organizer;
+            }
+        }
+        return attendee && attendee.participationStatus;
+    }
+
+    let firstStatusOccurrences = aItems.length && getInvStat(aItems[0]);
+    let firstStatusParents = aItems.length && getInvStat(aItems[0].parentItem);
+    let sameStatusOccurrences = aItems.every(function (x) getInvStat(x) == firstStatusOccurrences);
+    let sameStatusParents = aItems.every(function (x) getInvStat(x.parentItem) == firstStatusParents)
+
+    let occurrenceChildren = aMenu.getElementsByAttribute("value", firstStatusOccurrences);
+    let parentsChildren = aMenu.getElementsByAttribute("value", firstStatusParents);
+
+    if (sameStatusOccurrences && occurrenceChildren[0]) {
+        occurrenceChildren[0].setAttribute("checked", "true");
+    }
+
+    if (sameStatusParents && parentsChildren[1]) {
+        parentsChildren[1].setAttribute("checked", "true");
+    }
+
+    return true;
+}
--- a/calendar/base/content/today-pane.xul
+++ b/calendar/base/content/today-pane.xul
@@ -41,16 +41,17 @@
 
 <!DOCTYPE overlay
 [
     <!ENTITY % dtd1 SYSTEM "chrome://calendar/locale/global.dtd" > %dtd1;
     <!ENTITY % dtd2 SYSTEM "chrome://lightning/locale/lightning.dtd" > %dtd2;
     <!ENTITY % dtd3 SYSTEM "chrome://messenger/locale/messenger.dtd" > %dtd3;
     <!ENTITY % dtd4 SYSTEM "chrome://calendar/locale/calendar.dtd" > %dtd4;
     <!ENTITY % dtd5 SYSTEM "chrome://global/locale/global.dtd" > %dtd5;
+    <!ENTITY % dtd6 SYSTEM "chrome://calendar/locale/calendar-event-dialog.dtd"> %dtd6;
 ]>
 
 <?xml-stylesheet href="chrome://calendar/skin/today-pane.css" type="text/css"?>
 <?xml-stylesheet href="chrome://calendar/skin/widgets/calendar-widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://calendar/content/widgets/calendar-widget-bindings.css" type="text/css"?>
 <?xml-stylesheet href="chrome://calendar/skin/widgets/minimonth.css" type="text/css"?>
 
 <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
@@ -195,64 +196,121 @@
                                iconsize="small"
                                orient="horizontal"
                                label="&calendar.newevent.button.label;"
                                tooltiptext="&calendar.newevent.button.tooltip;"
                                oncommand="agendaListbox.createNewEvent(event)">
                   <observes element="calendar_new_event_command" attribute="disabled"/>
                 </toolbarbutton>
              </hbox>
-             <menupopup id="agenda-menu" onpopupshowing="if (event.target == event.currentTarget) { agendaListbox.buildAgendaPopupMenu(this) }"/>
-             <vbox id="agenda-menu-box" hidden="true">
-               <menuitem label="&calendar.context.modifyorviewitem.label;"
-                         accesskey="&calendar.context.modifyorviewitem.accesskey;"
-                         observes="agenda_edit_event_command"/>
-               <menu id="agenda-context-menu-convert-menu"
-                     label="&calendar.context.convertmenu.label;"
-                     accesskey="&calendar.context.convertmenu.accesskey.calendar;">
-                 <menupopup id="agenda-context-menu-convert-menupopup">
-                   <menuitem id="agenda-context-menu-convert-message-menuitem"
-                             label="&calendar.context.convertmenu.message.label;"
-                             accesskey="&calendar.context.convertmenu.message.accesskey;"
-                             oncommand="calendarMailButtonDNDObserver.onDropItems(agendaListbox.getSelectedItems())"/>
-                   <menuitem id="agenda-context-menu-convert-task-menuitem"
-                             class="event-only"
-                             label="&calendar.context.convertmenu.task.label;"
-                             accesskey="&calendar.context.convertmenu.task.accesskey;"
-                             oncommand="calendarTaskButtonDNDObserver.onDropItems(agendaListbox.getSelectedItems())"/>
-                 </menupopup>
-               </menu>
-               <menuseparator id="calendar-today-pane-menuseparator-before-delete"/>
-               <menuitem label="&calendar.context.deleteevent.label;"
-                         accesskey="&calendar.context.deleteevent.accesskey;"
-                         key="calendar-delete-item-key"
-                         observes="agenda_delete_event_command"/>
-             </vbox>
              <vbox id="richlistitem-container" hidden="true">
                 <agenda-checkbox-richlist-item id="today-header-hidden"
                                                title="&calendar.today.button.label;"
                                                checked="true"
                                                persist="checked"/>
                 <agenda-checkbox-richlist-item id="tomorrow-header-hidden"
                                                title="&calendar.tomorrow.button.label;"
                                                checked="false"
                                                persist="checked"/>
                 <agenda-checkbox-richlist-item id="nextweek-header-hidden"
                                                title="&calendar.soon.button.label;"
                                                checked="false"
                                                persist="checked"/>
-             </vbox>
-             <richlistbox id="agenda-listbox" flex="1" context="agenda-menu"
+              </vbox>
+              <richlistbox id="agenda-listbox" flex="1" context="_child"
                            onblur="agendaListbox.onBlur();"
                            onfocus="agendaListbox.onFocus();"
                            onkeypress="agendaListbox.onKeyPress(event);"
                            ondblclick="agendaListbox.createNewEvent(event);"
                            ondraggesture="nsDragAndDrop.startDrag(event, calendarCalendarButtonDNDObserver);"
                            ondragover="nsDragAndDrop.dragOver(event, calendarCalendarButtonDNDObserver);"
-                           ondragdrop="nsDragAndDrop.drop(event, calendarCalendarButtonDNDObserver);"/>
+                           ondragdrop="nsDragAndDrop.drop(event, calendarCalendarButtonDNDObserver);">
+                <menupopup id="agenda-menupopup" onpopupshowing="return agendaListbox.setupContextMenu(event.target)">
+                  <menuitem label="&calendar.context.modifyorviewitem.label;"
+                           accesskey="&calendar.context.modifyorviewitem.accesskey;"
+                           observes="agenda_edit_event_command"/>
+                  <menu id="agenda-context-menu-convert-menu"
+                       label="&calendar.context.convertmenu.label;"
+                       accesskey="&calendar.context.convertmenu.accesskey.calendar;">
+                   <menupopup id="agenda-context-menu-convert-menupopup">
+                     <menuitem id="agenda-context-menu-convert-message-menuitem"
+                               label="&calendar.context.convertmenu.message.label;"
+                               accesskey="&calendar.context.convertmenu.message.accesskey;"
+                               oncommand="calendarMailButtonDNDObserver.onDropItems(agendaListbox.getSelectedItems())"/>
+                     <menuitem id="agenda-context-menu-convert-task-menuitem"
+                               class="event-only"
+                               label="&calendar.context.convertmenu.task.label;"
+                               accesskey="&calendar.context.convertmenu.task.accesskey;"
+                               oncommand="calendarTaskButtonDNDObserver.onDropItems(agendaListbox.getSelectedItems())"/>
+                   </menupopup>
+                  </menu>
+                  <menuseparator id="calendar-today-pane-menuseparator-before-delete"/>
+                  <menuitem label="&calendar.context.deleteevent.label;"
+                           accesskey="&calendar.context.deleteevent.accesskey;"
+                           key="calendar-delete-item-key"
+                           observes="agenda_delete_event_command"/>
+                  <menu id="calendar-today-pane-menu-attendance-menu"
+                       class="attendance-menu"
+                       label="&calendar.context.attendance.menu.label;"
+                       accesskey="&calendar.context.attendance.menu.accesskey;"
+                       oncommand="setContextPartstat(event.target.value, event.target.getAttribute('scope'), agendaListbox.getSelectedItems({}))"
+                       observes="calendar_attendance_command">
+                   <menupopup id="calendar-today-pane-menu-attendance-menupopup">
+                     <label id="calendar-today-pane-attendance-thisoccurrence-label"
+                            class="calendar-context-heading-label"
+                            scope="all-occurrences"
+                            value="&calendar.context.attendance.occurrence.label;"/>
+                     <menuitem id="calendar-today-pane-menu-attend-accept-menuitem"
+                               type="radio"
+                               scope="this-occurrence"
+                               name="calendar-today-pane-attendance"
+                               label="&read.only.accept.label;" value="ACCEPTED"/>
+                     <menuitem id="calendar-today-pane-menu-attend-tentative-menuitem"
+                               type="radio"
+                               scope="this-occurrence"
+                               name="calendar-today-pane-attendance"
+                               label="&read.only.tentative.label;" value="TENTATIVE"/>
+                     <menuitem id="calendar-today-pane-menu-attend-declined-menuitem"
+                               type="radio"
+                               scope="this-occurrence"
+                               name="calendar-today-pane-attendance"
+                               label="&read.only.decline.label;" value="DECLINED"/>
+                     <menuitem id="calendar-today-pane-menu-attend-needsaction-menuitem"
+                               type="radio"
+                               scope="this-occurrence"
+                               name="calendar-today-pane-attendance"
+                               label="&read.only.needs.action.label;" value="NEEDS-ACTION"/>
+                     <label id="calendar-today-pane-attendance-alloccurrence-label"
+                            class="calendar-context-heading-label"
+                            scope="all-occurrences"
+                            value="&calendar.context.attendance.all.label;"/>
+                     <menuitem id="calendar-today-pane-menu-attend-accept-all-menuitem"
+                               type="radio"
+                               scope="all-occurrences"
+                               name="calendar-today-pane-attendance-all"
+                               label="&read.only.accept.label;" value="ACCEPTED"/>
+                     <menuitem id="calendar-today-pane-menu-attend-tentative-all-menuitem"
+                               type="radio"
+                               scope="all-occurrences"
+                               name="calendar-today-pane-attendance-all"
+                               label="&read.only.tentative.label;" value="TENTATIVE"/>
+                     <menuitem id="calendar-today-pane-menu-attend-declined-all-menuitem"
+                               type="radio"
+                               scope="all-occurrences"
+                               name="calendar-today-pane-attendance-all"
+                               label="&read.only.decline.label;" value="DECLINED"/>
+                     <menuitem id="calendar-today-pane-menu-attend-needsaction-all-menuitem"
+                               type="radio"
+                               scope="all-occurrences"
+                               name="calendar-today-pane-attendance-all"
+                               label="&read.only.needs.action.label;" value="NEEDS-ACTION"/>
+                   </menupopup>
+                  </menu>
+                </menupopup>
+              </richlistbox>
             </vbox>
         </modevbox>
         <splitter id="today-pane-splitter" persist="hidden"/>
         <modevbox id="todo-tab-panel" flex="1" mode="mail,calendar"
                   collapsedinmodes="mail,task"
                   broadcaster="modeBroadcaster"
                   persist="height collapsedinmodes"
                   ondraggesture="nsDragAndDrop.startDrag(event, calendarTaskButtonDNDObserver);"
--- a/calendar/base/themes/pinstripe/calendar-views.css
+++ b/calendar/base/themes/pinstripe/calendar-views.css
@@ -645,17 +645,17 @@ description.tooltipBody {
 #calendar-view-context-menu[type="mixed"] .todo-only,
 #calendar-item-context-menu[type="event"] .todo-only,
 #calendar-item-context-menu[type="todo"] .event-only,
 #calendar-item-context-menu[type="mixed"] .event-only,
 #calendar-item-context-menu[type="mixed"] .todo-only {
     display: none;
 }
 
-#calendar-item-context-menu-attendance-menu[itemType="single"] > menupopup > *[scope="all-occurrences"] {
+.attendance-menu[itemType="single"] > menupopup > *[scope="all-occurrences"] {
     display: none;
 }
 
 .calendar-context-heading-label {
     font-weight: bold;
 }
 
 calendar-event-box,
--- a/calendar/base/themes/winstripe/calendar-views.css
+++ b/calendar/base/themes/winstripe/calendar-views.css
@@ -645,17 +645,17 @@ description.tooltipBody {
 #calendar-view-context-menu[type="mixed"] .todo-only,
 #calendar-item-context-menu[type="event"] .todo-only,
 #calendar-item-context-menu[type="todo"] .event-only,
 #calendar-item-context-menu[type="mixed"] .event-only,
 #calendar-item-context-menu[type="mixed"] .todo-only {
     display: none;
 }
 
-#calendar-item-context-menu-attendance-menu[itemType="single"] > menupopup > *[scope="all-occurrences"] {
+.attendance-menu[itemType="single"] > menupopup > *[scope="all-occurrences"] {
     display: none;
 }
 
 .calendar-context-heading-label {
     font-weight: bold;
 }
 
 calendar-event-box,