A Simple Alternative to Umbraco Courier - Part 2

In my previous post, I described a simple alternative to using Umbraco Courier for basic staging sites and how it could be accomplished through just a few lines of custom code.   In that post, however, I did not cover any means to set and store the host names for which we wanted to display the staging data.

The most logical place for this setting is the “Culture and Hostnames” dialog.  A simple “Staging” checkbox should do the trick nicely:


However, we don’t want to touch the core dialog.  Instead we want to replace the original with our own.  This way our plugin is much less invasive and easier to install/uninstall.

We do this on the TreeControllerBase.MenuRendering event:

  TreeControllerBase.MenuRendering += TreeControllerBase_MenuRendering;  

Below we simply remove the original dialog and substitute our own!

  private void TreeControllerBase_MenuRendering(TreeControllerBase treeControllerBase, MenuRenderingEventArgs menuRenderingEventArgs)  
       //Only do this for the assignDomain menu  
       if (menuRenderingEventArgs.Menu == null || menuRenderingEventArgs.Menu.Items.All(x => x.Alias != "assignDomain")) {   
       //find original dialog and it's index  
       MenuItem oldItem = menuRenderingEventArgs.Menu.Items.FirstOrDefault(x => x.Alias == "assignDomain");  
       int i = menuRenderingEventArgs.Menu.Items.FindIndex(x => x.Alias == "assignDomain");  

       //remove original dialog (and our new dialog if it's already been added  
       menuRenderingEventArgs.Menu.Items.RemoveAll(x => x.Alias == "assignDomain");  
       menuRenderingEventArgs.Menu.Items.RemoveAll(x => x.Alias == "stageSetup");  

       //finally create our new replacement dialog   
       MenuItem stageSetup = new MenuItem("stageSetup", oldItem.Name);  
       stageSetup.Icon = oldItem.Icon;  
       string dialogURL = string.Format("{0}?id={1}", "/umbraco/ustage/dialog/assigndomain2sv.aspx", menuRenderingEventArgs.NodeId);  
       stageSetup.LaunchDialogUrl(dialogURL, "Set Domains for Staging and Live Sites");  

       //and insert it into the menu items collection  
       menuRenderingEventArgs.Menu.Items.Insert(i, stageSetup);  


The key difference in our version of the dialog controller is that we have an additional flag for whether the domain is a staging domain or not. To handle this staging data, we inherit from base “DomainsApiController” and add our own action in which we call not only the base “SaveLanguageAndDomain” method, but also our logic to save the staging domains in a separate table (again, to keep separation from core).

     public PostBackModelWithStaging SaveLanguageAndDomainsStaging(PostBackModelWithStaging model)  
       //first populate standard PostBackModel with required properties including domains  
       PostBackModel baseModel = new PostBackModel();  
       baseModel.NodeId = model.NodeId;  
       baseModel.Valid = model.Valid;  
       baseModel.Language = model.Language;  
       DomainModel[] dms = new DomainModel[model.Domains.Count()];  
       int i = 0;  
       foreach (var d in model.Domains)  
         var dm = new DomainModel(d.Name, d.Lang);  
         dms[i] = dm;  
       baseModel.Domains = dms;  

       //Call the base SaveLangueageAndDomains, passing in its baseModel that it wants  
       baseModel = base.SaveLanguageAndDomains(baseModel);  

       //update staging version with updated base in case there are changes  
       model.NodeId = baseModel.NodeId;  
       model.Valid = baseModel.Valid;  
       model.Language = baseModel.Language;  
       DomainModelWithStaging[] dms2 = new DomainModelWithStaging[baseModel.Domains.Count()];  
       i = 0;  
       foreach (var d in baseModel.Domains)  
         var dm = new DomainModelWithStaging(d.Name, d.Lang,false);          

         //get proper staging setting assigned to domain model  
         dm.Staging = model.Domains.Where(x => x.Name.ToLower() == dm.Name.ToLower()).FirstOrDefault().Staging;  
         dms2[i] = dm;  
       model.Domains = dms2;  

       //save data to database  
       if (model.Valid)  
         model = ProcessDomainsForStaging(model);  
       return model;  

As you can see above, we also have a new "PostbackWithStaging" model to include the staging data and a new “StagingDomain” class, which inherits from the core "Domain" class and handles the CRUD operations for us. 

To handle the deletion of domains from the dialog, we attach to the delete event of the domain.  Our full list of new event handlers are now listed below:

       //Prepare the stagingdocument on save  
       ContentService.Saved += Saved;  

        //replace original dialog with custom menu on menuRendering  
       TreeControllerBase.MenuRendering += TreeControllerBase_MenuRendering;  

       //handle delete event of StagingDomain to delete staging table info  
       StagingDomain.AfterDelete += StagingDomain.DeleteStagingInfo;  

All that is left is to ensure the table exists on startup:

  protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)  
       Database db = applicationContext.DatabaseContext.Database;  
       //Check if the DB table does NOT exist  
       if (!db.TableExist("uStageDomains"))  
         //Create DB table - and set overwrite to false  

That’s it!  We now have our staging information controlled alongside our culture and hostnames data.  We retrieve it like so within our contentfinder;

  private static bool IsStagingDomain(string domain) {  

       StagingDomain sd = new StagingDomain(domain);  

       return sd.IsStaging;     


The full plugin (as well as full source code) is available here. We welcome any improvements, enhancements, or suggestions. Thanks!

Other Posts in This Series

Looking for help in Umbraco? We're an Umbraco Certified Partner. Contact us and let's talk about how Liquid can help you!