Developing an Alerts Web Service that Uses Outlook 2003 Alerts
Angela Wong
Microsoft Corporation
April 2004
Applies to:
Microsoft® Office Outlook® 2003
Microsoft Office SharePoint™ Portal Server 2003
Microsoft Windows® SharePoint™ Services
Summary: As a third-party application developer, have you wanted to use e-mail to notify your users of an event, like an updated shared document or the price of a stock reaching a certain level? Learn how to use e-mail to notify your users of an event. Microsoft Office Outlook 2003 lets third-party applications use Outlook alerts to notify users of specified events. (32 printed pages)
Contents
Introduction
Scenario: User Sets up an Alert in an Application
Scenario: User Creates, Edits, or Deletes an Alert in Outlook
Alert Web Service: Support Outlook in Removing an Alert
Sample: The RSS Alerts Web Service
Scenario: User Deletes Alerts for RSS-Enabled Web Sites
Conclusion
Introduction
Microsoft® Office Outlook® 2003 lets third-party applications use Outlook alerts to notify users of specific events. Alerts can be very useful to many types of applications. For example, in team collaboration applications such as Microsoft Office SharePoint™ Portal Server 2003 and Microsoft Windows® SharePoint™ Services, information is gathered and shared in central repositories, and sifting through these repositories may require much effort. Team members can reduce this type of overhead by setting up alerts for items in which they are interested. When the content of an item changes, the site where the item resides, or the alert source site, sends an e-mail to the user. The user can apply Outlook rules on e-mails received from these alert source sites. Furthermore, Outlook lets users set up alerts on different Web servers, but manage their alerts from one central location in Outlook.
A developer can easily incorporate Outlook alerts into an application through an alerts Web service that collaborates with Outlook. The user can then set up an alert in that application or in Outlook. For example, the user can set up an alert for a Windows SharePoint Services site from Windows SharePoint Services or from Outlook. The user can then manage his or her alerts (edit or delete alerts) in Outlook 2003.
The Web service maintains a database for user alert data. To collaborate with Outlook, the alerts Web service notifies Outlook when the user sets up an alert, the Web service sends appropriate SOAP messages to Outlook when the user views, edits, or deletes his alerts in Outlook. The next two sections of this article describe how an alerts Web service can perform these tasks. The last section describes a sample alerts Web service, the RSS Alerts Web service.
Scenario: User Sets up an Alert in an Application
In an application that uses Outlook alerts, a user can specify conditions for an alert. For example, a user can set up an alert for a document repository in a team collaboration application so that the user receives a weekly summary of the documents that were changed in the repository that week.
To set up alerts, Outlook needs information about each alert that a user sets up. Outlook requires the alerts Web service to do one or both of the following tasks when a user sets up an alert:
- Send an alert setup e-mail to the user's Outlook account
- Send a cookie to the user's computer
Send an Alert Setup E-Mail
There are two purposes to this initial alert setup e-mail: to confirm to the user that the alert has been set up properly, and to provide Outlook information about the alert. This information is contained as X-headers as shown in Table 1. X-AlertWebSoap and Message-ID are mandatory headers.
Table 1. X-headers in an alert setup e-mail
X-Header | Description |
---|---|
From | The encoded name of the originating server for the alert. |
Subject | The e-mail title. |
X-AlertServerType | An ASCII string that uniquely identifies the server type and determines the type of icon that is displayed in Outlook for alerts from that server. |
X-AlertId | An ASCII string that uniquely identifies the alert. |
X-AlertTitle | An ASCII or UTF-8 B64 encoded string containing the name of the alert. |
X-AlertWebUrl | An ASCII or UTF-8 B64 encoded string containing the URL for the Web site in which the alert originates. After the server is verified, this URL provides a link for the user to navigate to the Web site. |
X-AlertWebSoap | An ASCII or UTF-8 B64 encoded string containing the URL that SOAP calls will be issued to. It is used to retrieve alert information from the server, including the alerts summary. |
Message-ID | A string containing a message ID that identifies the message as an alert setup e-mail. |
When Outlook processes an incoming alert setup e-mail, it extracts and processes the X-headers, and stores the URL specified by X-AlertWebSoap in an alert sources list. The alert source list contains unique URLs for Web servers on which the user has set up alerts. Outlook uses the alert source list when the user subsequently needs to create, modify, or delete alerts.
Figure 1 shows how Outlook uses the alert source list to track new alerts being set up on multiple Web servers.
Figure 1. Outlook tracking user's alerts on multiple servers
Send a Cookie to the User's Computer
The cookie contains the fields shown in Table 2. The AlertWebSoap field is mandatory.
Table 2. Fields in an alert cookie
Field | Description |
---|---|
AlertServerType | An ASCII string that uniquely identifies the server type and determines the type of icon that is displayed in Outlook for alert e-mails from that server. |
AlertWebSoap | An ASCII or UTF-8 B64 encoded string containing the URL to which SOAP calls are issued. It retrieves alert information from the server, including the alerts summary. |
AlertWebTitle | An ASCII or UTF-8 B64 encoded string containing the name for the alert. |
AlertWebUrl | An ASCII or UTF-8 B64 encoded string containing the URL for the Web site in which the alert originates. After the server is verified, this URL provides a link for the user to navigate to the Web site. |
Scenario: User Creates, Edits, or Deletes an Alert in Outlook
Outlook provides the Manage Alerts tab of the Rules and Alerts dialog box as a central location for a user to manage all his alerts. On this tab, the user can create a new alert, modify an alert, navigate to the alert source site, manage all alerts on that alert source site, view the alerted item, create rules for alert e-mails, and delete an alert. When Outlook opens the Manage Alerts tab, Outlook populates the tab with all the alerts that the user has set up on different alert source sites.
Figure 2. Manage Alerts tab in Outlook provides a central location to manage all alerts (Click picture to view larger image)
Alert Web Service: Support Outlook to List all Alerts on the Web Server
To populate the Manage Alerts tab, Outlook checks two places:
- Outlook goes through the alert source list and extracts the Simple Object Access Protocol (SOAP) address of each Web server in that list. It then sends a GetAlerts() Web method call to each of the SOAP addresses.
- Outlook searches the client computer for alert cookies. For each cookie, Outlook extracts the
AlertWebSoap
field and sends a GetAlerts() Web method call to that SOAP address.
The alert Web service of each Web server should implement the GetAlerts() Web method and return a SOAP message in the following format:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetAlertsResponse
xmlns="http://schemas.microsoft.com/
sharepoint/soap/2002/1/alerts/">
<GetAlertsResult>
<CurrentUser>string</CurrentUser>
<AlertServerName>string</AlertServerName>
<AlertServerUrl>string</AlertServerUrl>
<AlertServerType>string</AlertServerType>
<AlertsManagementUrl>string</AlertsManagementUrl>
<AlertWebTitle>string</AlertWebTitle>
<NewAlertUrl>string</NewAlertUrl>
<AlertWebId>string</AlertWebId>
<Alerts>
<Alert>
<Id>string</Id>
<Title>string</Title>
<Active>boolean</Active>
<EventType>string</EventType>
<AlertForTitle>string</ AlertForTitle>
<AlertForUrl>string</AlertForUrl>
<EditAlertURL>string</EditAlertURL>
<DeliveryChannels>
<DeliveryChannel xsi:type="string">
<Frequency>string</Frequency>
<Address>string</Address>
</DeliveryChannels>
</Alert>
</Alerts>
</GetAlertsResult>
</GetAlertsResponse>
</soap:Body>
</soap:Envelope>
The XML tags are defined in Table 3. The tags in bold are mandatory.
Table 3. XML tags in the GetAlerts() schema
XML Tag | Value |
---|---|
AlertServerName | An ASCII string for the name of the Web server that the alert originates from. |
AlertServerUrl | The URL for the Web server that the alert originates in. After the server is verified, this URL provides a link for the user to navigate to the Web site. This URL is mapped to Alert Source in the Rules and Alerts dialog box. See Figure 2 for the Rules and Alerts dialog box. |
AlertServerType | An ASCII string that uniquely identifies the server type and determines the type of icon that is displayed in Outlook for alert e-mails from that Web server. |
AlertsManagementUrl | The URL for the Web page that lists all of the user's alerts on that Web server and lets the user to manage the alerts. The URL is mapped to Go to the alerts management page in the Alert Properties dialog box. See Figure 3 for the Alert Properties dialog box. |
AlertsWebTitle | The friendly name for the Web site the alert is referencing. This friendly name is displayed as Alert Source in the Rules and Alerts dialog box. See Figure 2 for the Rules and Alerts dialog box. |
NewAlertUrl | The URL for the Web page that lets the user to create an alert on that Web server. |
AlertWebId | The ID for the Web site to which the alert refers. |
Id | An index that identifies the alert in the list of all alerts on that Web server. |
Title | An ASCII string that describes the alert. This friendly name is mapped to Alert Description in the Rules and Alerts dialog box. See Figure 2 for the Rules and Alerts dialog box. |
Active | Whether an alert is active or inactive. A user can temporarily deactivate an alert without deleting it. |
EventType | The event that caused the alert to be fired. |
AlertForUrl | The URL to the object that the alert was fired to. This URL is mapped to View Item in the Alert Properties dialog box. See Figure 3 for the Alert Properties dialog box. |
EditAlertUrl | The URL to the Web page that allows the user to modify the selected alert. This URL is mapped to Modify Alert in the Alert Properties dialog box. See Figure 3 for the Alert Properties dialog box. |
DeliveryChannel xsi:type | EmailChannel. The means by which the user is alerted. |
Frequency | The frequency at which an alert e-mail is sent to the user. |
Figure 3. Opening Alert Properties allows further management and viewing of alerted item
By conforming to the preceding format when responding to a GetAlerts() Web method call, an alerts Web service provides the necessary information for Outlook to complete the user tasks in Table 4.
Table 4. XML tags that support user tasks
User Task | XML tags in schema for GetAlerts() |
---|---|
Create a new alert | NewAlertUrl |
Modify an alert | EditAlertUrl |
Navigate to alert source site | AlertServerUrl |
Manage all of user's alerts on the alert source site | AlertsManagementUrl |
View the alerted item | AlertForUrl |
Create rules for alert e-mails | Title and AlertsWebTitle |
Alert Web Service: Support Outlook in Removing an Alert
When the user deletes one or more alerts on the same Web server and clicks OK in the Manage Alerts tab (see Figure 2), Outlook sends a DeleteAlerts() Web method call to the SOAP address for the corresponding Web server. Here is the format of a DeleteAlerts() SOAP message:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/
XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<DeleteAlerts
xmlns="http://schemas.microsoft.com/sharepoint/soap/2002/
1/alerts/">
<IDs>
<string>string</string>
<string>string</string>
</IDs>
</DeleteAlerts>
</soap:Body>
</soap:Envelope>
The alert Web service for that Web server should implement the DeleteAlerts() Web method and return a SOAP response indicating the result of each of the delete requests. The SOAP message should be in the following format:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<DeleteAlertsResponse
xmlns="http://schemas.microsoft.com/sharepoint/soap/2002/
1/alerts/">
<DeleteAlertsResult>
<DeleteFailure>
<Id>string</Id>
<Error>None or AccessDenied or ServerError or
TooManyErrors</Error>
</DeleteFailure>
<DeleteFailure>
<Id>string</Id>
<Error>None or AccessDenied or ServerError or
TooManyErrors</Error>
</DeleteFailure>
</DeleteAlertsResult>
</DeleteAlertsResponse>
</soap:Body>
</soap:Envelope>
The Id
tag identifies an alert in the list of all alerts on that Web server. The alert Web service should return None
with the Error
tag if the delete request is successful, and specify the cause of failure otherwise. Upon receiving a success for the delete request, Outlook updates the Manage Alerts tab to remove the deleted alerts.
Sample: The RSS Alerts Web Service
This sample applies to the situation where a user has subscribed to an RSS-enabled Web site (such as CNET News.com). RSS-enabled Web sites allow headlines and summaries from the site to be aggregated on another Web site such as a weblog (blog). This sample allows the user to be informed when news feeds are posted to the site. The RSS Alerts Web service allows a user to receive Outlook alerts at specified times, daily for example, for new news feeds.
The following sections outline the major design aspects of the RSS Alerts Web service, and include sample code for these major areas.
Data Storage
The RSS Alerts Web service stores data in a SQL database to facilitate quick and efficient storage and retrieval of data. Figure 4 shows the data structure.
Figure 4. The RSS Alerts Web service stores data in this structure
The UserSubscriptions
table lists all subscriptions on the Web server, with each subscription associated with an individual user defined in the UserList
table. The UserList
table stores the unique user ID and e-mail address for each user subscribing to an RSS-enabled Web site using the RSS Alert Web service. The FeedEntries
table lists the news feeds for subscriptions in the UserSubscriptions
table. The fields are defined in Table 5.****
Table 5. Description of data fields for the UserList table
Field | Description |
---|---|
ulIndex | A unique ID for each row in the table. |
UserID | The user's unique user ID (domain\user_name). |
EmailAddress | The user's e-mail address. |
Table 6. Description of data fields for the UserSubscriptions table
Field | Description |
---|---|
usIndex | A unique ID for each row in the table. |
ulIndex | Each subscription corresponds to a unique entry in the UserList table. This entry is the link between the two tables. |
Name | <TBD> |
Location | The URL of the XML feed. |
MailFrequency | The frequency of alert e-mails, for example, daily. |
LastSent | The date and time of the last alert e-mail. |
Table 7. Description of data fields for the FeedEntries table
Field | Description |
---|---|
feIndex | A unique ID for each row in the table. |
usIndex | Each feed corresponds to a specific entry in the UserSubscriptions table. This entry is the link between the two tables. |
Title | The title of this news feed. |
Body | The formatted body of this news feed. |
DateEntered | The date and time this news feed was put into the database. |
Generating Outlook Alerts
The user can specify the frequency that Outlook alerts should be sent. Currently, the RSS Alert Web service supports daily alerts.
Each day starting at 4:00 P.M. Pacific Standard Time, the RSS Alerts Web service goes through the UserSubscriptions
table, creates a summary for each user, and sends the summary as an alert e-mail to the user that created the subscription.
After the alert e-mail is sent, the Web service deletes all rows in the FeedEntries
table that belong to that subscription (with the same usIndex
) with a DateEntered
timestamp older than the time the summary was created, This keeps the table small.
Once the summary is sent, the Web service updates the LastSent
field in the UserSubscriptions
table with the current date and time.
An alert e-mail appears as follows:
Subject
<UserSubscriptions.Name
> subscription for <CurrentDate
>
Body
This is a summary of your <UserSubscriptions.Name
> subscription for <CurrentDate
>:
<FeedEntries.Body>
Scenario: User Creates an Alert for an RSS-Enabled Site
When a user creates an alert for an item in an RSS-enabled Web site, the RSS Alerts Web service sends a setup e-mail to Outlook with X-header values as shown in Table 8.
Table 8. X-header values in an alert setup e-mail sent by the RSS Alerts Web service
X-Header | Value |
---|---|
From | RSS Alerts Sample <RSSAlertsSample@noreply> |
Subject | You have successfully created an alert for <UserSubscription.Name>. |
X-AlertServerType | RSSAlertsService |
X-AlertId | UserSubscriptions.usIndex |
X-AlertTitle | UserSubscription.Name |
X-AlertWebUrl | <The address of the Web server is TBD.> |
X-AlertWebSoap | <The SOAP address of the Web service is TBD.> |
Message-ID | 3BD50098E401463AA228377848493927<counter> |
The RSS Alerts Web service also sends a cookie to the client computer. The cookie contains values as shown in Table 9.
Table 9. Cookie values in a cookie sent by the RSS Alerts Web service
Field | Description |
---|---|
AlertServerType | RSSAlertsService |
AlertWebSoap | <The SOAP address of the Web service is TBD.> |
AlertWebTitle | RSS Alerts Sample |
AlertWebUrl | <The address of the Web server is TBD.> |
Scenario: User Opens Manage Alerts Tab in Outlook
When a user opens the Manage Alerts tab in Outlook to view, edit or delete an alert, Outlook makes a GetAlerts
() call to each of the Web servers in its alerts source list. Figure 5 shows an alert entry for those RSS-enabled Web sites that the user has subscribed to.
Figure 5. User opens the Manage Alerts tab in Outlook
GetAlerts() as Implemented by the RSS Alerts Web service
The RSS Alerts Web service implements GetAlerts
() that is shown in the Sample Code section below. It also returns a SOAP message to Outlook that contains the values in Table 10.
Table 10. Values in SOAP response message for GetAlerts() as implemented by the RSS Alerts Web service
XML Tag | Value |
---|---|
AlertServerName | RSS Alerts Sample |
AlertServerUrl | This URL is mapped to Alert Source in the Rules and Alerts dialog box. See Figure 2 for the Rules and Alerts dialog box. |
AlertServerType | RSS Alerts Service |
AlertsManagementUrl | This URL is mapped to Go to the alerts management page in the Alert Properties dialog box. See Figure 3 for the Alert Properties dialog box. |
AlertsWebTitle | <N/A> |
NewAlertUrl | This URL for the Web page that allows the user to create a new alert on that Web server. In this case, it is the same as AlertServerURL. |
AlertWebId | <N/A> |
Id | UserSubscriptions.usIndex |
Title | UserSubscriptions.Name |
Active | True |
EventType | <N/A> |
AlertForUrl | This URL is mapped to View Item in the Alert Properties dialog box. See Figure 3 for the Alert Properties dialog box. |
EditAlertUrl | This URL is mapped to Modify Alert in the Alert Properties dialog box. See Figure 3 for the Alert Properties dialog box. |
DeliveryChannel xsi:type | EmailChannel. |
Frequency | UserSubscriptions.MailFrequency |
Scenario: User Deletes Alerts for RSS-Enabled Web Sites
The user can delete multiple items simultaneously through the Delete Alerts dialog box in Outlook, as shown in Figure 6.
Figure 6.User deletes alerts for RSS-enabled Web sites
DeleteAlerts() as Implemented by the RSS Alerts Web service
When the user clicks Delete in the Manage Alerts tab, Outlook calls DeleteAlerts
() that is implemented by the RSS Alerts Web service, as shown in the Sample Code section below. The RSS Alerts Web service also returns a SOAP message to Outlook that contains the values shown in Table 12.
Table 12. Values in SOAP Response Message for DeleteAlerts() as Implemented by the RSS Alerts Web service
XML Tag | Value |
---|---|
Id | UserSubscriptions.usIndex |
Error | None or AccessDenied |
To delete an alert entry, the RSS Alerts Web service deletes the relevant row (as indicated by the Id
tag) in the UserSubscriptions
table, as well as all other related rows in the FeedEntries
table as identified by the usIndex
key. This is done to ensure that:
- The subscription is no longer fetched.
- There won't be data left over from deleted subscriptions.
Sample Code
This section lists sample code showing how the RSS Alerts Web service implements GetAlerts() and DeleteAlerts(). Note that Outlook 2003 only calls these two Web methods. The other Web methods are used for server-side data management.
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Web.Mail;
using System.Data.SqlClient;
using System.Configuration;
using System.Text.RegularExpressions;
using System.Security;
namespace AlertsSample
{
using System;
using System.Xml;
// Outlook Alerts Schema
//
// The AlertsNamespace and following structures are the schema expected by Outlook 11
// and should not be changed.
public class AlertsConst
{
public const string AlertsNamespace = "http://schemas.microsoft.com/sharepoint/soap/2002/1/alerts/";
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace=AlertsConst.AlertsNamespace)]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(DeliveryChannel))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(EmailChannel))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Alert))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(AlertInfo))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(DeleteFailure))]
public abstract class DeliveryChannel
{
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace=AlertsConst.AlertsNamespace)]
public class EmailChannel : DeliveryChannel
{
public string Frequency;
public string Address;
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace=AlertsConst.AlertsNamespace)]
public struct Alert
{
public string Id;
public string Title;
public bool Active;
public string EventType;
public string AlertForTitle;
public string AlertForUrl;
public string EditAlertUrl;
public DeliveryChannel[] DeliveryChannels;
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace=AlertsConst.AlertsNamespace)]
public struct AlertInfo
{
public string CurrentUser;
public string AlertServerName;
public string AlertServerUrl;
public string AlertServerType;
public string AlertsManagementUrl;
public string AlertWebTitle;
public string NewAlertUrl;
public string AlertWebId;
public Alert[] Alerts;
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace=AlertsConst.AlertsNamespace)]
public enum ErrorType
{
None = 0,
AccessDenied,
ServerError,
TooManyErrors // Operation aborted in unknown state -- client should re-query
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace=AlertsConst.AlertsNamespace)]
public struct DeleteFailure
{
public string ID; // Alert id that failed
public ErrorType Error;
}
// Alerts Sample Web Service
//
// Note that GetAlerts() and DeleteAlerts() are the only methods called by
// Outlook 11 -- the other methods here are used for server-side data management.
//
[WebService(Namespace=AlertsConst.AlertsNamespace)]
public class Alerts : System.Web.Services.WebService
{
public enum AddAlertErrorType
{
None = 0,
AlreadyExists,
DatabaseUpdateError,
EmailMismatch,
HttpException,
SMTPSendError
}
public Alerts()
{
//CODEGEN: This call is required by the ASP.NET Web Services Designer
InitializeComponent();
}
#region Component Designer generated code
//Required by the Web Services Designer
private IContainer components = null;
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if(disposing && components != null)
{
components.Dispose();
}
base.Dispose(disposing);
}
#endregion
/* ValidUserName
* ============
* NOTE: To prevent SQL Injection attacks, we're being fairly
* restrictive & requiring user names to contain only alpha
* characters (or a '\' delimiter) before being used in a SQL command.
* */
private bool ValidUserName(string userName)
{
// for our example, force alpha or backslash
// (which has to be escaped for both C# and Regex)
Regex r = new Regex("^[a-zA-Z\\\\]+$");
return r.IsMatch(userName);
}
/* ValidAlertId
* ============
* NOTE: AlertId string is passed in via SOAP and
* should not be trusted. To prevent SQL Injection
* attacks, the string must be verified to be a
* known valid format before being used in a SQL command.
* */
private bool ValidAlertId(string alertId)
{
// for our example, force numeric Id of no more than 8 digits
Regex r = new Regex("^[0-9]{1,8}$");
return r.IsMatch(alertId);
}
/* IdentifyUser
* ============
* Helper function for authentication */
private string IdentifyUser()
{
if (!User.Identity.IsAuthenticated) // true on first try
{
HttpContext context = HttpContext.Current;
HttpResponse resp = context.Response;
resp.StatusCode = 401;
resp.Clear();
resp.Write("401 UNAUTHORIZED");
resp.Flush();
resp.End();
return null;
}
else
{
// prevent SQL injection via user name
if (!ValidUserName(User.Identity.Name))
throw new SecurityException();
return User.Identity.Name;
}
}
SqlConnection connection = new SqlConnection(ConfigurationSettings.AppSettings["DBConnectionString"]);
/* GetAlerts
* =========
* Webmethod called by Outlook to retrieve all alerts for
* a particular user. NTLM authentication required to identify
* user. Outlook will attempt an anonymous first try,
* to which we must throw an exception before receiving the
* second, authenticated request. */
[WebMethod]
public AlertInfo GetAlerts()
{
// First, find out who the user is
AlertInfo si = new AlertInfo();
si.CurrentUser = IdentifyUser();
si.AlertServerName = "RSS Alerts Sample";
si.AlertServerUrl = ConfigurationSettings.AppSettings["webserviceurl"];
si.AlertServerType = "Third Party";
si.AlertWebTitle = "RSS Alerts Web Service";
si.AlertWebId = "{1FFAB040-A674-4eaa-96A8-9442B79E1E00}";
// Point to management page for this Alert
si.AlertsManagementUrl = ConfigurationSettings.AppSettings["webserviceurl"];
// Point to new Alert page for this site
si.NewAlertUrl = ConfigurationSettings.AppSettings["webserviceurl"];
try
{
string commandString = "SELECT ulIndex FROM UserList "
+ "WHERE UserID ='" + si.CurrentUser + "'";
SqlCommand command = connection.CreateCommand();
command.CommandText = commandString;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataSet user = new DataSet();
DataSet usersubs = new DataSet();
DataSet title = new DataSet();
connection.Open();
string userIndex = "";
string subIndex = "";
int numSubs;
if (adapter.Fill(user, "UserList") == 0)
{
numSubs = 0;
}
else
{
DataTable usertable = user.Tables["UserList"];
foreach (DataRow row in usertable.Rows) userIndex = row[0].ToString();
commandString = "SELECT usIndex,Location,Title,MailFrequency FROM UserSubscriptions "
+ "WHERE ulIndex ='" + userIndex + "'";
command.CommandText = commandString;
adapter.SelectCommand = command;
numSubs = adapter.Fill(usersubs, "UserSubscriptions");
}
si.Alerts = new Alert[numSubs];
DataTable substable = usersubs.Tables["UserSubscriptions"];
int iSub = 0;
// Get each subscription
foreach (DataRow row in substable.Rows)
{
subIndex = row["usIndex"].ToString();
si.Alerts[iSub].Active = true;
si.Alerts[iSub].EventType = "All";
si.Alerts[iSub].Id = subIndex;
si.Alerts[iSub].AlertForUrl = row["Location"].ToString();
// Point to Edit page for this subscription
si.Alerts[iSub].EditAlertUrl = ConfigurationSettings.AppSettings["webserviceurl"];
EmailChannel emc = new EmailChannel();
emc.Frequency = row["MailFrequency"].ToString();
emc.Address = ConfigurationSettings.AppSettings["senderEmailAddr"];
si.Alerts[iSub].DeliveryChannels = new DeliveryChannel[1];
si.Alerts[iSub].DeliveryChannels[0] = emc;
si.Alerts[iSub].Title = row["Title"].ToString();
++iSub;
}
}
catch (Exception ex)
{
connection.Close();
return si;
}
connection.Close();
return si;
}
// Returns "error" if there is an error, the attribute value otherwise
[WebMethod]
public string GetAlertInfo(string usIndex, string attrib)
{
if (!(attrib.Equals("Title") || attrib.Equals("Location") || attrib.Equals("MailFrequency")))
return "error";
try
{
string commandString = "SELECT " + attrib + " FROM UserSubscriptions "
+ "WHERE usIndex=@index";
SqlCommand command = connection.CreateCommand();
command.CommandText = commandString;
command.Parameters.Add("@index", SqlDbType.Int).Value = usIndex;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataSet sub = new DataSet();
connection.Open();
string answer = "";
if (adapter.Fill(sub, "UserSubscriptions") == 0) return "error";
DataTable subs = sub.Tables["UserSubscriptions"];
foreach (DataRow row in subs.Rows) answer = row[0].ToString();
return answer;
}
catch (Exception e)
{
throw e;
return "error";
}
}
// Returns 1 if there is an error, 0 otherwise
[WebMethod]
public int SetAlertInfo(string usIndex, string attrib, string val)
{
if (!(attrib.Equals("Title") || attrib.Equals("Location") || attrib.Equals("MailFrequency"))) return 1;
try
{
string commandString = "UPDATE UserSubscriptions SET " + attrib + "=@val "
+ "WHERE usIndex=@index";
SqlCommand command = connection.CreateCommand();
command.CommandText = commandString;
command.Parameters.Add("@val", SqlDbType.VarChar, 256).Value = val;
command.Parameters.Add("@index", SqlDbType.Int).Value = usIndex;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataSet sub = new DataSet();
connection.Open();
adapter.Fill(sub, "UserSubscriptions");
return 0;
}
catch (Exception e)
{
throw e;
return 1;
}
}
[WebMethod]
public DeleteFailure[] DeleteAlerts(string [] IDs)
{
DeleteFailure[] dfReturn = new DeleteFailure[IDs.Length];
string userID = IdentifyUser();
bool allFailed = false;
try
{
// A match on both userID and usIndex must occur
// before we proceed with the subscription removal
string commandString = "SELECT ulIndex FROM UserList "
+ "WHERE UserID ='" + userID + "'";
SqlCommand command = connection.CreateCommand();
command.CommandText = commandString;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataSet user = new DataSet();
connection.Open();
string userIndex = "";
if (adapter.Fill(user, "UserList") == 0) allFailed = true;
else
{
DataSet usersubs = new DataSet();
DataSet feeds = new DataSet();
DataSet count = new DataSet();
int i=0;
DataTable usertable = user.Tables["UserList"];
foreach (DataRow row in usertable.Rows) userIndex = row[0].ToString();
foreach (string id in IDs)
{
// Make sure the id fits our format before using it
if (!ValidAlertId(id))
continue;
commandString = "DELETE FROM FeedEntries "
+ "WHERE usIndex='" + id + "'";
command.CommandText = commandString;
adapter.SelectCommand = command;
dfReturn[i].ID = id;
adapter.Fill(feeds, "FeedEntries");
commandString = "DELETE FROM UserSubscriptions "
+ "WHERE ulIndex ='" + userIndex + "' "
+ "AND usIndex='" + id + "'";
command.CommandText = commandString;
adapter.SelectCommand = command;
adapter.Fill(usersubs, "UserSubscriptions");
dfReturn[i].Error = ErrorType.None;
++i;
}
commandString = "SELECT COUNT(*) FROM UserSubscriptions WHERE ulIndex='" + userIndex + "'";
command.CommandText = commandString;
adapter.SelectCommand = command;
adapter.Fill(count, "UserSubscriptions");
DataTable datatable = count.Tables["UserSubscriptions"];
string numSubsLeft = "";
foreach (DataRow row in datatable.Rows) numSubsLeft = row[0].ToString();
if (numSubsLeft.Equals("0"))
{
commandString = "DELETE FROM UserList WHERE ulIndex='" + userIndex + "'";
command.CommandText = commandString;
adapter.SelectCommand = command;
adapter.Fill(user, "UserList");
}
}
}
catch (Exception ex)
{
allFailed = true;
}
if (allFailed)
{
int i=0;
foreach (string id in IDs)
{
dfReturn[i].Error = ErrorType.AccessDenied;
dfReturn[i].ID = id;
++i;
}
}
connection.Close();
return dfReturn;
}
public const string AlertTitle = "RSS Feeds";
public const string msgBody = "An alert has successfully been added on 'RSS News
Aggregator' for ";
public const string msgPrefix = "3BD50098E401463AA228377848493927";
private static int msgIdCounter = 0;
/* AddAlert
* ========
* Called by Web page UI. Sends email message
* with SOAP x-headers to user's Outlook client,
* telling it to add us to its subscriptions.
* For this sample service, note that once email
* address is entered, it cannot be changed. */
[WebMethod]
public AddAlertErrorType AddAlert(string emailAddr, string username, string URL)
{
string userID = IdentifyUser(); // First, authenticate
if (emailAddr.Equals("")) return AddAlertErrorType.EmailMismatch;
// Then add alert and user info to our database
try
{
string commandString = "SELECT ulIndex,EmailAddress FROM UserList "
+ "WHERE UserID ='" + userID + "'";
SqlCommand command = connection.CreateCommand();
command.CommandText = commandString;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
DataSet userlist = new DataSet();
DataSet usersubs = new DataSet();
DataSet feeds = new DataSet();
connection.Open();
string currIndex="";
DataTable datatable;
// if this user and email address does not yet exist
// in UserList table, add it
if (adapter.Fill(userlist, "UserList") == 0)
{
commandString = "INSERT INTO UserList (UserID, EmailAddress) "
+ "VALUES ('" + userID + "', @EmailAddress)";
command.CommandText = commandString;
command.Parameters.Add("@EmailAddress", SqlDbType.VarChar, 256).Value = emailAddr;
adapter.SelectCommand = command;
adapter.Fill(userlist, "UserList");
commandString = "SELECT ulIndex FROM UserList WHERE EmailAddress =@EmailAddress "
+ "AND UserID ='" + userID + "'";
command.CommandText = commandString;
// command.Parameters still includes emailAddr
adapter.SelectCommand = command;
adapter.Fill(userlist, "UserList");
datatable = userlist.Tables["UserList"];
}
else
{
string em ="";
datatable = userlist.Tables["UserList"];
foreach (DataRow row in datatable.Rows) em = row["EmailAddress"].ToString();
if (!emailAddr.Equals(em)) return AddAlertErrorType.EmailMismatch;
}
foreach (DataRow row in datatable.Rows) currIndex = row["ulIndex"].ToString();
commandString = "SELECT usIndex FROM UserSubscriptions WHERE Location =@location "
+ "AND ulIndex ='" + currIndex + "'";
command.CommandText = commandString;
command.Parameters.Clear();
command.Parameters.Add("@location", SqlDbType.VarChar, 256).Value = URL;
adapter.SelectCommand = command;
int matchingSubs = adapter.Fill(usersubs, "UserSubscriptions");
// Now check if user already has this subscription
// and return error page if he or she does
if (matchingSubs > 0)
{
connection.Close();
return AddAlertErrorType.AlreadyExists;
}
if (matchingSubs < 0)
{
connection.Close();
return AddAlertErrorType.DatabaseUpdateError;
}
else // add this subscription
{
// First add to UserSubscriptions table
commandString = "INSERT INTO UserSubscriptions (ulIndex, Name, Location, Title, LastSent) "
+ "VALUES ('" + currIndex + "', @username, @location, @location, CURRENT_TIMESTAMP)";
// Note we use CURRENT_TIMESTAMP because we want to start compiling from current time
command.CommandText = commandString;
// command.Parameters still includes URL
command.Parameters.Add("@username", SqlDbType.VarChar, 256).Value = username;
adapter.SelectCommand = command;
adapter.Fill(usersubs, "UserSubscriptions");
}
connection.Close();
}
catch (Exception ex)
{
connection.Close();
return AddAlertErrorType.DatabaseUpdateError;
}
// Send email confirmation regarding new subscription
// if one was successfully created
try
{
MailMessage Message = new MailMessage();
Message.To = emailAddr;
Message.From = ConfigurationSettings.AppSettings["senderEmailAddr"];
Message.Subject = "You have successfully created an alert for " + URL + ".";
Message.Headers.Add("X-AlertWebUrl", ConfigurationSettings.AppSettings["alertweburl"]);
Message.Headers.Add("X-AlertTitle",AlertTitle);
string msgId = "<" + msgPrefix + (msgIdCounter++).ToString() + "@microsoft.com>";
Message.Headers.Add("message-id",msgId);
Message.Headers.Add("X-AlertId","{D32A01E6-8A46-41BF-8E0E-936F254ACA4A}");
Message.Headers.Add("X-AlertServerType","Third Party");
Message.Headers.Add("X-AlertWebSoap",ConfigurationSettings.AppSettings["webserviceurl"]);
Message.Body = "Hello, " + username + ".\n\n" + msgBody + URL + ".\n\n- RSS Sample
Web Service";
try
{
SmtpMail.SmtpServer = ConfigurationSettings.AppSettings["smtpserver"];
SmtpMail.Send(Message);
}
catch(System.Web.HttpException ehttp)
{
return AddAlertErrorType.HttpException;
}
}
catch(System.Exception e)
{
return AddAlertErrorType.SMTPSendError;
}
return AddAlertErrorType.None;
}
}
}
Conclusion
The Alerts Web Service provides a new level of integration between Microsoft Office Outlook 2003 and other collaborative applications. These applications can now send alerts to their users of events through e-mail. Users, in turn, can read and organize their alert e-mail messages in Outlook. This new functionality can greatly improve the efficiency of many collaborative processes.