Background
E-mail subscription in EPiServer CMS 5 is based on ASP.NET profiles. Starting and stopping subscriptions through code is easy, but I was missing a plugin where administrators could easily add new subscribers, remove existing subscriptions and list all subscribers. This post explains how to implement such a module, and I’ll use a module for managing press release subscriptions as an example.
Functionality of the EPiServer subscriptions plugin
The module should support the following:
- List all subscribers for swedish and english press releases, respectively
- Allow administrators to add a subscriber for either swedish or english press releases
- Allow administrators to remove a subscription
And obviously we want some sweet AJAX behavior when the plugin is loaded! :)
We’ll click Press Release Subscribers in the Tools list:
The plugin (an .ASPX page) will load, but we won’t load the subscriber list until the page has rendered:
Next we’ll get a list of existing subscribers, and we’re also able to add additional subscribers:
We use the Subscription dropdown to specify what a new subscriber should subscribe to:
Whenever we change the subscription type the subscriber list will be refreshed to list all subscribers for the selected subscription:
Now, to add a new subscriber to the selected subscription we simply specify the e-mail address and click Save:
We’ll then see a brief AJAX progress indicator…
…followed by a confirmation that the subscriber has been added:
And the subscriber list has been refreshed to include the newly added subscriber:
Now, if we want to delete a subscription we simply click Delete. This will bring up a dialog:
We’ll confirm by clicking Delete and once again an AJAX progress indicator is displayed briefly…
…followed by a confirmation message:
Ok, you get the idea of how it works from a user’s perspective. Now, let’s look at how it’s implemented!
Implementing a subscription management plugin for EPiServer
I’ll describe the key parts of the admin plugin to avoid this post from getting to cluttered. But feel free to post comments if you have any questions regarding something I may have left out!
First, we’ll create a new class inheriting from Page. Then we’ll add the GuiPlugIn attribute to set the properties of the admin plugin:
[GuiPlugIn(
Area = PlugInArea.AdminMenu,
DisplayName = "Press Release Subscribers",
Url = "/Templates/Public/Pages/Plugins/SubscribersList.aspx",
RequiredAccess = AccessLevel.Administer)]
public partial class SubscribersList : Page
{
// TODO Add page logic
}
Next we’ll add a ScriptManager and an UpdatePanel to AJAX-enable our plugin:
<asp:UpdatePanel ID="pnlList" runat="server">
<ContentTemplate>
<!-- Subscriber list goes here -->
</ContentTemplate>
</asp:UpdatePanel>
I’ll also add an UpdateProgress control. Note that I’ve set an ID for the <P> tag to allow me to change the text within the UpdateProgress control client-side:
<asp:UpdateProgress AssociatedUpdatePanelID="pnlList" DisplayAfter="50" ID="progress" runat="server">
<ProgressTemplate>
<p id="status-message"></p>
<img src="/Templates/Gfx/ajax-loader-admin.gif" alt="Loading" />
</ProgressTemplate>
</asp:UpdateProgress>
I can now customize the message within the progress indicator when a button is clicked like so:
<asp:Button Text="Delete" OnClientClick="$get('status-message').innerHTML='Deleting subscription...';" ID="btnConfirmDelete" runat="server">
List all subscribers
Within the UpdatePanel’s ContentTemplate element I’ll add a GridView control to help me list all subscribers:
<asp:GridView
AutoGenerateColumns="false"
BorderStyle="None"
AllowPaging="true"
PageSize="50"
EnableSortingAndPagingCallbacks="false"
AutoGenerateDeleteButton="true"
onpageindexchanging="subscriberListSelectedIndexChanged"
OnRowDeleting="subscriberListDeleting"
ID="grdSubscribers"
runat="server">
<Columns>
<asp:BoundField DataField="Email" HeaderText="E-mail" />
<asp:BoundField DataField="Lang" HeaderText="Language" />
<asp:BoundField DataField="Updated" HeaderText="Last updated" />
</Columns>
<EmptyDataTemplate>No subscribers added</EmptyDataTemplate>
</asp:GridView>
Now, by databinding this GridView to a DataTable we’ll get a list of subscribers, complete with paging:
// Create data table for the grid view
DataTable dt = new DataTable("tblSubscriptions");
dt.Columns.Add("Email", typeof(string));
dt.Columns.Add("Updated", typeof(DateTime));
dt.Columns.Add("Lang", typeof(string));
// Primary key required for paging
dt.PrimaryKey = new DataColumn[1] { dt.Columns["Email"] };
// Iterate all ASP.NET profiles to find subscribers
foreach (ProfileInfo pi in ProfileManager.GetAllProfiles(System.Web.Profile.ProfileAuthenticationOption.All))
{
// Get the EPiServer profile
EPiServerProfile profile = EPiServerProfile.Get(pi.UserName);
// Check if the profile is subscribing
if(profile.SubscriptionInfo.IsSubscribingTo(subscriptionTargetPageLink, ddLanguage.SelectedValue))
{
dt.Rows.Add(new object[3] { profile.Email, profile.LastUpdatedDate, ddLanguage.SelectedValue });
}
}
// Databind the subscriber list
grdSubscribers.DataSource = dt;
grdSubscribers.DataBind();
Stop a subscription
The GridView control includes a column including a Delete link:
As we saw in the markup before, the OnRowDeleting event has been hooked up to by an event handler called subscriberListDeleting. The event handler simply displays controls for verifying that the subscriber should be deleted:
Here’s how it’s done:
/// <summary>
/// Handles the "Delete" command in the grid view
/// </summary>
protected void subscriberListDeleting(object sender, GridViewDeleteEventArgs e)
{
// Get the e-mail from the selected GridView row
string selectedEmailAddress = grdSubscribers.Rows[e.RowIndex].Cells[1].Text;
// Set the confirmation message
litConfirmDeleteMessage.Text = string.Format("<p>Please confirm you wish to delete this subscriber: <strong>{0}</strong></p>",selectedEmailAddress);
// Set the command argument of the Delete button
btnConfirmDelete.CommandArgument = selectedEmailAddress;
// Display the confirmation controls
phConfirmDelete.Visible=true;
}
The Delete button’s CommandName property has been set to “Delete”. The OnCommand event is then hooked up to the following event handler (code abbreviated for clarity):
/// <summary>
/// Handles commands executed inside an action area
/// </summary>
protected void buttonActionCommandHandler(object sender, CommandEventArgs e)
{
switch (e.CommandName)
{
case "Delete":
// Get profile to unsubscribe based on command argument
EPiServerProfile profileToUnsubscribe = EPiServerProfile.Get((string)e.CommandArgument);
// Remove subscription
profileToUnsubscribe.SubscriptionInfo.UnSubscribe(subscriptionTargetPageLink, ddLanguage.SelectedValue);
// Save profile
profileToUnsubscribe.Save();
break;
}
}
Add a new subscriber
To start a new EPiServer subscription we have a set of controls for specifying the type of subscription and the e-mail address of the subscriber:
Code similar to the following is executed when the Save button is clicked:
IList<EPiServerProfile> profiles = EPiServerProfile.GetProfiles(address);
if (profiles.Count > 1) // Specified address returned group
{
litGenericConfirmationMessage.Text = string.Format("Specified e-mail address returned {0} profiles. Subscriptions cannot be added for groups.");
break;
}
else // Single profile, or non-existing profile
{
// Get the existing/new profile
EPiServerProfile profile = profiles[0];
// Cancel if subscription already exists
if (profile.SubscriptionInfo.IsSubscribingTo(subscriptionTargetPageLink))
{
litGenericConfirmationMessage.Text = string.Format("Subscription to <strong>{0}</strong> is already active for e-mail address <strong>{1}</strong>.", subscriptionTargetPage.PageName, address);
break;
}
// Add the subscription
profile.Email = address;
profile.SubscriptionInfo.SubscribeTo(subscriptionTargetPageLink, ddLanguage.SelectedValue);
profile.Save();
}
I’ve left out most of the “estethics” logic, but I hope you get the idea! Comments and feedback are welcome as usual!