<commit_before>---
layout: post
title: Umbraco Setup and Configuration
---

Describe umbraco, summaries what we're going to do
Custom content goes in App_Plugins folder

Description of how to set up an Umbraco website using TeamCity and Octopus deploy for building and deployment.

Will cover: (Add links)
- Setup
    - Installation
    - Logging & Dependency Injection
    - AU Hacks
- Build and deployment config
    - Folder permissions
    - Virtual folders for media
    - Forcing cache refresh on deployment
- Creating new backoffice Sections
    - Create menus
    - Javascript file structure
    - Templates
    - Controllers
        - Types
        - Code snippets
    - Downloading from API controller
    - Uploading with parameters

# Umbraco Project Setup

## Logging and Dependency Injection Container Setup

Using Autofac for the IoC setup
- Autofac
- Autofac.WebApi - WeApi dependency injection
- Autofac.Mvc5 - MVC dependency injection
- Autofac.Web - WebForms dependency injection

Using Serilog for the logging
- Serilog
- Serilog.Sinks.Seq - For writing logs to seq
- Serilog.WebClassic - For logging web content
- Serilog.WebClassic.WebApi - For logging webapi
- Serilog.Enrichers.Process - Supplies information on the running process

As Umbraco requires its UmbracoApplication class to be instantiated as part of application startup for all of its features to work correctly. UmbracoApplication already implements many of the normally used startup callbacks preventing them from being used, but provides additional callbacks that can be overridden in an inheriting class. 

Example implementation of Global.asax, registering Autofac for dependency injection and Serilog for logging

{% highlight c# %}
public class Global : UmbracoApplication, IContainerProviderAccessor // IContentProviderAccessor is required for WebForms dependency injection
{
    protected override void OnApplicationError(object sender, EventArgs e)
    {
        var exception = Server.GetLastError();
        var httpException = exception as HttpException;
        if (httpException != null && httpException.GetHttpCode() >= 500)
        {
            Log.Error(httpException, "Application error");
        }
    }
    protected override IBootManager GetBootManager()
    {
        return new WebBootManager(this);
    }

    static IContainerProvider _containerProvider;
    public IContainerProvider ContainerProvider
    {
        get { return _containerProvider; }
    }

    // Equivalent of Application_Start
    protected override void OnApplicationStarted(object sender, EventArgs e)
    {
        base.OnApplicationStarted(sender, e);

        var runtimeEnvironment = ConfigurationManager.AppSettings["Environment"];
        var seqUrl = ConfigurationManager.AppSettings["SeqUrl"];

        var log = new LoggerConfiguration()
            .MinimumLevel.Is(LogEventLevel.Debug)
            .Enrich.WithProcessId()
            .Enrich.WithProperty("MachineName", Environment.MachineName)
            .Enrich.WithProperty("Source", "Umbraco Web")
            .Enrich.WithProperty("Environment", runtimeEnvironment)
            .Enrich.With<UserNameEnricher>()
            .Enrich.With<HttpRequestUrlEnricher>()
            .Enrich.With<HttpRequestUserAgentEnricher>()
            .Enrich.With<HttpSessionIdEnricher>()
            .Enrich.With<HttpRequestIdEnricher>()
            .Enrich.FromLogContext()
            .WriteTo.Seq(seqUrl)
            .CreateLogger();
        Log.Logger = log;

        var builder = new ContainerBuilder();
        var config = GlobalConfiguration.Configuration;

        // Register Umbraco Context, MVC Controllers and API Controllers.
        builder.Register(c => UmbracoContext.Current).AsSelf();
        builder.Register(c => ApplicationContext.Current.Services.ContentService).As<Umbraco.Core.Services.IContentService>();

        builder.RegisterControllers(Assembly.GetExecutingAssembly()); // Register our MVC controllers
        builder.RegisterApiControllers(typeof(UmbracoApplication).Assembly); // Register umbraco backoffice controllers
        builder.RegisterApiControllers(typeof(Global).Assembly); // Register our API controllers

        builder.RegisterInstance(log).As<ILogger>();
        builder.Register(c => new HttpContextWrapper(HttpContext.Current)).As<HttpContextBase>();
        //builder.RegisterType<PagingService>().As<IPagingService>();
        //builder.RegisterType<WebApiCsvGenerator>().As<IWebApiCsvGenerator>();

        var container = builder.Build();
        _containerProvider = new ContainerProvider(container);
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        RouteConfig.RegisterRoutes(RouteTable.Routes);
        DependencyResolver.SetResolver(new AutofacDependencyResolver(_containerProvider.ApplicationContainer));

        AppDomain.CurrentDomain.UnhandledException += (s, args) =>
        {
            var exp = (Exception)args.ExceptionObject;
            Log.Error(exp, "Unhandled exception");
        };

        Log.Information("Website Started.");
    }
}
{% endhighlight %}

## AU Language support
Note for Australian languages: Umbraco doesn't support Australian English at the time of writing, only UK English and American English. Setting UK English gives the correct date formats, but uses the pound symbol for currency, while American English gives the dollar for currency, but uses the wrong date format. To create an EN_AU locale for umbraco, copy the existing US English or UK English and rename the file to "en_au.xml" replace the language element with `<language alias="en_au" intName="English (AU)" localName="English (AU)" lcid="" culture="en-AU">`. This will allow you to set users to have Australian English as their language in umbraco.

[Sample en_au language file]({{ site.url }}/download/en_au.xml)

The moment locale did sometimes not set correctly to en-au, so I had to add "moment.locale('en-au');" to line 10682 and 11026 in "umbraco.controllers.js" after the moment script was loaded to ensure it was working correctly.

## Build and deployment
### Refreshing cached files
Umbraco uses the client dependency framework for caching resources when running in release mode. A cache refresh is triggered when the value of the version attribute of the ClientDependency element in the ClientDependency.config file. As Umbraco does not automatically update this value, it needs to be manually updated as part of the build and deployment process, or else clients will have issues with cached resources.

A simple method of resolving this is to set the content of the attribute to a unique numeric value for the file .ie "0000000000", and the a find and replace build step to perform the substitution.
As the file is XML, powershell's xml support could also be used to perform the substitution.

### Setting folder permissions
The configuration screens in umbraco require the website to be able to modify files in the config and app_data folders. This can be configured as part of the deployment process using the following powershell script in octopus deploy:

{% highlight powershell %}
$siteRoot = $OctopusParameters['Octopus.Action[Deploy Step Name].Output.Package.InstallationDirectoryPath']
$siteName = $OctopusParameters["Site name"]
$configPath = Join-Path $siteRoot 'config'
$appDataPath = Join-Path $siteRoot 'App_Data'

Write-Host $SiteName

$acl = Get-ACL $configPath
$accessRule= New-Object System.Security.AccessControl.FileSystemAccessRule(`
    "IIS APPPOOL\$siteName", `
    "full", `
    "ContainerInherit,Objectinherit", `
    "InheritOnly", `
    "Allow")
$acl.AddAccessRule($accessRule)
Set-Acl $configPath $acl

$acl2 = Get-ACL $appDataPath
$accessRule2= New-Object System.Security.AccessControl.FileSystemAccessRule(`
    "IIS APPPOOL\$siteName", `
    "full", `
    "ContainerInherit,Objectinherit", `
    "InheritOnly", `
    "Allow")
$acl.AddAccessRule($accessRule2)
Set-Acl $appDataPath $acl2
{% endhighlight %}

# Custom content

Extensions can go in App_Plugins to provide separation from standard umbraco content/features
All plugin.manifest files are loaded, when you're in the backoffice, regardless of what page you're on. Because of this, a 'Shared' plugin is not required, but is neater.



For new section backoffice menu:

Create new application - Provides the new menu ico

{% highlight c# %}
[Application("CommerceManagement", "CommerceManagement", "management.gif", 15)]
public class CommerceManagementApplication : IApplication
{
}
{% endhighlight %}

Create new menu tree - Defines the navigation structure within a tree

{% highlight c# %}
[PluginController("CustomSection")]
[Umbraco.Web.Trees.Tree("CustomSection", "CustomSectionTree", "Custom Section", iconClosed: "icon-doc")]
public class CommerceManagementTreeController : TreeController
{
    protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
    {<commit_msg>Added section on media and security settings<commit_after>---
layout: post
title: Umbraco Setup and Configuration
---

Describe umbraco, summaries what we're going to do
Custom content goes in App_Plugins folder

Description of how to set up an Umbraco website using TeamCity and Octopus deploy for building and deployment.

Will cover: (Add links)
- Setup
    - Installation
    - Logging & Dependency Injection
    - AU Hacks
- Build and deployment config
    - Folder permissions
    - Virtual folders for media
    - Forcing cache refresh on deployment
- Creating new backoffice Sections
    - Create menus
    - Javascript file structure
    - Templates
    - Controllers
        - Types
        - Code snippets
    - Downloading from API controller
    - Uploading with parameters

# Umbraco Project Setup

## Logging and Dependency Injection Container Setup

Using Autofac for the IoC setup
- Autofac
- Autofac.WebApi - WeApi dependency injection
- Autofac.Mvc5 - MVC dependency injection
- Autofac.Web - WebForms dependency injection

Using Serilog for the logging
- Serilog
- Serilog.Sinks.Seq - For writing logs to seq
- Serilog.WebClassic - For logging web content
- Serilog.WebClassic.WebApi - For logging webapi
- Serilog.Enrichers.Process - Supplies information on the running process

As Umbraco requires its UmbracoApplication class to be instantiated as part of application startup for all of its features to work correctly. UmbracoApplication already implements many of the normally used startup callbacks preventing them from being used, but provides additional callbacks that can be overridden in an inheriting class. 

Example implementation of Global.asax, registering Autofac for dependency injection and Serilog for logging

{% highlight c# %}
public class Global : UmbracoApplication, IContainerProviderAccessor // IContentProviderAccessor is required for WebForms dependency injection
{
    protected override void OnApplicationError(object sender, EventArgs e)
    {
        var exception = Server.GetLastError();
        var httpException = exception as HttpException;
        if (httpException != null && httpException.GetHttpCode() >= 500)
        {
            Log.Error(httpException, "Application error");
        }
    }
    protected override IBootManager GetBootManager()
    {
        return new WebBootManager(this);
    }

    static IContainerProvider _containerProvider;
    public IContainerProvider ContainerProvider
    {
        get { return _containerProvider; }
    }

    // Equivalent of Application_Start
    protected override void OnApplicationStarted(object sender, EventArgs e)
    {
        base.OnApplicationStarted(sender, e);

        var runtimeEnvironment = ConfigurationManager.AppSettings["Environment"];
        var seqUrl = ConfigurationManager.AppSettings["SeqUrl"];

        var log = new LoggerConfiguration()
            .MinimumLevel.Is(LogEventLevel.Debug)
            .Enrich.WithProcessId()
            .Enrich.WithProperty("MachineName", Environment.MachineName)
            .Enrich.WithProperty("Source", "Umbraco Web")
            .Enrich.WithProperty("Environment", runtimeEnvironment)
            .Enrich.With<UserNameEnricher>()
            .Enrich.With<HttpRequestUrlEnricher>()
            .Enrich.With<HttpRequestUserAgentEnricher>()
            .Enrich.With<HttpSessionIdEnricher>()
            .Enrich.With<HttpRequestIdEnricher>()
            .Enrich.FromLogContext()
            .WriteTo.Seq(seqUrl)
            .CreateLogger();
        Log.Logger = log;

        var builder = new ContainerBuilder();
        var config = GlobalConfiguration.Configuration;

        // Register Umbraco Context, MVC Controllers and API Controllers.
        builder.Register(c => UmbracoContext.Current).AsSelf();
        builder.Register(c => ApplicationContext.Current.Services.ContentService).As<Umbraco.Core.Services.IContentService>();

        builder.RegisterControllers(Assembly.GetExecutingAssembly()); // Register our MVC controllers
        builder.RegisterApiControllers(typeof(UmbracoApplication).Assembly); // Register umbraco backoffice controllers
        builder.RegisterApiControllers(typeof(Global).Assembly); // Register our API controllers

        builder.RegisterInstance(log).As<ILogger>();
        builder.Register(c => new HttpContextWrapper(HttpContext.Current)).As<HttpContextBase>();
        //builder.RegisterType<PagingService>().As<IPagingService>();
        //builder.RegisterType<WebApiCsvGenerator>().As<IWebApiCsvGenerator>();

        var container = builder.Build();
        _containerProvider = new ContainerProvider(container);
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        RouteConfig.RegisterRoutes(RouteTable.Routes);
        DependencyResolver.SetResolver(new AutofacDependencyResolver(_containerProvider.ApplicationContainer));

        AppDomain.CurrentDomain.UnhandledException += (s, args) =>
        {
            var exp = (Exception)args.ExceptionObject;
            Log.Error(exp, "Unhandled exception");
        };

        Log.Information("Website Started.");
    }
}
{% endhighlight %}

## AU Language support
Note for Australian languages: Umbraco doesn't support Australian English at the time of writing, only UK English and American English. Setting UK English gives the correct date formats, but uses the pound symbol for currency, while American English gives the dollar for currency, but uses the wrong date format. To create an EN_AU locale for umbraco, copy the existing US English or UK English and rename the file to "en_au.xml" replace the language element with `<language alias="en_au" intName="English (AU)" localName="English (AU)" lcid="" culture="en-AU">`. This will allow you to set users to have Australian English as their language in umbraco.

[Sample en_au language file]({{ site.url }}/download/en_au.xml)

The moment locale did sometimes not set correctly to en-au, so I had to add "moment.locale('en-au');" to line 10682 and 11026 in "umbraco.controllers.js" after the moment script was loaded to ensure it was working correctly.

## Build and deployment
### Refreshing cached files
Umbraco uses the client dependency framework for caching resources when running in release mode. A cache refresh is triggered when the value of the version attribute of the ClientDependency element in the ClientDependency.config file. As Umbraco does not automatically update this value, it needs to be manually updated as part of the build and deployment process, or else clients will have issues with cached resources.

A simple method of resolving this is to set the content of the attribute to a unique numeric value for the file .ie "0000000000", and the a find and replace build step to perform the substitution.
As the file is XML, powershell's xml support could also be used to perform the substitution.

### Setting folder permissions
The configuration screens in umbraco require the website to be able to modify files in the config and app_data folders. This can be configured as part of the deployment process using the following powershell script in octopus deploy:

{% highlight powershell %}
$siteRoot = $OctopusParameters['Octopus.Action[Deploy Step Name].Output.Package.InstallationDirectoryPath']
$siteName = $OctopusParameters["Site name"]
$configPath = Join-Path $siteRoot 'config'
$appDataPath = Join-Path $siteRoot 'App_Data'

Write-Host $SiteName

$acl = Get-ACL $configPath
$accessRule= New-Object System.Security.AccessControl.FileSystemAccessRule(`
    "IIS APPPOOL\$siteName", `
    "full", `
    "ContainerInherit,Objectinherit", `
    "InheritOnly", `
    "Allow")
$acl.AddAccessRule($accessRule)
Set-Acl $configPath $acl

$acl2 = Get-ACL $appDataPath
$accessRule2= New-Object System.Security.AccessControl.FileSystemAccessRule(`
    "IIS APPPOOL\$siteName", `
    "full", `
    "ContainerInherit,Objectinherit", `
    "InheritOnly", `
    "Allow")
$acl.AddAccessRule($accessRule2)
Set-Acl $appDataPath $acl2
{% endhighlight %}

### Media Folder
As users can upload content into umbraco, it is recommended to set Umbraco's media directory to a directory that will be persisted between deployments. This can be done by changing the umbracoMediaPath appsetting to point to a folder outside of the deployment directory, or configuring the website to have a virtual directory for the default "~/media" path. [Umbraco App Settings](https://our.umbraco.org/Documentation/Reference/Config/webconfig/)

### Security
By default umbraco uses an implmentation of the ASP.net identity provider for internal users and external membership, but does not have very practical 
security options enabled. 

If your site is using SSL (Which it should), require it for logging into umbraco with the appsetting `<add key="umbracoUseSSL" value="true" />`

I recommend setting the following attributes on the UsersMembershipProvider:
enablePasswordRetrieval="false" 
enablePasswordReset="true" 
requiresQuestionAndAnswer="false" 
passwordFormat="<PASSWORD>" 
allowManuallyChangingPassword="<PASSWORD>"

 [Umbraco Security settings](https://our.umbraco.org/Documentation/Reference/Security/)

# Custom content

Extensions can go in App_Plugins to provide separation from standard umbraco content/features
All plugin.manifest files are loaded, when you're in the backoffice, regardless of what page you're on. Because of this, a 'Shared' plugin is not required, but is neater.



For new section backoffice menu:

Create new application - Provides the new menu ico

{% highlight c# %}
[Application("CommerceManagement", "CommerceManagement", "management.gif", 15)]
public class CommerceManagementApplication : IApplication
{
}
{% endhighlight %}

Create new menu tree - Defines the navigation structure within a tree

{% highlight c# %}
[PluginController("CustomSection")]
[Umbraco.Web.Trees.Tree("CustomSection", "CustomSectionTree", "Custom Section", iconClosed: "icon-doc")]
public class CommerceManagementTreeController : TreeController
{
    protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
    {
{'fraction_non_alphanumeric': 0.07791867533011947, 'fraction_numerical': 0.0031439949696080486, 'mean_word_length': 4.0436046511627906, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 42, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5520402', 'n_tokens_mistral': 4976, 'n_tokens_neox': 4772, 'n_words': 1771}