Last Comment Bug 380423 - Popup alarms/reminders don't work with WCAP server
: Popup alarms/reminders don't work with WCAP server
Status: NEW
: relnote
Product: Calendar
Classification: Client Software
Component: Provider: WCAP (show other bugs)
: unspecified
: All All
: -- enhancement with 6 votes (vote)
: ---
Assigned To: Nobody; OK to take it and work on it
:
:
Mentors:
: 439329 449166 558755 (view as bug list)
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2007-05-11 10:56 PDT by jlarsen
Modified: 2013-05-30 03:38 PDT (History)
17 users (show)
dbo.moz: wanted‑calendar1.0+
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---


Attachments
calAlarmService.js file with fixed alarm reminder (23.74 KB, patch)
2009-03-02 12:06 PST, Backstage
no flags Details | Diff | Splinter Review
calAlarmService.js file with fixed alarm reminder for WCAP calendar only (24.46 KB, text/plain)
2009-03-02 14:59 PST, Backstage
no flags Details

Description jlarsen 2007-05-11 10:56:34 PDT
Set a WCAP network calendar and set an event with alarm. Alarm never goes off.

The remote WCAP server is out of my control and is in beta and having connection issues, but even if remote server is disconnected local calendar knows about event and shows it, so shouldn't alarm be going off?
Comment 1 Stefan Sitter 2007-05-11 12:44:17 PDT
Hi, do you mixed up some things? Are you using a locale calendar or are you using a remote calendar on a Sun Java System Calendar Server? In the latter case you always need a connection to the server to view/edit/create events.
Comment 2 jlarsen 2007-05-11 13:03:23 PDT
Using a remote calendar on a Sun Java System Calendar Server. Yea I am connected to the server when I create the event, I can confirm that the event is viewable on my local computer and the remote webpage on the server. The alarm inside thunderbird never fires. I don't know if it has to do with the connection issue (it times out frequently) or what, but seems to me it holds a local cache and should fire from that independent of connection.??
Comment 3 Daniel Boelzle [:dbo] 2007-05-12 06:44:57 PDT
This feature is currently not implementable, because X-props can't be saved (more specifically replaced) reliably. Right now, if you specify an alarm (and you have defined an email address in your calendar settings), an email notification alarm is set on the server using the respective time offset. I change this bug to RFE, still on my list, of course.
Comment 4 Daniel Boelzle [:dbo] 2007-10-31 05:52:56 PDT
We might head towards a solution for WCAP popup alarms when we have offline support: We could keep the full calendar data (including alarm definitions/dismiss stamps) locally, while we omit those data (e.g. X-prop dismiss stamps) when writing to the server.
Comment 5 Stefan Sitter 2008-02-27 10:49:38 PST
*** Bug 419871 has been marked as a duplicate of this bug. ***
Comment 6 Stefan Sitter 2008-06-15 09:32:41 PDT
*** Bug 439329 has been marked as a duplicate of this bug. ***
Comment 7 anil srivastava 2008-07-01 21:12:27 PDT
Is this going to be fixed when the offline support is available with M9 build?  The approach suggested by Daniel on 1007-05-12 is  the approach Outlook Connector uses for popup alarms too.
Comment 8 Bill Shannon 2008-07-02 11:25:37 PDT
Any solution that allows me to configure the alarm behavior in the server so
that all clients know what alarms to trigger seems fine.  If every client
connected to the server presents the alarm, even though I dismiss the alarm
on the one client I'm sitting on front of, that's good enough.  Better to get
alarms on every client than alarms on no clients.
Comment 9 Daniel Boelzle [:dbo] 2008-07-07 09:52:13 PDT
(In reply to comment #7)
> Is this going to be fixed when the offline support is available with M9 build? 
No, we most possibly won't do that for 0.9, but head for a solution storing X-props post 0.9 (because the related server bug is fixed in cs 6.4).
Comment 10 Philipp Kewisch [:Fallen] 2008-08-04 21:42:47 PDT
*** Bug 449166 has been marked as a duplicate of this bug. ***
Comment 11 Backstage 2009-03-02 12:02:54 PST
Following changes will make alarm reminders work with Sun System Calendar Server (WCAP):

Instead of using the X-props you should use the alarmOffset parameter of the calendar event item.

If an alarm is fired and the user selects:
a) dismiss alarm
     if(newParent.calendar.type == "wcap")
     {
       newParent.alarmOffset = null;		
     }
-----> alarmOffset=null removes the alarm

b) snooze alarm
-----> duration = alarmTime.subtractDate(newEvent.startDate); //newEvent is after has to be after the alarmTime
-----> newEvent.alarmOffset = duration;
Comment 12 Backstage 2009-03-02 12:06:58 PST
Created attachment 364975 [details] [diff] [review]
calAlarmService.js file with fixed alarm reminder
Comment 13 Philipp Kewisch [:Fallen] 2009-03-02 13:19:37 PST
So to make them "work", you remove the actual offset? Also, please note that the alarm service will change quite a bit in 1.0pre.
Comment 14 Backstage 2009-03-02 14:12:31 PST
Yes, i replace the actual offset with the user selection (dismiss,snooze).

I think that the replacement of the offset is better than not having a alarm reminder ;-)

By the way:
I also tested the actual Lightning 1.0pre Version with Thunderbird 3.0b2 where i found following bugs (with WCAP-Calendar):
1) When i try to insert an event with an alarm time -> The alarm time won't be stored !
2) Following steps will show "MODIFICATION_FAILED" message:
     a) Insert a new event
     b) Open the created event
     c) "Save/Close" the event without changing anything
     d) --> MODIFICATION_FAILED
Comment 15 Backstage 2009-03-02 14:52:59 PST
Comment on attachment 364975 [details] [diff] [review]
calAlarmService.js file with fixed alarm reminder

>/* ***** BEGIN LICENSE BLOCK *****
> * Version: MPL 1.1/GPL 2.0/LGPL 2.1
> *
> * The contents of this file are subject to the Mozilla Public License Version
> * 1.1 (the "License"); you may not use this file except in compliance with
> * the License. You may obtain a copy of the License at
> * http://www.mozilla.org/MPL/
> *
> * Software distributed under the License is distributed on an "AS IS" basis,
> * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
> * for the specific language governing rights and limitations under the
> * License.
> *
> * The Original Code is Oracle Corporation code.
> *
> * The Initial Developer of the Original Code is Oracle Corporation
> * Portions created by the Initial Developer are Copyright (C) 2005
> * the Initial Developer. All Rights Reserved.
> *
> * Contributor(s):
> *   Stuart Parmenter <stuart.parmenter@oracle.com>
> *   Joey Minta <jminta@gmail.com>
> *   Daniel Boelzle <daniel.boelzle@sun.com>
> *   Philipp Kewisch <mozilla@kewis.ch>
> *   Martin Schroeder <mschroeder@mozilla.x-home.org>
> *
> * Alternatively, the contents of this file may be used under the terms of
> * either the GNU General Public License Version 2 or later (the "GPL"), or
> * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
> * in which case the provisions of the GPL or the LGPL are applicable instead
> * of those above. If you wish to allow use of your version of this file only
> * under the terms of either the GPL or the LGPL, and not to allow others to
> * use your version of this file under the terms of the MPL, indicate your
> * decision by deleting the provisions above and replace them with the notice
> * and other provisions required by the GPL or the LGPL. If you do not delete
> * the provisions above, a recipient may use your version of this file under
> * the terms of any one of the MPL, the GPL or the LGPL.
> *
> * ***** END LICENSE BLOCK ***** */
>
>const kHoursBetweenUpdates = 6;
>
>function nowUTC() {
>    return jsDateToDateTime(new Date()).getInTimezone(UTC());
>}
>
>function newTimerWithCallback(callback, delay, repeating)
>{
>    var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
>    
>    timer.initWithCallback(callback,
>                           delay,
>                           (repeating) ? timer.TYPE_REPEATING_PRECISE : timer.TYPE_ONE_SHOT);
>    return timer;
>}
>
>function calAlarmService() {
>    this.wrappedJSObject = this;
>
>    this.mLoadedCalendars = {};
>    this.mTimerLookup = {};
>    this.mObservers = new calListenerBag(Components.interfaces.calIAlarmServiceObserver);
>
>    this.calendarObserver = {
>        alarmService: this,
>
>        // calIObserver:
>        onStartBatch: function() { },
>        onEndBatch: function() { },
>        onLoad: function co_onLoad(calendar) {
>            // ignore any onLoad events until initial getItems() call of startup has finished:
>            if (calendar && this.alarmService.mLoadedCalendars[calendar.id]) {
>                // a refreshed calendar signals that it has been reloaded
>                // (and cannot notify detailed changes), thus reget all alarms of it:
>                this.alarmService.initAlarms([calendar]);
>            }
>        },
>        onAddItem: function(aItem) {
>            var occs = [];
>            if (aItem.recurrenceInfo) {
>                var start = this.alarmService.mRangeEnd.clone();
>                // We search 1 month in each direction for alarms.  Therefore,
>                // we need to go back 2 months from the end to get this right.
>                start.month -= 2;
>                occs = aItem.recurrenceInfo.getOccurrences(start, this.alarmService.mRangeEnd, 0, {});
>            } else {
>                occs = [aItem];
>            }
>
>            for each (var occ in occs) {
>                this.alarmService.addAlarm(occ);
>            }
>        },
>        onModifyItem: function(aNewItem, aOldItem) {
>            if (!aNewItem.recurrenceId) {
>                // deleting an occurrence currently calls modifyItem(newParent, *oldOccurrence*)
>                aOldItem = aOldItem.parentItem;
>            }
>
>            this.onDeleteItem(aOldItem);
>            this.onAddItem(aNewItem);
>        },
>        onDeleteItem: function(aDeletedItem) {
>            this.alarmService.removeAlarm(aDeletedItem);
>        },
>        onError: function(aCalendar, aErrNo, aMessage) {},
>        onPropertyChanged: function(aCalendar, aName, aValue, aOldValue) {
>            switch (aName) {
>                case "suppressAlarms":
>                case "disabled":
>                    this.alarmService.initAlarms([aCalendar]);
>                    break;
>            }
>        },
>        onPropertyDeleting: function(aCalendar, aName) {
>            this.onPropertyChanged(aCalendar, aName);
>        }
>    };
>
>    this.calendarManagerObserver = {
>        alarmService: this,
>
>        onCalendarRegistered: function(aCalendar) {
>            this.alarmService.observeCalendar(aCalendar);
>            // initial refresh of alarms for new calendar:
>            this.alarmService.initAlarms([aCalendar]);
>        },
>        onCalendarUnregistering: function(aCalendar) {
>            // XXX todo: we need to think about calendar unregistration;
>            // there may still be dangling items (-> alarm dialog),
>            // dismissing those alarms may write data...
>            this.alarmService.unobserveCalendar(aCalendar);
>        },
>        onCalendarDeleting: function(aCalendar) {}
>    };
>}
>
>var calAlarmServiceClassInfo = {
>    getInterfaces: function (count) {
>        var ifaces = [
>            Components.interfaces.nsISupports,
>            Components.interfaces.calIAlarmService,
>            Components.interfaces.nsIObserver,
>            Components.interfaces.nsIClassInfo
>        ];
>        count.value = ifaces.length;
>        return ifaces;
>    },
>
>    getHelperForLanguage: function (language) {
>        return null;
>    },
>
>    contractID: "@mozilla.org/calendar/alarm-service;1",
>    classDescription: "Calendar Alarm Service",
>    classID: Components.ID("{7a9200dd-6a64-4fff-a798-c5802186e2cc}"),
>    implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
>    flags: Components.interfaces.nsIClassInfo.SINGLETON
>};
>
>calAlarmService.prototype = {
>    mRangeEnd: null,
>    mUpdateTimer: null,
>    mStarted: false,
>    mTimerLookup: null,
>    mObservers: null,
>
>    QueryInterface: function cas_QueryInterface(aIID) {
>        if (aIID.equals(Components.interfaces.nsIClassInfo))
>            return calAlarmServiceClassInfo;
>
>        if (!aIID.equals(Components.interfaces.nsISupports) &&
>            !aIID.equals(Components.interfaces.calIAlarmService) &&
>            !aIID.equals(Components.interfaces.nsIObserver))
>        {
>            throw Components.results.NS_ERROR_NO_INTERFACE;
>        }
>
>        return this;
>    },
>
>    /* nsIObserver */
>    // This will also be called on app-startup, but nothing is done yet, to
>    // prevent unwanted dialogs etc. See bug 325476 and 413296 
>    observe: function cas_observe(subject, topic, data) {
>        if (topic == "profile-after-change" || topic == "wake_notification") {
>            this.shutdown();
>            this.startup();
>        }
>        if (topic == "xpcom-shutdown") {
>            this.shutdown();
>        }
>    },
>
>    /* calIAlarmService APIs */
>    mTimezone: null,
>    get timezone() {
>        return this.mTimezone || calendarDefaultTimezone();
>    },
>
>    set timezone(aTimezone) {
>        return (this.mTimezone = aTimezone);
>    },
>
>    snoozeAlarm: function cas_snoozeAlarm(event, duration) {
>        /* modify the event for a new alarm time */
>        // Make sure we're working with the parent, otherwise we'll accidentally
>        // create an exception
>				
>        var newEvent = event.parentItem.clone();
>        var alarmTime = nowUTC();
>		
>        // Set the last acknowledged time to now.
>        newEvent.alarmLastAck = alarmTime;
>
>        alarmTime = alarmTime.clone();
>        alarmTime.addDuration(duration);
>
>        // check if it is a wcap calendar
>        if(event.calendar.type != "wcap")
>        {
>          if (event.parentItem != event) {
>              // This is the *really* hard case where we've snoozed a single
>              // instance of a recurring event.  We need to not only know that
>              // there was a snooze, but also which occurrence was snoozed.  Part
>              // of me just wants to create a local db of snoozes here...
>              newEvent.setProperty("X-MOZ-SNOOZE-TIME-" + event.recurrenceId.nativeTime,
>                                   alarmTime.icalString);
>          } else {
>              newEvent.setProperty("X-MOZ-SNOOZE-TIME", alarmTime.icalString);
>          }          
>        }
>        else
>        {
>	   alarmWithCorrectTimezone = alarmTime.getInTimezone(newEvent.startDate.timezone);
>		
>  	   //check if alarm is after event
>	   if(alarmWithCorrectTimezone.icalString > newEvent.startDate.icalString)
>	   {
>	     duration=null;
>	   }
>	   else
>	   {
>	     // calculate new duration value		  
>	     duration = alarmWithCorrectTimezone.subtractDate(newEvent.startDate);
>	   }		
>  	   newEvent.alarmOffset = duration;		
>        }
>        // calling modifyItem will cause us to get the right callback
>        // and update the alarm properly
>	 newEvent.calendar.modifyItem(newEvent, event.parentItem, null);
>    },
>
>    dismissAlarm: function cas_dismissAlarm(item) {
>	    var now = nowUTC();
>	    // We want the parent item, otherwise we're going to accidentally create an
>        // exception.  We've relnoted (for 0.1) the slightly odd behavior this can
>        // cause if you move an event after dismissing an alarm
>        var oldParent = item.parentItem;
>	    var newParent = oldParent.clone();
>	    newParent.alarmLastAck = now;
>	    // Make sure to clear out any snoozes that were here.
>
>        // check if it is a wcap calendar
>	if(newParent.calendar.type=="wcap")
>	{
>	  newParent.alarmOffset = null;		
>	}
>	else
>	{	
>          if (item.recurrenceId) {
>	       newParent.deleteProperty("X-MOZ-SNOOZE-TIME-" + item.recurrenceId.nativeTime);
>	   } else {
>            newParent.deleteProperty("X-MOZ-SNOOZE-TIME");
>	   }
>	}
>		
>	    newParent.calendar.modifyItem(newParent, oldParent, null);
>	},
>
>    addObserver: function cas_addObserver(aObserver) {
>        this.mObservers.add(aObserver);
>    },
>
>    removeObserver: function cas_removeObserver(aObserver) {
>        this.mObservers.remove(aObserver);
>    },
>
>    notifyObservers: function cas_notifyObservers(functionName, args) {
>        this.mObservers.notify(functionName, args);
>    },
>
>    startup: function cas_startup() {
>        if (this.mStarted)
>            return;
>
>        LOG("[calAlarmService] starting...");
>
>        var observerSvc = Components.classes["@mozilla.org/observer-service;1"]
>                          .getService
>                          (Components.interfaces.nsIObserverService);
>
>        observerSvc.addObserver(this, "profile-after-change", false);
>        observerSvc.addObserver(this, "xpcom-shutdown", false);
>        observerSvc.addObserver(this, "wake_notification", false);
>
>        /* Tell people that we're alive so they can start monitoring alarms.
>         */
>        this.notifier = Components.classes["@mozilla.org/embedcomp/appstartup-notifier;1"]
>                                  .getService(Components.interfaces.nsIObserver);
>        var notifier = this.notifier;
>        notifier.observe(null, "alarm-service-startup", null);
>
>        getCalendarManager().addObserver(this.calendarManagerObserver);
>
>        for each(var calendar in getCalendarManager().getCalendars({})) {
>            this.observeCalendar(calendar);
>        }
>
>        /* set up a timer to update alarms every N hours */
>        var timerCallback = {
>            alarmService: this,
>            notify: function timer_notify() {
>                var now = nowUTC();
>                var 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 Sunbird/Lightning
>                    // for a month, they'll miss some, but that's a slim chance
>                    start = now.clone();
>                    start.month -= 1;
>                } else {
>                    // This is a subsequent search, so we got all the past alarms before
>                    start = this.alarmService.mRangeEnd.clone();
>                }
>                var until = now.clone();
>                until.month += 1;
>                
>                // We don't set timers for every future alarm, only those within 6 hours
>                var end = now.clone();
>                end.hour += kHoursBetweenUpdates;
>                this.alarmService.mRangeEnd = end.getInTimezone(UTC());
>
>                this.alarmService.findAlarms(getCalendarManager().getCalendars({}),
>                                             start, until);
>            }
>        };
>        timerCallback.notify();
>
>        this.mUpdateTimer = newTimerWithCallback(timerCallback, kHoursBetweenUpdates * 3600000, true);
>
>        this.mStarted = true;
>    },
>
>    shutdown: function cas_shutdown() {
>        /* tell people that we're no longer running */
>        var notifier = this.notifier;
>        notifier.observe(null, "alarm-service-shutdown", null);
>
>        if (this.mUpdateTimer) {
>            this.mUpdateTimer.cancel();
>            this.mUpdateTimer = null;
>        }
>        
>        getCalendarManager().removeObserver(this.calendarManagerObserver);
>
>        for each (var cal in this.mTimerLookup) {
>            for each (var itemTimers in cal) {
>                for each (var timer in itemTimers) {
>                    if (timer instanceof Components.interfaces.nsITimer) {
>                        timer.cancel();
>                    }
>                }
>            }
>        }
>        this.mTimerLookup = {};
>
>        for each(var calendar in getCalendarManager().getCalendars({})) {
>            this.unobserveCalendar(calendar);
>        }
>
>        this.notifier = null;
>        this.mRangeEnd = null;
>
>        var observerSvc = Components.classes["@mozilla.org/observer-service;1"]
>                          .getService
>                          (Components.interfaces.nsIObserverService);
>
>        observerSvc.removeObserver(this, "profile-after-change");
>        observerSvc.removeObserver(this, "xpcom-shutdown");
>        observerSvc.removeObserver(this, "wake_notification");
>
>        this.mStarted = false;
>    },
>
>    observeCalendar: function cas_observeCalendar(calendar) {
>        calendar.addObserver(this.calendarObserver);
>    },
>
>    unobserveCalendar: function cas_unobserveCalendar(calendar) {
>        calendar.removeObserver(this.calendarObserver);
>        this.disposeCalendarTimers([calendar]);
>        this.notifyObservers("onRemoveAlarmsByCalendar", [calendar]);
>    },
>
>    getAlarmDate: function cas_getAlarmTime(aItem) {
>        var alarmDate = null;
>        if (aItem.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_START) {
>            alarmDate = aItem.startDate || aItem.entryDate || aItem.dueDate;
>        } else {
>            alarmDate = aItem.endDate || aItem.dueDate || aItem.entryDate;
>        }
>
>        if (!aItem.alarmOffset || !alarmDate) {
>            // If there is no alarm offset, or no date the alarm offset could be
>            // relative to, then there is no valid alarm.
>            return null;
>        }
>
>        alarmDate = alarmDate.clone();
>
>        // Handle all day events.  This is kinda weird, because they don't have
>        // a well defined startTime.  We just consider the start/end to be
>        // midnight in the user's timezone.
>        if (alarmDate.isDate) {
>            alarmDate = alarmDate.getInTimezone(this.timezone);
>            alarmDate.isDate = false;
>        }
>
>        var offset = aItem.alarmOffset;
>
>        alarmDate.addDuration(offset);
>        alarmDate = alarmDate.getInTimezone(UTC());
>
>        return alarmDate;
>    },
>
>    addAlarm: function cas_addAlarm(aItem) {
>        // Get the alarm time
>        var alarmTime = this.getAlarmDate(aItem);
>        if (!alarmTime || (isToDo(aItem) && aItem.isCompleted)) {
>            // If there is no alarm time, don't add the alarm. Also, if the item
>            // is a task and it is completed, don't add the alarm.
>            return;
>        }
>
>        // Check for snooze
>        var snoozeTime;
>        if (aItem.parentItem != aItem) {
>            snoozeTime = aItem.parentItem.getProperty("X-MOZ-SNOOZE-TIME-"+aItem.recurrenceId.nativeTime)
>        } else {
>            snoozeTime = aItem.getProperty("X-MOZ-SNOOZE-TIME");
>        }
>
>        if (snoozeTime && !(snoozeTime instanceof Components.interfaces.calIDateTime)) {
>            var time = createDateTime();
>            time.icalString = snoozeTime;
>            snoozeTime = time;
>        }
>        LOG("[calAlarmService] considering alarm for item: " + aItem.title +
>            " alarm time: " + alarmTime + " snooze time: " + snoozeTime);
>
>        // If the alarm was snoozed, the snooze time is more important.
>        alarmTime = snoozeTime || alarmTime;
>
>        var now = jsDateToDateTime(new Date());
>        if (alarmTime.timezone.isFloating) {
>            now = now.getInTimezone(calendarDefaultTimezone());
>            now.timezone = floating();
>        } else {
>            now = now.getInTimezone(UTC());
>        }
>        LOG("[calAlarmService] now is " + now);
>        var callbackObj = {
>            alarmService: this,
>            item: aItem,
>            notify: function(timer) {
>                this.alarmService.alarmFired(this.item);
>                this.alarmService.removeTimers(this.item);
>            }
>        };
>
>        if (alarmTime.compare(now) >= 0) {
>            LOG("[calAlarmService] alarm is in the future.");
>            // We assume that future alarms haven't been acknowledged
>
>            // delay is in msec, so don't forget to multiply
>            var timeout = alarmTime.subtractDate(now).inSeconds * 1000;
>
>            var timeUntilRefresh = this.mRangeEnd.subtractDate(now).inSeconds * 1000;
>            if (timeUntilRefresh < timeout) {
>                LOG("[calAlarmService] alarm is too late.");
>                // we'll get this alarm later.  No sense in keeping an extra timeout
>                return;
>            }
>
>            this.addTimer(aItem, newTimerWithCallback(callbackObj, timeout, false));
>            LOG("[calAlarmService] adding alarm timeout (" + timeout + ") for " + aItem);
>        } else {
>            var lastAck = aItem.alarmLastAck || aItem.parentItem.alarmLastAck;
>            LOG("[calAlarmService] last ack was: " + lastAck);
>            // This alarm is in the past.  See if it has been previously ack'd
>            if (lastAck && lastAck.compare(alarmTime) >= 0) {
>                LOG("[calAlarmService] " + aItem.title + " - alarm previously ackd.");
>                return;
>            } else { // Fire!
>                LOG("[calAlarmService] alarm is in the past and unack'd, firing now!");
>                this.alarmFired(aItem);
>            }
>        }
>    },
>
>    removeAlarm: function cas_removeAlarm(aItem) {
>        // make sure already fired alarms are purged out of the alarm window:
>        this.notifyObservers("onRemoveAlarmsByItem", [aItem]);
>        for each (var timer in this.removeTimers(aItem)) {
>            if (timer instanceof Components.interfaces.nsITimer) {
>                timer.cancel();
>            }
>        }
>    },
>
>    addTimer: function cas_addTimer(aItem, aTimer) {
>        var cal = this.mTimerLookup[aItem.calendar.id];
>        if (!cal) {
>            cal = {};
>            this.mTimerLookup[aItem.calendar.id] = cal;
>        }
>        var itemTimers = cal[aItem.id];
>        if (!itemTimers) {
>            itemTimers = { mCount: 0 };
>            cal[aItem.id] = itemTimers;
>        }
>        var rid = aItem.recurrenceId;
>        itemTimers[rid ? rid.getInTimezone(UTC()).icalString : "mTimer"] = aTimer;
>        ++itemTimers.mCount;
>    },
>
>    removeTimers: function cas_removeTimers(aItem) {
>        var cal = this.mTimerLookup[aItem.calendar.id];
>        if (cal) {
>            var itemTimers = cal[aItem.id];
>            if (itemTimers) {
>                var rid = aItem.recurrenceId;
>                if (rid) {
>                    rid = rid.getInTimezone(UTC()).icalString;
>                    var timer = itemTimers[rid];
>                    if (timer) {
>                        delete itemTimers[rid];
>                        --itemTimers.mCount;
>                        if (itemTimers.mCount == 0) {
>                            delete cal[aItem.id];
>                        }
>                        return { mTimer: timer };
>                    }
>                } else {
>                    delete cal[aItem.id];
>                    return itemTimers;
>                }
>            }
>        }
>        return {};
>    },
>
>    disposeCalendarTimers: function cas_removeCalendarTimers(aCalendars) {
>        for each (var cal in aCalendars) {
>            var calTimers = this.mTimerLookup[cal.id];
>            if (calTimers) {
>                for each (var itemTimers in calTimers) {
>                    for each (var timer in itemTimers) {
>                        if (timer instanceof Components.interfaces.nsITimer) {
>                            timer.cancel();
>                        }
>                    }
>                }
>                delete this.mTimerLookup[cal.id];
>            }
>        }
>    },
>
>    findAlarms: function cas_findAlarms(calendars, start, until) {
>        var getListener = {
>            alarmService: this,
>            onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) {
>                // calendar has been loaded, so until now, onLoad events can be ignored:
>                this.alarmService.mLoadedCalendars[aCalendar.id] = true;
>            },
>            onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
>                for (var i = 0; i < aCount; ++i) {
>                    var item = aItems[i];
>                    // assure we don't fire alarms twice, handle removed alarms as far as we can:
>                    // e.g. we cannot purge removed items from ics files. XXX todo.
>                    this.alarmService.removeAlarm(item);
>                    this.alarmService.addAlarm(item);
>                }
>            }
>        };
>
>        const calICalendar = Components.interfaces.calICalendar;
>        var filter = calICalendar.ITEM_FILTER_COMPLETED_ALL |
>                     calICalendar.ITEM_FILTER_CLASS_OCCURRENCES |
>                     calICalendar.ITEM_FILTER_TYPE_ALL;
>
>        for each(var calendar in calendars) {
>            // assuming that suppressAlarms does not change anymore until refresh:
>			if (!calendar.getProperty("suppressAlarms") &&
>                (calendar.getProperty("capabilities.alarms.popup.supported") !== false) &&
>                !calendar.getProperty("disabled")) {
>                calendar.getItems(filter, 0, start, until, getListener);
>            }
>        }
>    },
>
>    initAlarms: function cas_initAlarms(calendars) {
>        // Purge out all alarm timers belonging to the refreshed/loaded calendar:
>        this.disposeCalendarTimers(calendars);
>
>        // Purge out all alarms from dialog belonging to the refreshed/loaded calendar:
>        this.notifyObservers("onRemoveAlarmsByCalendar", calendars);
>
>        // 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 Sunbird/Lightning
>        // for a month, they'll miss some, but that's a slim chance
>        var start = nowUTC();
>        var until = start.clone();
>        start.month -= 1;
>        until.month += 1;
>        this.findAlarms(calendars, start, until);
>    },
>
>    alarmFired: function cas_alarmFired(event) {
>		if(event.calendar.type=="wcap")
>		  bIsWCAP = true;
>		else
>		  bIsWCAP = false;
>
>          if (!bIsWCAP && (event.calendar.getProperty("suppressAlarms") ||
>            event.calendar.getProperty("capabilities.alarms.popup.supported") === false)) {
>            return;
>        }
>        this.notifyObservers("onAlarm", [event]);
>    }
>};
Comment 16 Backstage 2009-03-02 14:59:22 PST
Created attachment 365040 [details]
 calAlarmService.js file with fixed alarm reminder for WCAP calendar only

Comment #15 should be remove...sorry...

Changed the snoozeAlarm behaviour. My changes now only affect WCAP calendar.
Comment 17 Philipp Kewisch [:Fallen] 2009-03-03 00:04:20 PST
Please only post patches instead of whole files, its really hard to understand this way. Also, make sure you are using the latest version of the file.

See https://developer.mozilla.org/En/Creating_a_patch and https://developer.mozilla.org/en/Mercurial_FAQ#How_can_I_diff_and_patch_files.3F
Comment 18 Backstage 2009-03-03 00:15:51 PST
Sorry!

I only modified the "snoozeAlarm" and "dismissAlarm" methods.
Comment 19 Daniel Boelzle [:dbo] 2009-03-04 09:20:29 PST
(In reply to comment #14)
> Yes, i replace the actual offset with the user selection (dismiss,snooze).
> 
> I think that the replacement of the offset is better than not having a alarm
> reminder ;-)
Then, what instance restores the original alarm offset? I don't think this approach is sensible. I think we should either
* go with a server fix (IIRC it's already been fixed, but don't know when it'll be published) and implement the X-prop based solution.
* *may* abuse a different alarm type that's yet unsupported, e.g. |alarmFlashing| to store the snooze stamps.

> By the way:
> I also tested the actual Lightning 1.0pre Version with Thunderbird 3.0b2 where
> i found following bugs (with WCAP-Calendar):
> 1) When i try to insert an event with an alarm time -> The alarm time won't be
> stored !
> 2) Following steps will show "MODIFICATION_FAILED" message:
>      a) Insert a new event
>      b) Open the created event
>      c) "Save/Close" the event without changing anything
>      d) --> MODIFICATION_FAILED
Please, file a new bug for this.
Comment 20 Vidanez 2009-03-11 02:03:13 PDT
*** Bug 482544 has been marked as a duplicate of this bug. ***
Comment 21 Vidanez 2009-03-16 03:43:14 PDT
I think maybe this bug is in relation with this other Bug 474426, am I right?
Please for emails reminder see the last note I posted in that bug. 
https://bugzilla.mozilla.org/show_bug.cgi?id=396865
Comment 22 Daniel Boelzle [:dbo] 2009-05-28 12:49:41 PDT
Adding relnote keyword unless we gonna fix this for 1.0...
Comment 23 Simon Paquet [:sipaq] 2010-04-12 06:15:50 PDT
*** Bug 558755 has been marked as a duplicate of this bug. ***
Comment 24 Maciek 2010-04-12 06:17:43 PDT
I've downloaded the calAlarmService.js file (which was attached to that bug) and replaced all this files in my system, but it doesn't resolve this issue. Maybe i did it wrong (means I should apply fixed  calAlarmService.js in the different way – not simply replace the files?)
Comment 25 keesdejong 2013-04-18 02:07:10 PDT
This bug is still present with Google Calendar notifications, they don't pop-up. Using the Lightning plugin on Ubuntu 12.04.
Comment 26 yu4cheem 2013-05-30 03:38:21 PDT
It would also be a big step forward if you could just provide an option to display a popup at the email alarm time, as a workaround. Thus, it'd be usable for now even if I get the additional email notification from the server which I can easily filter to /dev/null.

For me, without popup reminders the WCAP functionality is quite useless - I reliably notice the email quite a long time after the meeting has startet. 8-}

Note You need to log in before you can comment on or make changes to this bug.