Tag <Header3> not implemented

Mehul Harry talks to Don Wibier about DevExpress Technologies used in CMS2GO v3. Watch this case study on the DevExpress Channel or simply click the video player below.

Tag <Header3> not implemented

CONCEPTS2GO is an online communication agency specializing in web, social media, mobile and online identities. We design and build websites, portals and manage online communication for our customers. We are based in the Netherlands.

When I started working at CONCEPTS2GO (back in 2005), there was need for a versatile reusable system which allowed us to create websites really fast, where web design is not limited by the boundaries of such a system, but also exchanging data with back office systems would not be a problem.

Existing commercial and open-source systems could not fulfill these needs, so I decided to start developing such a system with the release candidate of the Microsoft .NET v2 platform.

Tag <block> not implemented

Shortly after v1 of CMS2GO I decided to redo quite a bit with the use of DevExpress ASP.NET Controls and a couple of database changes which would result in CMS2GO v2. This version has a custom developed data access layer which only allows one to use MS SQL Server 2005 and higher thanks to a couple need features introduced in MS SQL Server 2005.

CMS2GO v2 is running for about 4 years in production and a little less than 100 sites are running on this system.

Because of technology progression and the ever changing way the World Wide Web works in terms of search engine optimization, social media, Adobe Flash vs. jQuery and the demand of new features and options, we ran into data model limits in CMS2GO v2. Therefore after four years we decided to build a complete new CMS2GO v3.

While developing sites in CMS2GO v2, I have always taken a good notion on which features are time consuming to implement and what other things could be implemented faster. Also my knowledge about search engine optimization and things like ySlow has improved which results in building better sites.

CONCEPTS2GO is currently evaluating several options to market CMS2GO in the near future or make it available to other developers. 

Tag <Header3> not implemented

For CMS2GO v3, the following commercial technology is used:

We are also using some open source / freeware technology:

Tag <Header3> not implemented

I have decided to use v4 of the framework because of standard support of MEF (Managed Extensibility Framework) which allows us to create a robust plugin system so we can reuse custom functionality developed for “customer A” really easy for “customer B” as well.

Tag <Header3> not implemented

As mentioned before, we are running against boundaries in the data model in v2. “Well, you can just make an update script to alter the structure of the database and do whatever you want to do” I hear you think. That’s correct but that’s quite a bit of work, to develop, test and maintain all those update scripts. It also has something to do with the way we handle updates of the CMS.

Our release management is as follows:

When we have released a website, it has the latest version of the CMS2GO core assemblies. It can then happen that we don’t need to change anything on that site for 6 months to a year but development on the CMS core will just continue just as DevExpress updates.

Whenever the customer requests something new, we will get a latest version of the site + latest version of the CMS core out of our version control system, upgrade DevExpress if needed, build it, run it and see if it still runs with latest additions, and finally implement the requested features. Once uploaded, the customer has his new features but also the latest version of the CMS.

This approach works well for us but as you can imagine, database changes are pretty dangerous with this release management.

To overcome these issues, this is my first reason to start using eXpress Persistent Objects (XPO). It will do all the schema changes for you without the need to maintain external SQL script file. A second reason for XPO is that it’s just fun to switch database backend by only changing your connection string.

A third reason for changing to XPO appeared to me when I was actually developing CMS2GO v3. It is so flexible and they really are Persistent Objects! Inheritance, overriding and all basic OOP features can be done, and now and then I’m amazed what SQL is only executed to get the results you want.

Also the way you can direct XPO how and where to store data is really great, and complicated queries are no longer needed once you’ve mastered the “Simplified Criteria Syntax”. Actually you don’t really need to master this since it’s quite logical and works like one would expect.

Tag <Header3> not implemented

Before I decided to start using the ASP.NET controls of DevExpress, I have tested quite a bit of 3rd party component libraries. The DevExpress Controls work, their performance, Visual Studio integration including documentation, and the really great JavaScript implementation on the client is just really well done.

If these are not enough reasons, the controls are THE sexiest looking controls in any browser even with the standard theme applied.

Tag <Header3> not implemented

What can I say about this; when things start to go wrong, you need to log that. Log4Net does just that. The only thing I’ve customized here is that I wrote a custom log appender which stores the latest 5000 log messages and exceptions in a database table through XPO.

Tag <Header3> not implemented

I needed something to provide site search capabilities. In version 2 of CMS2GO we had a spider mechanism based on the SearchAroo search which did work great but I saw a couple of things that I didn’t like e.g. you have to start the spider now and then which causes the search catalog not to be real-time updated. When spidering a site, you will need to parse the HTML and might want to exclude portions of it by placing HTML comments like the menu and header and footer and so on.

Lucene.NET can be used really nice in combination with XPO. Once I had determined how the catalog file should look, I only needed to code some events in certain persistent classed like OnSaved and OnDeleted to update the index real-time which makes sure you never have to re-index the site on a regular basis. Another plus with this external indexing engine is that you can easily switch to another database backend since you’re not depending on e.g. Full Text Search which is only available on certain editions of MS SQL Server.

Tag <Header3> not implemented

Cross-site scripting (XSS) attacks exploit vulnerabilities in Web-based applications that fail to properly validate and/or encode input that is embedded in response data. Malicious users can then inject client-side script into response data causing the unsuspecting user's browser to execute the script code. The script code will appear to have originated from a trusted site and may be able to bypass browser protection mechanisms such as security zones.

I just use this library to make sure nobody is fooling around with forms and input controls and make sure everything submitted to the site is safe. It works really well together with the DevExpress ASP.NET Controls.

Tag <Header3> not implemented

This library lets me upload a zip archive and extract it on the server. It’s used for the plugin-system which is in experimental stage.

Tag <Header3> not implemented

The system has the following main objectives:

This has resulted in a system which contains a couple of interesting cornerstones where the system is running on.

Tag <Header3> not implemented

I’ve created custom providers for the Microsoft membership API as well as the membership Profile provider and a custom Sitemap provider which gets its datanodes out of the database.

Because of XPO there is no need to run the aspnet_regsql.exe tool to configure your database.

To make it more clear; below is a portion of my web.config:

Tag <code> is invalid. No code blocks defined

One advantage of this approach is that you can use any menu control including DevExpress ASP.NET Menu which supports a sitemap datasource.

The same goes for the membership functionality, you can just use it the way you were used to.

Because of a couple of neat tricks in the Microsoft implementation concerning anonymous profiles, I left the table structure the same except for the profile table. The original implementation (de)serializes the entire profile object in one XML stream in a database field. This can cause performance problems if you want to select all users whose birthday is tomorrow. You need to go through the XML to determine who is it and that can take some time.

I’ve decided to reconstruct the profile table and store every property in one record, where simple properties like string, DateTime, int etc. are stored in a dedicated column (which is indexed) and complex ones are serialized in the database. Now you can make quick selections on birthdays etc.

These providers are also usable without the CMS.

Tag <Header3> not implemented

One of the things which I find a dull, boring and time-consuming task is creating the markup for forms to collect user input. This gave me the idea of creating a form control which renders a neat form loaded with DevExpress controls. At this stage there are a couple different flavors of this flexible form control and the CMS is heavily relying on this component in its admin screens.

It did cost me quite some time to get it running including support for the DevExpress ASP.NET Upload Control which is a bit of an annoying thing though it does work really well.

To give you an example of the markup of the first flavor of this FormControl, take a look below:

Tag <code> is invalid. No code blocks defined

The markup above will render the form below:

User Input Form: CMS2GO - DevExpress Case Study

If you apply one of the predefined DevExpress ASP.NET Themes to your site, the form will be rendered in that specific theme.

This form is put inside a DevExpress ASP.NET Callback Panel and has a helper method in the code-behind which is used in the OnInit event of the "Verstuur" link:

Tag <code> is invalid. No code blocks defined

This will cause the upload controls to do their upload(s) sequentially and finally the PerformCallback is called of the ASPxCallbackPanel. In the server-side callback event you can process the data including the uploaded files:

Tag <code> is invalid. No code blocks defined

Another version of this control works with attributes placed in a data class which works really well with persistent classes of XPO.

Tag <code> is invalid. No code blocks defined

The FormControl has a property DataItemType and methods BindToDataFromDataObject and an AssignValuesToDataObject method which will populate the form and stores it back in an object.

The markup of such a form looks like:

Tag <code> is invalid. No code blocks defined

As you can imagine with this we can build good looking forms in a snap and they work well in pages as well as DevExpress ASP.NET Popup Controls!

Tag <Header3> not implemented

To create a flexible system concerning the web design the App_Theme system which ships from .NET v2 wasn’t enough, since you can only put in .SKIN files with CSS and images.

I also needed to put in masterpages, pages and user controls but the theming system doesn’t allow that so I’ve created a SkinEngine which works together with the ms-theming.

The skins are stored in an "~/App_Skins/<skinname>/" folder where there is a skin.config file with a couple of definitions. Most important in this file are the page template definitions for a specific skin.

A config is mentioned below:

Tag <code> is invalid. No code blocks defined

As you can see also custom error pages are supported so you can have 2 skins configured with their own set of error pages. All paths specified are relative to the skin root folder.

To make things run fast, the SkinEngine is using a singleton instance to read in all skin definitions for all skins found in the "~/App_Skin" folder. As a result of this, when a site is running and you make a change to the skin.config, the system will not see that because of the singleton, but this is easily solved by configuring a FileSystemWatcher to monitor changes in the "~/App_Skins" folder and reload the skins, so this is what I’ve implemented in the system.

Tag <Header3> not implemented

One of the things which needs to be in a CMS is the use of friendly URLs, so no more .ASPX extensions and have a bit of control on how the URLs look.

For that I used the Routing Engine which ships with MVC. Though the CMS is based on web forms, it’s quite easy to use the Routing Engine.

The result is that I have configured a couple of routes which have a unique pattern for different kind of things like navigation from the sitemap, standalone pages and dynamic content.

The sitemap nodes have a field called SEO Name which will determine one part of the URL.

An example is "/en-US/Products/ASPxGridView".

In this case, the first part is the language-code of the site (en-US), the second part is the SEO Name of the parent node, and the last part is the SEO Name of the current node.

For pages which are not bound to a sitemap node (parentless pages) a url will look like this:
"/Pages/en-US/Technology/ASPxGridView"

The first part is for the routing engine to determine it’s a page route, second is language, third is the group in which the page is organized, and the last one is the SEO Name of the page.

If this page is bound to the sitemap, and you go to the above URL, you will be redirected to the sitemap URL with a permanent redirect so your menu will be correctly synchronized and the page is only accessible on one URL which improves your indexing by Google.

Tag <Header3> not implemented

Dynamic content is a really powerful feature in this CMS and allows you to create collections of data in a structured way which then can be put in lists on pages like news, events, blogs, and vacancies and so on.

Dynamic Content types have a couple of common fields like title, seo name, description, keywords and any number of custom fields specific for the content type.

As well as pages, dynamic content can be tagged and put into categories which then can be used to display selections of these items on a page.

In the definition of the dynamic content types (which can be done runtime), you can also configure one or more response definitions per item-type. On the front-end this results in a couple of forms where the entered data is stored in the system as a reaction on that item.

For a blog type, you will only have one response definition which has a name, e-mail address and comment but for a vacancy you can configure several forms like “Apply for the job” and “Ask a question” were on the first you will need to enter all your personal information including an uploaded CV where on the last you just need to enter your name e-mail address and your question.

Tag <Header3> not implemented

One of the things what is really easy to do for a developer is creating a page template.

A page template is an ordinary .ASPX page with or without a master page with the only difference that they are derive from Concepts2Go.Cms.UI.WebPage and Concepts2Go.Cms.UI.Masterpage which will make the page CMS enabled.

Together with the above, you include CMS controls instead of normal controls on your page:

Tag <code> is invalid. No code blocks defined

Once you log in as an administrator, you’ll see a menu above the existing layout which has a button called edit page, which will then put a marker above your label and when you click it, it will show an editor where you can change in this case the text.

For the above behavior to happen, the CMS is depending on three things; a data provider class which holds the editable properties of the control and stores it in the database, a control for presenting the data in the provider and an editor to edit the properties of the control.

The data provider class for the above CMS label is fairly simple and looks like this:

Tag <code> is invalid. No code blocks defined

If you take a look at the code you will see a couple of attributes here and there where the Export property is from MEF and makes sure this control is automatically registered to the CMS as a control.

The second attribute ControlDataProviderAttribute tells the CMS it’s a control and not a widget. Another interesting attribute is the CmsSearchableData which makes sure that specific property is being indexed with the Lucene search engine.

The other two attributes are for the FormControl so you don’t need to code an editor screen!

For displaying this data in the page, there are a couple of base control classes which make it again simple to implement the visual part. The CmsLabel control looks like this:

Tag <code> is invalid. No code blocks defined

Another interesting base control for these purposes is the CmsBaseTemplateControl.This is a general purpose control which has a Template where you can use DataBinding expressions because the provider object is data bound to an instance of this template.

Widgets are different from CMS controls since they can be put in widget containers where a content editor can change the order of widgets by dragging and dropping. The principle for widgets is the same as CMS controls except that they derive from different base classes and there are a couple of different attributes. Another thing with widgets is that in most scenarios, a user control (.ASCX) is used for displaying the information of that widget. In this user control, you can again use databinding expressions against the data provider object which is associated with the particular widget.

If you take a look at the skin.config file, you’ll see a section widgetContainers, in that section you can specify which widgets can be used in what widget container and which control to use on the front end. This gives you the possibility to reuse data provider classes on different parts of a webpage in different looks, and restricting the content editor what widgets to use where.

I think all together it’s quite a flexible system which is rather easy to use as a developer and really easy to use as an end user. Thanks to the DevExpress Controls, it looks really sharp and thanks to XPO, I was able to set up a steady and flexible data model which runs on different database back ends by just changing the connection string.


Don Wibier
CMS2GO Developer
Concepts2Go