Posts
201
Comments
1110
Trackbacks
51
April 2010 Entries
Create Orchard Module in a Separate Project

The Orchard Project is a new OOS Microsoft project that is being developed up on CodePlex. From the Orchard home page on CodePlex, it states

“Orchard project is focused on delivering a .NET-based CMS application that will allow users to rapidly create content-driven Websites, and an extensibility framework that will allow developers and customizers to provide additional functionality through modules and themes.”

The Orchard Project site contains additional information including documentation and walkthroughs. The ability to create a composite solution based on a collection of modules is a compelling feature. In Orchard, these modules can just be created as simple MVC Areas or they can also be created inside of stand-alone web application projects.  The walkthrough for writing an Orchard module that is available on the Orchard site uses a simple Area that is created inside of the host application. It is based on the Orchard MIX presentation. This walkthrough does an effective job introducing various Orchard concepts such as hooking into the navigation system, theme/layout system, content types, and more.  However, creating an Orchard module in a separate project does not seem to be concisely documented anywhere. Orchard ships with several module OOTB that are in separate assemblies – but again, it’s not well documented how to get started building one from scratch.

The following are the steps I took to successfully get an Orchard module in a separate project up and running.

Step 1 – Download the OrchardIIS.zip file from the Orchard Release page. Unzip and open up the solution.

Step 2 – Add your project to the solution. I named my project “Orchard.Widget” and used and “MVC 2 Empty Web Application” project type. Make sure you put the physical path inside the “Modules” sub-folder to the main project like this:

At this point the solution should look like:

Step 3 – Add assembly references to Orchard.dll and Orchard.Core.dll.

Step 4 – Add a controller and view.  I’ll just create a Hello World controller and view. Notice I created the view as a partial view (*.ascx). Also add the [Themed] attribute to the top of the HomeController class just like the normal Orchard walk through shows it.

Step 5 – Add Module.txt to the project root. The is a very important step. Orchard will not recognize your module without this text file present.  It can contain just the name of your module: name: Widget

Step 6 – Add Routes.cs. Notice I’ve given an area name of “Orchard.Widget” on lines 26 and 33.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Web.Mvc;
   4:  using System.Web.Routing;
   5:  using Orchard.Mvc.Routes;
   6:   
   7:  namespace Orchard.Widget
   8:  {
   9:      public class Routes : IRouteProvider
  10:      {
  11:          public void GetRoutes(ICollection<RouteDescriptor> routes)
  12:          {
  13:              foreach (var routeDescriptor in GetRoutes())
  14:              {
  15:                  routes.Add(routeDescriptor);
  16:              }
  17:          }
  18:   
  19:          public IEnumerable<RouteDescriptor> GetRoutes()
  20:          {
  21:              return new[] {
  22:                  new RouteDescriptor { 
  23:                     Route = new Route(
  24:                         "Widget/{controller}/{action}/{id}",
  25:                         new RouteValueDictionary {
  26:                             {"area", "Orchard.Widget"},
  27:                             {"controller", "Home"},
  28:                             {"action", "Index"},
  29:                             {"id", ""}
  30:                         },
  31:                           new RouteValueDictionary(),
  32:                           new RouteValueDictionary {
  33:                               {"area", "Orchard.Widget"}
  34:                           },
  35:                           new MvcRouteHandler())
  36:                  }
  37:              };
  38:          }
  39:      }
  40:  }

Step 7 – Add MainMenu.cs. This will make sure that an item appears in the main menu called “Widget” which points to the module.

   1:  using System;
   2:  using Orchard.UI.Navigation;
   3:   
   4:  namespace Orchard.Widget
   5:  {
   6:      public class MainMenu : INavigationProvider
   7:      {
   8:          public void GetNavigation(NavigationBuilder builder)
   9:          {
  10:              builder.Add(menu => menu.Add("Widget", item => item.Action("Index", "Home", new
  11:              {
  12:                  area = "Orchard.Widget"
  13:              })));
  14:          }
  15:   
  16:          public string MenuName
  17:          {
  18:              get { return "main"; }
  19:          }
  20:      }
  21:  }

Step 8 – Clean up web.config. By default Visual Studio adds numerous sections to the web.config. The sections that can be removed are: appSettings, connectionStrings, authentication, membership, profile, and roleManager.

Step 9 – Delete Global.asax. This project will ultimately be running from inside the Orchard host so this “sub-site” should not have its own Global.asax.

 

Now you’re ready the run the app.  When you first run it, the “Widget” menu item will appear in the main menu because of the MainMenu.cs file we added:

We can then click the “Widget” link in the main menu to send us over to our view:

 

Packaging

From start to finish, it’s a relatively painless experience but it could be better. For example, a Visual Studio project template that encapsulates aspects from this blog post would definitely make it a lot easier to get up and running with creating an Orchard module.  Another aspect I found interesting is that if you read the first paragraph of the walkthrough, it says,

“You can also develop modules as separate projects, to be packaged and shared with other users of Orchard CMS (the packaging story is still to be defined, along with marketplaces for sharing modules).”

In particular, I will be extremely curious to see how the “packaging story” evolves. The first thing that comes to mind for me is: what if we explored MvcContrib Portable Areas as a potential mechanism for this packaging? This would certainly make things easy since all artifacts (aspx, aspx, images, css, javascript) are all wrapped up into a single assembly. Granted, Orchard does have its own infrastructure for layouts and themes but it seems like integrating portable areas into this pipeline would not be a difficult undertaking. Maybe that’ll be the next research task. :)

Posted On Thursday, April 22, 2010 6:34 PM | Comments (4)
MVC Portable Areas Enhancement – Embedded Resource Controller

MvcContrib contains a feature called Portable Areas which I’ve recently blogged about. In short, portable areas provide a way to distribute MVC binary components as simple .NET assemblies where the aspx/ascx files are actually compiled into the assembly as embedded resources. This is an extremely cool feature but once you start building robust portable areas, you’ll also want to be able to access other external files like css and javascript.  After my recent post suggesting portable areas be expanded to include other embedded resources, Eric Hexter asked me if I’d like to contribute the code to MvcContrib (which of course I did!).

Embedded resources are stored in a case-sensitive way in .NET assemblies and the existing embedded view engine inside MvcContrib already took this into account. Obviously, we’d want the same case sensitivity handling to be taken into account for any embedded resource so my job consisted of 1) adding the Embedded Resource Controller, and 2) a little refactor to extract the logic that deals with embedded resources so that the embedded view engine and the embedded resource controller could both leverage it and, therefore, keep the code DRY.

The embedded resource controller targets these scenarios:

  1. External image files that are referenced in an <img> tag
  2. External files referenced like css or JavaScript files
  3. Image files referenced inside css files

Embedded Resources Walkthrough

This post will describe a walkthrough of using the embedded resource controller in your portable areas to include the scenarios outlined above. I will build a trivial “Quick Links” widget to illustrate the concepts.

The portable area registration is the starting point for all portable areas. The MvcContrib.PortableAreas.EmbeddedResourceController is optional functionality – you must opt-in if you want to use it.  To do this, you simply “register” it by providing a route in your area registration that uses it like this:

   1:  context.MapRoute("ResourceRoute", "quicklinks/resource/{resourceName}",
   2:      new { controller = "EmbeddedResource", action = "Index" },
   3:      new string[] { "MvcContrib.PortableAreas" });

First, notice that I can specify any route I want (e.g., “quicklinks/resources/…”).  Second, notice that I need to include the “MvcContrib.PortableAreas” namespace as the fourth parameter so that the framework is able to find the EmbeddedResourceController at runtime.

The handling of embedded views and embedded resources have now been merged.  Therefore, the call to:

   1:  RegisterTheViewsInTheEmmeddedViewEngine(GetType());

has now been removed (breaking change).  It has been replaced with:

   1:  RegisterAreaEmbeddedResources();

Other than that, the portable area registration remains unchanged.

The solution structure for the static files in my portable area looks like this:

I’ve got a css file in a folder called “Content” as well as a couple of image files in a folder called “images”. To reference these in my aspx/ascx code, all of have to do is this:

   1:  <link href="<%= Url.Resource("Content.QuickLinks.css") %>" rel="stylesheet" type="text/css" />
   2:  <img src="<%= Url.Resource("images.globe.png") %>" />

This results in the following HTML mark up:

   1:  <link href="/quicklinks/resource/Content.QuickLinks.css" rel="stylesheet" type="text/css" />
   2:  <img src="/quicklinks/resource/images.globe.png" />

The Url.Resource() method is now included in MvcContrib as well. Make sure you import the “MvcContrib” namespace in your views.

Next, I have to following html to render the quick links:

   1:  <ul class="links">
   2:      <li><a href="http://www.google.com">Google</a></li>
   3:      <li><a href="http://www.bing.com">Bing</a></li>
   4:      <li><a href="http://www.yahoo.com">Yahoo</a></li>
   5:  </ul>

Notice the <ul> tag has a class called “links”. This is defined inside my QuickLinks.css file and looks like this:

   1:  ul.links li 
   2:  {
   3:      background: url(/quicklinks/resource/images.navigation.png) left 4px no-repeat;
   4:      padding-left: 20px;
   5:      margin-bottom: 4px;
   6:  }

On line 3 we’re able to refer to the url for the background property.

As a final note, although we already have complete control over the location of the embedded resources inside the assembly, what if we also want control over the physical URL routes as well. This point was raised by John Nelson in this post. This has been taken into account as well. For example, suppose you want your physical url to look like this:

   1:  <img src="/quicklinks/images/globe.png" />

instead of the same corresponding URL shown above (i.e., “/quicklinks/resources/images.globe.png”). You can do this easily by specifying another route for it which includes a “resourcePath” parameter that is pre-pended. Here is the complete code for the area registration with the custom route for the images shown on lines 9-11:

   1:  public class QuickLinksRegistration : PortableAreaRegistration
   2:  {
   3:      public override void RegisterArea(System.Web.Mvc.AreaRegistrationContext context, IApplicationBus bus)
   4:      {
   5:          context.MapRoute("ResourceRoute", "quicklinks/resource/{resourceName}",
   6:              new { controller = "EmbeddedResource", action = "Index" },
   7:              new string[] { "MvcContrib.PortableAreas" });
   8:   
   9:          context.MapRoute("ResourceImageRoute", "quicklinks/images/{resourceName}",
  10:              new { controller = "EmbeddedResource", action = "Index", resourcePath = "images" },
  11:              new string[] { "MvcContrib.PortableAreas" });
  12:   
  13:          context.MapRoute("quicklink", "quicklinks/{controller}/{action}",
  14:              new {controller = "links", action = "index"});
  15:   
  16:          this.RegisterAreaEmbeddedResources();
  17:      }
  18:   
  19:      public override string AreaName
  20:      {
  21:          get
  22:          {
  23:              return "QuickLinks";
  24:          }
  25:      }
  26:  }

The Quick Links portable area results in the following requests (including custom route formats):

The complete code for this post is now included in the Portable Areas sample solution in the latest MvcContrib source code. You can get the latest code now.  Portable Areas open up exciting new possibilities for MVC development!

Posted On Tuesday, April 13, 2010 9:23 PM | Comments (15)
Philly.NET Code Camp – Code Samples

Thanks to everyone who attended my C# 4.0 presentation this weekend at Philly.NET Code Camp.  The code samples and PowerPoint can be downloaded here: C# 4 New Features.  The video replay URL is here: C# 4 video replay.

Posted On Monday, April 12, 2010 9:04 AM | Comments (0)
Philly.NET Code Camp

This Saturday I will be at the Philly.NET Code Camp presenting C# 4.0.  The code camp is currently registered to capacity (800 attendees) but you will be able to view certain presentations on a Live Meeting simulcast (and later on Channel 9).  You can tune it at 3:30PM Eastern time to view my presentation. The attendee URL is here.

Posted On Wednesday, April 7, 2010 9:47 PM | Comments (0)
MVC Portable Areas – Static Files as Embedded Resources

This is the third post in a series related to build and deployment considerations as I’ve been exploring MVC Portable Areas:

In the last post, I walked through a convention for managing static files.  In this post I’ll discuss another approach to manage static files (e.g., images, css, js, etc.).  With this approach, you *also* compile the static files as embedded resources into the assembly similar to the *.aspx pages. Once again, you can set this to happen automatically by simply modifying your *.csproj file to include the desired extensions so you don’t have to remember every time you add a file:

   1:  <Target Name="BeforeBuild">
   2:    <ItemGroup>
   3:      <EmbeddedResource Include="**\*.aspx;**\*.ascx;**\*.gif;**\*.css;**\*.js" />
   4:    </ItemGroup>
   5:  </Target>

We now need a reliable way to serve up these static files that are embedded in the assembly. There are a couple of ways to do this but one way is to simply create a Resource controller whose job is dedicated to doing this.

   1:  public class ResourceController : Controller
   2:  {
   3:      public ActionResult Index(string resourceName)
   4:      {
   5:          var contentType = GetContentType(resourceName);
   6:          var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
   7:          return this.File(resourceStream, contentType);
   8:      }
   9:   
  10:      private static string GetContentType(string resourceName)
  11:      {
  12:          var extention = resourceName.Substring(resourceName.LastIndexOf('.')).ToLower();
  13:          switch (extention)
  14:          {
  15:              case ".gif":
  16:                  return "image/gif";
  17:              case ".js":
  18:                  return "text/javascript";
  19:              case ".css":
  20:                  return "text/css";
  21:              default:
  22:                  return "text/html";
  23:          }
  24:      }
  25:  }

In order to use this controller, we need to make sure we’ve registered the route in our portable area registration (shown in lines 5-6):

   1:  public class WidgetAreaRegistration : PortableAreaRegistration
   2:  {
   3:      public override void RegisterArea(System.Web.Mvc.AreaRegistrationContext context, IApplicationBus bus)
   4:      {
   5:          context.MapRoute("ResourceRoute", "widget1/resource/{resourceName}",
   6:              new { controller = "Resource", action = "Index" });
   7:   
   8:          context.MapRoute("Widget1", "widget1/{controller}/{action}", new
   9:          {
  10:              controller = "Home",
  11:              action = "Index"
  12:          });
  13:   
  14:          RegisterTheViewsInTheEmbeddedViewEngine(GetType());
  15:      }
  16:   
  17:      public override string AreaName
  18:      {
  19:          get { return "Widget1"; }
  20:      }
  21:  }

In my previous post, we relied on a custom Url helper method to find the actual physical path to the static file like this:

   1:  <img src="<%: Url.AreaContent("/images/arrow.gif") %>" /> Hello World!

However, since we are now embedding the files inside the assembly, we no longer have to worry about the physical path. We can change this line of code to this:

   1:  <img src="<%: Url.Resource("Widget1.images.arrow.gif") %>" /> Hello World!

Note that I had to fully quality the resource name (with namespace and physical location) since that is how .NET assemblies store embedded resources. I also created my own Url helper method called Resource which looks like this:

   1:  public static string Resource(this UrlHelper urlHelper, string resourceName)
   2:  {
   3:      var areaName = (string)urlHelper.RequestContext.RouteData.DataTokens["area"];
   4:      return urlHelper.Action("Index", "Resource", new { resourceName = resourceName, area = areaName });
   5:  }

This method gives us the convenience of not having to know how to construct the URL – but just allowing us to refer to the resource name. The resulting html for the image tag is:

   1:  <img src="/widget1/resource/Widget1.images.arrow.gif" />

so we can always request any image from the browser directly. This is almost analogous to the WebResource.axd file but for MVC. What is interesting though is that we can encapsulate each one of these so that each area can have it’s own set of resources and they are easily distinguished because the area name is the first segment of the route. This makes me wonder if something like this ResourceController should be baked into portable areas itself.

I’m definitely interested in anyone has any opinions on it or have taken alternative approaches.

Posted On Monday, April 5, 2010 4:52 PM | Comments (12)
MVC Portable Areas – Deploying Static Files

This is the second post in a series related to build and deployment considerations as I’ve been exploring MVC Portable Areas:

As I’ve been digging more into portable areas, one of the things I’ve liked best is the deployment story which enables my *.aspx, *.ascx pages to be compiled into the assembly as embedded resources rather than having to maintain all those files separately. In traditional web forms, that was always the thing to prevented developers from utilizing *.ascx user controls across projects (see this post for using portable areas in web forms).  However, though the aspx pages are embedded, the supporting static files (e.g., images, css, javascript) are *not*. Most of the demos available online today tend to brush over this issue and focus solely on the aspx side of things. But to create truly robust portable areas, it’s important to have a good story for these supporting files as well.  I’ve been working with two different approaches so far (of course I’d really like to hear if other people are using alternatives).

Scenario

For the approaches below, the scenario really isn’t that important. It could be something as trivial as this partial view:

   1:  <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
   2:  <img src="<%: Url.Content("~/images/arrow.gif") %>" /> Hello World!

The point is that there needs to be careful consideration for *any* scenario that links to an external file such as an image, *.css, *.js, etc. In the example shown above, it uses the Url.Content() method to convert to a relative path. But this method won’t necessary work depending on how you deploy your portable area.

One approach to address this issue is to build your portable area project with MSDeploy/WebDeploy so that it is packaged properly before incorporating into the host application.

All of the *.cs files are removed and the project is ready for xcopy deployment – however, I do *not* need the “Views” folder since all of the mark up has been compiled into the assembly as embedded resources. Now in the host application we create a folder called “Modules” and deploy any portable areas as sub-folders under that:

At this point we can add a simple assembly reference to the Widget1.dll sitting in the Modules\Widget1\bin folder. I can now render the portable image in my view like any other portable area. However, the problem with that is that the view results in this:

 

It couldn’t find arrow.gif because it looked for /images/arrow.gif and it was *actually* located at /images/Modules/Widget1/images/arrow.gif. One solution is to make the physical location of the portable configurable from the perspective of the host like this:

   1:  <appSettings>
   2:    <add key="Widget1" value="Modules\Widget1"/>
   3:  </appSettings>

Using the <appSettings> section is a little cheesy but it could be better formalized into its own section. In fact, if were you willing to rely on conventions (e.g., “Modules\{areaName}”) then then config could be eliminated completely. With this config in place, we could create our own Html helper method called Url.AreaContent() that “wraps” the OOTB Url.Content() method while simply pre-pending the area location path:

   1:  public static string AreaContent(this UrlHelper urlHelper, string contentPath)
   2:  {
   3:      var areaName = (string)urlHelper.RequestContext.RouteData.DataTokens["area"];
   4:      var areaPath = (string)ConfigurationManager.AppSettings[areaName];
   5:   
   6:      return urlHelper.Content("~/" + areaPath + "/" + contentPath);

With these two items in place, we just change our Url.Content() call to Url.AreaContent() like this:

   1:  <img src="<%: Url.AreaContent("/images/arrow.gif") %>" /> Hello World!

and the arrow.gif now renders correctly:

 

 

Since we’re just using our own Url.AreaContent() rather than the built-in Url.Content(), this solution works for images, *.css, *.js, or any externally referenced files.  Additionally, any images referenced inside a css file will work provided it’s a relative reference and not an absolute reference.

An alternative to this approach is to build the static file into the assembly as embedded resources themselves. I’ll explore this in another post (linked at the top).

Posted On Monday, April 5, 2010 4:50 PM | Comments (0)
MVC Portable Areas – Web Application Projects

This is the first post in a series related to build and deployment considerations as I’ve been exploring MVC Portable Areas:

Portable Areas is a relatively new feature available in MvcContrib that builds upon the new feature called Areas that was introduced in MVC 2. In short, portable areas provide a way to distribute MVC binary components as simple .NET assemblies rather than an assembly along with all the physical files for the views. At the heart of portable areas is a custom view engine that delivers the *.aspx pages by pulling them from embedded resources rather than from the physical file system. A portable area can be something as small as a tiny snippet of html that eventually gets rendered on a page, to something as large as an entire MVC web application. You should read this 4-part series to get up to speed on what portable areas are.

Web Application Project

In most of the posts to date, portable areas are shown being created with a simple C# class library. This is cool and it serves as an effective way to illustrate the simplicity of portable areas. However, the problem with that is that the developer loses out on the normal developer experience with the various tooling/scaffolding options that we’ve come to expect in visual studio like the ability to add controllers, views, etc. easily:

I’ve had good results just using a normal web application project (rather than a class library) to develop portable areas and get the normal vs.net benefits. However, one gotcha that comes as a result is that it’s easy to forget to set the file to “Embedded Resource” every time you add a new aspx page. To mitigate this, simply add this MSBuild snippet shown below to your *.csproj file and all *.aspx, *ascx will automatically be set as embedded resources when your project compiles:

   1:  <Target Name="BeforeBuild">
   2:    <ItemGroup>
   3:      <EmbeddedResource Include="**\*.aspx;**\*.ascx" />
   4:    </ItemGroup>
   5:  </Target>

Also, you should remove the Global.asax from this web application as it is not the host.

Being able to have the normal tooling experience we’ve come to expect from Visual Studio makes creating portable areas quite simple. This even allows us to do things like creating a project template such as “MVC Portable Area Web Application” that would come pre-configured with routes set up in the PortableAreaRegistration and no Global.asax file.

Posted On Monday, April 5, 2010 4:49 PM | Comments (2)

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud