What happens when I request a page in Orchard?
If you look at a typical request for a page, the route will resolve the URL to the Display method of the ItemController in Orchard.Core/Routable/Controllers. That action will retrieve the content item for that route from the content manager and ask it to build the display. The ShapeResult it will produce is what is returned by the action.
Let's pause for a second here and mention that we'll get back in a moment to what really happened during that BuildDisplay call.
Now starts a second phase where that result gets executed and the shape tree starts getting rendered. One very important shape already exists at this point on the work context, and that is the Layout shape.
When the time comes to render the Layout shape, Orchard will look for the most specific template for it, which is usually found in the current theme. The template contains the rendering calls for the zones, looking like this:
@Zone(Model.Content)
Again, Orchard will look for the most specific way to render a Zone shape, which unless you overrode the template in your theme, is going to be a method on CoreShapes:
[Shape] public void Zone(dynamic Display, dynamic Shape, TextWriter Output) { string id = Shape.Id; IEnumerable<string> classes = Shape.Classes; IDictionary<string, string> attributes = Shape.Attributes; var zoneWrapper = GetTagBuilder("div", id, classes, attributes); Output.Write(zoneWrapper.ToString(TagRenderMode.StartTag)); foreach (var item in ordered_hack(Shape)) Output.Write(Display(item)); Output.Write(zoneWrapper.ToString(TagRenderMode.EndTag)); }
This code renders a div around the zone with an id and optionally some classes, and then moves to looping over contained shapes and calling Display on them.
In the case of the Content zone, remember that our controller action described what to inject. We have a Content shape there, which will be rendered by a Content.cshtml template. Part of that template is the definition of new nested local zones (Header, Meta, Content and Footer) defined by calls looking like this:
@Display(Model.Content)
What goes into these local zones was determined by what happened during that call to BuildDisplay that we mentioned earlier. Now is the time to get a closer look at what happened there.
The method (which you can find in DefaultContentDisplay) created the shape for the content item with a reference to the actual content item and some metadata such as the display type (Detail, Summary, etc.).
Then, it determined placement information, which typically comes from placement.info files. That information will help the system dispatch the part and field shapes into the local zones that Content.cshtml created.
Finally, we invoke all handlers in the system to give them a chance to participate in building the shape tree for this request.
This is when drivers can chime in and create a shape for the part they are responsible for. The Display method of the driver of each part in the content item will get called. The resulting shapes can then be added to the local zones under the content item's shape, according to placement information.
If we look into the shape tree using the new Shape Tracing feature from Orchard 1.1, here is what we see:
All that remains to do now is render each of these shapes in order, using the most specific template for each.
And there you have it: a page got rendered by that controller action and then by all these disconnected drivers and templates. The resulting rendering system is both extremely flexible and extensible at will.