Bug 1287067 - Cannot snooze alarms for more than 22 days;r=mmecca
☠☠ backed out by da98f4fe5212 ☠ ☠
authormakemyday@gmx-topmail.de
Wed, 27 Jul 2016 21:21:30 +0200
changeset 25577 2b47a6bef4c72ebff403f0bbe6fd842a66b41120
parent 25576 15822e1bf8c8c7641a23763806ff08e7367beca6
child 25578 4620308fb7036b2b7a61af6255f7b0efb5337338
push id1725
push userclokep@gmail.com
push dateMon, 19 Sep 2016 17:35:08 +0000
treeherdercomm-beta@6ead1abf3817 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmmecca
bugs1287067
Bug 1287067 - Cannot snooze alarms for more than 22 days;r=mmecca
calendar/base/content/dialogs/calendar-alarm-dialog.js
calendar/base/public/calIAlarmService.idl
calendar/base/src/calAlarmService.js
calendar/locales/en-US/chrome/calendar/calendar.properties
--- a/calendar/base/content/dialogs/calendar-alarm-dialog.js
+++ b/calendar/base/content/dialogs/calendar-alarm-dialog.js
@@ -22,19 +22,21 @@ function getAlarmService() {
 /**
  * Event handler for the 'snooze' event. Snoozes the given alarm by the given
  * number of minutes using the alarm service.
  *
  * @param event     The snooze event
  */
 function onSnoozeAlarm(event) {
     // reschedule alarm:
-    let duration = cal.createDuration();
-    duration.minutes = event.detail;
-    duration.normalize();
+    let duration = getDuration(event.detail);
+    if (aboveSnoozeLimit(duration)) {
+        // we prevent snoozing too far if the alarm wouldn't be displayed
+        return;
+    }
     getAlarmService().snoozeAlarm(event.target.item, event.target.alarm, duration);
 }
 
 /**
  * Event handler for the 'dismiss' event. Dismisses the given alarm using the
  * alarm service.
  *
  * @param event     The snooze event
@@ -148,19 +150,21 @@ function updateRelativeDates() {
 }
 
 /**
  * Function to snooze all alarms the given number of minutes.
  *
  * @param aDurationMinutes    The duration in minutes
  */
 function snoozeAllItems(aDurationMinutes) {
-    let duration = cal.createDuration();
-    duration.minutes = aDurationMinutes;
-    duration.normalize();
+    let duration = getDuration(aDurationMinutes);
+    if (aboveSnoozeLimit(duration)) {
+        // we prevent snoozing too far if the alarm wouldn't be displayed
+        return;
+    }
 
     let alarmRichlist = document.getElementById("alarm-richlist");
     let parentItems = {};
 
     // Make a copy of the child nodes as they get modified live
     for (let node of alarmRichlist.childNodes) {
         // Check if the node is a valid alarm and is still part of DOM
         if (node.parentNode && node.item && node.alarm &&
@@ -168,16 +172,59 @@ function snoozeAllItems(aDurationMinutes
             // We only need to acknowledge one occurrence for repeating items
             parentItems[node.item.parentItem.hashId] = node.item.parentItem;
             getAlarmService().snoozeAlarm(node.item, node.alarm, duration);
         }
     }
 }
 
 /**
+ * Receive a calIDuration object for a given number of minutes
+ *
+ * @param  {long}           aMinutes     The number of minutes
+ * @return {calIDuration}
+ */
+function getDuration(aMinutes) {
+    const MINUTESINWEEK = 7 * 24 * 60;
+
+    // converting to weeks if any is required to avoid an integer overflow of duration.minutes as
+    // this is of type short
+    let weeks = Math.floor(aMinutes / MINUTESINWEEK);
+    aMinutes -= weeks * MINUTESINWEEK;
+
+    let duration = cal.createDuration();
+    duration.minutes = aMinutes;
+    duration.weeks = weeks;
+    duration.normalize();
+    return duration;
+}
+
+/**
+ * Check whether the snooze period exceeds the current limitation of the AlarmService and prompt
+ * the user with a message if so
+ * @param   {calIDuration}   aDuration   The duration to snooze
+ * @returns {Boolean}
+ */
+function aboveSnoozeLimit(aDuration) {
+    const LIMIT = Components.interfaces.calIAlarmService.MAX_SNOOZE_MONTHS;
+
+    let currentTime = cal.now().getInTimezone(cal.UTC());
+    let limitTime = currentTime.clone();
+    limitTime.month += LIMIT;
+
+    let durationUntilLimit = limitTime.subtractDate(currentTime);
+    if (aDuration.compare(durationUntilLimit) > 0) {
+        let msg = PluralForm.get(LIMIT, cal.calGetString("calendar", "alarmSnoozeLimitExceeded"));
+        showError(msg.replace("#1", LIMIT));
+        return true;
+    }
+    return false;
+}
+
+/**
  * Sets up the window title, counting the number of alarms in the window.
  */
 function setupTitle() {
     let alarmRichlist = document.getElementById("alarm-richlist");
     let reminders = alarmRichlist.childNodes.length;
 
     let title = PluralForm.get(reminders, calGetString("calendar", "alarmWindowTitle.label"));
     document.title = title.replace("#1", reminders);
--- a/calendar/base/public/calIAlarmService.idl
+++ b/calendar/base/public/calIAlarmService.idl
@@ -40,16 +40,22 @@ interface calIAlarmServiceObserver : nsI
    */
   void onAlarmsLoaded(in calICalendar calendar);
 };
 
 [scriptable,uuid(42cfa9ce-49d6-11e5-b88c-5b90eedc1c47)]
 interface calIAlarmService : nsISupports
 {
   /**
+   * Upper limit for the snooze period for an alarm. To avoid performance issues, don't change this
+   * to a value larger then 1 at least until bug 861594 or a similar concept is implemented.
+   */
+  const unsigned long MAX_SNOOZE_MONTHS = 1;
+
+  /**
    * This is the timezone that all-day events will be converted to in order to
    * determine when their alarms should fire.
    */
   attribute calITimezone timezone;
 
   /**
    * Will return true while the alarm service is in the process of loading alarms
    */
--- a/calendar/base/src/calAlarmService.js
+++ b/calendar/base/src/calAlarmService.js
@@ -236,24 +236,24 @@ calAlarmService.prototype = {
                 let now = nowUTC();
                 let start;
                 if (!this.alarmService.mRangeEnd) {
                     // This is our first search for alarms.  We're going to look for
                     // alarms +/- 1 month from now.  If someone sets an alarm more than
                     // a month ahead of an event, or doesn't start Lightning
                     // for a month, they'll miss some, but that's a slim chance
                     start = now.clone();
-                    start.month -= 1;
+                    start.month -= Components.interfaces.calIAlarmService.MAX_SNOOZE_MONTHS;
                     this.alarmService.mRangeStart = start.clone();
                 } else {
                     // This is a subsequent search, so we got all the past alarms before
                     start = this.alarmService.mRangeEnd.clone();
                 }
                 let until = now.clone();
-                until.month += 1;
+                until.month += Components.interfaces.calIAlarmService.MAX_SNOOZE_MONTHS;
 
                 // We don't set timers for every future alarm, only those within 6 hours
                 let end = now.clone();
                 end.hour += kHoursBetweenUpdates;
                 this.alarmService.mRangeEnd = end.getInTimezone(UTC());
 
                 this.alarmService.findAlarms(getCalendarManager().getCalendars({}),
                                              start, until);
@@ -564,18 +564,18 @@ calAlarmService.prototype = {
         }
 
         // Total refresh similar to startup.  We're going to look for
         // alarms +/- 1 month from now.  If someone sets an alarm more than
         // a month ahead of an event, or doesn't start Lightning
         // for a month, they'll miss some, but that's a slim chance
         let start = nowUTC();
         let until = start.clone();
-        start.month -= 1;
-        until.month += 1;
+        start.month -= Components.interfaces.calIAlarmService.MAX_SNOOZE_MONTHS;
+        until.month += Components.interfaces.calIAlarmService.MAX_SNOOZE_MONTHS;
         this.findAlarms(aCalendars, start, until);
     },
 
     alarmFired: function cAS_alarmFired(aItem, aAlarm) {
         if (!aItem.calendar.getProperty("suppressAlarms") &&
             !aItem.calendar.getProperty("disabled") &&
             aItem.getProperty("STATUS") != "CANCELLED") {
             this.mObservers.notify("onAlarm", [aItem, aAlarm]);
--- a/calendar/locales/en-US/chrome/calendar/calendar.properties
+++ b/calendar/locales/en-US/chrome/calendar/calendar.properties
@@ -445,16 +445,20 @@ alarmYesterdayAt=Yesterday at %1$S
 
 # Alarm interface strings
 # LOCALIZATION NOTE: These strings do not get displayed. They are only visible
 # when exporting an item with i.e a DISPLAY alarm, that doesn't have a
 # description set, or an EMAIL alarm that doesn't have a summary set.
 alarmDefaultDescription=Default Mozilla Description
 alarmDefaultSummary=Default Mozilla Summary
 
+# LOCALIZATION NOTE (alarmSnoozeLimitExceeded): Semi-colon list of plural
+# forms.
+alarmSnoozeLimitExceeded=You cannot snooze an alarm for more than #1 month.;You cannot snooze an alarm for more than #1 months.
+
 taskDetailsStatusNeedsAction=Needs Action
 
 # LOCALIZATION NOTE (taskDetailsStatusInProgress):
 # used for a display of how much of a task is completed '25% Complete'
 #    %1$S will be replaced with the number of percentage completed
 taskDetailsStatusInProgress=%1$S%% Complete
 taskDetailsStatusCompleted=Completed