NotificationSubscribers - Part 1 - Introduction
When you request a Dynamicweb page in the browser, quite a lot happens. Dynamicweb checks the URL, optionally converts a SEO friendly URL to an internal URL, permissions are checked, the visibility of pages and paragraphs are checked, modules are loaded, designs are applied and finally the combination of all HTML emitted by templates, paragraphs, (custom) modules and so on is sent to the browser.
Often it would be extremely useful if you could hook into this process with custom code and make modifications to the flow or output of the page. Fortunately, this is pretty easy with the numerous extensibility options Dynamicweb ships with. To enable extensibility by custom development coders, Dynamicweb includes a number of extension points including Extenders, Providers and NotificationSubscribers. The former two are discussed in separate articles in the future. This post introduces you to the NotificationSubscriber and shows you a simple example that you can use to change the title of the page in the browser. In future articles in this series I'll discuss other NotificationSubscribers and provide relevant examples.
Introduction
The concept of a NotificationSubscriber is pretty simple. It enables you to write code in response to a predefined action that takes places in Dynamicweb. Here's a short run-down of the mechanism:
- Some code in Dynamicweb runs for which a developer has created a notification. For example, when a user logs in to the Extranet module successfully, the OnExtranetLogin notification is executed. Internally, this is done with code similar to this:
NotificationManager.Notify(Dynamicweb.Notifications.Standard.User.OnExtranetLogin, new Standard.User.OnExtranetLoginArgs());
- The Notify method of the NotificationManager class checks if there's a valid list of classes in the project that inherit from the NotificationSubscriber class (more on that class later). When the list is not built yet, .NET reflection is used to loop through all assemblies in the bin folder looking for classes that inherit from NotificationSubscriber. This means that any custom NotificationSubscriber you've deployed to the bin folder of the application is picked up automatically.
- The code then loops through the list of NotificationSubscriber classes looking for classes with a SubscribeAttribute whose Name property matches that of the notification being raised (a string matching the constant Dynamicweb.Notifications.Standard.User.OnExtranetLogin in this example). You'll see more of this attribute later.
- Finally, the class's OnNotify method is called which executes any custom code you may have added. The OnNotify method is passed the notification as a string (in the notification parameter) so you can determine which notification was raised in case you designated OnNotify to handle multiple types of notifications at once. Additionally, the method receives a NotificationArgs instance or an object[] which can provide relevant context sensitive information to the notification.
While this may look a little complex, most of it is executed under the hood by Dynamicweb so you never have to deal with it. Instead, all you need to do is write some .NET code in the OnNotify method of your custom NotificationSubscriber class. You'll see how this works next.
Creating Your Own NotificationSubscriber
In this short exercise I'll show you how to create a notification subscriber that enables you to change the page title. There can be many reasons your custom code may want to change the page's title. For example, you may want each of your page's titles to end with the full domain name of your site. So, rather than using a title such as Product Catalog, you can have it changed automatically to reflect the current domain, like Product Catalog - www.devierkoeden.com. A check is added to the code to ensure the domain name is not added when the current page's title already contains it. To implement this using a Dynamicweb NotificationSubscriber, follow these steps:
- In your Custom Module project (or in a separate class library that is referenced by your Custom Module project or whose assembly is outputted in the main application's bin folder), create a class that inherits from Dynamicweb.Extensibility.NotificationSubscriber, the base class for all notification subscribers, and add the OnNotify method:
using System; using Dynamicweb.Extensibility; namespace Dynamicweb.Samples.Lib { public class IncludeDomainNameInTitleSubscriber : NotificationSubscriber { public override void OnNotify(string notification, object[] args) { } } }
In addition to a method that accepts an object[] called args, there's another overloaded OnNotify method that you can override:
public override void OnNotify(string notification, NotificationArgs args)
{ }
- To link the IncludeDomainNameInTitleSubscriber class to a notification that enables you to change the page title, add the following attribute to the class definition:
[Subscribe(Dynamicweb.Notifications.Standard.Page.PageTitle)]
public class IncludeDomainNameInTitleSubscriber : NotificationSubscriber { ... }
To handle other notifications, you need to change the Name parameter of the SubscribeAttribute (passed to it in its constructor) or apply an additional SubscribeAttribute like this:
[Subscribe(Dynamicweb.Notifications.Standard.Page.Deleted)] [Subscribe(Dynamicweb.Notifications.Standard.Page.PageTitle)]
public class IncludeDomainNameInTitleSubscriber : NotificationSubscriber { ... }
- With this code in place, Dynamicweb will call OnNotify whenever it's about to set the page title. You can then write code in this method to change it by making sure the title contains the domain name. Add the following implementation to OnNotify:
Again, it takes some debugging to figure out what exactly is passed to OnNotify. The actual types and number of instances vary from notification to notification. In the case of the PageTitle notification, one argument is passed which is of type PageView, the type in Dynamicweb responsible for the page layout and content. This object has a Meta property that in turns has a Title you can set. To prevent the domain name from being added when it's already present in the page (for example, for a page called Welcome to www.devierkoeden.com), a check is made using IndexOf before the title is built up and assigned to the Title property. With this code in place, a page called Home on the domain test.devierkoeden.com now ends up like this in my browser:public override void OnNotify(string notification, object[] args) {
HttpRequest request = HttpContext.Current.Request; PageView pageView = args[0] as PageView; string currentTitle = pageView.Meta.Title; string domainName = request.Url.Host; if (currentTitle.IndexOf(domainName, StringComparison.InvariantCultureIgnoreCase) < 0) { currentTitle = string.Format("{0} - {1}", currentTitle, domainName); pageView.Meta.Title = currentTitle; }
}
Figure 1 - The Changed Page Title
Besides the PageTitle, you can hook into many other NotificationSubscribers as well. Refer to the Extensibility API Documentation, the User Management Extensibility Documentation and the Shopping Cart v2 Extensibility Documentation for more examples. Additionally, follow me on Twitter as I'll be writing more about NotificationSubscribers and many other Dynamicweb topics in the future and use these channels to keep you updated on new posts.
Using Multiple Subscribers
When you have the need for multiple subscribers in a single class, you have two options. First, you can create a new class for each notification and apply a single Subscribe attribute to each class. This works fine for just a few notifications but may lead to lots of duplicate code if you have multiple classes executing more or less the same code.
As an alternative, you can apply multiple attributes to the class at once:
[Subscribe(Dynamicweb.Notifications.Standard.Page.Deleted)] [Subscribe(Dynamicweb.Notifications.Standard.Page.PageTitle)]
public class IncludeDomainNameInTitleSubscriber : NotificationSubscriber { ... }
If the code inside the method is the same for all notifications, this is all you need. However, if you want to know for which notification your code was called and act differently for each notification, you can look at the notification parameter. However, when you do so, there's one thing to watch out for. The underlying value of the constant string you supply to the attribute's constructor is in ALL CAPS while for some reason, the notification parameter is converted to lower case before it's passed to OnNotify. This means that the code you're most likely to write won't work:
public override void OnNotify(string notification, object[] args) { if (notification == Dynamicweb.Notifications.Standard.Page.PageTitle) { // Handle PageTitle notification } }
Instead, you need to do a string comparison or convert both ends to lower or upper case:
public override void OnNotify(string notification, object[] args) { if (notification.Equals( Dynamicweb.Notifications.Standard.Page.PageTitle, StringComparison.InvariantCultureIgnoreCase)) { // Handle PageTitle notification } }
Alternatively when your class is set up to handle lots of notifications you can use a switch statement to execute the correct code of block. Again, make sure the casing used for the notification parameter and the case blocks in the switch statement are the same or they won't fire.