DEV Community

Jesper Mayntzhusen
Jesper Mayntzhusen

Posted on • Updated on

Umbraco backoffice listview + infinite editing - part 1

Hi my name is Jesper 👋

I've recently started a new job as an Umbraco developer at a company called Limbo. I recently had to learn how to create a custom listview that uses infinite editing and thought I would share.

This blogpost will show you how to create a custom listview in the Umbraco backoffice, for the example we will create a custom dashboard to show it in, but it could just as well be a content app / section / dialogue menu / anything else! Let's dig in!

Step 1 - Creating a simple dashboard in the backoffice

To create our dashboard we need to create a few files inside a new folder. So first we create a folder called "CustomDashboard" inside App_Plugins. In that folder we create the following files:

image

If you are unfamiliar with this process - Umbraco automatically registers Backoffice extensions on startup - it runs through things in the App_Plugins folder, and everything registered in a package.manifest file will be added.

So for the dashboard to show up we need to fill out the package.manifest file, and then restart the site so it gets picked up!

Here is the initial content of each file:

package.manifest
{
    "dashboards":  [
        {
            "alias": "myCustomDashboard",
            "view":  "/App_Plugins/CustomDashboard/dashboard.html",
            "sections":  [ "content" ],
            "weight": 15
        }
    ],
    "javascript": [
        "~/App_Plugins/CustomDashboard/dashboard.controller.js"
  ]
}

Note: We set the section to be content and the weight to 15, which will place it between the welcome dashboard and redirects. Read more on default dashboard weights.

dashboard.html
<div ng-controller="MyDashboardController as vm">
    <h1>Welcome to my dashboard!</h1>
</div>

dashboard.controller.js
(function () {
    "use strict";

    function DashboardController() {

    }

    angular.module("umbraco").controller("MyDashboardController", DashboardController);

})();

Note: It is intentional I register the controller at the bottom here as "MyDashboardController", want to make sure it's not conflicting with any of Umbracos own namespaces!

lang/en-US.xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<language>
  <area alias="dashboardTabs">
    <key alias="myCustomDashboard">Custom Dashboard</key>
  </area>
</language>

Note: This language file is nessecary to show the dashboard name in the backoffice. Dashboard names are localized by default, the key alias is the alias of the dashboard set in the package.manifest

After I generate and save all these files I will restart the site (adding a space to web.config and saving is a super quick way of doing this), and the dashboard is ready for us to start working on a listview:

image

Step 2 - Getting data in your controller and output it

Alright, let's get some content to populate our listview with. In most cases like this you probably have some data from an external source or you may gather some data from Umbraco itself.

To make it fast and easy I will get a list of content that has a scheduled publish date within the coming week. I've created a Backoffice authorized API controller:

DashboardController.cs
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.WebApi;

namespace Backoffice.Controllers
{
    public class DashboardController : UmbracoAuthorizedApiController
    {
        private readonly IContentService _contentService;

        public DashboardController(IContentService contentService)
        {
            _contentService = contentService;
        }

        public IEnumerable<IContent> GetContentForReleaseNextWeek ()
        {
            var endDate = DateTime.Now.AddDays(7);

            return _contentService.GetContentForRelease(endDate);                
        }
    }
}

You can read more about UmbracoAuthorizedApiControllers in the Umbraco docs
Only thing you need to know is it is automatically authorized from the backoffice, and not from elsewhere. It is also automatically routed to ~/Umbraco/backoffice/Dashboard/GetContentForReleaseNextWeek

If you are in doubt about where to put your C# controller, I would recommend a seperate Class library that is compiled into a dll for your website. Here is my setup:

image

Let's get data from this API in our dashboard Angular controller!

First I like to set up an init function that I call at the end of the controller, which can then be used to run functions you want to run right when the dashboard is loaded. Then you can also set a vm.loading variable so you can display the built in <umb-load-indicator>. We also want to inject the Angular $http service for calling our Api controller. Here is an initial setup:

(function () {
    "use strict";

    function DashboardController($http) {

        var vm = this;

        function init() {            
            getContentForRelease();            
        }

        function getContentForRelease() {
            vm.loading = true;
            $http({
                method: 'GET',
                url: '/Umbraco/backoffice/api/Dashboard/GetContentForReleaseNextWeek',
                headers: {
                    'Content-Type': 'application/json'
                }
            }).then(function (response) {
                console.log(response); // logging the response so we know what to do next!

                vm.loading = false;
            });
        }

        init();

    }

    angular.module("umbraco").controller("MyDashboardController", DashboardController);

})();
Enter fullscreen mode Exit fullscreen mode

To start with I am just console.log()'ing the response so I know the data structure for the next step:

image

So now that we know the data coming back, let's amend the getContentForRelease function:

function getContentForRelease() {
    vm.loading = true;
    $http({
        method: 'GET',
        url: '/Umbraco/backoffice/api/Dashboard/GetContentForReleaseNextWeek',
        headers: {
            'Content-Type': 'application/json'
        }
    }).then(function (response) {
        console.log(response); // logging the response so we know what to do next!

        if (response.data.length > 0) {
            vm.weHaveRecords = true;
            vm.records = response.data;
        } else {
            vm.weHaveRecords = false;
        }               

        vm.loading = false;
    });
}
Enter fullscreen mode Exit fullscreen mode

and the view:

<div ng-controller="MyDashboardController as vm">
    <h1>Welcome to my dashboard!</h1>

    <umb-box ng-if="vm.weHaveRecords">
        <umb-box-content>
            <!-- The following is a super nice way to just get an overview of the data you get and make sure the data is there! -->
            {{vm.records | json}}
        </umb-box-content>
    </umb-box>

    <umb-box ng-if="!vm.weHaveRecords">
        <umb-box-content>
            No records at this point!
        </umb-box-content>
    </umb-box>

    <umb-load-indicator ng-if="vm.loading">
    </umb-load-indicator>
</div>
Enter fullscreen mode Exit fullscreen mode

And it outputs the data!

image

Step 3 - Getting more relevant data

So let's look at the data we have available and decide what we want to display in the listview:

image

So when looking at this data, here is what I think would make sense to include in the listview:

  • Content name

Not a lot more useful in the data - here is the thing though, I would probably want some other data as well:

  • Url
  • Scheduled publish date
  • Who scheduled it for publishing
  • Maybe a breadcrumb?

Getting the url

Now the Url you can kind of generate, since the data has both an Id and Key - if you don't know already you can visit any content node on a fixed path with id or guid, so with the data above I know I can go to both:
https://localhost:44303/umbraco#/content/content/edit/202cdc2f-1a45-40f4-a653-a7b321e1c54c
https://localhost:44303/umbraco#/content/content/edit/1110

So I can use either one as the URL. The other info is not immediately available though, so let's see what we can do!

Getting the scheduled publish date

This proved to be a bit difficult, but I found a way to get it from IContent. So I decided to change the API controller a little bit:

DashboardController.cs

public class DashboardController : UmbracoAuthorizedApiController
{
    private readonly IContentService _contentService;

    public DashboardController(IContentService contentService)
    {
        _contentService = contentService;
    }

    public List<ContentWithScheduledInfo> GetContentForReleaseNextWeek ()
    {
        var endDate = DateTime.Now.AddDays(7);

        var response = _contentService.GetContentForRelease(endDate);

        var contentWithScheduledInfo = new List<ContentWithScheduledInfo>();

        foreach(IContent item in response)
        {
            contentWithScheduledInfo.Add(new ContentWithScheduledInfo
            {
                Content = item,
                ScheduleInfo = item.ContentSchedule
            });
        }

        return contentWithScheduledInfo; 
    }
}

public class ContentWithScheduledInfo
{
    public IContent Content { get; set; }
    public ContentScheduleCollection ScheduleInfo { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

The new content output looks like this:

image

So in response.data[0].ScheduleInfo.FullSchedule[0].Date we have the date for the scheduling, it also contains an Action which is either 0 or 1 based on this enum so if it's 0 it's a scheduled publish and a 1 is a scheduled unpublish.

Outro

This blogpost is getting quite long at this point, so I've decided to split it up in atleast 2 parts 😄

In the next part we will look at how we can get info on who scheduled the publishing, present all the data in a listview and try to open the content nodes in an infinite editor window when you click a node!

If you like this post, have any feedback, suggestions, improvements to my hacky code or anything else please let me know on Twitter - @jespermayn

Latest comments (1)

Collapse
 
fatmazayedsayed profile image
fatmazayedsayed

you made my day
that what i want