Orchard Alpha released

As promised, Project Orchard reached with Alpha milestone just now with the downloads available at http://orchard.codeplex.com

Among the features included in this release

  • The core module API is mostly baked
  • Data migrations for updating database schema on module activation and upgrade between versions of modules
  • Basic command-line scaffolding of modules and data migrations
  • Dynamic compilation of module source, for notepad-style development
    • Defining custom content types, parts, and fields
    • Define new content types (admin panel or code)
    • Add fields to content types (admin panel or code)
    • Associate content parts to types (admin panel or code)
    • Add fields or parts to existing content types (admin panel or code)
  • Packaging and installing modules (as zip files) – can include source, binaries, or both
  • Sharing modules via an ATOM feed (to be exposed on OrchardProject.net), and installing from the feed using a new “Gallery” module
  • Enabling, disabling, and upgrading module features and their dependencies (admin panel or command-line)
  • Search
  • Localization support for both the Orchard application and database-driven content items
  • Azure support
  • Multi-tenancy support
  • Interactive command-line support for many administrative tasks
  • Some basic reporting/logging functionality
  • Everything from previous Mix release: pages, blogs, comments, tags, users/roles, media, menu navigation, and basic theme support

A 7 part tutorial on project Orchard based on the latest change set

 

Over the weekend I created a tutorial to create a module inside project Orchard. I referred to the original walkthrough found inside project Orchard’s website. The original walkthrough was created for the March 2010 release of project orchard and I have modified the codes and steps so that it will work with the latest change set.

Below is the Table of Content

What is project Orchard and a Hello World sample

Hooking into Admin panel

Working with Data

Orchard Content Type

Attaching a part to content type

Composing view using different content parts

Finishing touch on the front end listing and shopping cart

For the complete project source code, download from my Windows Live SkyDrive here.

 

 

It is best if you check out the Mar 2010 build and the original tutorial as well for more info on Orchard.

 

Cheers.

Hello Orchard Final Part–Building the module front end

With the backend Product administration page done, now we can build the front end to list the products and a simple shopping cart

Following are copy-pastes from the walkthrough at orchardproject.net with my modifications

1. Let’s first update our HomeController query the ContentManager for the list of products, passing the model to the View:

using Orchard.Commerce.Models;using Orchard.ContentManagement;

[Themed]    public class HomeController : Controller    {

        private readonly IContentManager _contentManager;

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

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

2. Now let’s update the Home/Index.ascx view page to render the list of products. To make this easier, delete the existing Index.ascx page and right-click the Index() action to “Add > View” in Visual Studio. This time, we’ll add a strongly-typed view that takes the Product type, and choose “List” for the View content type:

image

3. In Index.ascx, add this at the top of the file:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Orchard.Commerce.Models.Product>>" %><div class="page-title"><%=Html.TitleForPage("Product list")%></div>    <table>

4. Type Ctrl-F5 to build and run the site. Click the “Shop” menu item on the front-end to reveal your list of products.

image

5. Let’s now create a “Details” view so we can verify we can see tags and add comments (recall the UI composition section above). In the HomeController, add this:

using Orchard.Mvc.Results;

public ActionResult Details(int id)        {            var product = _contentManager.Get<Product>(id);            if (product == null)                return new NotFoundResult();

            var viewModel = _contentManager.BuildDisplayModel<Product>(product,            "Details");

            return View(viewModel);        }

6. Let’s then create the “Details.ascx” view under Views > Home:

image

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Mvc.ViewModels.ContentItemViewModel<Orchard.Commerce.Models.Product>>" %><div class="page-title">    <%=Html.TitleForPage("Product details")%></div><%= Html.DisplayForItem(Model) %><div>    <%=Html.ActionLink("Back to List", "Index") %></div>



Note the “Details” string. It a convention for the type of display (other include summary, list, etc.). Let’s update the ProductDriver to handle the composition:

protected override DriverResult Display(Product product, string displayType){   return ContentPartTemplate(product, "Product.Fields").Location("primary", "1");}

This is very similar to the “Editor” method. Product.Fields is the template used to render the fields. Let’s create Product.Fields.ascx in the “DisplayTemplates” folder.

image

7. Add Product.Fields.ascx under the “Views > DisplayTemplates” folder:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Commerce.Models.Product>" %><fieldset>    <legend>        <%= Html.Encode(Model.Sku)%></legend>    <p>    </p>    <h3>        <%= Html.Encode(Model.Description)%></h3>    <p>        Price:        <%= Html.Encode(String.Format("{0:F}", Model.Price))%></p></fieldset>

8. And finally update the Index view to link the “sku” field to the product details page:

Replace this:

<td>   <%: item.Sku %></td>

With this:

<td>  <%= Html.ActionLink(item.Sku ?? "sku", "Details", new { id = item.Id })%></td>

9. Type Ctrl-F5 to build and run the site. Now, when clicking on a “sku” in the index view, we get to the product details page (with comments and tags).

clip_image006

Implementing a basic shopping cart

To give another take on UI composition, we are going to implement a simple shopping cart, and we will display it in the “sidebar” zone using the MVC 2 RenderAction method.

1. Let’s first define the data model we are going to use to store the shopping cart data (we are using simple records, not content items for this simple example). Add a ShoppingCartRecord class in the Models folder:

using Orchard.Data.Conventions;using System.Collections.Generic;

namespace Orchard.Commerce.Models{    public class ShoppingCartRecord    {        public ShoppingCartRecord()        {            Entries = new List<ShoppingCartEntryRecord>();        }

        public virtual int Id { get; set; }

        public virtual int UserId { get; set; }

        [CascadeAllDeleteOrphan]        public virtual IList<ShoppingCartEntryRecord> Entries { get; set; }

    }

    public class ShoppingCartEntryRecord    {        public virtual int Id { get; set; }        public virtual ProductRecord Product { get; set; }        public virtual int Quantity { get; set; }    }}

2. Next, let’s update the HomeController class to use more services we are going to need:

using Orchard.Data;using Orchard.UI.Notify;using Orchard.Security;using Orchard.Localization;

[Themed]    public class HomeController : Controller    {        private readonly IContentManager _contentManager;        private readonly IRepository<ShoppingCartRecord> _repository;        private readonly INotifier _notifier;        public virtual IUser CurrentUser { get; set; }        public Localizer T { get; set; }

        public HomeController(IContentManager contentManager, IRepository<ShoppingCartRecord> repository, INotifier notifier)        {            _contentManager = contentManager;            _repository = repository;            _notifier = notifier;        }

Some things to note about this code:

§ “IRespository<ShoppingCartRecord> gives access to the database

§ “CurrentUser” gives access to the logged-in user

§ “T” gives access to the localization service

§ “INotifier” gives access to the notification area of the application

Now, let’s implement the “AddToCart” action…

We’ll create the cart for the user if needed, add 1 product to the list, and display a “Product added” message to the notification area of the site.

Note that in this simple example, we don’t handle anonymous users very well – there is a single shopping cart for the whole site. We also don’t support “merging” the shopping cart content when an anonymous user decides to log in after creating his shopping cart. These are more advanced applications of a shopping cart implementation that we will skip for now.

3. In HomeController, add the AddToCart action method:

public ActionResult AddToCart(int id)        {            // Retrieve shopping cart for current user

            ShoppingCartRecord cart;

            if (CurrentUser == null)            {                cart = _repository.Fetch(c => c.UserId == 0).SingleOrDefault();            }

            else            {                cart = _repository.Fetch(c => c.UserId ==                CurrentUser.Id).SingleOrDefault();            }

            // Create cart if none found

            if (cart == null)            {                cart = new ShoppingCartRecord();                _repository.Create(cart);                cart.UserId = (CurrentUser == null ? 0 : CurrentUser.Id);            }

            // Add product to cart entry            var product = _contentManager.Get<Product>(id).Record;            var entry = cart.Entries.Where(e => e.Product == product).SingleOrDefault();

            if (entry == null)            {                entry = new ShoppingCartEntryRecord                {                    Product = product,                    Quantity = 0                };                cart.Entries.Add(entry);            }

            entry.Quantity++;            _notifier.Add(NotifyType.Information, T("Added {1} to shopping cart",            entry.Quantity, product.Sku));            // Back to product list            return RedirectToAction("Index");        }

4. Finally, update the “Index.ascx” view (under Views > Home) to include a “Add to cart” column:

a. Add a “Buy” column…

<th>Buy</th>

b. And add an “Add To Cart” link…

<td><%= Html.ActionLink("Add to cart", "AddToCart", new { id= item.Id })%></td>

5. Type Ctrl-F5 to build and run the site.

NOTE: Since we created a new record class, there is a need to update the database. Go into the admin page and trigger Database Update under Site Configuration –> Dev Tools menu.

image

We now have a working “Add to cart” link next to each product in the list

We also get notification that a product has been added to the cart

image

Well that’s it! There is another section about rendering the shopping cart content on the sidebar which I will update with another post later due to some issue with the Controller rendering.

If you want to revisit back the previous past, you can go to the TOC page I created here.

Hello Orchard 6–Composing view using different content parts

This is the 6th part of my Orchard introduction. As of today I updated my source code to reflect today’s build and also because I need it for certain bug fixes. For more info about my previous posting, check out

Part 1

Part 2

Part 3

Part 4

Part 5

In part 5, we talk about composing content type with parts to import commonly used properties such as Owner and Published Date. Now we want to reused UI in our view composition. According to the original walkthrough document on how UI composition works:-

image

The idea behind UI composition is that the view of a product (in our example, the editor view in the admin panel) should be assembled from the constituent views from all the attached parts. Each part defines its own view templates that expect a specific view model type to be passed to it. The obtain these view models, the Controller action calls the BuildXxxModel methods on ContentManager. The ContentManager delegates to ContentDriver types (one for each part) to retrieve the appropriate view models for each part. The ContentManager then aggregates those into a single view model that is handed back to the Controller. What is actually passed back from the ContentDriver is a ContentPartTemplate object – the view, the model for that view, and the zone and location/position within the zone to render the part (note that the zone/position information will eventually be driven from metadata/templates instead of code).

Let’s get started

1. First of all, add reference to Orchard.Tags and Orchard.Comments

image

2. Then let’s enable the “Comments” and “Tags” part for our ProductHandler:

using Orchard.Tags.Models;using Orchard.Comments.Models;
public ProductHandler(IRepository<ProductRecord> repository)        {            Filters.Add(new ActivatingFilter<Product>(ProductHandler.ContentType.Name));            Filters.Add(new ActivatingFilter<Orchard.Core.Common.Models.CommonPart>(ProductHandler.ContentType.Name));            Filters.Add(new ActivatingFilter<CommentsPart>(ProductHandler.ContentType.Name));            Filters.Add(new ActivatingFilter<TagsPart>(ProductHandler.ContentType.Name));            Filters.Add(StorageFilter.For(repository));        }

 

For now, the association of content parts is done in code. However, eventually this will move to a metadata system that enables association of parts to items on-the-fly, using the Orchard admin panel instead of writing code to do this. This would enable our Product type to be completely independent, with no compile-time dependency on specific parts.

2. Now let’s update the “Create” method of our AdminController to create the ContentItemViewModel for the product:

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

3. We also need to update the “Create.ascx” view to use the “ContentItemViewModel” and dispatch the form composition to the Orchard HtmlHelper.

<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.Mvc.ViewModels.ContentItemViewModel<Orchard.Commerce.Models.Product>>" %><h1>    <%=Html.TitleForPage(T("Create Product").ToString()) %></h1><% using (Html.BeginFormAntiForgeryPost())   { %><%=Html.ValidationSummary() %><%=Html.EditorForModel(Model)%><fieldset>    <input type="submit" value="Create" /></fieldset><% } %><div>    <p>        <%=Html.ActionLink("Back to List", "Index") %></p></div>

4. Next, we will define a driver and template for the Product type that participates in this UI composition process.

Create a new Drivers folder and then add a new ProductDriver class (that derives from ContentItemDriver). This class is responsible for returning the appropriate view model, template, and location for the various views of a Product (in our example, the editor view):

using System;using System.Collections.Generic;using System.Linq;using System.Web;using Orchard.ContentManagement.Drivers;using Orchard.Commerce.Models;using Orchard.ContentManagement;

namespace Orchard.Commerce.Drivers{    public class ProductDriver : ContentItemDriver<Product>    {

        protected override bool UseDefaultTemplate { get { return true; } }

        protected override DriverResult Display(Product product, string displayType)        {            return ContentPartTemplate(product, "Product.Fields").Location("primary", "1");        }

        //GET        protected override DriverResult Editor(Product product)        {            return ContentPartTemplate(product, "Product.Fields").Location("primary", "3");        }

        //POST        protected override DriverResult Editor(Product part, IUpdateModel updater)        {            updater.TryUpdateModel(part, Prefix, null, null);            return Editor(part);        }    }}

This code says that the template to use for displaying the product is “Product.Fields”. Notice how we pass the “products” instance as the view model for that template. We also mention it’s going to be displayed in the “primary” zone, at the top of the zone (position=”1”).

5. Let’s now create “Product.Fields.ascx” under the Commerce > Views > EditorTemplates folder. This is essentially the same as our former “Create” view, which displayed the editors for properties for the Product type.

clip_image002

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Commerce.Models.Product>" %><fieldset>    <legend>Fields</legend>    <%=Html.HiddenFor(model=>model.Id) %>    <div class="editor-label">        <%= Html.LabelFor(model => model.Sku) %>    </div>    <div class="editor-field">        <%= Html.TextBoxFor(model => model.Sku)%>        <%= Html.ValidationMessageFor(model => model.Sku)%>    </div>    <div class="editor-label">        <%= Html.LabelFor(model => model.Description)%>    </div>    <div class="editor-field">        <%= Html.TextBoxFor(model => model.Description)%>        <%= Html.ValidationMessageFor(model => model.Description)%>    </div>    <div class="editor-label">        <%= Html.LabelFor(model => model.Price) %>    </div>    <div class="editor-field">        <%= Html.TextBoxFor(model => model.Price) %>        <%= Html.ValidationMessageFor(model => model.Price) %>    </div></fieldset>

6. Type Ctrl-F5 to build and run the site.

Now when we click on “Create new Product” link, we see a form which contains the fields of the products, but also tags, comments and owner fields.

clip_image004

7. The last thing we need to do is re-implement the “Create” “Post” action in AdminController.cs:

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

            if (!ModelState.IsValid)            {                return View(model);            }

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

Note the “this” parameter, passed to UpdateEditorModel. To enable validation of the model, we need an interface between the MVC controller and the content drivers. We are going to implement this interface in the “AdminController” itself, by simply delegating the default MVC base class.

using Orchard.Localization;

namespace Orchard.Commerce.Controllers{    [Admin]    [Themed]    public class AdminController : Controller, IUpdateModel    {        bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)        {            return TryUpdateModel(model, prefix, includeProperties, excludeProperties);        }

        void IUpdateModel.AddModelError(string key, LocalizedString errorMessage)        {            ModelState.AddModelError(key, errorMessage.ToString());        }

The update/validation of the model is going through the ContentManager, to all the content drivers, and then back to our AdminController. The last thing we need to do is implement the model update/validation for the “Product” part. We do that in the ProductDriver:

protected override DriverResult Editor(Product part, IUpdateModel updater){    updater.TryUpdateModel(part, Prefix, null, null);    return Editor(part);}

8. Type Ctrl-F5 to build and run the site.

We can now create a new product, including model validation!

clip_image006

Hello Orchard Part 5–Attaching a part to content type

One of the coolest feature in Orchard is the ability to compose different content part together to make a complete content type. This promotes composite applications e.g. the comment content part with its logic and UI can be reused in different part of the application.

Part 5 of this tutorial will shows you how to attach a Common part to the Product content type where this Common part contains properties like Published Data and Owner.

1. Add a reference to Orchard.Core

image

2. Go to the ProductRecord.cs file and update it to include these namespaces:

using System.ComponentModel.DataAnnotations;using Orchard.Core.Common.Models;

Now, let’s illustrate the power of the data composition story… We are going to add a “Common” part to the “Product” content type. The “Common” part is an Orchard extension which automatically keeps track of Owner, Container, CreationDate, etc. for content items.

Here is the definition of the ICommonAspect interface (no need to type this – it’s already defined by Orchard in the Orchard.Core.Common.Models namespace). Once we add this part to our Product type, the CommonAspect data will be saved (as a “Content Part”) with each instance of “Product”.

public interface ICommonAspect : IContent {

IUser Owner { get; set; }

IContent Container { get; set; }

DateTime? CreatedUtc { get; set; }

DateTime? PublishedUtc { get; set; }

DateTime? ModifiedUtc { get; set; }

DateTime? VersionCreatedUtc { get; set; }

DateTime? VersionPublishedUtc { get; set; }

DateTime? VersionModifiedUtc { get; set; }

}

3. Change the ProductHandler class to add a “CommonPart” (was called CommonAspect) part to the “Product” content type:

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

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(new ActivatingFilter<Orchard.Core.Common.Models.CommonPart>(ProductHandler.ContentType.Name));            Filters.Add(StorageFilter.For(repository));        }

    }}

That’s it! Now, whenever a “Product” is going to be manipulated by the application, a “CommonAspect” will go with it. Under the hood, the content manager will activate the “CommonAspect” part when “Products” are activated. The “CommonAspect” implementation will keep track of creation date, modification date and publication dates automatically for us.

3. Let’s illustrate that by displaying the “Created”, “Published” date for each product in the Index.ascx view. Add these 2 lines at the beginning of the file:

<%@ Import Namespace="Orchard.Core.Common.Models"%><%@ Import Namespace="Orchard.ContentManagement"%>

a. Add “Created” and “Published” columns:

<th>   Created</th><th>   Published</th>

b. Access to the “Created” and “Published” dates as follows:

<td>   <%= Html.Encode(String.Format("{0}", item.As<CommonPart>().CreatedUtc))%></td><td>   <%= Html.Encode(String.Format("{0}", item.As<CommonPart>().PublishedUtc)) %></td>

4. Type Ctrl-F5 to build and run in Visual Studio.

Note: If you don’t empty the database at this point, the product created until now will have no “Common” data value. Orchard doesn’t have a story for database upgrade for now. To reset the database (destroy all data for the site), delete the App_Data/Sites folder in your project. You may need to “Show All Files” in Visual Studio’s Solution Explorer before you can see this folder. After deleting the database, you’ll need to walk through the Orchard setup screen again.

Previously the tutorial would ask you to empty the database and recreate the website again. But with the latest data migration facility you need not do so. Just go to Site Configuration –> Dev Tools and trigger the Update Database command.

Notice how the table now shows created and published dates (they have been setup automatically by the content manager, stored in a “Common” aspect table).

clip_image002

Let’s look at what happens when a product instance is created. Set a debug breakpoint in “AdminController.Create” action, on the line of code that invokes _contentManager.Create(product). Type F5 in Visual Studio to begin debugging, and then create a new product using the admin panel in order to hit the breakpoint:

clip_image004

The “Product” content item has 2 parts: the Product part and the “CommonPart” part. If we look at the data maintained by the “CommonPart”, we see that everything is taken care of internally. Here is the “Product” instance after the call to “Create” of content manager:

clip_image006