Sunday, September 11, 2016

Publishing Targets: Restriction on selection of multiple publishing targets

This blog post guides you through the steps taken to add restriction on selection of multiple publishing target database by specific roles to publish items in Sitecore .

I have tried to cover all the steps taken to implement the customization with a Question(Q) and Answer(A) approach as below.

Q1: What exactly the usecase is?
A:  Specific user roles need to be restricted from selecting multiple publishing target(in this example it is CDPublish and CMPublish) for publishing Items in Sitecore.
For example: The administrator should be able to select both the publishing targets and proceed with publishing but when an user with some specific roles(like  Content regional Publisher, Content Author) select both the publishing targets for publishing they should get an custom alert message like "This role do not have the permission to publish using both the publishing targets". These specific users can publish with only one publishing targets.

Q2: Where to find/add multiple publishing target in Sitecore?
A: You can find this in the Sitecore Content Editor (/sitecore/system/Publishing targets), Master db.



Q3: Where the Publishing Targets option appears?
A: You can see this in the Publishing Wizard just before final submission of Publish, in the Publishing Targets section.



Q4: Where and how can I edit the Publish wizard?
A: The Publish Wizard details are present in Publish.xml and can be found inside the "shell" folder (your website\Website\sitecore\shell\Applications\Dialogs\Publish).




Q5: What changes need to be done to invoke the custom code?
A: Edit the Publish.xml file by adding your custom code in the CodeBeside attribute. Replace the existing PublishForm,  Sitecore.client with your custom code. 
For Example: <WizardForm CodeBeside="Your_Project_namespace.CustomPublishForm,Your_assembly">



Q6: How to know what the custom code should contain and what should we edit and where?
A: You have to override the PublishForm present inside Sitecore.client dll.Use a decompiler like ILSpy or .NET Reflector to look into the details of Sitecore.client assembly and understand how and where it selects the value of the Publishing Targets.Try to find out the code in the decompiler which checks on the selection of publishing targets.In this case you can find it in the ActivePageChanging method.



Q7: What should be taken care while adding the custom code?

A: Make sure you do not modify any existing logic while overriding the method until and unless it is intended for.
While modifying any SheerUI components or xml files like this Publish.xml do remember to copy the modified XML file from the source directory to the sitecore\shell\Override folder. This is done to ensure that the modified XML file is used instead of the standard one. More details on this you can find here.

Here is the sample code added in the <CodeBeside> to implement the requirement.
 public class CustomPublishingTarget : PublishForm  
   {
     protected override bool ActivePageChanging(string page, ref string newpage)  
     {  
       Assert.ArgumentNotNull(page, "page");  
       Assert.ArgumentNotNull(newpage, "newpage");  
       if (page == "Retry")  
       {  
         newpage = "Settings";  
       }  
       if (newpage == "Publishing")  
       {  
         if (GetLanguages().Length == 0)  
         {  
 SheerResponse.Alert(Translate.Text("You must pick at least one language to publish."), new string[0]);  
            return false;  
         }  
         if (GetPublishingTargetDatabases().Length == 0)  
         {  
 SheerResponse.Alert(Translate.Text("You must pick at least one publishing target."), new string[0]);  
            return false;  
         }  
 /* If both the publishing targets are selected */  
         if (GetPublishingTargetDatabases().Length == 2)  
         {  
           User user = Context.User;  
           UserRoles roles = null;  
           if (user != null)  
           {  
             roles = user.Roles;  
           }  
 /* User/Role restriction */  
           if (roles != null && roles.Count() != 0 && !user.IsAdministrator)  
           {  
 var listRolesToPublishTarget = ConfigurationManager.AppSettings["RolesForPublishTarget"].Split('|').ToList();  
            bool publishAdminFlag = true;  
             foreach (string publishRole in listRolesToPublishTarget)  
             {  
               if (Context.User.IsInRole(publishRole))  
               {  
                 publishAdminFlag = false;  
               }  
             }  
             if (publishAdminFlag)  
             {  
               var rolesList = roles.ToList();  
 string listRolesToRestrictPublishTarget = ConfigurationManager.AppSettings["RestrictedRolesForPublishTarget"].ToString();  
               foreach (var restrictedRole in rolesList)  
               {  
 string restrictedRoleName = ((Account)(restrictedRole)).Name.ToString();  
 if(listRolesToRestrictPublishTarget.Contains(restrictedRoleName + '|'))  
               {  
 string alertMessage = ConfigurationManager.AppSettings["PublishTargetAlertMessage"].ToString().Replace("\\n", Environment.NewLine);  
                   SheerResponse.Alert(Translate.Text(alertMessage, new string[0]));  
 return false;  
               }  
               }  
             }  
           }  
         }  
  if ((this.PublishChildrenPane.Visible || this.Republish.Checked) && !this.ConfirmedOptions)  
         {  
           Context.ClientPage.Start(this, "ConfirmPublishingOptions");  
           return false;  
         }  
       }  
       return base.ActivePageChanging(page, ref newpage);  
     }  
 }  

Voila! now the restricted users/roles(like  Content regional Publisher, Content Author) are unable to publish using both the publishing targets and they get a popup "This role do not have the permission to publish using both the publishing targets". Only the administrator(or any role configured in the config with key "RolesForPublishTarget") can publish using both the Publishing Targets.