Hello -
I've been using the WebSchedule components to see how difficult our implementation will be. In doing so I've run into some quirks of the reminder features. Any insight on the items below would be appreciated:
1) Snooze Functionality - It doesn't look like the original data structures account for storing snooze information. I also see that there are 2 different persistance types for snooze information (Cookie and ControlState). I didn't notice any difference when using these two. When I refresh the page I always get reminders for the items I previously snoozed. Is there a trick to this that I'm missing? I'm just setting the SnoozePersistenceType on my custom DataProvider. Should I have implemented something to handle this?
2) Reminders and Recurrence - When a reminder is dismissed, I see the UpdateReminder event firing, which is great. The problem is that the same event seems to fire when dismissing a recurring event and a standard activity - but it sends in a recurrenceID (not an activityID) when dismissing a recurring event. I haven't seen a way to distinguish whether I should update a recurrence or a standard activity. The UpdateRemindersContext has no way to tell between the two. Complicating this is the fact that recurrences seem to be added to the reminder list by default. I'm not adding them in my FetchReminders method, but reminders are popping up for them (and I can't dismiss them properly). Has anyone else noticed these problems?
brassier said: 1) Snooze Functionality - It doesn't look like the original data structures account for storing snooze information. I also see that there are 2 different persistance types for snooze information (Cookie and ControlState). I didn't notice any difference when using these two. When I refresh the page I always get reminders for the items I previously snoozed. Is there a trick to this that I'm missing? I'm just setting the SnoozePersistenceType on my custom DataProvider. Should I have implemented something to handle this?
If you are using a custom data provider, you need to implement IDataUpdate::Update(DataContext dc). Your Update implementation will be passed a DataContext that is an UpdateSnoozeContext (identifiable by inspecting its Operation property which will be "UpdateSnooze").
If you are subclassing one of the built-in data providers, then if you call base.Update( dc) passing your DataContext for those operations you do not handle at the end of your Update method -- you can reuse the data provider's built-in implementation of Cookie and ControlState persistence. e.g.,
public override void Update( DataContext dc) { if ( dc.Operation.CompareTo("UpdateActivity") == 0) { UpdateActivityContext ctx = dc as UpdateActivityContext; // ... custom code to implement update activity operation // ... } else if ( dc.Operation.CompareTo("RemoveActivity") == 0) { RemoveActivityContext ctx = dc as RemoveActivityContext; // custom code to implement remove activity operation // ... } else { // any operations not handled, let base class handle base.Update( dc); }}
If you are not subclassing, then in a pinch you can construct a new WebScheduleDataProviderBase-derived control, add it to the Page's Controls collection, set its SnoozePersistenceType to your desired snooze method, and then for UpdateSnoozeContext only delegate your Update call to it. It will not require a database connection to do it's thing to the page's cookies or the list of snoozes on the UpdateSnoozeContext. Here is some pseudocode for this:
// initialize field once during your data provider's initializationthis.snoozeDataProvider = new WebScheduleOleDbDataProvider( );this.snoozeDataProvider.SnoozePersistenceType = SnoozePersistenceType.Cookie;// need this next step so DP can access Cookies collection w/o null referencethis.Page.Controls.Add( this.snoozeDataProvider);
// . . .
// later, inside your Update method's implementationif ( dc.Operation.CompareTo( "UpdateSnooze") == 0 ) { // re-use Infragistics implementation of snooze updates this.snoozeDataProvider.Update( dc);}
(Of course, you can alternately implement snooze updates to the Page's Response's Cookies collection yourself without resulting to this delegation, it'd just take me a little more in the way of sample code I don't have with me.)
Thanks for that suggestion Derek - I would have never thought of calling the base object's snooze method. I did as you suggested, and the call got into the Infragistics UpdateSnoozeInterval method which threw a null exception. Any thoughts on that? If not, I can look into the Infragistics source to try to find it too. The stack trace is below.
The more important question to me is - do you have any thoughts on my second question? Have you noticed this behavior with Reminders and Recurrence? Here's the stack trace I referenced above. Thanks for your help - it is very appreciated!
{"Object reference not set to an instance of an object."}
at Infragistics.WebUI.Data.WebScheduleDataProviderBase.UpdateSnoozeInternal(UpdateSnoozeContext context) at Infragistics.WebUI.Data.WebScheduleDbProvider.UpdateSnooze(UpdateSnoozeContext context) at Infragistics.WebUI.Data.WebScheduleDbProvider.Update(DataContext context) at WebScheduleTest.PVOracleDataProvider.Update(DataContext context) in D:\VSS\Sandbox\WebScheduleTest\WebScheduleTest\PVOracleDataProvider.cs:line 377 at Infragistics.WebUI.WebSchedule.WebScheduleInfo.OnRaisePostDataChangedEvent(ArrayList eventList) at Infragistics.WebUI.WebControls.SmartWebControl.RaisePostDataChangedEvent() at System.Web.UI.Page.RaiseChangedEvents() at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
brassier said: I took the UpdateSnoozeInterval code from Infragistics and pulled it into my solution. It's referencing the Page object, which has no context and creates the exception. I changed those references to this.WebscheduleInfo.Page which seemed to work (not sure if that's the proper fix).
I took the UpdateSnoozeInterval code from Infragistics and pulled it into my solution. It's referencing the Page object, which has no context and creates the exception. I changed those references to this.WebscheduleInfo.Page which seemed to work (not sure if that's the proper fix).
Yes, if you add the data provider control to which you are delegating to the Page's Controls collection then it ought to have a non-null Page property and avoid that null reference (since it needs the Page to get at the cookies). However, any Page reference should do as well as any other (since they should all be the same Page object with the same cookies).
brassier said: The Recurrence and Reminders is still a hot item for me. Any insight on what I brought up in the original post (and how it affects snoozing from this post) would be appreciated.
The Recurrence and Reminders is still a hot item for me. Any insight on what I brought up in the original post (and how it affects snoozing from this post) would be appreciated.
You've found in another post in this thread that the Recurrence Engine generates occurrences according to the instructions of the Recurrence object. These are produced dynamically so that they don't have to be persisted in the database (consider, a weekly recurrence with no end date that would require 52 activity records per year every year until the year 9999.) The only time information about an occurrence is persisted is as a variance, in which case an activity record will be created at a specific spot (the OriginalStartDateTimeUtc) in the recurrence. Variances come into being if the user changes something about an individual occurrence, this may include toggling the EnableReminder property of a particular occurence if you are so disposed and want to force records to be created, but it doesn't include "has this user or hasn't this user dismissed a reminder?" The answer to this question is stored by the LastReminderDateTimeUtc mechanism which is recurrence-wide and works as follows.
Above is a timeline of a weekly recurrence, each occurrence that will be generated dynamically for this recurrence is marked along this timeline. There are two dates to be aware of: (a) the LastReminderDateTimeUtc which is saved with the recurrence will identify the start date/time of its last dismissed occurrence, (b) now, the date/time at which a LoggedOnUser accesses WebSchedule to check his/her schedule and receives reminder notifications. You can move these two dates anywhere along the timeline and convince yourself the following logic delivers a reminder notification for each occurrence at least once.
To the left of the LastReminderDateTimeUtc are occurrences whose reminder notifications (possibly multiple) have already been displayed to the LoggedOnUser, but WebSchedule has recorded that the user last dismissed reminders for this recurrence as of Sep 12 so they do not display a reminder notification again. When that dismissal happened, the UpdateReminders operation wrote to the database an updated value for LastReminderDateTimeUtc.
To the right of Now, are occurrences in the future. Note that if the ReminderInterval had been 48 hours (2 days) or more, then a reminder notification would be generated for Sep 30. However, any occurrences bound to happen in front of Now plus their ReminderInterval do not produce a reminder notification yet (they were dynamically generated for display/editing purposes.)
Between LastReminderDateTimeUtc and Now are the occurrences for which reminder notifications are displayed to the LoggedOnUser. Unless the rightmost occurrence is within the lead-time of a ReminderInterval window (which may be uncommon if the user only sporadically, like once a week, checks his/her schedule) then all reminders for past occurrences are undoubtedly of the overdue nature.
As far as snooze goes, a cookie can be encoded to snooze the occurrences because they have a data key derived for them based on the recurrence key and their original start date/time UTC value (the combination of which makes them uniquely identifiable). If they are not ruled out of a future pop-up of the reminder notification form because the LastReminderDateTimeUtc has been moved forward by the user dismissing a later occurrence in this recurrence, then those same occurrence objects will be dynamically generated for you by the Recurrence Engine again with the same keys in future requests.
Derek - Thanks a ton for your detailed answer. I have one follow-up question that I need clarification on.
Your post mentions that I can create a cookie to uniquely identify each occurrence. Do I need to manually remove snoozed occurrences from the reminders list at some point in the lifecycle? Or will the DisplayReminderDialogResolved method handle stand-alone and recurring events as long as I create cookies with the proper data key (as you explained above)?
Thanks again for your help Derek.
Quick update on this. Earlier in this thread I took the UpdateSnooze method from the Infragistics implementation. This method uses the snooze.ActivityKey to create the snooze cookie. For recurrences this does use RecurrenceKey_OriginalStartDate. I see the cookies created, seemingly with the appropriate data key, but the snooze never works. Snooze does work for stand-alone appointments.
Is there anything else that could be causing snooze to not work for recurrences? Maybe I need to manually remove snoozed occurrences from the reminders list at some point (not sure where)?
It was pretty painful, but I think I found the problem. It would be great if an Infragistics employee could verify, and recommend a solution to get this fix available. I've been trying to get answers using a support request, but have had no luck. I need to know if Infragistics usually makes hotfixes for things like this, or if they expect customers to rebuild and deploy themselves?
Here's what I found. As Derek mentioned above, the UpdateSnooze method passes an activity key of the form RecurrenceKey_StartDateTime for an occurrence. This is needed to make occurrences unique. I see the cookie being created, and everything looks good. However, the occurrence snooze never sticks.
The reason seems to be in the WebScheduleInfoRenderer class. The RenderCollectionProperties method makes a call to GetSnoozeFromKey, trying to obtain the snooze for the occurrence (and stand-alone activities). The problem is that it passes the Activity's Data Key, which for an occurrence defaults to the root activity's data key (see AppointmentOccurrence constructor). This is not unique across occurrences, and it isn't used in the UpdateSnoozeContext. What I think it should do is pass this.GetExtendedDataKey(activity) to the call to GetSnoozeFromKey, which will pull out the snooze properly for both stand-alone and occurrence activities. I made that change locally and it seems to work.
Can someone from Infragistics confirm this? Can someone give me a recommended direction for getting a fix (Hotfix, instructions to build/deploy myself with the rest of the standard assemblies, etc.)?
Hi Brian,
I have logged a Development Issue regarding this behavior. If the issue is determined as a bug, it will be queued to be fixed in the next private hotfix. Until then, I would recommend you to apply your local fix until the issue has been identified as a bug and has been queued for a fix.