Integrating N2CMS into Who Can Help Me? Part 4
Uncategorized No Comments »This is part 4 in my series of posts about integrating the .Net open source content management system N2CMS into the Sharp Architecture demo application Who Can Help Me?.
In the first, second and third posts I
- Added the N2 assemblies
- Added the edit site to the web project
- Created the initial content definitions
- Created the database schema
- Added root nodes to the CMS database
- Updated the Web.config
- Set up the N2 Content Routes and intialised the CMS engine
- Added a base CMS controller
- Updated the HomeController to be a CMS controller
- Tackled authentication with Open ID and the N2 membership providers
Step 11 – Build up the HomePage content definition
Our HomePage definition has nothing on it – which means that although we can create a HomePage in the N2, there’s no properties to set, so no CMS content is actually being displayed on the Home Page.
I’m going to keep this simple and just add a BodyText property, using the N2 FreeTextArea control so the user gets a full WYSIWYG editor. I add the following property definition to HomePage.cs:
[PageDefinition("Home Page",
Description = "A home page template.",
SortOrder = 440,
InstallerVisibility = InstallerHint.PreferredRootPage | InstallerHint.PreferredStartPage,
IconUrl = "~/edit/img/ico/png/page_world.png")]
[WithEditableTitle("Title", 5, Focus = true, ContainerName = Tabs.Content)]
[RestrictParents(typeof(SiteRoot))]
public class HomePage : AbstractPage
{
/// <summary>
/// Gets or sets BodyText.
/// </summary>
/// <value>
/// The body text.
/// </value>
[EditableFreeTextAreaAttribute("Body Text", 100, ContainerName = Tabs.Content,
HelpText = "Set the body text for the page")]
public string BodyText
{
get { return (string)GetDetail("BodyText"); }
set { SetDetail("BodyText", value); }
}
}
This now means that when we browse to our edit site and edit the Home page, we see the following edit view:
Step 12 – Update the HomePageViewModelMapper
Ultimately we want to display the CMS content on the HomePage view, but our Home page also displays a new feed that comes from the NewsTasks i.e. nothing to do with the CMS data. This is fine – it just means that we need to map both the CMS content and the News feed data to the view.
Lets start by updating our mapper interface:
public interface IHomePageViewModelMapper : IMapper<IList<NewsItem>, HomePage, HomePageViewModel>
{
}
We now map from a HomePage (from the CMS) and a list of NewsItems (from the NewsTasks) and return a HomePageViewModel. This is where the use of view models in MVC really becomes important – anything other than a really simple application will be displaying data from a variety of sources and so it’s not always possible to bind directly to your domain model. I find that it’s always a good idea to start with the use of view models from the offset as you will inevitably need them and it saves you refactoring time later on. As we already are inheriting from a base view model mapper, and utilising the power of Automapper, updating the HomePageViewModelMapper is easy. All we need to do is update the CreateMap method to set up the Automapper strategy for mapping from a HomePage to a HomePageViewModel. We’ll rely on the out the box behaviour of Automapper that properties with the same name are automatically mapped:
public class HomePageViewModelMapper : BasePageViewModelMapper<IList<NewsItem>, HomePage, HomePageViewModel>,
IHomePageViewModelMapper
{
private readonly INewsItemViewModelMapper newsItemViewModelMapper;
public HomePageViewModelMapper(
IPageViewModelBuilder pageViewModelBuilder,
INewsItemViewModelMapper newsItemViewModelMapper)
: base(pageViewModelBuilder)
{
this.newsItemViewModelMapper = newsItemViewModelMapper;
}
protected override void CreateMap()
{
Mapper.CreateMap<IList<NewsItem>, HomePageViewModel>().ConvertUsing(list => this.DoMapping(list));
Mapper.CreateMap<HomePage, HomePageViewModel>();
}
private HomePageViewModel DoMapping(IList<NewsItem> input)
{
return new HomePageViewModel
{
NewsItems = input.MapAllUsing(this.newsItemViewModelMapper)
};
}
}
Step 13 – Update the HomeController
Now we’ve updated our mapper, our HomeController needs to change as it’s this which calls the mapper. I start by updating the specs for the HomeController, to make sure that both the HomePage and the list of news items are passed to the mapper:
[Subject(typeof(HomeController))]
public class when_the_home_controller_is_asked_for_the_default_view : specification_for_home_controller
{
static HomePageViewModel the_view_model;
static IList<NewsItem> the_news_items;
static ActionResult result;
Establish context = () =>
{
the_view_model = new HomePageViewModel();
the_news_items = new List<NewsItem>();
news_tasks.Stub(nt => nt.GetProjectBuzz()).Return(the_news_items);
home_view_model_mapper.Stub(hvmm => hvmm.MapFrom(the_news_items, home_page)).Return(the_view_model);
};
Because of = () => result = subject.Index();
It should_return_the_default_view = () =>
result.ShouldBeAView().And().ShouldUseDefaultView();
It should_map_the_home_page_content_and_the_list_of_news_items_the_view_model =
() => home_view_model_mapper.AssertWasCalled(hvmm => hvmm.MapFrom(the_news_items, home_page));
It should_pass_the_view_model_to_the_view =
() => result.Model<HomePageViewModel>().ShouldBeTheSameAs(the_view_model);
}
And then we can update the HomeController itself, which should make the code compile again and make the tests pass. The HomePage content from the CMS is accessed via the CurrentItem property which is surfaced from the base N2Controller and kindly automatically populated for us by N2:
[Cached(CacheName.AdHoc)]
private PageViewModel IndexInner()
{
var buzz = this.newsTasks.GetProjectBuzz();
return this.homePageViewModelMapper.MapFrom(buzz, CurrentItem);
}
Step 14 – Update the HomePageViewModel
All we need to do is add a new property that matches the BodyText property on our HomePage definition for Automapper to be able to map the properties:
public class HomePageViewModel : PageViewModel
{
public HomePageViewModel()
{
this.NewsItems = new List<NewsItemViewModel>();
}
public IList<NewsItemViewModel> NewsItems { get; set; }
public string BodyText { get; set; }
}
Step 15 – Update the view
Now everything should be wired up, we need to display the CMS content on the home page view. I remove the hard coded text in the Spark view file and replace it with with the BodyText property of the view model. Note the Spark syntax start with !{} – the exclamation mark means that Spark will not HTML encode this value, which is what we need as the content from the CMS will be a HTML string:
<viewdata model="WhoCanHelpMe.Web.Controllers.Home.ViewModels.HomePageViewModel"/>
<content name="title">
What's all this then?
</content>
!{Model.BodyText}
<h3>The latest WCHM buzz...</h3>
<NewsHeadlines />
Step 16 – Disable caching
WCHM makes use of caching in an attempt to show how to set up different expiration times on related pieces of data. Unfortunately, as our CMS system allows users to update content, our views are going to need to be a bit more dynamic – for now I’m just going to disable the caching service so that any changes I make will be displayed. I’ll revisit this next time to try to come up with a better solution that works for the CMS:
The cache can be disabled in the container.components.config file:
<configuration>
<components>
<component id="CachingService"
service="WhoCanHelpMe.Framework.Caching.ICachingService, WhoCanHelpMe.Framework"
type="WhoCanHelpMe.Infrastructure.Caching.HttpCachingService, WhoCanHelpMe.Infrastructure"
lifestyle="singleton">
<parameters>
<enabled>false</enabled>
<cacheDurations>
<dictionary>
<item key="VeryShort">60</item>
<item key="Short">120</item>
<item key="Medium">300</item>
<item key="Long">10800</item>
<item key="Permanent">86400</item>
</dictionary>
</cacheDurations>
<caches>
<dictionary>
<item key="AdHoc">Medium</item>
<item key="MvcTempData">Short</item>
</dictionary>
</caches>
</parameters>
</component>
</components>
</configuration>
Step 17 – Update the N2 Item table schema (D’oh!)
So my authentication workaround had a slight problem – the openId username that I’m using for my N2 username is considerably longer than the 50 character limit that N2 imposes on its Item table’s SavedBy column. I updated this to NVCHAR(255) to match the column in the WCHM profile table.
Try it all out!
I can now browse to the N2 edit site and switch into edit mode of the Home Page:
If I add some content and click Save and Publish, we can now see the N2 content on the Who Can Help Me? home page! -
Next…
So we’ve now got a working content management system and we can set the body text for the HomePage and see it displayed in the view alongside our news feed content. Next time I’m going to try and deal with the caching issue and also do the same for the About page which will force me to tackle the navigation, which will also need to come from the CMS…

Recent Comments