Tweaking Umbraco 7 Back Office
Heads Up!
This article is several years old now, and much has happened since then, so please keep that in mind while reading it.
In the past the only real approach has been to run from a custom build of the core which often leads to the inability to upgrade or run the risk of losing your changes. With v7 though there are a few more options available allowing you to make changes but maintain upgradability.
Custom CSS
So what if you just want to tweak some of the styling? Maybe the grid has too much whitespace? or a third party package isn't quite laying out how it should? One of the simplest tweaks you can make is overriding some of the core CSS styles, but how to inject the custom CSS? Thanks to Umbraco's plugin system, it's actually pretty easy.
Start by creating a folder inside the App_Plugins folder giving it a name of tweaks. Then inside your tweaks folder, create a blank file named package.manifest. The package manifest is just a plain old text file containing a bit of JSON which tells Umbraco about the various files and plugins contained inside your folder. Next, inside your tweaks folder, create another folder named css and inside that folder create a tweaks.css file. This will be where we write our overriding styles, but first we need to inject it. To do this, open up your package.manifest file in a text editor and enter the following:
{
css: [
'~/App_Plugins/tweaks/css/tweaks.css'
]
}
And that's it, now if you give your browser a refresh, your custom CSS file will be automatically included. Obviously, it doesn't do much right now, but if you use your browser's dev tools to inspect the UI's markup, you can find references to the various classes applied to elements and simply create overriding styles inside your custom CSS file.
Custom JS
What if you want to run some additional logic in the back office? Well, you can pretty much do the same with JavaScript too. Go ahead and create a js folder inside your tweaks folder, and inside that create a tweaks.js file. Now, back in your package.manifest file, update it to match the following:
{
javascript: [
'~/App_Plugins/tweaks/js/tweaks.js'
],
css: [
'~/App_Plugins/tweaks/css/tweaks.css'
]
}
Give your browser a refresh and tada, you've now injected a custom JS file too.
Tweaking Views
Ok, so including a JS file on its own is all well and good, but how can you actually get it to modify the UI? There are a couple of options here and both revolve around a key concept of AngularJS and that is $http interceptors.
Interceptors allow you to decorate the standard $http service with additional methods that intercept the standard request/response methods giving it extra functionality. Given that the $http service is used to load pretty much everything within an AngularJS app from views to data requests, it's a handy resource to be able to hook into. Sounds scary, but in practise it's pretty simple.
So how can we use these to help us tweak the UI? Simple, by intercepting the requests made via the $http service you can alter that request before it gets processed, thus redirecting to an alternative location.
So let's say for example you wanted to make a change to a core Umbraco view, such as the listview.html file. What we can do then is take a copy of the listview.html file, and place it inside a views folder within a tweaks plugin folder, and then inside our custom JS file add the following:
angular.module('umbraco.services').config([
'$httpProvider',
function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'request': function (request) {
// Redirect any requests for the listview to our custom list view UI
if (request.url === "views/propertyeditors/listview/listview.html")
request.url = '/App_Plugins/tweaks/views/listview.html';
return request || $q.when(request);
}
};
});
}]);
Now, whenever a listview is requested, it'll now use our custom listview.html file rather than the core one, so we can happily tweak the markup without fear of losing our changes should we wish to upgrade (of course, the listview might get updated, so you'd need to keep your file in sync, but at least you won't just have your changes overwritten).
Tweaking Logic
You aren't just limited to overriding view requests though. You can pretty much intercept any API request too. So if you wished a service worked slightly differently, then you can just as easily detect the API URL and redirect it to your own custom API controller (of course, it's not advised that you go overriding the core APIs though as you could easily introduce some security holes, but it's good to know you can do this).
However there is another way you can tweak logic without replacing APIs. So far we've been intercepting request calls for the $http service, but you can also intercept the response handler too, gaining access the data retrieved from the API before the CMS starts to process it, for example, tweaking the data for a content editor:
angular.module('umbraco.services').config([
'$httpProvider',
function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'response': function (response) {
// Intercept requests content editor data responses
if (response.config.url.indexOf("/umbraco/backoffice/UmbracoApi/Content/GetById") === 0){
//TODO: Tweak response.data
}
return response;
}
};
});
}]);
With code like this, we can intercept the raw data used to construct the content editor page and do various things like hiding tabs or properties from view (if you do this you'll need to keep track of what you remove and re-add them back in on save as the back end is expecting them to be there, but you get the idea).
So there you have it, four options for tweaking the back office whilst keeping some sense on upgradability. I know I haven't gone too much into detail about how to do specific things, but I hope these give you the keys to see how you might be able to tweak things without having to go for a completely custom build.
And to reiterate what I said from the start, if you are making tweaks that could benefit everyone, please do think about submitting a PR to the core.
Hacky christmas everyone :)
Matt Brailsford
Matt is on Twitter as @mattbrailsford