The closest thing that Dynamics CRM 2011 has to a notification is the concept of Announcements. There are some drawbacks to Announcements, most important being that they cannot be scheduled. Also, can we expect the user to keep going back to the Announcements page each time to see the latest? I think not :)
1. Create a new custom entity called "Notifications". Add the following fields:
2. Add javascript that will enforce the required fields in the Notifications record
There are a few options that are available in the webosphere that could help. In this post, I am going to talk about a solution to have custom html notifications which can be scheduled to fire only at the appropriate time, as well as provide options on how to display the notification.
Business Case:
User should see the notification only between certain date/times, with the content of the notification itself coming from an html web resource. Options are needed for scheduling and to define how the notification would display. If more than one notification is valid, the order of the notification alert should be configurable.
Design:
We will be creating a new entity call "Notifications" that is visible from the Settings area. The notification record will have details of the start and end time/ duration (if applicable). The order of display is determined by the ordinal value
Implementation Details:
Field Name | Type | Required (Y/N) | Description |
Name | Text | Y | Name of the record |
Start Date/Time | DateTime | Y | Start date and time for notification |
End Date/Time | DateTime | Y | End date and time for notification |
Duration | Number | Y, unless Always in selected as Recurrence | Duration of notification in minutes |
Modal dialog or New Window | Boolean | Y | Shows the alert as modal dialog, or as a new window |
Recurrence | Drop Down | Y | Contains values Always, One Time, Daily and Weekly |
Recur Every | Drop Down | Y, If weekly is selected | Contains values with days of the week |
Web Resource | Text | Y | URL for the html page |
Ordinal Value | Number | N | Defines order of notification |
2. Add javascript that will enforce the required fields in the Notifications record
//function to set the value of Recur Every fields as required if Weely is selected as the transfer reason. Fires on onchange of Recurrence, and onload of form
function setReqFields() {
var rc1 = Xrm.Page.getAttribute("new_recurrence").getValue;
if (rc1 != null) {
var recurrence = Xrm.Page.getAttribute("new_recurrence").getSelectedOption().text;
if (recurrence == "Weekly") {
Xrm.Page.getAttribute("new_recurevery").setRequiredLevel("required");
} else {
//set recur every field to not required
Xrm.Page.getAttribute("new_recurevery").setRequiredLevel("none");
}
//duration is required unless recurrence is of type Always
if (recurrence != "Always") {
Xrm.Page.getAttribute("new_durationinminutes").setRequiredLevel("required");
} else {
//set recur every field to not required
Xrm.Page.getAttribute("new_durationinminutes").setRequiredLevel("none");
}
}
}
//function to get values from the crm Notification entity - onload of Phone call form
function getNotifications() {
var formType = Xrm.Page.ui.getFormType();
//notification only for updates
if (formType == 2) {
var serverUrl = Xrm.Page.context.getServerUrl();
var today = new Date();
var jsonText = JSON.stringify(today);
jsonText = jsonText.replace(/\"/g, '');
//alert(jsonText);
//The XRM OData end-point
var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";
var odataSetName = "new_notificationSet";
//get all notifications that have the end date greater than current date
var odataSelect = serverUrl + ODATA_ENDPOINT + "/" + odataSetName + "?$orderby=new_OrdinalValue&$filter=new_EndDateTime gt datetime\'" + jsonText + "\'";
//alert(odataSelect);
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: odataSelect,
beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
success: function (data, textStatus, XmlHttpRequest) {
//alert(data.d.results.length);
if (data.d.results && data.d.results != null) {
for (var indx = 0; indx < data.d.results.length; indx++) {
//alert("Name – " + data.d.results[indx].new_name);
notificationName = data.d.results[indx].new_name;
//get notification values
var recurrence = eval(data.d.results[indx].new_Recurrence.Value);
var windowType = eval(data.d.results[indx].new_NotificationDisplay.Value);
var webresource = data.d.results[indx].new_Webresource;
var startDate = data.d.results[indx].new_StartDateTime;
startDate = startDate.replace(/\//g, '');
startDate = startDate.replace("Date(", '');
startDate = startDate.replace(")", '');
var sd = new Date(Number(startDate));
var endDate = data.d.results[indx].new_EndDateTime;
endDate = endDate.replace(/\//g, '');
endDate = endDate.replace("Date(", '');
endDate = endDate.replace(")", '');
var ed = new Date(Number(endDate));
var duration = data.d.results[indx].new_DurationinMinutes;
var de1 = new Date(Number(startDate));
var durationEnd = de1.setMinutes(de1.getMinutes() + Number(duration));
var de = new Date(Number(durationEnd));
//recurrence - change the option set values based on yours
if (recurrence == 912630000) {
//alert("always");
always(sd, ed, today, windowType, webresource);
}
else if (recurrence == 912630001) {
//alert("one-time");
notification(sd, today, de, windowType, webresource);
}
else if (recurrence == 912630002) {
//alert("daily");
sd.setFullYear(today.getFullYear());
sd.setDate(today.getDate());
sd.setMonth(today.getMonth());
de.setFullYear(today.getFullYear());
de.setDate(today.getDate());
de.setMonth(today.getMonth());
notification(sd, today, de, windowType, webresource);
}
else if (recurrence == 912630003) {
//alert("weekly");
//get last character of the option set
var recurEvery = eval(data.d.results[indx].new_RecurEvery.Value).toString();
recurEvery = recurEvery.charAt(recurEvery.length - 1);
if (today.getDay().toString() == recurEvery.toString()) {
//alert("call weekly");
sd.setFullYear(today.getFullYear());
sd.setDate(today.getDate());
sd.setMonth(today.getMonth());
de.setFullYear(today.getFullYear());
de.setDate(today.getDate());
de.setMonth(today.getMonth());
notification(sd, today, de, windowType, webresource);
}
}
}
}
},
error: function (XmlHttpRequest, textStatus, errorThrown) { alert('OData Select Failed: ' + odataSelect+" Error thrown: "+errorThrown+" Status: "+textStatus); }
});
}
}
function notification(startDate, todayDate, durationEnd, windowType, webresource) {
if (todayDate.getTime() > startDate.getTime() && todayDate.getTime() < durationEnd.getTime()) {
if (windowType == 912630000) { //modal window
var myWin = window.showModalDialog(webresource);
}
else if (windowType == 912630001) { //new window
var myWin = window.open(webresource, 'Notification');
myWin.focus();
}
}
}
function always(startDate, endDate, todayDate, windowType, webresource) {
if (todayDate.getTime() > startDate.getTime() && todayDate.getTime() < endDate.getTime()) {
//alert("always");
if (windowType == 912630000) { //modal window
var myWin = window.showModalDialog(webresource);
}
else if (windowType == 912630001) { //new window
var myWin = window.open(webresource, 'Notification');
}
}
}
Let me walk through the code a bit here. The first step is to create the odata url to return all notifications that are active (i.e, current date/time is before the End Date/time) and ordered by the ordinal value. Once we get the active notifications, depending on the recurrence option set value, either function "notification" or "always" is called.
Within function "notification" or "always", depending of the type of notification display, the webresource is shown as a new window, or as a modal dialog.
If recurrence is set to "Always", the notification happens if current Date/time is within start and end date/times.
If recurrence is set to "One Time", the notification happens if current Date/time is within (start date/time) and (start date/time + duration).
If recurrence is set to "Daily", the notification happens if current Date/time is within (start time) and (start time + duration).
If recurrence is set to "Weekly" and current day equals the "Recur Every" day, the notification happens if current Date/time is within (start time) and (start time + duration).
Note: The "Recur Every" option set has values for the days, starting from Sunday which has value of 912630000. JS date.getDay() function returns the value of the week, starting with 0 for Sunday. Since odata calls do not get the value of the option set text value, I worked around it by getting the value of the last character of the option set, and comparing it with the getDay function.
Here is an example of a daily notification between 10 am and 11 am (10 am + duration time):
Here is an example of the notification that displays when the account record is opened and if the current time is between 10 am and 11 am, warning the user not to update the account details.
Conclusion:
In this post I have laid out a pretty easy way to setup your user alerts, and have it configured by a manager/ admin. The nice thing is the notification content itself is outside of the js code, and can be setup as a webresource with all the style/design options available in html.
You would effectively "turn off" an existing notification alert by moving the end date/time to before the current date/ time.
You would effectively "turn off" an existing notification alert by moving the end date/time to before the current date/ time.
Note that he admin will need read/ write privilege to the notification entity to create new notifications, while the other users need read privilege to the notification object to pull in the list of active notifications.
Thanks for reading!