A closer look at Content Types, Drivers, Shapes and Placement

We will see how a request comes in, how a content item is rendered, where drivers and placement fit into the picture, and how a resulting shape is turned into HTML to be sent down the wire.

The target audience of this article are module and theme developers that have some experience with content, drivers, shapes and placement, but are looking to get a firmer grasp on how this all works together.

Buckle up, as we shall start with Shapes.

What is a Shape?

So what is a shape, really? When we typically talk about the shape of something, we talk about as it being a property or a certain quality of an object. For example, the shape of the planet Earth seems to be an ellipsoid, and Egyptian masonry structures have the shape of polyhedron pyramids. So what is the meaning of the word "shape" in Orchard? Well, a shape is just an instance of type Shape and statically typed as a dynamic object. We can stick all sorts of information into a shape at runtime, and even add stuff to the shape as if it were a list. Essentially "shaping" the thing as we go. This is where I think the term "shape" stems from: the ability to shape the object on the fly. Anyway, this is how the Shape clas looks like:

public class Shape : Composite, IShape, IEnumerable {
 public ShapeMetadata Metadata { get; set; }
 public Id { get; set; }
 public IList Classes { get; }
 public IDictionary<string, string> Attributes { get; }
 public IEnumerable Items { get; }
  
 public virtual Shape Add(object item, string position = null) {}
 public virtual Shape AddRange(IEnumerable items, string position = "5") {}
 IEnumerator IEnumerable.GetEnumerator() {}
 public virtual IEnumerator GetEnumerator() {}
 public override bool TryConvert(ConvertBinder binder, out object result) {}
 public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) {}
 public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {}
}

The magic that enables us to stick anything into a Shape is enabled by the implementation of IEnumerable<object> (which lets us iterate over child shapes) and TryInvokeMember, which will add the member being invoked to an internal dictionary as a key value pair (this is implemented by the Shape's base class called Composite). TryInvokeMember is invoked for you when you have an instance of a Shape pointed to by a dynamic variable. For example, consider the following code:

 1: @{
 2:    var mySampleShape = New.MySampleShape();
 3:    mySampleShape.Color = "Green";
 4: }

On line 2, we are creating a new shape via the shape factory, which is stored in the New property of the view. Because the New property is typed as 'dynamic', C# will type the returned shape as 'dynamic'.

On line 3, we are setting a property named 'Color' of the created shape. When that code executes, what internally happens is that C# invokes the TryInvokeMember of the Shape instance, which is implemented in such a way that the member name ('Color') is stored as a key in an internal dictionary, and the assigned value as its value.

Quite clever if you ask me!

The following post has some interesting additional information about shapes: Using shapes as Html helpers in Orchard.

Now that we know a little more about shapes, let's talk about what they are used for. The answer is quite simply: for rendering purposes, of course. But what exactly does that mean?

Rendering a Shape - Naming Syntax

When we talk about rendering a shape, what we really mean is that we take a Razor view and render that using the shape object as its model. The result of this render operation is a string that gets sent to the HTTP response's output, as is the case for any Razor view rendered by the Razor view engine. But how does Orchard know what Razor view to use when rendering a shape in the first place? Can we control which Razor view is used? Yes, and the answer lies in the shape's Metadata property. The Metadata object has a string property called Type, and might look something like "MySampleShape". Based on this name, Orchard will instruct the Razor view engine to locate a view named "MySampleShape.cshtml", and execute that view, using the shape as its Model. The Metadata property also has a collection property called Alternates, which we will discuss a little later.

What is important to know is that the name of a shape must adhere to C# member and variable naming rules. The reason for this is the fact that we can create shapes as if they were method names on a shape factory (as we saw earlier), without having to specify its name as a string value when using the shape factory statically typed as IShapeFactory (more on that later). We can create shapes as if we were invoking a method. For example, the following are valid shape names:

  • MySampleShape
  • My_Sample_Shape
  • My__Sample_Shape
The following are invalid shape names:
  • My.Sample-Shape
  • My_Sample-Shape
  • My-SampleShape
The reason these are invalid is because such names would not compile in C#.
However, the Orchard developers probably didn't like underscores in view file names (rightfully so), so they came up with a convention to map a shape type name into a Razor view file name.
The convention is as follows:
  • A single underscore is replaced with a dot (.).
  • A double underscore is replaced with a hyphen (-).
So if you have a shape named as My_Sample__Shape, the Razor file to look for would be "My.Sample-Shape.cshtml".
Check out the section "Naming Shapes and Templates" in the documentation for more details on naming shapes and Razor views (aka Templates).

Where do Shapes come from?

So where do shapes actually come from? How are they created? How do we create shapes ourselves? The answer to these questions is: shapes are created by an implementation of a thing called IShapeFactory.

As we have already seen, when you are in a Razor view, you can access the shape factory via the New property that each Razor view inherits. The New property is statically typed as dynamic, but it is an actual instance of a class that implements IShapeFactory. The reason this New property is typed as dynamic is to make it easier to work with the shape factory. For example, if you have the statically typed IShapeFactory, this is how you would create a shape: (note: the following code should be put in a Razor file. You could choose the Layout.cshtml file of your theme for example).

 1: @{
 2:  
 3: var shapeFactory = (IShapeFactory)New; // Casting New to IShapeFactory for demo purposes.
 4:  
 5: var myShape = (dynamic)shapeFactory.Create("MySampleShape", Parameters.From({
 6:    MyProperty1 = "Some property value",
 7:    MyProperty2 = 42 
 8: }));
 9:  
 10: }

Compare that with the following code, where we work with the shape factory typed as dynamic:

 1: @{
 2:  
 3: var shapeFactory = New;
 4:  
 5: var myShape = shapeFactory.MySampleShape(
 6:    MyProperty1: "Some property value",
 7:    MyProperty2: 42 
 8: );
 9:  
 10: }

Although the number of lines is the same, notice that the syntax is simpler and looks nicer. Notice also that we were able to create a shape as if "MySampleShape" were an existing method on shapeFactory. This of course is not the case, but the shape factory implements its dynamic behavior by intercepting method calls and creating a shape based on the invoked method name, taking its arguments and turning them into shape properties. Very neat indeed.

So there it is: shapes come from IShapeFactory. Whenever you have an IShapeFactory, you can create shapes. Razor views provide access to the shape factory through its New property, and you can inject IShapeFactory into your own code as you wish.
To learn more about creating shapes, check out this post on Creating shapes on the fly.

Rendering a Shape - Alternates

We briefly mentioned shape alternates. What are they? Technically speaking, they are just a list of strings, stored in the Alternates property of a shape's Metadata property. When Orchard is about to instruct the view engine to look up the view to render, it checks if there's a view that matches any of the alternates defined. Orchard applies the same file name mapping from shape name syntax to Razor view file name syntax as described earlier. So what populates the list of alternates? The answer: any piece of code that has its hands on a shape object can add alternates. Typically this is done using a so called shape table provider.

Shape Table Providers

A shape table provider is a class that implements the IShapeTableProvider interface, and is a way to attach event handlers to shapes. The possible shape events are:

  • Creating
  • Created
  • Displaying
  • Displayed
What is typically done is supplying a handler for the Displaying event, and in this handler one could add alternates to the shape. Let's see a simple example.
First of all, let's imagine we create a shape that has a Color property. Our goal is to provide two Razor views that can be used when rendering the shape. The view to be used should be based on the Color value of the shape.
Creating and rendering the shape would look like this:
 1: @{
 2:    var myBlueColoredShape = New.ColorShape(Color: "Blue");
 3:    var myRedColoredShape = New.ColorShape(Color: "Red");
 4: }
 5:  
 6: @Display(myBlueColoredShape)
 7: @Display(myRedColoredShape)

In order for that code to work, we should at least create a view file whose name matches the ColorShape name. Let's create a default view that simply renders the specified color:

Views/ColorShape.cshtml:

 1: @{
 2:    var color = Model.Color;
 3: }
 4: <p>My color is: @color</p>

See how that the ColorShape.cshtml view is accessing the Color property on the Model? This works because when we invoke @Display on a shape, Orchard will have the view engine locate the view file and execute it by passing in the shape as the view's model.

Now let's say we want to use a completely different view file based on the color. We can do this by creating a new class file in our module that looks like this:

 1: public class ColorShapeTableProvider : IShapeTableProvider {
 2:     public void Discover(ShapeTableBuilder builder) {
 3:         builder.Describe("ColorShape")
 4:             .OnDisplaying(displaying => {
 5:                 var shape = displaying.Shape;
 6:                 var color = shape.Color;
 7:  
 8:                 displaying.ShapeMetadata.Alternates.Add(String.Format("ColorShape__{0}", color));
 9:             });
 10:     }
 11: }

The first thing we do is "describe" the shape (line 3), which essentially means: "configure the following shape".
We configure the shape by specifying a handler for its OnDisplaying method will be invoked when Orchard is about to render our shape. In case you're wondering, you don't have to worry about overwriting other event handlers - the API will register multiple configuration entries for a shape, so this is all good.
Notice that we can access our shape and its Color property (line 6). We use the value of this property to generate an alternate and add it to the shape's metadata Aternates collection (line 8).

With this shape table provider in place, we can now create the following two view files:

  • ColorShape-Blue.cshtml
  • ColorShape-Red.cshtml

Orchard makes good use of this throughout the system. For example, when rendering Content shapes by providing alternates based on a content's content type name and the display type being used. More on that later.

We have seen how to create a shape using the shape factory and render it using the Display method that is inherited by every view. Before we move on I wanted to show that you can actually create and render a custom shape in a single line (which is also explained in the post Creating shapes on the fly). It looks like this:

 1: @Display.ColorShape(Color: "Yellow")

That will create and then display the shape using just one line.

Also be sure to check out this post that talks about shape table providers as well: Customizing Orchard shapes.

Adding Shapes to a Zone

This is cool! We are getting a better understanding of what shapes are and how they work. As it turns out, it is not that complex after all.

So let's talk a little bit about zones. In Orchard, when we talk about zones, we really talk about a thing that we render somewhere in some view (generally in Layouts.cshtml) and that can hold shapes. So what is this thing exactly?

Well my dear Padawan, it turns out that a zone is just another shape, as is very well explained in this post about what zones really are. The only difference is that a Zone shape inherits from a class called ZoneHolding, which in turn inherits from Shape. We will see why this is important shortly.

We generally talk about global zones and local zones. Global zones are the zones defined on the root-of-all-shapes, being the Layout shape. Orchard creates this shape for us and is accessible from every view via the WorkContext property.

If the view you are modifying is the view of the Layout shape, then you can access these zones directly via the Model property of the view. To reiterate, when you want to add shapes to a zone on the Layout shape from other views, you can access this Layout shape via the WorkContext object.

Let's see how we can add a custom shape to a zone. For that we will imagine we have a Layout.cshtml in our theme.

Views/Layout.cshtml

 1: @{
 2:    // Create a new shape.
 3:    var mySampleShape = New.SampleShape();
 4:  
 5:    // Add the shape to the AsideSecond zone. This view is the Layout shape view, so we can access the Layout shape via the Model property of the view, instead of having to access the Layout shape via WorkContext.Layout (because Model == WorkContext.Layout in this Layout.cshtml view).
 6:    Model.AsideSecond.Add(mySampleShape);
 7: }
 8:  
 9: @*Somewhere in the view, render the AsideSecond zone*@
 10: @Display(Model.AsideSecond)

The above code will create a sample shape (make sure you create a corresponding view file when trying this out), add it to a zone called AsideSecond, and then display that zone. The result will be that our sample shape gets rendered where the AsideSecond zone is rendered.

ZoneHolding

So here's a question: can we come up with any arbitrary zone name, or do we have to define them somewhere?

As it turns out, the Layout shape is a special type of shape, called a ZoneHolding shape. The ZoneHolding class derives from the Shape class, and will create a zone shape on the fly as you access properties on the Layout shape.

If the Layout shape were a regulare shape (deriving from Shape) then you would have to first new up the zone shape you want to add, and then assign it to a property on the layout.

Thanks to the ZoneHolding shape type, this is done automatically for us.

But wait - then why do we have to create a list of zone names in the theme manifest (theme.txt) file?

A good question indeed. The reason we specify a list of zone names in the theme manifest has actually nothing to do with our ability to render arbitrary zones. Instead, this list is used to render the available zones in the Widgets section in the dashboard. For example, let's take TheThemeMachine. The following zone names are defined in its Theme.txt file:

Header, Navigation, Featured, BeforeMain, AsideFirst, Messages, BeforeContent, Content, AfterContent, AsideSecond, AfterMain, TripelFirst, TripelSecond, TripelThird, FooterQuadFirst, FooterQuadSecond, FooterQuadThird, FooterQuadFourth, Footer
These zone names will cause them to appear on the Widgets screen:
When you create a widget and add it to a zone, the code that renders the widgets uses the stored zone name to actually add the widget shape to that zone shape. Beautiful.

Rendering Content

Now that we have seen what shapes are and how we can use and render them, let's really dig into what it takes to render a content item. In order to get a better understanding of how a content item is rendered, we must understand its anatomy, what content drivers and content handlers are, and where (and why) Placement.info comes into play.

Anatomy of a Content Item

A content item essentially consists of the following:

  • It has a Content Type
  • It has a collection of Content Parts

A content type is essentially the blueprint of a content item. For example, take the Page content type: it has a technical name called "Page" and a display name (also know as the friendly name) called "Page".

It also has a collection of content parts, such as:

  • TitlePart
  • BodyPart

These content parts define data and behavior for a content item of that content type.

Thus, a Page content item is a content item that has "Page" as its content type, and a copy of the content parts of its content type. An important aspect of content parts is that any given content type can only have one instance of the same content part type. So for instance, no content item can have more than one TitlePart. But of course a content item can have both a TitlePart as well as a BodyPart for example.

Parts themselves can have properties. For example,  the TitlePart has a Title property of type String, and the BodyPart has a Text property of type String.

In addition to that, all Parts have a collection property called Fields, which is a collection of Content Fields. In contrast to content items that can only have a single part of a certain type, a content part itself can have many content fields of the same type. A few examples of content fields are:

  • Text Field
  • Boolean Field
  • Media Library Picker Field
  • Taxonomy Field
  • Date Time Field
  • Content Picker Field
  • Enumeration Field

Users can use the dashboard (the Orchard administration back end) to add content parts to content types and content fields to content parts. To add properties to a content part, a developer must create a C# class that has the same name as the technicnal name of the content part.

The following is a simple tree view representation showing the hierarchy of a content type, its parts, and each parts' fields, as well as the structure of a content item, which is essentially the same as its content type:

As you can see, a content type can have many parts, and each part can have many fields.

A content item has the same structure, and stores actual data for each part and field.

Now let's say we want to create a new Page content item using code, and render that using our view. We could have a controller and action method that looks like the following:

Controller:

 1:  public class ItemController: Controller {
 2:     private IContentManager _contentManager;
 3:   
 4:     public ItemController(IContentManager contentManager) {
 5:        _contentManager = contentManager;
 6:     }
 7:   
 8:     public ActionResult Display(int id) {
 9:        var contentItem = _contentManager.Create("Page", VersionOptions.Published);
 10:        var titlePart = contentItem.As<TitlePart>();
 11:        var bodyPart = contentItem.As<BodyPart>();
 12:  
 13:        titlePart.Title = "Hello Orchard";
 14:        bodyPart.Text = "<p>Welcome to my new page!</p>";
 15:             
 16:        return View(contentItem);
 17:    }
 18: }

View:

 1: @{
 2:    var contentItem = (ContentItem)Model; // We passed in the content item as our model.
 3:  
 4:    // Get a reference to the title part and body part so we can render their values.
 5:    var titlePart = (TitlePart)contentItem.As<TitlePart>();
 6:    var bodyPart = contentItem.As<BodyPart>();
 7: }
 8:  
 9: <h1>@titlePart.Title</h1>
 10: @Html.Raw(bodyPart.Text)

What we see in first code snippet is code that creates a new content item of content type "Page" and accessing its TitlePart and BodyPart to initialize them with some values, and finally returning a view with this content item as the view's model.

The second code snippet shows how we access the two parts of the content item and render out their values.

This is straightforward enough. However, there's one caveat: code like this requires hardcoded knowledge about the structure of the content item. Yet the true power of Orchard is that you can attach reusable content parts and fields to any content type and not have to create specific views for those content types. So how is that pulled off? The answer is: let each content part and content field decide for itself how it is to be rendered.

Content Part and Field Drivers

The good old Orchard folks came up with an API that can take as its input a content item, and returns a shape as its output. That shape can then be rendered. This API is implemented as a method on IContentManager called BuildDisplay, and its signature looks like this:

 1: /// <summary>
 2: /// Builds the display shape of the specified content item
 3: /// </summary>
 4: /// <param name="content">The content item to use</param>
 5: /// <param name="displayType">The display type (e.g. Summary, Detail) to use</param>
 6: /// <param name="groupId">Id of the display group (stored in the content item's metadata)</param>
 7: /// <returns>The display shape</returns>
 8: dynamic BuildDisplay(IContent content, string displayType = "", string groupId = "");

The default implementation of IContentManager (DefaultContentManager) doesn't do much itself; instead, it delegates the shape building to an instance of IContentDisplay.

On a high level, this is what the default implementation of IContentDisplay does:

  1. Create a new shape whose type is the same as the sterotype of the content type. By default, this is "Content", so a new shape of type "Content" will be created. The base type of this shape is ZoneHolding, and is thus capable of automatically creating zone shapes, as the Layout shape can.
  2. The DisplayType of the shape is set to whatever display type argument was passed in. If no display type was specified "Detail" is used. The DisplayType is an additional piece of metadata about a shape that is used by Placement.info, which can contain <Match DisplayType="..."> elements to control which shapes are displayed for which display types. In addition, there is a shape table provider for the Content shape that adds alternates to this shape based on its DisplayType (which is stored in the Metadata property). This is what enables you to create shape templates such as Content-BlogPost.Summary.cshtml for example. The base name is Content, the content type is BlogPost and the display type is Summary.
  3. The BuildDisplay method is invoked on a list of ContentHandler implementations.
  4. There are two implementations of ContentHandler that implement the content part driver system: one for content parts and one for content fields: ContentPartDriverCoordinator and ContentFieldDriverCoordinator, respectively. The purpose of these handlers is to iterate over each part and field of the content item and find their drivers. For each found driver, the BuildDisplayShape method is invoked, which in turn invokes its protected Display method, whose implementation is to be provided by the concrete driver implementation.
  5. This concrete Display implementation ultimately returns a DriverResult, which typically is an instance of ContentShapeResult. A ContentShapeResult holds the name of a shape to render, and a so called shape factory method that will actually create the shape to be rendered. The documentation demonstrates a sample implementation of a content part driver.
  6. Notice that I said that the driver returns a content shape result that holds the name of the shape to be rendered, and a reference to a method that will create the actual shape. The reason that we don't return a created shape right away is because we don't want to create a shape if we at some point determine we don't want to render it. There is a system called Placement that will determine where shapes should go and wether or not they should be rendered at all. We will talk about that shortly, but I wanted to mention that this is the reason we provide the shape name and the shape factory mentod separately (so the shape is lazily created if you will).
  7. As each driver's Display method is invoked, the invoking code will determine where the shape needs to go, if it needs to be rendered at all. This is determined by reading an XML file called Placement.info, which contains shape names and position information. For example, if a driver returns a shape called "Parts_Title", the placement.info file could have an element such as <Place Parts_Title="Header:1" />, which would place the shape in a zone called "Header" at position "1" of the item shape created in step 1.

What we end up with is an item shape that has an hierarchy of part and field shapes. All Orchard has to do is render this shape, which in turn will render its child shapes. As you can see, this is quite powerful, as each part and field only has to concern itself with rendering itself, and Placement.info is used to externally control where these shapes go. This is especially powerful since Placement.info can be overridden by themes.

The following is a flow chart representation of the code execution path from the ItemController in the Contents module (found in Orchard.Core) down to the drivers, and back to the controller, down to the view and the rendering of the shapes:

I don't know about you, but I felt exhilirated when I was finally able to wrap my head around the place of drivers in the request pipeline of a content item (when requested using its route, that is, because rendering widgets start from an ActionFilter rather than a controller. But it is all the same starting from node 2 in the flow chart), as well as understanding that shapes really can come from anywhere: from controllers, drivers, action filters, and even created on the fly from views (also known as ad-hoc shapes).

This concludes part 1 of content types, drivers, shapes and placement. If you feel that after reading this, things are still unclear, please let me know and I will try and dig deeper, or find another way to explain a certain piece. Also, if you have any other topic you would like to see a post about, leave a comment or send me a tweet.

In the next part, we will try a few things in a hands-on lab style of post. Until then, stay cool!

6 comments

  • ... Posted by giannis Posted 11/07/2014 07:31 AM

    excellent article sipke. we all feel the same trying to understand shapes. Thanks for helping clear things out in our head. giannis

  • ... Posted by Michael Sawczyn Posted 11/07/2014 07:31 AM

    Sipke,

    Nicely done! Of course, it raises a question or two ... :-)

    Consider the case where we have a catagorized list of ContentParts. For other reasons, using Taxonomies or Tags isn't appropriate. I'd normally store them in memory as Dictionary< string, List< CustomPart > >. Do I need to create a special ContentType to get the View discovery mechanism to display this? If so, what would you suggest that look like?

    Is there a solution that would allow me to extend the pattern recursively? Say that I'd like to categorize and display those things as well (i.e., Dictionary< string, Dictionary< string, List< CustomPart > > >), potentially ad infinitum.

    If this is more suited for your "Ways to Render Lists of Things" blog post, I'll move it. :-P

    -- Michael

  • ... Posted by Jorge Castillo Pino Posted 11/07/2014 07:32 AM

    Thanks a lot for this post. Now content types, drivers, shapes and placement.info are clearer. I'm looking forward for the next post, too.

  • ... Posted by Simon Poissant Posted 11/07/2014 07:32 AM

    Very nice article! I'm really looking forward to the next part.

    One little question, in the "ShapeTable Providers section", you mention: We have seen how to create a ZONE using the shape factory [...]

    I believe it should be: We have seen how to create a SHAPE using the shape factory [...]

    Or maybe I`m missing something :)

  • ... Posted by Sipke Schoorstra Posted 11/07/2014 07:33 AM [http://www.ideliverable.com]

    Good catch! I updated the post. Thanks!

  • ... Posted by Nate Posted 10/16/2016 01:58 AM

    Hi, can you update this - with building a sample like building an orgchart or a workflow. Also can you describe how user/identity/membership works, with multi-tenenacy

    thanks nate

Leave a comment