香港鬼片四个人打麻将:Integrating N2CMS into Who Can Help Me? Part 3

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 22:25:09

Integrating N2CMS into Who Can Help Me? Part 3

Tags: N2 CMS, Who Can Help Me February 12th, 2010

This is part 3 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 and second post 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

Step 8 – Add a base CMS controller

To link our content items in the CMS database to the appropriate controllers, we need our CMS related controllers to inherit from the N2 controller class ContentController where T is the type of content definition that the controller is handling. This means that we have access to a strongly typed instance of the current content item that the controller is handling within our controller actions.

We already have a BaseController class in WCHM, as we wanted to set up some defaults for the master page name to use etc in the Spark views. Ideally, I’d like to be able to reuse this code, but as our CMS controllers need to be generic, I had to create a new base class, N2Controller which inherits the N2 ContentController and duplicate the method overrides for setting up the default master page etc.

view sourceprint?01.namespace WhoCanHelpMe.Web.Controllers 02.{ 03.    using System.Web.Mvc; 04.    using N2; 05.    using N2.Web.Mvc; 06.  07.    /// 08.    /// Base N2 controller 09.    /// 10.    /// 11.    /// The content item type 12.    /// 13.    public class N2Controller : ContentController where T : ContentItem 14.    { 15.        /// 16.        /// Stores the site master name 17.        /// 18.        private const string MasterName = "Site"; 19.  20.        /// 21.        /// Default controller action 22.        /// 23.        /// 24.        /// The default view with the current CMS item as the model 25.        /// 26.        public override ActionResult Index() 27.        { 28.            return View(CurrentItem); 29.        } 30.  31.        /// 32.        /// Returns a view result with default view and master name 33.        /// 34.        /// 35.        /// View Result 36.        /// 37.        protected new ViewResultBase View() 38.        { 39.            return View(string.Empty, MasterName, null); 40.        } 41.  42.        /// 43.        /// Returns a view result with specified view name and the default master name 44.        /// 45.        /// 46.        /// The view name. 47.        /// 48.        /// 49.        /// View Result 50.        /// 51.        protected new ViewResultBase View(string viewName) 52.        { 53.            return View(viewName, MasterName, null); 54.        } 55.  56.        /// 57.        /// Returns a view result with the specified view name, default master name 58.        /// and the specified model 59.        /// 60.        /// 61.        /// The view name. 62.        /// 63.        /// 64.        /// The model. 65.        /// 66.        /// 67.        /// View Result 68.        /// 69.        protected new ViewResult View(string viewName, object model) 70.        { 71.            return View(viewName, MasterName, model); 72.        } 73.  74.        /// 75.        /// Returns a view result with default view and master name 76.        /// 77.        /// 78.        /// The model. 79.        /// 80.        /// 81.        /// View Result 82.        /// 83.        protected new ViewResult View(object model) 84.        { 85.            return View(string.Empty, MasterName, model); 86.        } 87.    } 88.}

Changeset reference 55248

I also added my specs for the behaviour of the base CMS controller:

view sourceprint?01.namespace MSpecTests.WhoCanHelpMe.Web.Controllers 02.{ 03.    using System.Web.Mvc; 04.    using System.Web.Routing; 05.    using global::WhoCanHelpMe.Domain.Cms.Pages; 06.    using global::WhoCanHelpMe.Web.Controllers; 07.    using Machine.Specifications; 08.    using Machine.Specifications.AutoMocking.Rhino; 09.    using Machine.Specifications.Mvc; 10.  11.    public abstract class context_for_n2_controller : Specification> 12.    { 13.        Establish context = () => 14.            { 15.                var routeData = new RouteData(); 16.                routeData.Values.Add("n2_item", new HomePage { Name = "Home Page" }); 17.  18.                subject.ControllerContext = new ControllerContext() { RouteData = routeData }; 19.            }; 20.    } 21.  22.    [Subject(typeof(N2Controller<>))] 23.    public class when_the_n2_controller_is_asked_for_the_default_action : context_for_n2_controller 24.    { 25.        static ActionResult result; 26.  27.        Because of = () => result = subject.Index(); 28.  29.        It should_return_the_default_view = () => 30.            result.ShouldBeAView().And().ShouldUseDefaultView(); 31.  32.        It should_use_the_default_master_page = () => 33.            result.ShouldBeAView().And().MasterName.ShouldEqual("Site"); 34.  35.        It should_populate_the_view_model_with_the_current_item_from_the_cms = () => 36.            result.Model().Name.ShouldEqual("Home Page"); 37.    } 38.}

What I’ve done here is add a base Index() action to the CMS controller that will just pass the current content item as the model into the view. In reality, I’ll probably not use this as in WCHM we’re pretty strict on using viewmodels to bind to our views, rather than domain entities (which is what the CMS definitions really are) however, this allows a quick and dirty way to pass the CMS content into the view as your’re building up your application. With anything but the most simple application, your view will be made up of more than just the content from the CMS, so you’ll need to build up a more specific model for the view anyway.

Note how N2 works – when it realises that it can handle an incoming request for a URL with a content item it creates that item for us and puts it into the RouteData before calling the associated controller. This means that we easily have access to the current item via the ContentController.CurrentItem property. We can easily simulate this for the purposes of our specs by adding a content item into the RouteData associated with our ControllerContext – (in the Establish context).

Changeset reference 55314

Step 9 – Update the HomeController

The goal of this exercise was to surface content from N2CMS onto our WCHM Home page and About page. We’ve now got a base CMS controller that we can use, so next I refactor the existing HomeController to inherit N2Controller.

view sourceprint?1.[Controls(typeof(HomePage))] 2.public class HomeController : N2Controller 3.{ 4.   ...existing code the same for now... 5.}

The things to note are that we now inherit N2Controller which means we can easily access the current item (as a HomePage) from within our controller, and the Controls[] attribute, which tells N2 which content types this controller is going to handle. Any requests matching a content item of type HomePage will now come this way.

We also need to update our specs as because we now inherit N2Controller, which in turn inherits ContentController (in the N2 namespace), our controller is going to try to access the current content item out of RouteData. Even though we’re not accessing it yet, unless we put it into RouteData, our specs will fail. I updated the Establish context for the HomeControllerSpecs in the same fashion as the specs for our base N2Controller, by adding a content item into the RouteData associated with our ControllerContext.

view sourceprint?01.Establish context = () => 02.{ 03.    var routeData = new RouteData(); 04.    home_page = new HomePage(); 05.    routeData.Values.Add("n2_item", home_page); 06.  07.    home_view_model_mapper = DependencyOf(); 08.    news_tasks = DependencyOf(); 09.  10.    ServiceLocatorHelper.AddCachingService(); 11.  12.    subject.ControllerContext = new ControllerContext() { RouteData = routeData }; 13.};

Changeset reference 55315

At this point, our site should build, compile, pass tests and load up in the normal WCHM fashion, so lets take a pause and go off tangent ever so slightly…

Step 10 -  Authentication

This is a bit of a tricky one in WCHM as we’re already using Open ID for users to authenticate themselves in order to create profiles. N2 uses the Asp.Net membership providers to manage users and access to the CMS system (although it has it’s own custom provider that stores the users as content items in it’s own database). We don’t want to allow anyone to be able to get into the CMS, but we need to be able to distinguish between those that can and cannot with one unified mechanism.

I went round in circles a bit with this one, and with some help from Jon, I ended up back right where I started, which was to do nothing. The solution I came up with is a little hacky, but it works, and requires no changes to N2, or the WCHM authentication, so all is good for now.

In order to set up a CMS user, I first created a profile in the usual way in WCHM, using my preferred Open ID provider. I then opened the Profiles database table in the WCHM database and found the Open ID username that has been returned and stored against my profile. If, like me, you use a Google account, it will look something like this: (this is not my real Open ID!)

https://www.google.com/accounts/o8/id?id=asdasdasdasd6auasoas_asdadssad_das

I then used this username to create a new admin user in the N2 database in the normal way I would do in N2. Firstly, I need to add the relevant membership config for N2 into the web.config:

view sourceprint?01.02.<membership defaultProvider="ContentMembershipProvider"> 03.  <providers> 04.    <clear/> 05.    <add name="ContentMembershipProvider" type="N2.Security.ContentMembershipProvider, N2.Security" /> 06.  providers> 07.membership> 08.<roleManager enabled="true" defaultProvider="ContentRoleProvider"> 09.  <providers> 10.    <clear/> 11.    <add name="ContentRoleProvider" type="N2.Security.ContentRoleProvider, N2.Security" /> 12.  providers> 13.roleManager> 14.<profile defaultProvider="ContentProfileProvider"> 15.  <providers> 16.    <clear /> 17.    <add name="ContentProfileProvider" type="N2.Security.ContentProfileProvider, N2.Security" /> 18.  providers> 19.profile>

Changeset reference 55352

Then, I temporarily replace the WCHM authentication configuration with the out-the-box default N2 authentication section:

view sourceprint?1.    <authentication mode="Forms"> 2.      <forms loginUrl="edit/login.aspx" protection="All" timeout="30000" path="/"> 3.<credentials passwordFormat="Clear"> 4.  5.  <user name="admin" password="changeme"/> 6.credentials> 7.      forms> 8.    authentication>

I can now load up my site, browse to the /edit url and be presented with the standard N2 login screen.

Logging in with the default admin user specified in config gets me into the N2 admin interface:

Now we’re getting somewhere! I can then use the N2 users screen to add a new user – I use the Open ID username as the N2 username, and fill the rest of the details in, but in fact, they’re not going to be used as we’re going to rely on Open ID to authenticate us. Once the user is added, we can see it in the N2 users table:

Now that we have our first real admin user, we can replace the authentication section in web.config with the original WCHM Open ID config:

view sourceprint?1.<authentication mode="Forms"> 2.    <forms defaultUrl="~/" loginUrl="~/user/login" timeout="2880" name="__wchm_auth"/> 3.authentication>

We can now log in and out of N2 using the Open ID authentication. If I try to browse to the /edit url in order to get to the admin site, I will get presented with the WCHM Open ID authentication screen. Once authenticated using Open ID, I can then access the edit site fine as the authenticated username matches an admin user of the same username in N2.

Next…

So, we’re practically there – we have a working site and a working N2 admin site which we can access using our Open ID authenticated username. The Home page still loads normally and is in fact being routed via the N2 content route. The final steps are to build up the content definitions, and pass the content from N2 to the view…