Dragonfly.Umbraco10.Theming.Web 1.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Dragonfly.Umbraco10.Theming.Web --version 1.1.0                
NuGet\Install-Package Dragonfly.Umbraco10.Theming.Web -Version 1.1.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Dragonfly.Umbraco10.Theming.Web" Version="1.1.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Dragonfly.Umbraco10.Theming.Web --version 1.1.0                
#r "nuget: Dragonfly.Umbraco10.Theming.Web, 1.1.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Dragonfly.Umbraco10.Theming.Web as a Cake Addin
#addin nuget:?package=Dragonfly.Umbraco10.Theming.Web&version=1.1.0

// Install Dragonfly.Umbraco10.Theming.Web as a Cake Tool
#tool nuget:?package=Dragonfly.Umbraco10.Theming.Web&version=1.1.0                

Dragonfly Umbraco 10 Theming

A theming system for Umbraco version 10 created by Heather Floyd.

For a general explanation of the concept, see the article "How to Create Multiple Unique Sites in One Installation Using Theming and the Umbraco Grid". Please be aware that this article was published in 2016 before the code was further developed and put into GitHub. Also, the article is based on Umbraco 7, thus should really just be used for a general understanding - this package includes all the code otherwise provided by the ZIP file mentioned in the article.

Credit goes to Shannon Deminick's Articulate package for inspiration and initial code.

NOTE: This project was ported from the v8 version. Please report any issues you experience.

Installation

Generally, you will want to install the .Web version of the package into your Umbraco project, which includes the App_Plugins, a setup of the Themes folders, and example Razor files:

Nuget Downloads

 PM>   Install-Package Dragonfly.Umbraco10.Theming.Web

There is also a .Core version available which only includes the DLL files. This can be used if you have an accompanying Class project which needs to access the ThemeHelperService:

Nuget Downloads

 PM>   Install-Package Dragonfly.Umbraco10.Theming.Core

Resources

GitHub Repository: https://github.com/hfloyd/Dragonfly.Umbraco10.Theming

Setup

Because this is a .Net Core site and Views are handled differently from static files, you will need to have two "Themes/[MyTheme]" folders - one next to the "Views" folder for holding Views and Partials, and one inside the wwwroot folder to hold all static theme-related files (css, js, images). Your Theme name should be the same in both "Themes" folders.

On your root Document Type, use the included "Theme Picker" Property Type to add a property for the site's chosen theme.

In appSettings.json add this section at the root-level (aka a sibling of 'Umbraco', not a child):

"DragonflyTheming": {
    "ThemeViewsAreInWwwRoot": false,
    "ThemesRootFolderName": "Themes",
    "ThemePickerPropertyAlias": "Theme",
    "CssFilePickerPropertyAlias": "SiteCss",
    "EnableDefaultThemeController": false,
    "ThemedAssetsCssFolder": "Css",
    "ThemedAssetsJsFolder": "Js",
    "FallbackAssetsFolder": "/Assets",
    "FallbackAssetsJsFolder": "/scripts",
    "FallbackAssetsCssFolder": "/css"
}

In your Startup.cs file, add:

	public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
		...
		app.UseStaticFiles();        
    }

The Controller which runs for each page request needs to determine the Themed View file to render the page with, so if you already have custom controllers operating in your site, be sure to include something that will route the theme correctly. For example:

namespace MySite
{
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.ViewEngines;
    using Microsoft.Extensions.Logging;
    using Umbraco.Cms.Core.Web;
    using Umbraco.Cms.Web.Common.Controllers;
    using Umbraco.Extensions;

    /// <summary>
    /// Find the theme setting and retrieve the appropriate view
    /// </summary>
    public class DefaultController : RenderController
    {
	private readonly ILogger _logger;
	private readonly ThemeHelperService _ThemeHelperService;

	public DefaultController(
	    ILogger<DefaultController> logger,
	    ThemeHelperService themeHelperService,
	    ICompositeViewEngine compositeViewEngine,
	    IUmbracoContextAccessor umbracoContextAccessor)
	    : base(logger, compositeViewEngine, umbracoContextAccessor)
	{
	    _logger = logger;
	    _ThemeHelperService = themeHelperService;
	}

	// GET: Default
	public override IActionResult Index()
	{
	    var themeProp = _ThemeHelperService.ThemePropertyAlias();

	    if (!string.IsNullOrEmpty(themeProp))
	    {
		var currentTemplateName = CurrentPage.GetTemplateAlias();

		//Set logic to get the node which has the Theme Picker on it
		var rootNode = CurrentPage.AncestorOrSelf(1);
		if (rootNode != null)
		{
		    var siteTheme = rootNode.Value<string>(themeProp);
		    if (string.IsNullOrEmpty(siteTheme))
		    {
			_logger.LogWarning($"Node '{rootNode.Name}' does not have a value for Theme picker property '{themeProp}'.");
			return base.CurrentTemplate(CurrentPage);
		    }
		    else
		    {
			var templatePath =
			    _ThemeHelperService.GetFinalThemePath(siteTheme, Theming.PathType.View, currentTemplateName);
			return View(templatePath, CurrentPage);
		    }
		}
		else
		{
		    _logger.LogWarning($"DefaultThemeController: Root Node is NULL for node #{CurrentPage.Id}");
		    return base.CurrentTemplate(CurrentPage);
		}
	    }
	    else
	    {
		_logger.LogWarning($"AppSetting 'DragonflyTheming.ThemePickerPropertyAlias' is not set.");
		return base.CurrentTemplate(CurrentPage);
	    }
	}
    }
}

If you are not using any custom controllers, you can enable the 'DefaultThemeController' via the appSettings entries, which will handle theme routing for you automatically:

"DragonflyTheming": {
	...
	"ThemePickerPropertyAlias": "Theme",
	"EnableDefaultThemeController": true
	...
}

Individual Theme Configuration

You can store information about the Theme in an XML 'Theme.config' file. See the example config at "Themes/~CopyForNewTheme/Theme.config" for the format.

If the file doesn't exist in a Theme, a default one will be created automatically.

This is most useful for storing information about the GridRenderer used for the Theme - in the event that different Themes use different rendering files.

Example for a View:

var thisTheme = ThemeHelper.GetSiteThemeName(Model);
var themeConfiguration = ThemeHelper.GetThemeConfig(thisTheme);

@Html.GetGridHtml(Model, "GridPropertyAlias", themeConfiguration.GridRenderer)

This allows you to store the basic renderers all together outside of the Themes (in the standard root-level "/Views/Partials/grid/" folder) for reuse across Themes.

Theme Conventions

Any folder in the "Themes" folder will have its name used as an available Theme, except for folders which start with a tilda (~), which will be ignored by the Theme Picker.

Any css file in the "Themes/~CssOverrides" folder will be available in the CSS Picker.

Views

The Views folder in your Theme is where all customized View files should go. Just like in the main Umbraco-provided Views folder, you can have a "Partials" subfolder, etc. When a page is routed to its View, the Theme folder will be checked first, and if a matching file is not found there, it will default to the file in the primary Views folder (so make sure all your templates do have a file in the primary Views folder - even if it is blank).

Assets

You can organize your assets folder however you like, but keep in mind that certain ThemeHelper functions rely upon knowing where the CSS and JS files are located. If you can standardize across all your themes, and make sure the AppSettings reflects your folder structure, that would be best. The config values represent the path once inside the 'Themes/MY_THEME/Assets/' folder:

"DragonflyTheming": {
	...
	"ThemedAssetsCssFolder": "css",
	"ThemedAssetsJsFolder": "js"
	...
}

If you have shared CSS/JS files (such as libraries, CSS frameworks, etc.) you can use web root relative folders (such as the Umbraco defaults "css" and "scripts" - which are available in the back-office) to hold those files, and reference them directly in your themed templates.

You can also have default fallback versions of certain CSS/JS files in the shared folders, if desired. Just make sure the config knows about your defaults:

"DragonflyTheming": {
	...
	"FallbackAssetsFolder": "/Assets",
	"FallbackAssetsCssFolder": "/css",
	"FallbackAssetsJsFolder": "/scripts",
	...
}

Config Files

If you have theme-specific configuration files of whatever type, they can be added to a Theme folder in a subfolder named "Configs". You should also provide a default fall-back version of any config file you plan to call in a folder in the "Themes" root named "~DefaultConfigs".

You can get either the current theme's file, or the default, if none exists in the current theme in your custom code using GetFinalThemePath() with Theming.PathType.Configs (or use the shortcut - GetThemeConfigsFolderPath()).

Changes from v7/v8 Version to v10 Version

If you are updating an Umbraco site which was previously using Dragonfly Theming, there are a few things you might want to know that have changed.

In terms of the code itself, there was some refactoring as well as bringing it into line with ASP.Net Core / Umbraco 10 best-practices (specifically better use of IoC / Dependency Injection). I've also added many more configurations for things which were otherwise hard-coded (such as folder locations, etc.).

Some things you will need to be aware of while updating your Themes and any custom code for v10:

  • Your single "Theme/MyTheme" folder will need to be separated into two - one for the View files and one for the static files. (See the Setup section, above for details.)
  • The static "ThemeHelper" has been converted to a non-static "ThemeHelperService". You will need to inject it into your views like this: @inject ThemeHelperService ThemeHelper (Put this into "_ViewImports.cshtml" and you won't have to add it to every View manually.)
  • The HtmlHelpers and UrlHelpers have been moved to their own static Extensions class, so they are available as before, except you will now need to pass in the ThemeHelperService. ex: @Url.ThemedAsset(ThemeHelper, thisTheme, "images/favicon.ico")
  • The HtmlHelpers RequiresThemedCss(), RequiresThemedJs(), RequiresThemedCssFolder(), and RequiresThemedJsFolder() have been removed, since ClientDependency has been replaced by Smidge. Take a look at the provided "_Master.cshtml" file (in the "Themes/~CopyForNewTheme/Views" folder) for example code using Smidge. You can also use whatever bundling framework you prefer, since now helpers are available to return themed file and folder paths.
  • All the included example Razor files have been updated as well, so if you are confused about anything, take a look at the contents of the "Themes/~CopyForNewTheme" folder, as well as "/Views/Partials/Grid/Bootstrap3WithTheming.cshtml" and "/Views/MacroPartials/~ExampleThemedMacro.cshtml"

Troubleshooting

Static Theme files are throwing 404 errors

If your CSS, JavaScript, and other theme assets don't seem to be rendering, make sure they are getting included in your publishing. Also, double-check the Setup section, above, and make sure you have all proper configurations in place.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.2.2 225 5/31/2024
1.2.0 138 4/28/2024
1.1.0 142 4/10/2024
1.0.0 1,357 10/5/2022
0.2.0 399 8/5/2022
0.1.0 395 7/20/2022