Orchard Content Type

This is part 4 of my walkthrough document for project Orchard based on the document released in March 2010. Check out here for previous posts – Part 1 Hello World, Part 2 Hocking up admin page, Part 3 Working with data

What is Content Type

Orchard Content Type maybe somewhat similar to SharePoint content type conceptually but architecturally its apple and orange. According to the walkthrough document, an Orchard Content Type is:-

Rather than work with database records directly, let’s start using some of the higher-level abstractions that Orchard CMS offers. Fundamental to the Orchard CMS is the idea of multiple content types – the built-in pages and posts types are examples….

Most importantly:

…we will take advantage of it in the next section, when we explore composition of additional “parts” on the content types (the way that comments and tags are parts applied to pages and posts). Assembling the data and UI of the application from multiple types and parts allows the system to remain loosely coupled and allows installed modules to extend each others types in interesting ways.

Objectives:

1. Create a Product content type

2. Add a ProductHandler for Product type

a. Associate repository and ProductRecord type

b. Associate Product type to content type name

Follow These Steps:

1. Go to the ProductRecord.cs file and update it to inherites ContentPartRecord <Note that I removed the ID property because the base class already has one>:

using System.ComponentModel.DataAnnotations;using System.Web.Mvc;using Orchard.Data;using Orchard.ContentManagement;using Orchard.ContentManagement.Records;using Orchard.ContentManagement.Handlers;

namespace Orchard.Commerce.Models{

    public class ProductRecord: ContentPartRecord      {

        [Required]        public virtual string Sku { get; set; }

        [Required]        public virtual string Description { get; set; }

        [Required]        public virtual decimal Price { get; set; }

    }

}

2. Add a Product.cs file in the Model folder.

using System;using System.Collections.Generic;using System.Linq;using System.Web;using Orchard.ContentManagement;using System.ComponentModel.DataAnnotations;

namespace Orchard.Commerce.Models{    public class Product : ContentPart<ProductRecord>    {        public int Id        {            get { return Record.Id; }            set { Record.Id = value; }        }

        [Required]        public string Sku        {            get { return Record.Sku; }            set { Record.Sku = value; }        }

        [Required]        public string Description        {            get { return Record.Description; }            set { Record.Description = value; }        }

        [Required]        public decimal Price        {            get { return Record.Price; }            set { Record.Price = value; }        }    } }

Then create a Handlers folder and add a  ProductHandler class

image

using Orchard.ContentManagement.Handlers;using Orchard.ContentManagement;using Orchard.Commerce.Models;using Orchard.Data;

namespace Orchard.Commerce.Handlers{    public class ProductHandler : ContentHandler    {        public readonly static ContentType ContentType = new ContentType        {            Name = "product",            DisplayName = "Product"        };

        public ProductHandler(IRepository<ProductRecord> repository)        {            Filters.Add(new ActivatingFilter<Product>(ProductHandler.ContentType.Name));            Filters.Add(StorageFilter.For(repository));        }

    }}

The Product type is an Orchard content type, using ProductRecord for storage. For convenience, we’ve wrapped the properties of the record on the Product type, but this is optional, as the Product type also exposes these properties directly from its Record property, for example Product.Record.SKU. The ProductHandler is responsible for registering the content type with the system, ensuring the the Product type is activated whenever a new “Product” content item is created, and associating the IRepository<ProductRecord> for storage.

We also need to update our Commerce admin pages to use the new content type instead of the record. To do this, we will go through the IContentManager interface to query for products instead of talking to the repository directly. The ContentManager is responsible for resolving the requested content type to a handler (in this case, ProductHandler) and instantiating the type for us.

3. In AdminController.cs…

a. Change all references from “ProductRecord” to “Product”…

b. Change all references to “IRepository<ProductRecord>” to “IContentManager”…

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;

using Orchard.Themes;using Orchard.UI.Admin;using Orchard.ContentManagement;using Orchard.Data;using Orchard.Commerce.Models;

namespace Orchard.Commerce.Controllers{    [Admin]    [Themed]    public class AdminController : Controller    {

        private readonly IContentManager _contentManager;

        public AdminController(IContentManager contentManager)        {            _contentManager = contentManager;        }

        public ActionResult Index()        {            return View(_contentManager.Query<Product>().List());        }

        public ActionResult Create()        {            return View(_contentManager.New<Product>("product"));        }

        [HttpPost]        public ActionResult Create(FormCollection input)        {            var product = _contentManager.New<Product>("product");

            if (!TryUpdateModel(product))            {                return View(product);            }

            _contentManager.Create(product);

            return RedirectToAction("Index");        }    }}

4. Change the “Create.ascx” view to use “Product” instead of “ProductRecord” in the first line (the view model):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Commerce.Models.Product>" %>

5. Change the “Index.ascx” view to use “Product” instead of “ProductRecord” in the first line (the view model):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Orchard.Commerce.Models.Product>>" %>

6. Type Ctrl-F5 to build and run in Visual Studio. And when you try to add a new product you will be hit by exception. This is because the database table schema has not been updated.

image

7. Orchard introduces a concept of data migration so that whenever an ContentType is created user will be prompted to update the database via the administration page.

Now create a DataMigrations folder and add a ProductDataMigration class

using Orchard.Data.Migration;

namespace Orchard.Commerce.DataMigrations{    public class ProductDataMigration : DataMigrationImpl    {        public int Create()        {            SchemaBuilder.CreateTable("ProductRecord", table => table                .ContentPartRecord()                .Column<string>("Sku")                .Column<string>("Description")                .Column<string>("Price")                );

            return 0010;        }    }

}

8. Hit Ctrl-F5 and go back to the admin page, you will notice a message on top of the dashboard.

image

9. Click on Site Configuration –> Features. Notice that Orchard.Commerce is in red. Click on the Update command

image

10. Surprisingly I am hit with a transaction error.

image

11. So I found a workaround by renaming the table first from Orchard_Commerce_Product to Orchard_Commerce_Product_bck

12. And I went back to click on Upgrade again. The table migrated successful.

image

13. And I can start data entry for Products.

image[8]

Hello Orchard, Part 3–Working with Data

This is the 3rd instalment of my attempts to modify and complete the original walkthrough of Orchard using the latest change set of the Orchard Framework. Check out part 1 and part 2 here.

Working with Data


Let’s now look at how to work with data. The objective for this section is to create a ProductRecord type that is persisted to the database. We’ll then update the Commerce area admin pages to be able to list and create new products.

Objectives:

1. Create a ProductRecord type that is persisted to the database

2. Add admin pages for creating and listing products

Follow These Steps:

1. Create “ProductRecord.cs” in “Models” folder

using System.ComponentModel.DataAnnotations;
using System.Web.Mvc; 

namespace Orchard.Commerce.Models
{
     public class ProductRecord
    {
         public virtual int Id { get; set; }

         [Required]
        public virtual string Sku { get; set; }

         [Required]
        public virtual string Description { get; set; }

         [Required]
        public virtual decimal Price { get; set; }
     }
 }

 

Orchard generally favors convention over configuration, and in the example above, there are a few conventions at work. First, the *Record suffix on the class, coupled with the fact that the class lives under the *.Models namespace, tells Orchard this is a persistent class that should be backed by a database table. Second, the property named “Id” is conventionally used as the primary key for the record.

2. Add the “IRepository<ProductRecord>” to the AdminController. This is another example of using dependency injection to receive access to services (in this case, the database).

using Orchard.Data;

using Orchard.Commerce.Models;
    [Admin]
    [Themed]
    public class AdminController : Controller
    {
        private readonly IRepository<ProductRecord> _repository;

         public AdminController(IRepository<ProductRecord> repository)
        {
            _repository = repository;
        }

         //
        // GET: /Admin/
         public ActionResult Index()
        {
            return View();
        }
     }

3. Create the 2 “Admin/Create” actions (typical MVC pattern for entity creation)

public ActionResult Create()
        {
            return View(new ProductRecord());
        }

         [HttpPost]
        public ActionResult Create(ProductRecord product)
        {
            if (!ModelState.IsValid)
            {
                return View(product);
            }

            _repository.Create(product);
            return RedirectToAction("Index");
        }

Now, we are going to add the corresponding “Create” view…

4. Build the project first, so the ProductRecord type is available in the Add View dialog.

5. Right-click the Create action and choose “Add View…”

6. Select the “Create a partial view (.ascx)” option

7. Select the “Create a strongly-typed view” option and type “Orchard.Web.Areas.Commerce.Models.ProductRecord” for the View data class. Select “Create” as the View content.

image

8. In the view markup/code, change the ID field to be hidden:

<div class="editor-label">
        <%= Html.HiddenFor(model => model.Id) %>
    </div>
    <div class="editor-field">
        <%= Html.HiddenFor(model => model.Id) %>
        <%= Html.ValidationMessageFor(model => model.Id) %>
    </div>

9. Change Html.BeginForm to Html.BeginFormAntiForgeryPost

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Commerce.Models.ProductRecord>" %>
<% using (Html.BeginFormAntiForgeryPost())   {%>
<%: Html.ValidationSummary(true) %>

10. In AdminController.cs, update the Index() method to pass the list of Products from the repository:

public ActionResult Index()
{
     return View(_repository.Table);
}

11. Delete “Views\Admin\index.ascx” file (we are going to re-create it as a “List” view).

12. In AdminController.cs, right-click the “Index()” action and chose “Add View…” to recreate the “Index” view.

a. Choose the partial view option

b. Select the strongly-typed view option, typing “Orchard.Web.Areas.Commerce.Models.ProductRecord” for the View data class.

c. Select “List” as the View content.

image

13. Open Index.ascx

d. Update the table class: <table class="items"> (so it looks nice)

14. Type Ctrl-F5 to build and run the site in Visual Studio.

15. Go to the “Manage Products” admin page

NOTE: So far we have been following the steps in the original document, which should be OK. However with Devtools module added in recent change set, the database table for Product records does not get built automatically. In fact you will get an error message if you click on Manage Products.

image

16. Login to admin module. Click on Site Configuration –> Features and enable Orchard.DevTools module

image

17. After the refresh you shall see Developer Tools under the Site Configuration menu, click on it. Then in the Dev Tools page, click on Database Migration

18. Then you are presented with only 1 menu item which is Update Database option. Click on it to start the table creation.

image

19. Once you see the “Database Updated successfully” option, go back to the Admin page.

Then click on Manage Products under the Commerce menu. And Boo Yah! You will see the manage products page rendered nicely.

image

20. You can create a product (with validation)

21. The “Index” view shows the list of product from the db:

clip_image004

22. Enter a few products to use as sample data…

Hello Orchard Part 2

Find out more about Orchard and how to create a simple Hello World module using latest change set from Codeplex from my first post here.

In my 1st port, I build a very simple module that displays “Hello World” on the front-end using the applied Orchard theme. We’ll also wire up the navigation menu to our module’s routes.

Hooking into the Admin Panel

The next thing we want to do is wire up our custom module to the admin panel, so we have a way to perform administration tasks on the back-end.

Objectives:

1. Add an admin panel page for the custom area

2. Add a menu item to the admin panel for the custom area

Follow These Steps:

1. Open up Orchard.Commerce project and right-click the “Controllers” folder and choose “Add > Controller…”

2. Type “AdminController” for the controller name and click [OK].

3. Add the following namespaces and attributes on the controller class:

using Orchard.Themes;
using Orchard.UI.Admin; 

namespace Orchard.Commerce.Controllers{
    [Admin]
    [Themed]
    public class AdminController : Controller

4. Right-click on the “Index()” method name and choose “Add View…”

5. Selected the “Create a partial view” option and click [Add]

6. Add the following HTML to the View page: <p>Commerce Area Admin</p>

7. Type Ctrl-F5 to build and run in VS.

8. Navigate to this URL in the browser: http://localhost:<port>/Admin/Commerce

clip_image002

ð Note that you will need to log in as admin before you can view this page. By convention, controllers named “Admin*” are protected from access by anonymous site visitors.

Hooking into the admin menu…

1. Add a reference to Orchard.Pages project

image

1. Add an AdminMenu.cs file to the root of the “OrchardCommerce” project. For convenience, you may copy this from another module folder (and change the namespace and area name appropriately).

using Orchard.Pages.Services;
using Orchard.UI.Navigation;
using Orchard.Localization; 

namespace Orchard.Web.Areas.Commerce{
    public class AdminMenu : INavigationProvider
    {
         public Localizer T { get; set; }
         private readonly IPageService _pageService;
         public AdminMenu(IPageService pageService)
        {
            _pageService = pageService;
        }

         public string MenuName { get { return "admin"; } }

         public void GetNavigation(NavigationBuilder builder)
        {
            builder.Add(T("Commerce"), "20",
                menu => menu
                        .Add(T("Manage Products"), "1.0",
                            item => item
                                .Action("Index", "Admin", new { area = "Orchard.Commerce" })));
        }
    }
 }

2. Type Ctrl-F5 to build and run in VS.

3. Navigate to this URL in the browser: http://localhost:<port>/Admin

clip_image004

Orchard uses dependency injection to provide services to the application. In the example above, the INavigationProvider interface derives from IDependency. Simply by including a constructor that accepts INavigationProvider as a parameter, an implementation of this interface will be provided by the application framework to this class when it is constructed. We use this interface to define a main menu item for our Commerce > Manage Products screen. In the next section, we will see dependency injection again when we use the IRepository interface to access the database.

Hello Orchard – Creating a Orchard module using latest source code

What is Orchard?

Project Orchard is the latest open source CMS project by folks in Microsoft based on ASP.NET MVC platform. It has some relationship with project Oxite which is another CMS used by the MIX event website. You can read more about Orchard from the official website here. And you can also check out the official download as well as the latest build from Codeplex. It is light weight where you can do copy-paste deployment onto shared webhosting, doesn’t require SQL Server to run yet at the same time you can add functionalities as a Orchard Module.

 

Official Download

The official download dates back to March 2010 at the time of MIX 2010. The feature sets are primitive and less composite than what’s in the roadmap. It has been 4 months since the release and the team will release a official Alpha release by end of July 2010.

Having said that, the March release comes with a walkthrough which you can follow to build a customized module for Orchard. You can check out the walkthrough here.

March 2010 release

Walkthrough Document

Video walkthrough

Latest Build

In the true spirit of open source, the Orchard team chose Mercurial instead of Team Foundation Server to be the source code host of the project. Codeplex supports both source control systems maybe because of some non-.NET or non-Windows hosted there.

In the March 2010 release Modules are actually ASP.NET MVC 2 Area where else in the latest build, they are individual ASP.NET MVC Web App. The new module design makes Orchard more suitable for composition and multi tenancy, in a way like SharePoint features.

Because of this architecture change, the walkthrough mentioned above does not work with the latest build. So in this and subsequent blog post, I will share more about creating a customized module for latest build of Orchard

Downloading latest build

To get the latest build, you either download from the source tree using TortoiseHG which is a Windows based Mercurial client (instructions here) or you can download the latest changeset from the source code page at Codeplex.

image

Whenever you download source code packages as a zip file from the Web, remember to ‘unblock’ it from the file property page else Visual Studio will give you a not trusted source warning message which can be annoying at times.

image

 

Understand the Orchard architecture

I won’t dwell too deep into this as Bertrand Le Roy wrote an extensive document titled How Orchard Works here. Architects should read this document first to understand how things work.

Accessing the latest change set

image

I downloaded the latest change set as of today and from Windows Explorer you will notice there are 2 Visual Studio solution file. Note also that it has been updated to support Visual Studio 2010 solution. You also note that there is a Windows Azure solution file here but let’s check this out in my future post.

The whole Orchard application framework is inside the Orchard folder while Orchard.Web is the ASP.NET project which can be hosted inside IIS. Let’s open up Orcahrd.sln using VS2010.

image[15]

All in all there are more than 30 projects in project Orchard with Orchard.Core, Orchard.Framework and Orchard.Web at the center of this CMS. On top of that there are some core modules which Orchard needs such as the User, Pages, and Setup modules.

Build and setup a sample site

Orchard supports both SQL Server and SQLite database but for sake of debugging and troubleshooting I will choose SQL Server. Let’s create an empty database inside SQL Server called OrchardTutorialDB in the local SQL Express instance.

image

Back to Visual Studio 2010 with the project opened, press Ctrl-F5. IE will be opened and you will see the Orchard setup page

image

At this page, you need to give a name for the website and assign a website admin account. Then click on ‘Use an existing SQL Server database’ to setup SQL Server. Key in the connection string in the following format

Data Source=.\sqlexpress;Initial Catalog=OrchardTutorialDB ;User Id=myUsername;Password=myPassword;

Note: Skip and leave the Database Table Prefix empty at the moment because of a known bug with using the prefix

image

Click ‘Finish Setup’ and a few minutes later you will see the website up and running!

image

I will normally change my theme to a more presentable one.

image

Hello Orchard – Adding a new Module

Note: from this point onwards I will reuse some of the text in the Orchard Walkthrough document.

NOTE: Step 2 to 4 and 15 to 17 are very additional steps to make the walkthrough works in the new change set.

The objective here is to build a very simply Hello World style webpage themed by Orchard with a menu item on top.

Objectives:

1. A simple custom area that renders “Hello World” on the app’s front-end

2. Views in the custom area that take advantage of the currently applied Orchard theme

3. A menu item on the front-end for navigating to the custom area’s view

Follow These Steps:

1. Right-click the Modules node in VS Solution Explorer, and choose “Add > New Project…”

image

2. In the project creation screen, choose “ASP.NET MVC 2.0 Empty Web Application”

<Very Important> On the path text box, it is default to <Orchard location>\src folder. Change it to <Orchard location>\src\Orchard.Web\Modules\ folder (Screenshot below). Else Orchard will not recognize the new module.

Finally Type “Orchard.Commerce” for the area name and click [OK].

image

3. For Orchard to discover this module, you need to add a module.txt file at the project root folder and insert the following content.

name: Commerce
antiforgery: enabled
author: Patrick Yong
website: http://patrickyong.net
version: 0.1
orchardversion: 0.1.2010.0312
description: Commerce
features:    Orchard.Commerce:
Description: Commerce.
Category: Commerce

4. Modify the web.config file accordingly. Basically I added <httphandlers> and <handlers> in <System.WebServer> for MVC routing.

<?xml version="1.0"?>
<!--  For more information on how to configure your ASP.NET application, please visit  http://go.microsoft.com/fwlink/?LinkId=152368  -->
<configuration>
<system.web>
   <!--             Set compilation debug="true" to insert debugging             symbols into the compiled page. Because this             affects performance, set this value to true only             during development.    -->
   <compilation debug="true" targetFramework="4.0">
   <assemblies>
      <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
   </assemblies>
</compilation>
<customErrors>
   <error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>    -->    <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID">
<namespaces>
   <add namespace="System.Web.Mvc"/>
   <add namespace="System.Web.Mvc.Ajax"/>
   <add namespace="System.Web.Mvc.Html"/>
   <add namespace="System.Web.Routing"/>
   <add namespace="System.Linq"/>
   <add namespace="System.Collections.Generic"/>
   <add namespace="Orchard.Mvc.Html"/>
   </namespaces>
</pages>
   <httpHandlers>
      <add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
   </httpHandlers>
</system.web>
<system.web.extensions/>
<!--         The system.webServer section is required for running ASP.NET AJAX under Internet        Information Services 7.0.  It is not necessary for previous version of IIS.  -->
<system.webServer>
   <validation validateIntegratedModeConfiguration="false"/>
   <modules runAllManagedModulesForAllRequests="true"></modules>
   <handlers>
      <remove name="MvcHttpHandler"/>
      <remove name="UrlRoutingHandler"/>
      <add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
   </handlers>
</system.webServer>
<runtime>
   <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
         <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
         <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
   </assemblyBinding>
</runtime>
</configuration>

5. Right-click the newly created “Ocrhard.Commerce > Controllers” folder, and choose “Add > Controller…”

6. Name the Controller “HomeController”

7. Right-click on the “Index()” method name and choose “Add View…”

image

8. Selected the “Create a partial view” option and click [Add]

image

9. Add the following HTML to the View page: <p>Hello World</p>

10. Add Orchard.Framework and Orchard.Themes to the project reference

image

11. Add the following namespace imports to the HelloController.cs file:

using Orchard.Themes;
using Orchard.UI.Navigation;

12. Add an [Themed] attribute to the HelloController class:

[Themed]
public class HomeController : Controller

13. Add another class to create a new Menu item, you can create a new .cs file in the project root folder or add it inside the controller class

public class MainMenu : INavigationProvider
 {
        public Localizer T { get; set; }
         public String MenuName
        {
            get { return "main"; }
        }

         public void GetNavigation(NavigationBuilder builder)
        {
            builder.Add(menu => menu.Add(T("Shop"), "4", item => item.Action("Index", "Home", new { area = "Orchard.Commerce" })));
        }
}

14. Type Ctrl-F5 to build and run in VS.

15. Login as admin (using the admin account you created during setup) and navigate to the admin page

image

16. Scroll down to look for Site Configuration menu and click on Features

image

17. Voila! If you had done everything right, you will see Orchard.Commerce as a module under the Commerce category. Now click on the ‘Enable’ button.

image

18. After a moment, message(s) will appear and to try out the feature, click on Your Site on top.

image

19. Now you notice an additional item on the top menu

image

 

10. Finally click on ‘Shop’ to Navigate to this URL in the browser: http://localhost:<port>/Commerce

image