Having a look at Umbraco Unicore Alpha 3 Release

Umbraco is continuing its journey of migrating to .NET Core. Alpha 3 was released in early February this year and in this blog post, I have attempted to create a little one-page website using Umbraco Unicore.

The first Alpha release of Umbraco Unicore focussed on the installer, the backoffice, and targeted .NET Core 3.1. The Alpha 2 focus remained as the backoffice while fixing some of the known issues and issues found by the community. With Alpha 3, things got more exciting!

  • Website rendering is now fully functional which means I can query and render content.
  • The embedded Models Builder now runs on ASP.NET Core
  • Umbraco Unicore runs on .NET 5. Even though .NET 5 is not an LTS version staying on the latest framework means that going from .NET 5 to .NET 6 becomes a smoother task. 

There are other updates in this Alpha release which you can read about here. So without much delay let me show you what I did to achieve the one-page website.

Creating a new solution

To begin with, make sure you have .NET 5 SDK installed. 

Add a new package source for Nuget packages. 

dotnet nuget add source "https://www.myget.org/F/umbracoprereleases/api/v3/index.json" -n "Umbraco Prereleases"

Install the Umbraco Template.

dotnet new -i Umbraco.Templates::0.5.0-alpha003

The dotnet new command creates a new project based on the template specified. You can read more about the command here. The option -i specifies that a new template pack must be installed. Prerelease versions of the template package to be installed must be specified in the format <package-name>::<package-version>.

Now I can set up a new solution based on the template.

dotnet new umbraco --UseSqlCe -n MyFirstUmbracoUnicoreWebsite

This creates a new project with the name MyFirstUmbracoUnicoreWebsite which can be opened up In Visual Studio. Note that I am using SQL Compact Edition for my database.

I can then use the Developer PowerShell in Visual Studio to build and run my project using the following commands.

dotnet build
dotnet run

This runs the project on the Kestrel Server and it is available on https://localhost:5001 and http://localhost:5000. I can use either of the URLs and it starts the installer and helps me complete the setup. Once the setup is complete I have to restart the application manually to browse to my local site and log in to Umbraco backoffice. This is a known issue that is documented here

What am I building?

I am putting together a simple one-page website based on a theme called Agency which I found on Start Bootstrap.

Umbraco Unicore  based website

Once I am inside the backoffice of Umbraco, it is exactly what Umbraco V8 is. I can create document types and nodes based on document types. In my example, I am using Nested Content to build this page. I have named the three components on this page as Hero, Pods, and Configuration Steps. And they will be available for me to add as Widgets in my Home document type. This is how my document types look like.

Home

Hero Document Type(Element Type)

Pods Document Type(Element Type)

Pod Block Document Type(Element Type)

Configuration Steps Document Type(Element Type)

Configuration Step Document Type(Element Type)

The process for creating document types, data types, etc hasn't changed at all from Umbraco V8 and infinite editing is available as well in the back office. There are, however, changes in the templating and rendering.

The first thing I notice is the new namespace for UmbracoViewPage on my Home template. My Layout View also uses the same inherits statement.

@inherits Umbraco.Web.Common.AspNetCore.UmbracoViewPage<Home>

Models Builder models are available for me in the templates. With Umbraco V8 I would be changing the configuration of Models Builder in web.config but with .Net Core the settings live in appSettings.json. So if I want to change the Models Builder mode to LiveAppData and specify the Models Builder mode I can do that in the ModelsBuilder section of the appSettings.json as shown below. The keys have not changed, so I can use the same key-value pairs as in V8 and this helps me generate the models as C# classes in the specified directory.

 "ModelsBuilder": {
    "ModelsMode": "LiveAppData",
    "Enable": true,
    "ModelsDirectory": "~/Models"
}

The static assets for ASP.NET Core applications live in web root (wwwroot)folder. So I have put all my css, js, and static images into the wwwroot folder. With Unicore, the media library and the Umbraco backoffice files also reside in the wwwroot folder.

To render my home page I use a foreach loop to go through my widgets and render them one by one. My partials are in the /Views/Partials folder and named the same as the alias of my element types for the sake of simplicity. 

Home.cshtml

@inherits Umbraco.Web.Common.AspNetCore.UmbracoViewPage<Home>
@{
    Layout = "_Layout.cshtml";
}

@foreach(var widget in Model.Widgets)
{
    @await Html.PartialAsync($"Partials/{widget.ContentType.Alias}",widget)
}

The partial views for my widgets are shown below. The major change I found was around the rendering of the media item for my hero widget. The .Url extension for IPublishedContent is now obsolete and to render an item from the media library I first need to inject an IPublishedUrlProvider into my Razor View. ASP.NET Core supports dependency injection into Views using the @inject directive which helps me inject the IPublishedUrlProvider into my partial. I can then use the GetMediaUrl() method passing in the key of the media item to render the image.

I really got stuck here, but thanks to Steve Temple who is also a member of the Unicore Team I found my way around it! 

hero.cshtml

@using Umbraco.Web.Routing
@inherits Umbraco.Web.Common.AspNetCore.UmbracoViewPage<Hero>
@inject IPublishedUrlProvider urlProvider

@{

    <header class="masthead" style="background-image:url('@urlProvider.GetMediaUrl(Model.HeroImage.Key)')">
        <div class="container">
            <div class="masthead-subheading">@Model.Heading</div>
            <div class="masthead-heading text-uppercase">@Model.Heading</div>
            <a class="btn btn-primary btn-xl text-uppercase js-scroll-trigger" target="@Model.CallToAction.Target" href="@Model.CallToAction.Url">@Model.CallToAction.Name</a>
        </div>
    </header>

}

 pods.cshtml

@inherits Umbraco.Web.Common.AspNetCore.UmbracoViewPage<Pods>

<section class="page-section" id="services">
    <div class="container">
        <div class="text-center">
            <h2 class="section-heading text-uppercase">@Model.Heading</h2>
            <h3 class="section-subheading text-muted">@Model.SubHeading</h3>
        </div>
        <div class="row text-center">
            @foreach (var pod in Model.Blocks)
            {
                <div class="col-md-4">
                    <span class="fa-stack fa-4x">
                        <i class="fas fa-circle fa-stack-2x text-primary"></i>
                        <i class="fas fa-heart fa-stack-1x fa-inverse"></i>
                    </span>
                    <h4 class="my-3">@pod.Heading</h4>
                    <p class="text-muted">@pod.Text</p>
                </div>
            }
        </div>
    </div>
</section>

configurationSteps.cshtml

@inherits Umbraco.Web.Common.AspNetCore.UmbracoViewPage<ConfigurationSteps>

<section class="page-section" id="about">
    <div class="container">
        <div class="text-center">
            <h2 class="section-heading text-uppercase">@Model.Heading</h2>
            <h3 class="section-subheading text-muted">@Model.SubHeading</h3>
        </div>
        <ul class="timeline">
            @foreach (var step in Model.Steps.Select((ol, i) => new { Step = ol, Index = i + 1 }))
            {
                var iconClass = step.Index % 2 == 0 ? "class=timeline-inverted" : string.Empty;
<li @iconClass>
    <div class="timeline-image"><img class="rounded-circle img-fluid" src="@(string.Format($"~../../../assets/img/steps/N{step.Index}.png"))" alt="" /></div>
    <div class="timeline-panel">
        <div class="timeline-heading">
            <h4>@step.Step.PreHeading</h4>
            <h4 class="subheading">@step.Step.Heading</h4>
        </div>
        <div class="timeline-body"><p class="text-muted">@step.Step.StepDescription</p></div>
    </div>
</li>
}
            <li class="timeline-inverted">
                <div class="timeline-image">
                    <h4>
                       Have fun!
                    </h4>
                </div>
            </li>
        </ul>
    </div>
</section>

Build and run and I have my one-page website rendering! Woohoo! 

As an aside, I tried importing some of the document types from a V8 install as .udt files, They worked well for me!

A little note, I tried building this one-page website as a way of trying out the Alpha 3 release and get a flavour of the rendering of pages in Umbraco Unicore. There could be things that are mentioned here but might change by the time Unicore is officially released.

Unicore is not production-ready, so do not start a project using this template that will end up in production! 

I would highly recommend anyone interested in understanding the migration of Umbraco to .NET Core to closely follow the product updates and give the Alpha release a try. You can raise an issue in the issue tracker if you spot anything or help fix existing issues. Finally, well done to HQ, Unicore Team and the community for the Alpha 3 release. It already looks promising and I am eagerly looking forward to Umbraco Unicore :-)


What is GraphQL?

27 April 2021

Know more about GraphQL, the important terms, pros and cons.