» UPDATE 12/14: This article has since been updated & rewritten. See the updated version here.
A project I'm currently on involves hundreds of channels, many of them nested, and some complex requirements where channels and sub channels will need to be created on the fly. A quick search on the CMS newsgroup and you'll see that this [programmatically working with custom properties in channels] is one of the most frustrating missing things within the CMS PAPI. Another downside to custom properties is the speed of searching them. Experienced CMS developers know that searching for a custom property is a very expensive performance hit on the server.
In light of all this, I decided to build my own library that moved custom properties from CMS and into my own database table (I know, they are already in the database… just bear with me). I sought out to achieve four challenges I see in the current PAPI implementation of Custom Properties:
- CMS PAPI doesn't provide a way to programmatically add custom properties to a channel.
- CMS PAPI doesn't provide an easy way to programmatically work with multiple values for custom properties
- Custom properties in CMS limited to posting, templates, and channels
- Custom properties can't be copied from one object to another easily
My implementation is very easy to use, lightweight and easy to integrate into your CMS projects. What I did was very simple: I moved all custom properties to a separate database table and created a CMS friendly library (one that understands CMS objects) for developers to work with the properties. The core functionality of the library is now complete and has been preliminarily tested… I do not endorse it as "production ready" at this time, but wanted to get it out for other CMS developers to work with and provide some feedback before I release a more robust version.
A lot of work was put into trying to keep all naming conventions and techniques similar to how Microsoft named all the CMS objects. For example, in the CMS PAPI, you have things like Searches.GetByGuid, Searches.GetByPath, etc. In CustomPropertyDbEx, I have Searches.GetByGuid, Searches.GetProperties([Channel|Tempalte|Resource|Posting][, propertyKey]), etc.
CustomPropertyDbEx exposes three primary objects:
- CustomPropertyDbEx: a a custom property object with information linking it back to a CMS asset (channel, template, posting, resource, site, or other).
- CustomPropertyDbExCollection: a collection of CustomPropertyDbEx objects.
- CustomPropertyDbExGlobal: essentially a static management-like class that contains a .Searches static class, similar to CMS' CmsContext.Searches static class.
The assembly has three dependencies:
- A database with the proper schema must exist and the application's *.config file must include a connection string application variable pointing to this database (the DDL files necessary to build the table & stored procedures are in the “_Repository“ project in the solution available for download at the end of this article).
- Microsoft's Data Application Block, used for database access.
Once you've created the database, add a reference to the CustomPropertyDbEx to your CMS project. That's it! Now let's look at some real implementation. Let's first take a look at how you would get the value of a CMS Channel custom property with the PAPI provided functionality:
The first thing you'll want to do is create some properties for a specific channel. In the sample application I've included, there's a method CreateProperties that creates three properties in a provided channel:
As you can see from the above you create a custom property by passing the CMS object and, optionally, the property key and value you wish to create. All custom property creation, edits, and deletes are stored in memory until you explicitly call the Commit method which then saves the changes to the database. Another thing you'll notice is that there are two values being inserted for the "PointOfContact" property. This is actually creating two different properties; you can get them all back from a collection easily as I'll show in figure 2.
The next most common thing you'll want to do is access these properties:
In figure two above you can see how easy it is to get all custom properties for a specific CMS object or all properties matching a specific key for that CMS object.
Displaying the property information is also easy as shown in figure 3 below:
I've added quite few overloaded methods to create properties. You can also create multiple custom properties using the CustomPropertyDbExCollection and then calling the Commit method on the collection to save all objects in the collection to the database OR calling Delete to mark all objects for deletion when Commit is called next. A big help are all the searching methods as listed below:
- GetByGuid - Finds a custom property by it's unique ID.
- GetByAssetType - Finds all custom properties for a specific CMS object type.
- GetByValue - Finds all custom properties who's value matches what's supplied (TSQL wildcards allowed).
- GetProperties - Finds all properties by a CMS asset as well as a specific property key.
What's left to do?
Plenty! Here's a short list of some things I want to add to this solution:
- Add database transactional support when committing a collection of objects.
- Create custom exceptions to notify the calling application of detailed errors.
- Implement multiple sorting options for the collection.
- Create elaborate help files (using XML comments & NDoc to generate HTML commends and CHM files).
- Console application implementing the main assembly, exposing it for batch/command line processing.
- WebForm/WinForm application, similar to CMS' Site Manager, to allow certain users to manage custom properties including:
- Create/edit/delete custom properties in channels, templates, resources, postings, and the overall site.
- Exporting/importing groups of object's custom properties to move between implementations.
- Migrating existing custom properties to the database for use with the CustomPropertyDbEx library.
- Handle the state of a posting so custom properties for a Published posting could be different for a AwaitingModeratorApproval version of a posting.
I have a few other ideas, but they are things that may wait until the next big version. These include support for saving an object (that can be deserialized) into a custom property, saving an image to a custom property, encrypting all data fields in the custom property database, etc.
So what's the roadmap?
v 0.1 [ initial release ]
- Initial version... library with core functionality to add/edit/remove & find properties
v 0.2 [ exception management & notifications, implement collection sorting, help file ]
- Enhanced exception management & notifications
- Sorting for collection
- Elaborate help file with samples and other information (CHM)
v 0.3 [ management/utility applications added to the suite ]
- Bug fixes
- WinForm/WebForm application to add & manage custom properties
- export/import properties
- copy properties from one object to another
- add/edit/remove properties
- Console application to add & manage custom properties (for batch scripting)
There are no plans to do v0.4-v0.9. These versions will get functionality improvements/additions if they are even necessary, however I anticipate jumping from v0.3 all the way to v1.
Take the time to download the code, take a look, and please provide some feedback. Is there something missing that you think would be a huge improvement? The only way this will get better is if I have some solid feedback.
Thanks and I hope you'll find this as helpful as I am in my current project.