Shape in Orchard Core CMS
What is a shape?
A Shape is an object that holds some data, like a view model, and also some metadata about how to render it.
Any class that implements IShape is a shape.
Shapes are used to as a way to render some html, and to do so you need to call the IDisplayHelper.DisplayAsync(shape) that returns Task<IHmlContent>
.
When calling this method you don't need to specify a template, it will trigger an event to allow any component in the system to chose what template is best suited to render this specific shape. This is why a shape can be rendered using a cshtml file, a liquid file, some template from the database, some code, ... it's completely extensible, but from your point of view you just ask it to be rendered. This is an advantage over Views in MVC where when return a ViewResult from an action the template is strongly associated with the action.
This indirection is very useful for theming, such that templates can come from different modules, or any module can redefine what template to use for a shape. So as a developer you can provide one default template with your shape that represents a Menu, but allow any other module or theme to redefine it.
Then to allow for better granularity in ways to select a template, the shape has a Metadata.Alternates property that contains which templates names are better suited for this shape, in order of priority. Like a shape representing an Article, which is a Content item, could accept template for an with a specific name, or a template for any article, or a template for any content item. This allows the themes to be able to customize the templates for very specific instances of shapes, or group of shapes.
When working in the decoupled CMS mode, you are removing theming altogether, and not using shapes at all, because you implement each view and route handler, and don't expect anything else to be able to override these. But most modules that expose front-end views need to use shapes in order to allow a theme to customizes parts of the system without having to re-implement all handlers and views.
Content from GitHub Discussion & credit to Sébastien Ros
Example of file structure of a Foo shape
$ tree ViewsViews├── Foo.cshtml└── Home└── Index.cshtml
- Note to get a template loaded property, you may need to restart an application.
Create a shape without ViewModel in a DisplayDriver class
We usually use
DisplayDriverBase.Initialize<TModel>
to create a ShapeResult for a content part.However, this method requires a view model which sometimes we don't need it.
We can use other methods to create a ShapeResult without a view model. For example:
var shapeResult = Dynamic(nameof(FooPart)).Location("Detail", "Content");return Factory(nameof(FooPart),async context => await context.New.FooPart()).Location("Detail", "Content");
Useful resource of how to override a template
- Orchard Core CMS's templates document
- Alternated shapes are attached to a shape metadata (Shape.MetaData.Alternates) in OrchardCore.Contents.Shape
- Available display types:
- Detail
- Summary
- DetailAdmin
- SummaryAdmin
- Useful template names to override, shape and template
- Content_[DisplayType]__[ContentType] => Content-Book.Summary.cshtml
- [ContentType]_[DisplayType]__[PartType] => ContentType-TitlePart.Summary.cshtml
How a template (.cshtml file) is mapped to a type name:
.
will be converted to_
e.g. Item.Mata.cshml => Item_Mata-
will be converted to__
e.g. Item-Mata.cshml => Item__Mata--
will be converted to__
e.g. Item--Mata.cshml => Item__Mata- It is defined in BasicShapeTemplateHarvester.cs
Add a new shape to an existing content item
We can create a derived class class of
ContentDisplayDriver
and override the following method to attached a new shape which will be rendered along with a content item.Available methods to override:
- public virtual methods defined in OrchardCore.DisplayManagement.Handlers.DisplayDriver
Example code to attach a
Content_SaveButton
shape to a Product content item rendered along with the content item on an edit page in an admin site.using OrchardCore.ContentManagement;using OrchardCore.ContentManagement.Display.ContentDisplay;using OrchardCore.DisplayManagement.Views;namespace MyCustomModule{public class ProductContentDisplayDriver : ContentDisplayDriver{private const string contentTypeName = "Product";public override IDisplayResult Edit(ContentItem contentItem){if (contentItem.ContentType != contentTypeName){return null;}// It uses Content.SaveButton.cshtml template in a Views folder of the current module.return Dynamic("Content_SaveButton").Location("Edit", "Action:0");}}}- You also need to register a custom ContentDisplayDriver in a Startup.cs file of the current module as following code:
services.AddScoped<IContentDisplayDriver, ProductContentDisplayDriver>();- You can also use
Combine(params IDisplayResult[] results)
method to return multiple shapes fromContentDisplayDriver
.
placement.json tips
- We can control how to place our shape with placement.json which is added to the root of a module.
- Here are code examples of how to use placement.json:
- Hide an existing shape of a specific content type:// placement.json{"ContentPreview_Button": [{"contentType": ["Blog"],"place": "-"}]}
- Change a local zone position of an existing shape only an frontend summary page (list item page):// placement.json{"TaxonomyField_Display__Tags": [{"contentType": ["BlogPost"],"displayType": "Summary","place": "Footer:0"}]}
- Hide an existing shape of a specific content type:
- Orchard can parse
placement.json
with a comment, so we can enable VS Code to support JSON comment by adding the following code to a settings.json in your.vscode
folder at the root level of your project.{"files.associations": {"*.json": "jsonc"}} - To learn more about placement.json, please check placement document.
Add a local zone without editing Content.cshtml
Useful source code for referencing
- https://github.com/OrchardCMS/OrchardCore/blob/dev/src/OrchardCore.Modules/OrchardCore.Demo/ContentElementDisplays/TestContentElementDisplayDriver.cs
- https://github.com/OrchardCMS/OrchardCore/blob/dev/src/OrchardCore/OrchardCore.DisplayManagement/Views/ShapeResult.cs
- https://github.com/OrchardCMS/OrchardCore/blob/dev/src/OrchardCore/OrchardCore.DisplayManagement/Implementation/DefaultShapeFactory.cs
- https://github.com/OrchardCMS/OrchardCore/blob/dev/src/OrchardCore/OrchardCore.DisplayManagement/Handlers/DisplayDriverBase.cs