Confinity Documentation
  • Latest Version
  • Latest Version
  • Getting Started

    • Introduction
    • Core Concepts
    • Create an Application
    • Glossary
  • Essentials

    • Authentication & SSO
    • Breaking Changes
    • Roslyn Source Analyzers
    • Changelog
    • ConfinityContent
    • ConfinitySelectable
    • Confinity Schedules
    • Data Seeding
    • Development guidelines [WIP]
    • Entity App
    • Entity Form
    • Entity Permissions
    • Frontend Configuration
    • Images
    • Known Issues
    • Localization
    • Migrations
    • Modules [WIP]
    • On-Site Editing
    • Settings
    • Cascade Delete
    • Replication
    • Infrastructure
  • Modules

    • Analytics Module
    • Assets Module
    • Blog Module
    • Cookie Consent Module
    • Forms Module
    • Friendly Captcha (Forms Module )
    • GeoIP Module
    • Htmx
    • Mail Module
    • Mailing Module
    • MediaPlayer Module
    • GoogleMyBusiness Module
    • OpenTelemetry Module
    • Pages Module [WIP]
    • Pattern Library Module
    • SIX Saferpay (worldline) Module
    • Products Module
    • Search Module
    • Wizard Module
  • Guides

    • Create a Custom Entity App Form Element
    • Date and Time
    • Entity Change Listener
    • File Upload / Temp File
    • HTTP security headers
    • conventions [WIP]
    • How to use Confinity with nginx
    • Notifications
    • Nullability
    • Rename Entity
    • Schedules
    • Useful snippets
    • Content Localization
  • Design Guidelines

    • Introduction
    • Page Components
    • Forms Module

Pages Module [WIP]

The Confinity Pages module allows you to create and manage websites.

Installation

No need to install the Pages module since it's a base module.

Websites, Pages & Placeholder Pages

Pages

Pages are structured hierarchically with at least one website at the top level and multiple pages underneath. A page consists of some meta data and a layout, which contains the content.

Websites

When you want to build a website with Confinity, you would first create a website in the Pages App in the Admin Panel. A website is basically a page but it can contain multiple domains and is used as the entry point or homepage for a given domain.

Structure of a Page

The following graphic illustrates an example of a page structure given a layout called Default with four sections called Header, Default, Sidebar and Footer.

Simple Page Structure

Layouts

A page always contains a layout, which is used to define the structure of a page. A layout consists of a model, a form configuration for the model and a ASP.NET layout file.

More about ASP.NET layouts

Sections

A section is an area in the layout, which can contain one or more page components, which can be added by an author. Typical sections can be the main content section, a sidebar section or a footer section. With a section, a developer gives an author possible placement options inside a layout, where the author can add content in form of components.

More about ASP.NET sections

Components

A page component is a single unit of content or functionality which can be placed inside a section by an author. A page component could be an image, a text block, a form, a list of entities and so on. A component consists of a model, a form configuration for the model and a ASP.NET view component.

More about ASP.NET view components

Creating a new Layout

To create a new layout for the pages app, you need a layout model, a layout file and you have to register the model with a form configuration.

Create the Layout Model

The layout model will define which data will be available to the layout file. Let's create a new class called MyLayoutModel which will inherit from PageLayoutModel.


public sealed class MyLayoutModel : PageLayoutModel
{
    public string? Title { get; set; }
    public PageSection DefaultSection { get; set; } = new();
    public PageSection FooterSection { get; set; } = new() { Inherit = true };
}

The model obviously has a Title property and two sections DefaultSection and FooterSection.

To avoid unnecessary null checks, the PageSection should have a property initializer. the initializer should match the inherit configuration of the matching page section configuration. As a rule of thumb Inherit should be set to true if CanInherit is enabled. For sections added at a later point this is essential so that the inherit does work properly.

Create the Layout File

Let's create a new ASP.NET layout by creating the file MyLayout.cshtml inside the folder Views\Shared. The layout might look like this:

@using Confinity.Pages
@model DocsDemos.Modules.Pages.MyLayoutModel

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>

    @await Html.ConfinityHeadAsync()
    @Html.ConfinityStylesheets()
</head>
<body>
@if (!string.IsNullOrWhiteSpace(Model.Title))
{
    <header>
        <h1>@Model.Title</h1>
    </header>
}

<main role="main">
    @RenderBody()
</main>

<footer>
    @await RenderSectionAsync("Footer", required: false)
</footer>

@Html.ConfinityJavaScripts()

</body>
</html>

Important

The name of the layout file must be equals to the name of the layout model, without the Model suffix. So for example the model DefaultLandingPageModel maps to the layout fileDefaultLandingPage.cshtml.

A few things to note in this example layout. First, the layout gets a model which we expect to be a MyLayoutModel. In the head area, we call a Confinity HTML helper, which will output the required meta tags for SEO. Last but not least, we have a few RenderSections and the RenderBody call in the layout.

The two HTML helpers Html.ConfinityStylesheets() and Html.ConfinityJavaScripts() will render all registered style and script resources. If you create a page component Foobar which requires a JavaScript and/or CSS file, this is the place where they are included.

The section Footer will render page components from the FooterSection property from the layout model.

The RenderBody will render the main content for this layout. If the layout model contains a PageSection with the name DefaultSection, its page components will be rendered at this position.

Register the Layout

The last step, to make the new layout available to the pages app, is the registration and the form configuration for the layout model. The registration takes place in your module registration and will look like this:


public class MyModule : IModuleConfiguration
{
    public string ModuleKey { get; } = "my-module";

    public void AddModule(IModuleContext module)
    {
        module.Configure.RegisterPageLayout<MyLayoutModel>("My Layout", form =>
        {
            form.AddTab(tab =>
            {
                tab.AddInput(p => p.Title);
                tab.AddSection(p => p.DefaultSection)
                    .Where(t => typeof(PageComponentModel).IsAssignableFrom(t));
                tab.AddSection(p => p.FooterSection)
                    .Where(t => t == typeof(WysiwygContent))
                    .CanInherit(true);
            });
        });
    }

    public void UseModule(IApplicationBuilder app)
    {
    }
}

To register a layout model, the method RegisterPageLayout with the type of the model has to be called with the form configuration as argument. Let's tear down the form configuration.

First we add an input for the Title property. Nothing special here. Now we add a page section element for the DefaultSection property. In the where clause, we define that we allow all registered page components in this section (a page component has to be derived from PageComponentModel). And last but not least, we add a section form element for the footer section, where we only allow the page component type WysiwygContent (this component does not exist out of the box and is only used as an example here). And finally with CanInherit(true) we active inheritance for this section.

Section Inheritance

Some sections in your layout might be the same on every page. For example the footer area of a layout has mostly the same content on every page. But still you don't want to hard-code its content into the layout but give the authors the possibility to change the footer content by themself. This can be accomplished, by allowing authors to choose if they want to inherit the content of a section from the parent page. In the case of a footer you might want to add its content on the root level page (the website) and then inherit on every child page. When you change the content on the root page, it will change on every child page as well.

Creating a Page Component

A page component consists of the following three things:

  1. An ASP.NET view component
  2. A model, which contains the required data for the view component
  3. A form configuration for the model which allows an author to fill the model with data

Let's create a simple page component, which allows an author to add rich text with a title to the page.

Create the Page Component Model

Create a class WysiwygContent which inherits from PageComponentModel like this:


public sealed class WysiwygContent : PageComponentModel
{
    public string? Title { get; set; }
    public IHtmlContent? Content { get; set; }
}

Create the View Component

Now create a class WysiwygContentViewComponent which inherits from ViewComponent:


public class WysiwygContentViewComponent : ViewComponent
{
    public IViewComponentResult Invoke(WysiwygContent model)
    {
        return View("~/Modules/Pages/WysiwygContent.cshtml", model);
    }
}

If you need more information about how view components work, please consult the corresponding .NET documentation.

Important The name of the model has to be the identical to the name

of view component without the ViewComponent suffix. If your model contains a Model suffix, the name of the view component should not include the suffix. For example the model TextModel will be mapped to the view component TextViewComponent.

The view for your view component could look like this:

@using Confinity.WebUI.Util
@model DocsDemos.Modules.Pages.WysiwygContent

<h2>@Model.Title</h2>

@await Html.FromWysiwygAsync(Model?.Content)

Register the Page Component

In order to make the page component available for authors in the pages app, we have to register it and add a form configuration for the model. As always the registration takes place in the module configuration and since a page component is basically just a ConfinityContent it is being registered like a any other ConfinityContent.


public void AddModule(IModuleContext module)
{
    module.Configure.RegisterConfinityContent<WysiwygContent>("WYSIWYG Content")
        .Icon(Icon.Text)
        .Editor(form =>
        {
            form.AddTab(tab =>
            {
                tab.AddInput(p => p.Title);
                tab.AddWysiwygEditor(p => p.Content);
            });
        });
}

Customize Wrapper

For confinity to be able to support a few features, a wrapper is added during the html output. Customization can be added with attributes on the ViewComponent.

Example:


[ConfinityWrapperTagName("section")]
[ConfinityWrapperCssClasses("my-awesome-class", "another-class")]
[ConfinityWrapperAttribute("data-type", "awesome")]
[ConfinityWrapperAttribute("my-marker")]
public class CustomWrapperViewComponent : ViewComponent
{
    public IViewComponentResult Invoke(CustomWrapper model)
    {
        return View("~/Modules/Pages/CustomWrapper.cshtml", model);
    }
}

The HTML for such a component would be:

<section class="my-awesome-class another-class text-component cfy-component" data-type="awesome" my-marker>
  <!-- content of component -->
</section>

Custom 404 Page ("Page not found")

For every website, authors can create a custom 404 page ("Page not found"). To create a new 404 page, a page with the reserved slug _error404 has to be created as a direct child of the corresponding website.

Configuration

appsettings Key: ConfinityPages

Configuration KeyDescriptionDefault
BasePathThis will prefix all paths to page with a base path. This is appended to the BasePath.
Prev
OpenTelemetry Module
Next
Pattern Library Module