How to create a custom section and dashboard in Umbraco 8

Posted written by Paul Seal on October 13, 2021 Umbraco

In this post I share some code with you that I used to create a custom section and dashboard in Umbraco 8.

The code comes from a discontinued package I made a while back. I might do a mini tutorial series on this one day. Credit to Kevin Jump who created the Do Stuff With Umbraco repository that I think I learned most of this from.

Anyway, here's what you need.

A dashboard.html file stored here "/App_Plugins/GAEvents/dashboards/dashboard.html"

<div ng-controller="GAEventsDashboardController as vm">

    <umb-load-indicator ng-if="vm.loading">
    </umb-load-indicator>

    <div ng-if="!vm.loading">
        <div class="alert alert-warning" ng-if="vm.licenseStatus == 1">
            You need a license before you can use this in production <strong><a href="http://www.gaevents.net/setup-instructions/" target="_blank">more info</a></strong>
        </div>

        <div class="alert alert-danger" ng-if="vm.licenseStatus == 3">
            Your license is invalid <strong><a href="http://www.gaevents.net/setup-instructions/" target="_blank">more info</a></strong>
        </div>

        <h1>GA Events</h1>
        <p>This package allows you to track events on your website, such as downloading PDFs, clicking on links, submitting forms etc.
            It helps you learn how your users are using your website. The data is sent straight to your Google Analytics account.</p>
        <p>Using this package you can create almost any event you wish, by hooking into events such as click, and submit and targeting specific elements for these events.</p>

        <h2>Getting set up</h2>
        <div>
            <p>For this package to work you need to use Google's analytics.js</p>
            <p>In the below code snippet, replace the UA-XXXXXXXXX-1 with your analytics id from Google Analtyics.</p>
            <p>Make sure you put this code after the opening body tag in your master template or on all of the pages you wish to track events on.</p>
            <pre>
&amp;lt;!-- Google Analytics --&amp;gt;
&amp;lt;script&amp;gt;
&amp;nbsp; (function(i,s,o,g,r,a,m){i[&amp;#39;GoogleAnalyticsObject&amp;#39;]=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,&amp;#39;script&amp;#39;,&amp;#39;https://www.google-analytics.com/analytics.js&amp;#39;,&amp;#39;ga&amp;#39;);

  ga(&amp;#39;create&amp;#39;, &amp;#39;UA-XXXXXXXXX-1&amp;#39;, &amp;#39;auto&amp;#39;);
&amp;lt;/script&amp;gt;
&amp;lt;!-- End Google Analytics --&amp;gt;
</pre>
            <p>On the front end of your site, include the gaEvents script which comes with this package:</p>
            <pre>
&amp;lt;script src=&amp;quot;/scripts/gaEvents.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;</pre>
            <p>Now all you need to do is create some events and you will start seeing them show up in the Events section of your Google Analytics account.</p>
        </div>
        <div>
            <button class="btn btn-primary btn-large" ng-click="vm.createBlank()">
                <i class="icon-edit"></i>
                Create New Event
            </button>
        </div>
    </div>
</div>

dashboardController.js file stored here "/App_Plugins/GAEvents/dashboards/dashboardController.js"

(function () {
    'use strict';

    function dashboardController($scope, $location, navigationService, gaEventsDataResource) {
        var vm = this;
        vm.loading = true;

        init();

        // functions
        vm.createBlank = createBlank;

        function createBlank() {
            $location.path('/gaEventsSection/GAEventsTree/edit/-1').search('create', 'true');
            navigationService.hideMenu();
        }

        function init() {
            vm.licenseStatus = 0;

            gaEventsDataResource.getLicense().then(function (response) {
                vm.licenseStatus = response.Status;
                vm.loading = false;
            });
        }

    }

    angular.module('umbraco')
        .controller('GAEventsDashboardController', dashboardController);
})();

Dashboard class to register the dashboard. GAEventsDashboard.cs

using System;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;

namespace GAEvents.Core.Dashboards
{
    [Weight(0)]
    public class GAEventsDashboard : IDashboard
    {
        public string Alias => "GAEventsDashboard";

        public string[] Sections => new[]
        {
            GAEvents.SectionName
        };

        public string View => "/App_Plugins/GAEvents/dashboards/dashboard.html";

        public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
    }
}

Section class GAEventsSection.cs

using Umbraco.Core.Models.Sections;

namespace GAEvents.Core.Sections
{
    /// <summary>
    ///  Custom Section Interface, see GAEventsSectionComposer for how
    ///  this is registered in umbraco, GAEventsSectionComponent ads the 
    ///  section to the admin group so it appears by default.
    /// </summary>
    public class GAEventsSection : ISection
    {
        /// <summary>
        ///  Alias for the section
        /// </summary>
        /// <remarks>
        ///  It makes sense to make this a constant (you don't have to)
        ///  because you end up refrencing it in things like tree's and 
        ///  dashboards.
        /// </remarks>
        public const string SectionAlias = "gaEventsSection";

        #region ISection Interface 

        /// <summary>
        ///  Alias of section, used in urls, trees etc
        /// </summary>
        public string Alias => SectionAlias;

        /// <summary>
        ///  name of the section - this isn't what the user sees
        ///  they see a value from the lang file
        /// </summary>
        public string Name => "GA Events";

        #endregion
    }
}

Composer and Component to regsiter the section GAEventsSectionComposer

using GAEvents.Core.Sections;
using System;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Services;
using Umbraco.Web;

namespace GAEvents.Core.Composing
{
    class GAEventsSectionComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            // register the section 
            composition.Sections().Append<GAEventsSection>();

            // register the component that adds the section
            // to the admin group 
            composition.Components().Append<GAEventsSectionComponent>();
        }
    }

    /// <summary>
    ///  Umbraco Component (runs at startup) 
    /// </summary>
    /// <remarks>This doesn't run unless it's registered in the Composer</remarks>
    public class GAEventsSectionComponent : IComponent
    {
        private readonly IUserService userService;
        private readonly IKeyValueService keyValueService;
        private readonly IProfilingLogger logger;

        private const string setupKey = "gaEventsSection_installed";

        public GAEventsSectionComponent(
            IUserService userService,
            IKeyValueService keyValueService,
            IProfilingLogger logger)
        {
            this.userService = userService;
            this.keyValueService = keyValueService;
            this.logger = logger;
        }


        public void Initialize()
        {
            // a quick version of only run once. 
            // for a more complete set of methods see Migrations samples
            var installed = keyValueService.GetValue(setupKey);
            if (installed == null || installed != "installed")
            {
                AddSection("admin", GAEventsSection.SectionAlias);
                keyValueService.SetValue(setupKey, "installed");
            }
        }

        /// <summary>
        ///  add the given section to the given user group 
        /// </summary>
        /// <remarks>
        ///  if umbraco throws an exception here, we capture it
        ///  because if we don't umbraco won't startup and that
        ///  might be a bad thing :( 
        /// </remarks>
        /// <param name="groupAlias"></param>
        /// <param name="sectionAlias"></param>
        private void AddSection(string groupAlias, string sectionAlias)
        {
            using (logger.DebugDuration<GAEventsSectionComponent>($"Adding Section {sectionAlias} to {groupAlias}"))
            {
                var group = userService.GetUserGroupByAlias(groupAlias);
                if (group != null)
                {
                    if (!group.AllowedSections.Contains(sectionAlias))
                    {
                        group.AddAllowedSection(sectionAlias);

                        try
                        {
                            userService.Save(group);
                            logger.Info<GAEventsSectionComponent>($"Section {sectionAlias} added to {groupAlias} group");
                        }
                        catch (Exception ex)
                        {
                            logger.Warn<GAEventsSectionComponent>("Error adding section {0} to group {1} [{2}]",
                                sectionAlias, groupAlias, ex.Message);
                        }
                    }
                }
            }
        }

        public void Terminate()
        {
            // umbraco is shutting down -
        }
    }
}

I hope this helps you out one day.