c#

Sitecore MVC in a multisite environment: area’s

by Chris van de Steeg. 0 Comments

If you are creating a multisite environment using Sitecore & MVC (or any MVC site for that matter), chance is you have to do some weird naming in your controllers to avoid naming conflicts.

This is why Microsoft introduced area’s in their implementation of MVC (as did others before them)

Using them in Sitecore is actually pretty easy, thanks to (again) sitecore’s flexible pipilines.

First, we extend the Initialize pipeline with our own step:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <!-- Loader -->
      <initialize>
        <processor type="BoC.Sitecore.Pipelines.InitializeRoutes, Thieme.Framework" />
      </initialize>
    </pipelines>
  </sitecore>
</configuration>

Then add our class to the project:

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Sitecore.Configuration;
using Sitecore.Mvc.Configuration;
using Sitecore.Pipelines;

namespace BoC.Sitecore.Pipelines
{
    public class InitializeRoutes
    {
        public virtual void Process(PipelineArgs args)
        {
            var scRoute = RouteTable.Routes[MvcSettings.SitecoreRouteName] as Route;
            var indx = RouteTable.Routes.IndexOf(scRoute);
            AreaRegistration.RegisterAllAreas();
            foreach (var info in Factory.GetSiteInfoList())
            {
                if (info.Properties["mvcArea"] != null)
                {
                    var newRoute = new Route(scRoute.Url, 
                        new RouteValueDictionary(scRoute.Defaults), 
                        new RouteValueDictionary(scRoute.Constraints), 
                        new RouteValueDictionary(scRoute.Constraints), 
                        scRoute.RouteHandler);
                    newRoute.DataTokens.Add("area", info.Properties["mvcArea"]);
                    newRoute.Constraints.Add("sc-issite", new IsCorrectSiteContraint(info.Name));

                    if (info.Properties["mvcNamespaces"] != null)
                    {
                        newRoute.DataTokens["Namespaces"] = info.Properties["mvcNamespaces"].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                        newRoute.DataTokens["UseNamespaceFallback"] = false;
                    }

                    RouteTable.Routes.Insert(indx, newRoute);
                }

            }
        }

        public class IsCorrectSiteContraint : IRouteConstraint
        {
            private readonly string _siteId;

            public IsCorrectSiteContraint(string siteId)
            {
                _siteId = siteId;
            }

            public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values,
                RouteDirection routeDirection)
            {
                return global::Sitecore.Sites.SiteContext.Current.Name == _siteId;
            }
        }
    }
}

Now what this code does, is to add a route to the routecollection for every Sitecore-site configured. It uses a routeconstraint to validate if the route is applyable for the current request. If it is applyable the property ‘mvcArea’ of the configured website is set as the MVC area for that request. So, we will have to extend the <site> tag of the sitecore configuration with an mvcArea attribute, like so:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <sites>
      <site  mvcarea="Corporate" mvcnamespaces="Corporate.Framework.*,Website.Corporate.*" language="nl-NL" content="web" database="web" scheme="http" patch:before="site[@name='website']" name="Corporate" hostname="coporate.com" targethostname="coporate.com" virtualfolder="/" physicalfolder="/" rootpath="/sitecore/content/Corporate" startitem="Home" allowdebug="true" cachehtml="true" htmlcachesize="10MB" enablepreview="true" enablewebedit="true" enabledebugger="true" disableclientdata="false" />
    </sites>
  </sitecore>
</configuration>

As you can see, we also specify “mvcnamespaces”, although these are optional, it allows you to tell MVC where to look for your controllers.

It’s this easy. So now you’re ready to go and create your controllers & views in the correct manner. Don’t forget to place your views in the correct folder (in this case: areas\corporate\views\)

Sitecore MVC Music Store

by Chris van de Steeg. 3 Comments

In my last blog post about using the MVC Framework inside Sitecore, I announced that I’d write another blog post on how to actually create a real world application with MVC inside Sitecore. The first blog post was just a proof of concept, creating a real world application with it, helps completing the code.

In the meantime, Sitecore has announced that it will support MVC in Sitecore version 6.5.1. Great news! I got a sneak preview of what they’ve made, looks great. From what I’ve seen, this tutorial won’t have to change much when they’ve released the official version. I’ll update the tutorial as soon as 6.5.1 will be released.

Since Microsoft has already created a nice tutorial for getting started with ASP.NET MVC : MVC Music Store tutorial. I figured it’d also be a nice tutorial to get started with MVC inside Sitecore. It’s an easy to follow, small tutorial, which makes it easy to go through for both experts and beginners in the world of MVC. Experts can just skip the parts explaining how MVC itself works and go straight to the action. For beginners, the entire tutorial takes less then 2 hours. It’ll be the best invested 2 hours of your programmer’s life so far :)!

Once you have completed the Microsoft tutorial, the steps in this blog-post will help you transform it to a Sitecore project.

Step 1 – complete the tutorial

First, follow the entire tutorial (or if you’re an expert on MVC already, just download the package:http://mvcmusicstore.codeplex.com/)

Step 2 – copy contents

Copy the wwwroot contents of the mvcmusicstore project into the wwwroot of an existing Sitecore project (or create a new one first), but SKIP the web.config files! Note that you should copy the web-files to the web folder of your sitecore project! (if you downloaded the package, the web-content is inside the ‘MvcMusicStore’ folder inside the zip file)

Now open the Sitecore project in visual studio, and add the project you just copied to your solution (File –> Add –> Existing project).

Step 3 – nuget package

Install the NuGet package BoC.Sitecore.Mvc (not the starter app) to your just added project. You can do that either through the package manager console or through tools->library package manager->Manage NuGet Packages for Solution…

This will modify your web.config to allow MVC applications inside your sitecore website, and will add the BoC.Sitecore.MVC reference which holds all the MVC magic.

Step 4 – connectionstrings

Now, copy the connectionstring in the web.config of the original created MVCMusicStore project, to the App_Config\Connectionstrings.config inside your sitecore solution.

If you now run your application, and everything went fine, your application should work exactly the same as when you finished the tutorial. (Note: if you downloaded the sample code instead of making your own, and you run in to the error code  “Unable to find the requested .Net Framework Data Provider.  It may not be installed.”, you need to install MS Sql Compact CE 4.0)

These first 4 steps were the steps required to get an existing MVC application working inside the sitecore wwwroot. Now we’ll start modifying the existing app to become an application fully powered by sitecore!

Step 5 – Global.asax

Running the application worked just fine, but there’s no sitecore magic somewhere in the pipeline, so let’s add that. First, we’ll modify the default route in global.asax.cs. You can do that by adding any static string value in front of the default route. I chose ‘musicstore_api’. Doing this will make all your controller actions accessible through /musicstore_api/home/index, this could come in handy for stuff you need to access without going through sitecore (eg. ajax calls, delete actions, etc)

image

Step 6 – Create ‘GetLayout’ controller action and view

Using BoC.Sitecore.Mvc, if you want a Razor view as your layout, you’ll have to load it through a controller action. If you don’t like creating a controller action for just loading the base layout, you could use Robert Jongkind’s RazorForSitecore module, it will all mix and match just fine. Sitecore 6.5.1’s version of MVC will also support picking a Razor view without a controller as your layout..

For now however, we’ll stick to using just the SitecoreMVC way. Open your HomeController.cs file and add new action called ‘DefaultLayout’. The only thing that action should do, is return View():

image

Also create the accompanying view, with just 1 line telling Sitecore it can render a placeholder with key “main” in this view. (Html.SitecorePlaceholder is an extension method inside BoC.Sitecore.MVC)

image

Step 7 – Create Sitecore templates

Run your application, and go the the Sitecore desktop (/sitecore/shell/default.aspx) and open the template manager.

Screenshot - 3-6-2012 , 14_14_50 

Add a new template folder underneath ‘Templates\User defined’ called ‘MVC MusicStore. Underneath that new folder, add a new template.

image

Call the new template ‘Default page’ and just let the base template field as it is. Click next, next, finish.

image

Select your new ‘Default page’ item in the tree, and on the ribbon ‘Builder options’ click the ‘Standard values’ button in the template section.

image

And next on the ‘Presentation’ ribbon, click the ‘Details’ button in the layout section

image

This will open the layout details wizard:

image

Here, click the Edit-button under the ‘Default’ section (marked with a red border in the screenshot above), which will open the “Device Editor” popup:

image

In the dropdown, choose the ‘DefaultLayout’ MVC action underneath the Home controller and click OK twice

image

Step 8 – Create sitecore items

Based on our just created ‘Default page’ template, we’re now going to add the Sitecore items based on this template.

Open up the content editor in sitecore, and delete the exiting homepage item (/sitecore/content/home). Add a new Home item by click the ‘Content’ item with the right mouse button and choosing ‘Insert –> Insert from Template’.

image

Call the item Home and pick our just created ‘Default page’ template as it’s template

image

Using this same template, create the following tree inside Sitecore:

image

Notice the * in front of Genre and Id…. these are wildcard mappings! The name of the items should be * and the displayname *Genre and *Id.

Step 9 – Attach controller actions

On these pages, we’ll attach the appropiate MVC Action. Start with the Home item. Click on it in the tree, and open the ‘Presentation’ ribbon. Now click on the details button:

 image
This will open the layout popup

image

Now click the Edit-button under the ‘Default’ section (marked with a red border in the screenshot above), which will open the “Device Editor” popup image

Click the controls tab, and choose ‘Add’. That will bring up the ‘Select a rendering’ window.

image

Choose MVC Actions/Home/Index and enter ‘main’ as the placeholder name (remember we added that $Html.SitecorePlaceholder(“main”) to DefaultLayout.cshtml?). Click on Select, and then on OK twice to save all your changes.

Now do the same for all other pages you’ve created except the ‘checkout’ page (that is just a placeholder). The other pages should get the following controls:

  • Checkout –> MVC Actions/Checkout/Complete
  • Checkout/AddressAndPayment –> MVC Actions/Checkout/AddressAndPayment
  • ShoppingCart –> MVC Actions/ShoppingCart/Index
  • Store –> MVC Actions/Store/Index
  • Store/Browse –> MVC Actions/Store/Browse
  • Store/Browse/*Genre –> MVC Actions/Store/Browse
  • Store/Details –> MVC Actions/Store/Details
  • Store/Details/*Id –> MVC Actions/Store/Details

Step 10 – Publish & test

Now, publish your entire site (! beware that if you’re using the sample database with the sample item template, that there is a worfklow attached to the new items by default… you need to approve them before publishing). After publishing, you can go checkout the homepage of your website.

Hey! the navigation menu and logo are shown twice!

image

Step 11 – Modify viewstart

The reason for the page being displayed twice, is because the default MVC Musicstore project does not take into account that the views can be accessed as a child-action. Adding the actions as a sublayout inside our sitecore pages, makes the actions being rendered as childactions. To skip the layout being rendered in child mode, open Views/_ViewStart.cshtml and modify the one line that’s in there to:

image

Save the file, and check the homepage again: Looking much better!

Step 12 – Modify Links

Though the app looks good now, all links are pointing to the /musicstore_api folder. Even though that will work just fine, that’s the native MVC you’re calling then. To point all links to the sitecore versions, we’ll have to change all link renderings. This is the most dramatic change in an existing MVC app. In fact, we’ll break compatibility here, since the links will become Sitecore specific. I haven’t figured out a way to keep this compatible. Then again, I don’t think you’d ever even want that. Thing is, with a standard MVC application, a controller action always has it’s own url. With SitecoreMvc any page in Sitecore can use the same MVC Action over and over again, on different url’s. This is so different, it’ll be impossible to keep things cross-compatible.

Open Views/Store/GenreMenu.cshtml. Modify the @Html.ActionLink in there, to the following:

image

See how we’ve mapped Genre as a parameter? The wildcard resolver will pick that up. We could have also placed the name in the path attribute, that would resolve just the same:

image

Now do the same for all other links in the project. In Views/Home/Index.cshtml and Views/Store/Browse.cshtml change the action Url.Action to
image

The Html.ActionLink in Views/Store/Index.cshtml to
image

The Html.ActionLink in Views/ShoppingCart/CartSummary.cshtml to
image

In Views/ShoppingCart/Index.cshtml, change the $.post(“/ShoppingCart/RemoveFromCart” to
image
(See how this will go directly to MVC! This won’t hit any Sitecore item, hence the url would become /musicstore_api/shoppingcart/removefromcart).

In the same file, replace the checkout Html.ActionLink with
image
and the details Html.ActionLink to
image

In Views/ShoppingCart/Complete.cshtml change the Html.ActionLink to
image

In Controllers/ShoppingCartController.cs, in the AddToCart method, change the RedirectToAction to
image

and, the last one, in Controllers/CheckoutController.cs, in the AddressAndPayment method, change the RedirectToAction to
image

Step 13 – Remove authorization

As a last step remove the Authorize attribute on the CheckoutController. In this tutorial, we’ll not be setting up Account related stuff, it’ll be taken care with in the follow-up of this post.

image

Step 14 – Run

Now run your project again! Everything but Account-related stuff should work fine now! Add albums to your cart, check out, etc.

Conclusion

That concludes this tutorial for now. It should give you a good idea on how to get MVC up and running for your Sitecore projects.In the next blog post about Sitecore & MVC, I will explain how to use the Sitecore security in this MVC Musicstore and we’ll get rid of the external database by creating Sitecore items for the albums. The albums will the be editable using both the Sitecore content-editor AND page-editor. So watch this blog for the follow-up, though I’ll first write a post on an other topic.

Sitecore MVC

by Chris van de Steeg. 3 Comments

January 1st I started my new Job at eFocus, a full service internet company, at the department specialized in Sitecore development. I’d never worked with Sitecore before, so it was a complete new experience. Since I love the ASP.NET MVC way of development, I started looking if there was an option to use it for Sitecore development. Unfortunately I did not find any community effort to do so, except for blog posts on how to use Sitecore and an MVC project side-by-side. So I decided to try to create my own solution, since I would then have something fun to do while getting to know the ins and outs of Sitecore, something I felt obliged to do anyway.

In this blog post I’ll show you how to install and use my end-result of this proof of concept. In a follow-up, I’ll create a sample application with it. This first version is really just that: a proof of concept, just showing that it works. For example, this version includes a simple valueprovider for binding sitecore items to a model, but this is far from optimal. When creating a sample application, I’ll also be digging into modelbinding and other issues that’ll show up when creating an application. Perhaps I’ll conclude that its better to use something like CustomItem generator or the glass project and wrap one of those in a modelbinder, we’ll see (shoot your opinion about that in the comments!). I also only tested this on Sitecore 6.4, so it’ll probably only work on that version and up.

Now, let’s go to the code! To get started, open an existing Sitecore project (or create a new one) in Visual Studio. Now add the NuGet package BoC.Sitecore.MvcApp either through the package manager console or through tools->library package manager->Manage NuGet Packages for Solution…

VerySimpleWebsite - Manage NuGet Packages_2012-02-28_18-36-28

The Sitecore Mvc starter app is the version I’ll use for this blog-post: it will add a basic mvc project to your solution to get started with. The other one (BoC.Sitecore.Mvc) will just add the dll + a config file to your current project, use this one if you want to set up your own Mvc project without any starting point.

Once you’ve installed the package, a new project named ‘yourproject’.mvcapp is added to your solution:

emptysite - Microsoft Visual Studio_2012-02-28_18-40-50

This is an empty ASP.NET MVC3 Razor project, with some stuff removed. The default web.config is removed, since we’ll have to use a modified version of Sitecore’s web.config and the account-stuff has been removed. As you can see, a default HomeController has been added, a default view and a default model (ItemView). Now, to see if BoC.Sitecore.Mvc works, let’s build the solution and open up the sitecore shell. Once logged in to your Sitecore shell, expand the tree items /sitecore/Layout and /sitecore/Layout/Layouts. You’ll see that underneath both, a new folder is added named ‘MVC Actions’. It’s in two places since you can use your MVC Actions both as Layout or as a rendering, more on that later.

Sitecore - Windows Internet Explorer_2012-02-28_19-02-43

Let’s see what the action and view for Home/Index looks like, so we can now what to expect to see when we try to load them in sitecore. The action itself looks very simple:

emptysite - Microsoft Visual Studio_2012-02-28_19-09-36

It expects an item of type ItemView and just passes that on to the view. As mentioned earlier, I added a simple valueprovider that will try to resolve any parameter named “item” with field-values from the current sitecore item (the current sitecore item by default is the context.item, unless you provided a datasource for the rendering). The model by default only includes the property DisplayName of the type RenderingString:

emptysite - Microsoft Visual Studio_2012-02-28_19-14-05

RenderingString is a special type added by BoC.Sitecore.Mvc : it will call RenderField when outputted in html so you can use the page editor. You could also change this to ‘public string DisplayName {get; set; }’ to have it filled with the normal string value. Since DisplayName is not editable in the page editor, and thus we cannot see the difference between RenderingString and normal String, let’s change this property to Title like so:

emptysite - Microsoft Visual Studio_2012-02-28_19-19-04

In our case we’ll use the ‘Sample Item’ template from the default setup, which has a Title field. If your item does not have a Title field, you’ll have to pick some other field name of course.

Now, open up the view in Views/Home/Index.cshtml:

emptysite - Microsoft Visual Studio_2012-02-28_20-05-05

The view still uses DisplayName, so we have to change that as well:

emptysite - Microsoft Visual Studio_2012-02-28_20-08-02

When the view will be fully rendered (thus not through a RenderChildAction), the view uses the ‘masterpage’ Views/Shared/_Layout.cshtml:

emptysite - Microsoft Visual Studio_2012-02-28_20-14-09

As you can see there is a special HtmlHelper extension used here: @Html.RenderPlaceholder(“main”) which is the equivalent of <sc:placeholder Key=”main” runat=”server” /> for the aspx version.

Now, compile your changes, and go back to the sitecore shell. Underneath the Homepage, add a new Sample Item template called ‘My new sample item’ and click the ‘Layout Details’ button in the ‘Presentation’ Ribbon.  In the dialog that opens, click on ‘edit’.

Sitecore MVC - Windows Live Writer_2012-02-28_20-11-17

In the next dialog, click on the layout to pick the MVC Actions / Home / Index layout

Sitecore -- Dialoogvenster van webpagina_2012-02-28_20-20-37

Now, save all modifications by click ‘OK’ twice.

Now in the ribbon presentation, click on the ‘preview’ button to see our new page.

Sitecore - Windows Internet Explorer_2012-02-28_20-26-29

And there it is! Our MVC rendered sitecore item. The page still holds all the other renderings from the original Sample Item template including the nested Sample Sublayout.ascx the Sample Inner Sublayout.ascx and the sample rendering.xslt. So now we have an MVC rendered razor layout, holding asp.net usercontrols with an xslt rendering! You can mix-and-match those all together, how cool is that!

The item’s title is displayed twice, since our ‘Layout’ renders the title also, remember (the Home/Index.cshtml ouputs the title, and we’ve picked that as our Layout for the sitecore item). To see that the page editor also still works (even for the razor code), click on the ‘page editor’ button in the ‘Publish’ ribbon.

httplocalhost56325sc_mode=edit&sc_itemid=%7b46A6E270-FD57-4A02-9BBE-4B6AB2_2012-02-28_20-35-35

Modify the fields, add some extra renderings, it all still works. To show off an extra mix-and-match, now let’s also add our action as rendering to the existing asp.net based homepage.

Click on the homepage-treeitem and click on the ‘Layout details’ button on the ‘Presentation’ ribbon. In the popup dialog, click on ‘edit’ and then, in the next popup dialog, click on controls.

Sitecore -- Dialoogvenster van webpagina_2012-02-28_20-39-30

Now, click the add button. In the next popup dialog, select MVCActions/Home/Index and add it to placeholder ‘/main/centercolumn/content’

Sitecore -- Dialoogvenster van webpagina_2012-02-28_20-41-12

Click on select, and then twice on OK to save this new addition. Now, on our preview pane, we see an extra Title appear!

Sitecore - Windows Internet Explorer_2012-02-28_20-44-43

This is our Views/Home/Index.cshtml ! Since the action is now added to the page as a rendering instead of being used as a layout, the /Views/Shared/_Layout.cshtml is not used. This is because this action is now being rendered by calling MVC’s RenderChildAction. Child actions never have a layout.

Now just go ahead and do some mixing of different options for yourself, you could try a different viewengine (aspx, spark viewengine) for example, works just fine. A little extra nice-to-know is that you can add a DescriptionAttribute to your controllers and/or actions to have them show up differently in sitecore. ({0} is thereby replaced with the original name).

I hope you’ll all enjoy playing with MVC within Sitecore and get some great ideas about it while you’re at it. Please leave all your great ideas and suggestions in the comments.

If you want to checkout the source, you can find that at github: http://github.com/csteeg/BoC.Sitecore.MVC