Editor-proofing Umbraco

Editors. They're the whole point of a CMS really, aren't they? You build a nice CMS for them to use, and then they get busy doing the day to day entry of the content. Umbraco makes it very easy for them to do this, but it also makes it quite easy for them to do things that you would rather they didn't.......

Let me give you an example. You've built a large(ish) site, and you have a settings section that contains things like sidebar boxes and adverts. So far, so good, but what happens when your client, who likes to tinker with stuff, deletes the settings section by accident? Nuking your carefully constructed settings? Then lets say one of the other editors empties the recycling bin. When they phone you up to ask you why the site is suddenly broken, the chances are that you'll look a bit like this.......

EEEEEEEEEEEEEEEEEEEEEEEEDIIIITOOOOOOOOOOOOORS!!!
EEEEEEEEEEEEEEEEEEEEEEEEDIIIITOOOOOOOOOOOOORS!!!

So how do you stop the client from deleting, copying and moving things that they shouldn't? Handily, the API exposes a bunch of methods that you can hook into to remove menu items etc. You can code this for each project you do, but it's REALLY boring (trust me, I used to do it for each project, and it sucked). So I wrote a package that would allow me to do it in a config file, no coding required!!!

You can download the package from it's our.umbraco project page. Install it just like you would with any other package, and you're good to go. Note: the package is not currently compatible with Umbraco 7. I'm working on a compatible version at the moment.

Here's an example site content tree with some settings nodes. Notice the context menu contains all sorts of things that we probably don't want the editors to do with the node, like delete, copy set hostnames etc.

Look at all those nasty options! *shudder*
Look at all those nasty options! *shudder*

When the package installs, it creates a new config file called "customMenus.config" in the /config folder of your Umbraco site. The config file looks a bit like this:

<?xml version="1.0" encoding="utf-8" ?>
<!--
ignoreForAdmin: set this to true if you'd like to show ALL menu options for members of the Administrator group

useInMediaSection: set this to true if you want the rules to apply to Media items as well

Custom Menus Rules:

You can add as many rules as you like, each rule has the following properties:
  docTypeAlias - the doc type alias the rule applies to (case sensitive)
  nodeId - the node the rule applies to (use instead of docTypeAlias)
  clickAction - if you set some javascript / a link here, will override the default click action for the node
  mentItems - a comma separated list of menu items (Case sensitive, see list below for common values)
  removeMenuItems - a comma separated list of menu items to remove (Case sensitive, see list below for common values)

This package should support any menu items that are installed in your application and applicable to contet items.
You should use the Alias of the menu actions, for reference, I have include the list of default content actions that come 
with Umbraco here:

assignDomain - assign host name
auditTrail - view audit trail
browse - browe node
copy - copy node
delete - delete node
emptyTrashcan - empty recycle bin
liveEdit - live editor link
move - move content
create - create new content
notify - notification link
protect - member authentication link
publish - publish page
refreshNode - reload nodes
rights - set admin user permissions list
rollback - rollback link
sendToTranslate - send to translation link
sort - sort documents
sendtopublish - send to publis
translate - translate link
unpublish - unpublish page
separator - menu divider

Example 1: disable click and only allow "refresh" menu item on "settings" doc type
<add docTypeAlias="settings" nodeId="" clickAction="javascript:UmbClientMgr.appActions().openDashboard('content');" menuItems="refreshNode" />

Example 2: normal click action, only affect node id 1057 and show create, sort and publish, with a divider
<add docTypeAlias="" nodeId="1057" clickAction="" menuItems="create,sort,separator,refreshNode" />

Example 3: remove delete option from the "homePage" docType
<add docTypeAlias="homePage" nodeId="" clickAction="" removeMenuItems="delete" />
-->
<customMenus>
  <ignoreForAdmin>false</ignoreForAdmin>
  <useInMediaSection>false</useInMediaSection>
  <menuRules>
    <!-- add your rules here -->
  </menuRules>
</customMenus>

An untouched custom menus config file, just waiting for you to fill it full of rules!

Basic Configuration

Right, first things first. The settings node itself. The only thing that you should be able to do with it really is reload the sub-nodes, and we don't really want the editors to be able to get to the edit screen either, as we don't want them to unpublish it, rename it, or preview it. Assuming that the settings node has an alias of "settings", the config file would now look like this:

<?xml version="1.0" encoding="utf-8" ?>
<customMenus>
  <ignoreForAdmin>false</ignoreForAdmin>
  <useInMediaSection>false</useInMediaSection>
  <menuRules>
    <add docTypeAlias="settings" nodeId="" clickAction="javascript:UmbClientMgr.appActions().openDashboard('content');" menuItems="refreshNode" />
  </menuRules>
</customMenus>

Our first rule, this sets the click action to open the content dashboard rather than the editor, and only has the reload nodes menu item!

Refreshing the tree (this is important! You MUST refresh the tree after you've changed the settings for the new menu items to take effect), we can now see that the right click menu only has the one option in it, "reload nodes". Also, if you click on the settings node, you'll get the content dashboard instead of the editor page.

Much, much better! *yay*
Much, much better! *yay*

How does it work?

Looking at the config file entry, you'll see there are the following options on each rule in the config file:

  • docTypeAlias - this allows you to specify the document type alias that the rule should apply to. ALL content nodes of this type will have this rule applied to them.
  • nodeId - if you want the rule to JUST apply to a single node, add it's node id here, and leave the docTypeAlias empty.
  • clickAction - if you want to overide the action when someone clicks on the node in the tree, paste the code you want in the link here. It can be a physical URL, or a javascript call.
  • menuItems - enter a comma separated list of the menu items that should be used for this rule. The default config file lists all of the available options that you can use, as well as the special separator line.

Using these settings, it's possible to have rules that apply to all documents of a specific type, or just individual content nodes. The plugin also respects the permissions of the user, so even if you define menu items that a user shouldn't see, they won't get shown them if they don't have permission to use them.

Now that you've seen how that works, you can go through and set up the rules for each of the other settings sections. So for example, you might specify that the LHS Boxes settings node will only have the options to create, sort and reload nodes, and that the boxes themselves can only have delete move and publish. Below is an example of a more complete customMenus.config file:

<?xml version="1.0" encoding="utf-8" ?>
<customMenus>
	<ignoreForAdmin>false</ignoreForAdmin>
	<useInMediaSection>false</useInMediaSection>
	<menuRules>
		<!-- general site content rules -->
		<add docTypeAlias="HomePage" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="NotFoundPage" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="ErrorPage" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="RssFeed" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="SiteSearch" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="Sitemap" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="About" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="Careers" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="ContactUs" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="CustomerCare" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="FaqAskQuestion" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="FaqSearch" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="Faqs" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="GoogleSitemap" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="News" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="DateFolder" nodeId="" clickAction="" removeMenuItems="copy,move" />
		<add docTypeAlias="ProductsLandingPage" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="" nodeId="1196" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="" nodeId="1207" clickAction="" removeMenuItems="delete,copy,move" />
		<add docTypeAlias="" nodeId="1211" clickAction="" removeMenuItems="delete,copy,move" />
		
		<!-- general settings section rules -->
		<add docTypeAlias="Settings" nodeId="" clickAction="javascript:UmbClientMgr.appActions().openDashboard('content');" menuItems="refreshNode" />
		<!-- lhs sidebar boxes rules -->
		<add docTypeAlias="SettingsLhsBoxes" nodeId="" clickAction="javascript:UmbClientMgr.appActions().openDashboard('content');" menuItems="create,sort,separator,refreshNode" />
		<add docTypeAlias="SettingsLhsBoxStat" nodeId="" clickAction="" menuItems="delete,separator,move,separator,publish" />
		<add docTypeAlias="SettingsLhsBoxTextImageLink" nodeId="" clickAction="" menuItems="delete,separator,move,separator,publish" />
		<add docTypeAlias="SettingsLhsBoxDownload" nodeId="" clickAction="" menuItems="delete,separator,move,separator,publish" />
		<add docTypeAlias="SettingsLhsBoxImage" nodeId="" clickAction="" menuItems="delete,separator,move,separator,publish" />
		<add docTypeAlias="SettingsLhsBoxSocial" nodeId="" clickAction="" menuItems="delete,separator,move,separator,publish" />
		<add docTypeAlias="SettingsLhsBoxVideo" nodeId="" clickAction="" menuItems="delete,separator,move,separator,publish" />
		<add docTypeAlias="SettingsLhsCtaButtons" nodeId="" clickAction="" menuItems="delete,separator,move,separator,publish" />
		<add docTypeAlias="SettingsLhsBoxFolder" nodeId="" clickAction="" menuItems="create,separator,delete,separator,move,separator,sort,publish,refreshNode" />
	</menuRules>
</customMenus>

Here is a more complete example with many rules set up for various different document types and specific nodes.

Removing menu items

What if you just want to remove certain options from the menu? Lets say that you have a structurally important page with some special functionality that should never be deleted, such as a contact form. Easy! Instead of "menuItems" on your rule, use "removeMenuItems" and list the menu items that you would like to remove.

The example rule below removes the delete and move options from a specific page:

<?xml version="1.0" encoding="utf-8" ?>
<customMenus>
  <ignoreForAdmin>false</ignoreForAdmin>
  <useInMediaSection>false</useInMediaSection>
  <menuRules>
    <add docTypeAlias="contactForm" nodeId="" clickAction="" removeMenuItems="delete,copy,move" />
  </menuRules>
</customMenus>

This rule will remove the delete, copy and move menu options from the "contactForm" document type.

Here's what the menu on the contact form looks like once this rule is in place:

Notice that the copy, move and delete menu items are gone!
Notice that the copy, move and delete menu items are gone!

Other options

There are two other options that you can set to affect the behaviour of the package:

  • ignoreForAdmin - setting this to true means that the rules are ignored for all members of the Administrator group, so you can still move and delete things that the editors can't.
  • useInMediaSection - setting this to true means that you can have rules that apply to the media section as well as the content section!

Alternatives to this method

You can also accomplish some of this by using the permissions in Umbraco. By right clicking on any node in the tree, you can set which users can perform what actions. However, I've found that if you have a lot of users and/or content, settings the rules for individual users can be quite time consuming, and as you can't set permissions for groups, you have to set the permissions for each new user that gets created (so you have to remember to set the permissions for each new user). Using the package means that you can set the rules at site launch, and all users have the menu rules automatically. If you'd like to find out more about using the built in Umbraco permissions, check out this page on our.umbraco!

Hiding tabs in the CMS

Another thing that you might want to do is hide certain tabs on some pages from a user, or groups of users. You can do this with either my own Tab Hider package, which you can download from our.umbraco, or with Anthony Dang's uHidesy (also available on our.umbraco). My tab hider package allows you configure tabs that should be hidden for certain DocTypes and users, again via a config file, whereas uHidesy adds a control that is only visible to administrators that allows you to set up hiding options for both tabs and properties on a particular page.

For the sake of space, I'm just going to use my Tab Hider package in this example, but I encourage you to try out uHidesy as well and see which one suits your requirements best!

Install the package as you would normally (again, this package is not currenlty compatible with Umbraco 7, and again, I'm working on fixing that!) and it will add another config file, called "tabHider.config" to your /config folder.

Here is an example config file, where I've hidden the "SEO" tab on all document types from the writer and editor groups on the site, and the "Right Hand Boxes" tab from editors and writers, but only on the "homePage" document type:

<?xml version="1.0" encoding="utf-8" ?>
<tabHider>
  <onlyShowToRoot>false</onlyShowToRoot>
  <disableHiddenValidators>true</disableHiddenValidators>
  <hideRules>
    <!-- 
    For each tab that you want to hide, include an entry here, the format is:

    <add tabName="" hideFrom="" docTypeAliases="" />

    tabName = the name of the tab to hide
    hideFrom = comma separated list of users to hide the tab from
    docTypeAliases = comma separated list of doctypes the hide applies to, leave empty or omit to have the rule apply to the tab on all doc types
    -->
    <add  tabName="seo" hideFrom="writer,editor" />
    <add tabName="Right Hand Boxes" hideFrom="writer,editor" docTypeAliases="homePage" />
  </hideRules>
</tabHider>

A basic tab hider configuration example

The rules are very simple! There are three properties that you can set for each rule:

  • tabName - this is the text of the tab that you want to hide.
  • hideFrom - a comma separated list of the user type aliases you would like to hide the tab from.
  • docTypeAliases - a comma separated list of the document type aliases that the rule should apply to. If you omit this property, the rule will apply to ALL document types.

Set the rules up that you need for all of the tabs and user groups, and you're good to go!

Boom! Your site is now considerably more editor proof!!!

With just a couple of simple package installs, and some easy config rules, you've now made it much harder for your editors to do anything that's likely to cause them (and by extension, YOU) problems, like deleting, copying or moving important things they shouldn't!

This is something that the editors probably won't even notice, but it's worth doing. I've been doing this as a matter of course on my sites for a few years now and it's worth it for the headaches it can save!

There are also a bunch of things you can do from a basic useability point of view that the mighty Doug Robar covered in his excellent talk on being an Umbraco Superhero. You can view the slides from his presentation on his blog. It's an interesting read, and I urge you to check it out!

Thanks for reading, I hope you found this useful!

Tim Payne

Tim is on Twitter as