IT Crossing
Monday, October 06, 2008 | Register | Login 
Minimize
 metaPost Team Blog Listing

Overview

Recently, I noted the progress on the new Announcements Provider that we're sharing with the DotNetNuke + metaPost community. For those who are interested, here's a few notes regarding the architecture that we've used.

Creating the Announcements Provider was a bit of a challenge due to the fact that this (and every) provider is essentially built "on top of" an existing 3rd party module (albeit, one of the core DotNetNuke modules). The challenge lies in the fact that we have no control over the architecture or code of that 3rd party module. Often, module developers think of their module as extending DotNetNuke, and while they (rightfully) expect stability in the DNN1 API, they often don't think of their own modules as being something that others might want to extend. As such, it's not uncommon for a team of module developers to make code changes in new versions that effectively "break" other people's extensions of that module. Such was the case for the Announcements module.

1

DNN is the short name given to DotNetNuke.

The metaPost Announcements Provider (which I'll simply refer to as the mAP2 for the rest of this article) was built to support older versions of the Announcements Modules. Basically, the idea was to target modules that were around the release of DNN 04.05.x, which was the first DNN version targeted by metaPost. Since then, the versions of Announcements that are going to be targeted for mAP are 03.03.05 and upward. But, there were some significant changes in method signatures between version 03.04.00 and 04.00.00. Realizing that these changes meant not only some code bloat for mAP, but also that future "breaking" changes might occur in Announcements, it became apparent that some kind of architecture to deal with these changes was needed for our provider.

2

mAP is the short name for the metaPost Announcements Provider - I just thot of it

Basically, the architecture selected for mAP was to separate out the code that would a) map data from people's posts to the data stored in Announcements and b) deal with the version changes of the Announcements module. To do that, we used an adapter4 to do the data mapping and a factory4 to deal with version changes.

The Adapter

The adapter is the simplest part of the architecture. Basically, it consists of a single class with two static methods:

  • FillAnnouncementInfo - for mapping data from metaPost to the AnnouncementInfo data, and
  • ExtractAnnouncementInfo - for mapping data from AnnouncementInfo objects to metaPost

FillAnnouncementInfo basically allows creating or updating announcements while ExtractAnnouncementInfo is used for getting (opening) previous announcements in WLW or MS Word3. Here's a screen shot of the class diagram.

Click this image to see a larger screen shot of the AnnouncementsInfoAdapter class

3

WLW (Windows Live Writer) and MS Word (Microsoft Word) are the two programs people use to post content through metaPost to their DNN website.

The Factory

For designing the "Factory", I simply used the Factory Pattern4 to deal with changes to the controller classes of the Announcements module. This meant the creation of four classes:

  • ControllerFactory - the class that is responsible for creating (and encapsulating) the "proxy" controller classes.
  • AnnouncementsController_Proxy - this is an abstract class that inherits from the AnnouncementsController and that defines the "replacement" methods to use for those that were changed in later releases of the Announcements module. The methods we needed to "replace" were the ones that added and deleted posts.
  • AnnouncementsController_3_3_5 - this is a concrete class that supports the method signatures for versions 03.03.05 and 03.04.00 of the Announcements module.
  • AnnouncementsController_4_4_0 - this is the concrete class that supports the Announcements module's new method signatures for version 04.00.00 and higher (or at least, until the next breaking change).

Here's a screen shot of the class diagram. Note that the abstract and concrete classes are actually internal to the ControllerFactory class. This is by design, largely to encapsulate (hide) the creation of the classes and to "force" us to only use the ControllerFactory.CreateController() method.

Click this image to see a larger screen shot of the classes in the ControllerFactory

Summary

What all of this allows us to do is to a) remove the complexities of breaking changes from the AnnouncementsProvider class, and b) position ourselves to deal with any possible breaking changes when future Announcements Module versions are released. In later posts, I'll release explanations of how the architecture and code works to achieve these goals.

4

Reminder to self - put in some notes & a link to explain the Adapter and Factory patterns ....

Well, it's been a few months since I first offered to Don to create an Announcements Provider for metaPost. I did a beta version (in VB) that I shared with Don in early April (2008), and for about three months I wasn't able to work on it due to other commitments. In that interim, Don was able to take a look at my work, port it over to C#, and address some of the concerns that arose out of breaking changes from the 3.x to 4.x versions of the module. Just recently, I was able to spend a few more days to look over Don's changes, and then I made a few more of my own. The end result is that I think we've finally got a "reasonable" version to ship in the near future.

Over the next while, I hope to blog a bit about the design decisions that we took in the Announcements Provider. Specifically, I want to talk a bit about the following (in no particular order):

  • Factory Patterns & Reflections (aka: "The perils of extending 3rd party modules")
  • Reflections on Writing Providers (or "Best Practices in metaPost Providers - So Far...")
  • The Architecture of the Announcements Provider
  • Unit Tests for metaPost Providers

I'm not sure when I'll get these all "finished", but I think my best bet is to simply start them with rough content and then edit them as I go along. In any case, I hope that my experiences will help other metaPost provider developers.

Well, let's get into the details of creating a metaPost provider.  You'll see in a few minutes that it's actually pretty easy.

If you've followed the steps in the previous post, then you have a Visual Studio project created and you're ready to start writing/changing some code.

IPublishable

imageOpen the source code for your provider and you'll see that the code contains the following sections: 

image

These were highlighted in the last post.  If you expand the IPublishable Members section of the code, you'll see:

image

In order to implement a metaPost provider, you'll need to implement a few public properties (we'll cover those in a second) as well as procedures for things like getting posts as well as adding, editing and deleting posts.  Finally, we may need to write some implementation for a few procedures related to working with categories.  Let's start with the Public Properties, but before we briefly explain each property, let's talk about the MetaPostServices object.  To make it as easy as possible to implement your provider, we've offloaded as much functionality as possible to a MetaPostServices helper object that contains shared methods for most of the public properties.  This means that when you write your implementation for most of the properties, you can just offload the implementation to the shared methods of the MetaPostServices object.  Of course, if you want to override this functionality, you're free to write your own custom code.

Implementation of Properties

  • ProviderKey - Used to return the FriendlyName of your module.  To retrieve this friendly name, simply pass in the Module Definition FriendlyName into the GetFriendlyNameFromModuleDefinition procedure as follows:

    return MetaPostServices.GetFriendlyNameFromModuleDefinition("Announcements");
  • ManifestFilePath - This is the path to the manifest file11 used by Windows Live Writer to determine the features of the Windows Live Writer that your module can use.  See the following URL for information regarding what Content can be placed in a WLW manifest file1: http://msdn2.microsoft.com/en-us/library/bb463260.aspx.  For the announcements provider, this property is set as follows:

    get { return "/DesktopModules/itcMetaPost/manifests/wlwAnnouncements.xml"; }
  • LocalizationFilePath - Returns the path to the localization file for the provider. 

    get { return "/DesktopModules/itcMetaPost/App_LocalResources/itcMetaPostAnnouncements"; }
  • ImageUploadPath - This is the path to the location where the images files will be stored, and by images, we're referring to the images included in a post.  Windows Live Writer will use the MetaWeblog API to save these images to the server.

    return MetaPostServices.GetProviderImageUploadPath(ProviderKey);
  • AttachmentUploadPath - Same as the ImageUploadPath only for files that are attached to the post.  Note that in the current version of Windows Live Writer, this can only be done through the use of a custom Insert addin.

    return MetaPostServices.GetProviderAttachmentUploadPath(ProviderKey);
  • SettingsUserControlPath - Not currently used

That's it for the properties and you can see that in most cases, we'll just be making a few minor changes to the code.  Also, a quick word here about the localization file and the manifest file1.  It's best to start with the files for the provider you've chosen and change the file as needed.  Copies of the localization files as well as the manifest files can be found in the SDK.  Note also that if you chose to ship your provider with your module, then these two files will need to be included in dnn file for your module.  You'll want to make sure the paths entered in the properties is consistent with the final destination of each file after they are installed.  If you want your provider to be shipped with metaPost, we'll need a copy of the source code and/or the dll for your provider along with the manifest file1 and localization file.

Implementation of Methods

There, that wasn't so bad.  Now, let's take a look at the methods that you may need to implement in order to allow metaPost to read and write content to your module.

However, before we take a look at the individual methods, let's take a quick look at some of the parameters you'll find in the methods listed below as well as some of the common properties of the item object you may find useful (the item object is one of the properties described below):

Common Method Parameters

  • moduleLevelId - This is used to track different information depending on the module.  For some modules, like the Text/HTML module, this may be the actual ModuleID, but for others, it may be a module specific ID. With the blog module provider, for example, the moduleLevelId is used to track the BlogId.
  • itemId - The itemId is used to track the individual items that can be published.  For the FAQs module, this would be the ID of an individual FAQ entry.  For an articles module, this may be an articleId.  For some modules, this string parameter may track more than one type of ID.  See the News Articles provider for an example where articleIDs and pageIds are both tracked through the itemId field.  The type of ID being tracked is differentiated by the string "aid" being prepended to the ID.
  • userInfo - This is the actual DotNetNuke UserInfo object that's passed from the metaPost infrastructure into the IPublishable interface.  This is where creating a metaPost provider can save you a lot of time over writing your own MetaWeblog API.
  • portalSettings - This is the DotNetNuke PortalSettings object related to the portal to which the user is connecting to insert, update or delete data.
  • providerKey - This is the unique key used to identify the metaPost providers.  It will be equal to the Friendly name of your module.  And by friendly name, we mean the name that will appear in the dropdown list of available modules in your portal.
  • requestType - This parameter is used with the GetRecentItems method below to specify whether the GetRecentMethods procedure is being called to show a list of recent entries or to show a list of available parent items.  The same procedure is used for both operations and since the code is often the same for both requests, we included the two in one procedure and differentiated the types of calls based on this parameter. 
  • item - This is the core of the IPublishable interface.  The item represents the object being published and this struct contains all of the properties needed by the metaPost infrastructure to communicate with publishing clients when publishing content.  You may want to take a few minutes to view this object using an object browser in Visual Studio.
  • itemType - Where it's appropriate, we pass the itemType in as a property of the item object.  However, some methods don't take an item object as a parameter.  This is the case with the GetItem and DeleteItem methods.  With these methods, an itemType parameter is passed to let you know whether you're dealing with a page or a post.  The IPublishable interface is designed to support both concepts which are available in Windows Live Writer if the manifest file1 used for your provider supports these features.  You'll recall that the manifest file1 is an xml configuration file used by Windows Live Writer to specify which features of Live Writer are needed by your module.  A good example of a provider which uses the page feature of Windows Live Writer is the Ventrian News Articles provider.  With this provider, you can use Windows Live Writer to manage both pages and posts.

In addition, you'll find it helpful to be familiar with the properties of the item object.  We'll start with the properties that convey information to the NewItem and EditItem procedures which you may find useful in writing the implementation.

Useful Item Properties

  • Item.Publish - Determines whether the user chose to publish the entry or just save as a draft.