_Alloy_ is a Optimizely Content Management System (CMS) demonstration template package that contains a site for a fictional company "Alloy Technologies", and to demonstrate the most important features of CMS and give you a site that is ready to explore or to start with.
The Alloy templates are built on the latest CMS platform. If you are new to CMS, you should start by [install a sample site](🔗) and explore it in detail. It will give you a great introduction to the user interface, as well as project structure and programming patterns.
### Tech summary
Based on the latest releases
Built on ASP.NET MVC 5
Based on the responsive Bootstrap by Twitter HTML framework
The templates are distributed as a dotnet template. See [install a sample site](🔗).
## Design philosophy
Template development with ASP.NET MVC is supported since version 7. The Alloy templates are created as an example of how to build a CMS site with good characteristics, both in terms of maintainability and performance. They also show an example of how conventions in MVC can be tuned to suit a website like Alloy.
The Alloy templates were initially built after discussions with developers working with ongoing CMS and MVC development projects. These discussions, prototyping, and the unique characteristics of the Alloy site were distilled into a number of design decisions geared towards making it easy to extend the Alloy site with new content types and functionality. Since then, the templates have been continually updated and extended.
Alloy is using MVC (Model-View-Controller) but CMS supports Razor Pages and templates built in client-side frameworks as well.
All non-partial views and layouts in the MVC templates use a model of type `
IPageViewModel<T>` where T must be a descendent of `
PageData`. The `
IPageViewModel` interface defines a `
CurrentPage` property as well as a couple of properties geared towards layouts and framework components. This way, all views including layout files, can rely on some common characteristics of the view model.
To free controllers from having to populate these common properties on the view model, an action filter named `
PageContextActionFilter` is registered globally at startup. This filter inspects the view model which is about to be passed to the view and, given that it is of type `
IPageViewModel`, sets the common properties required by the site’s framework.
Should a specific controller or action want to influence the common properties of the view model, it can do so by populating them on the view model as the filter then will not modify them. Alternatively, a controller can implement an interface named `
IModifyLayout` that tells the filter to pass the Layout property of the view model to the controller after having populated it with default values.
An example of a controller which implements `
IModifyLayout` is the controller for the preview page, `
PreviewController`. It modifies the view model in order to instruct the view to hide the site’s header and footer.
### View locations and a default controller
The Alloy templates have a controller named `
DefaultPageController`, which can handle all page types and simply returns a view result with a view location set to _/Views/\<page\_type\_name>/Index.cshtml_.
This lets you add views for page types by following the above convention for view locations. For page types that require a controller, all we need to do is to create one which is more specific in terms of what page type it handles.
### A custom view location expander
There are different ways to create partial renderers, templates for pages and blocks in content areas and such when using ASP.NET MVC Core. Like:
create a view component
create a partial view in the _/Views/Shared_ folder whose name matches the type name of the content type or,
create a partial view which is registered at startup using a class that implements the `
IViewTemplateModelRegistrator` interface (see the `
The Alloy templates use the two latter approaches extensively. Partly because a view component is not really needed for many of the blocks in Alloy. As a result, the _/Views/Shared_ folder contains a lot of views, which makes it difficult to find a specific one. Therefore, the templates feature a custom view location expander, `
SiteViewLocationExpander`, which adds two additional folders in which the partial views for CMS content can be stored – _/Views/Shared/PagePartials_ and _/Views/Shared/Blocks_.
### Content area rendering
The Alloy templates feature a customized rendering of content areas.
To support the design requirements for the Alloy website, we implemented a class called `
AlloyContentAreaRenderer` for custom rendering of content areas. See the `
CustomizedRenderingInitialization` class to see how we make CMS use our custom renderer for all content areas.
### Error handling
The Alloy templates contain a bonus feature in the form of the `
ErrorHandlingContentRenderer` class. This class is registered as the default type for the `
IContentRenderer` interface during the site’s initialization. It wraps the CMS default implementation and extends it to provide error handling while rendering partial content.
This has the effect that an exception, of a number of non-critical types, thrown while rendering a block or page in a content area will not crash the entire page. Instead, it will simply hide the failing partial content. If the HTTP request is made by an editor, it renders an error message that can be reported to a developer.
The error handling is only in place if the application is not attached to a debugger. When attached to a debugger, the request is probably made by a developer and then the standard error handling is used instead, allowing developers to more easily see the error message and stack trace.
Beyond making the site more robust, this functionality also illustrates the flexibility of CMS API.
### Navigation components
The Alloy templates feature a single extension method for the `
HtmlHelper` class named `
MenuList` which is used to build all three navigations components on the Alloy site – the top menu, the sub navigation and the breadcrumbs.
While there are many possible ways to build each of those navigation elements for an MVC site, the `
MenuList` method is especially designed for flexibility through the use of `
The method takes a root page and a `
Razor` helper as argument. It fetches the children of the root page and filter them based on whether they should be visible to the current visitor and whether they should be displayed in navigation or not. Finally, it invokes the `
Razor` helper once for each page.
Razor` helpers are essentially C# code it can be used for many different things, especially since the `
Razor` helpers can recursively invoke themselves.
### Dependency resolving
ASP.NET MVC is designed for testability and flexibility, and CMS features the abstractions necessary to utilize that. Therefore, almost all communication with the API is kept out of the views and placed in separate classes and, to a lesser extent, in the controllers.
To let us switch out components without modifying controllers and to make the code practical to write unit tests for, all dependencies in controllers are injected through their constructors. In ASP.NET MVC, dependencies for controllers and other components are resolved using a `
DependencyResolver`. The templates feature such a class, `
ServiceLocatorDependencyResolver`, which is set as the default dependency resolver through the `
DependencyResolverInitialization` initialization module.
The dependency resolver wraps the IoC container used by the CMS itself, meaning that we do not have to register implementations for the CMS API. Custom classes in the templates generally do not have corresponding interfaces but instead have virtual methods. This means that we do not have to register them while maintaining the ability to write unit tests for code that relies on such types.