Second Life of a Hungarian SharePoint Geek

April 14, 2014

How to Extend the SharePoint Ribbon to Enable Pushing Down Permissions to Child Items

Filed under: JavaScript, Permissions, Ribbon, Security, SP 2010 — Tags: , , , , — Peter Holpar @ 20:03

If you are familiar with the security options of the NTFS file system you should know the dialog below as well (accessible via the Security tab of the folder properties / Advanced button / Change Permissions… button):

image

By selecting the highlighted checkbox you can push down the permissions set on the current folder to all child items (folders and files). Unfortunately, there is no such option in the standard SharePoint toolbox. As you can see on the screenshot below, the Inheritance group of the Permission Tools on the ribbon includes the options Manage Parent and Stop Inheriting Permissions, but no way to force the inheritance of the current permissions to the child items.

image

In my recent posts I discussed how to check from code whether an site / list has child items with unique permissions, and how to to push down permissions from code.

In this post I will illustrate, how to create a Ribbon extension that enables the missing functionality from SharePoint.

In our CustomAction elements we register the loader ScriptBlock of the custom PageComponent (implemented in InheritPermissions.js, it will be loaded only we are on the permissions page, users.aspx). Next, we register our button (Ribbon.Permission.Manage.InheritDown) in the Inheritance button group (Ribbon.Permission.Parent, defined in 14\TEMPLATE\GLOBAL\XML\CMDUI.XML).

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3.   <CustomAction Location="ScriptLink" ScriptBlock="function startScript() { document.write('&lt;script type=&quot;text/javascript&quot; src=&quot;/_layouts/InheritPermissions/InheritPermissions.js&quot;&gt;&lt;/' + 'script&gt;'); } if (document.location.pathname.toLowerCase().indexOf('_layouts/user.aspx') > -1 ) { ExecuteOrDelayUntilScriptLoaded(startScript, 'sp.js'); }" Sequence="1000"/>
  4.   
  5.   <CustomAction Location="CommandUI.Ribbon">
  6.     <CommandUIExtension>
  7.       <CommandUIDefinitions>
  8.         <CommandUIDefinition Location="Ribbon.Permission.Parent.controls._children">
  9.           <Button
  10.               Id="Ribbon.Permission.Manage.InheritDown"
  11.               Command="Ribbon.Permission.Manage.InheritDown"
  12.               Sequence="50"
  13.               Image16by16="/_layouts/images/InheritPermissions/InheritDown16x16.png"
  14.               Image32by32="/_layouts/images/InheritPermissions/InheritDown32x32.png"
  15.               LabelText="Inherit to Child Objects"
  16.               ToolTipTitle="Inherit to Child Objects"
  17.               ToolTipDescription="Reset permissions on child objects to inherit the permissions of this object.&lt;br&gt;Any custom permissions configured on child objects will be lost.&lt;br&gt;This tool is available only to farm administrators."
  18.               TemplateAlias="o1"/>
  19.         </CommandUIDefinition>
  20.       </CommandUIDefinitions>
  21.     </CommandUIExtension>
  22.   </CustomAction>
  23.  
  24. </Elements>

The event handler functionality custom PageComponent is implemented in InheritPermissions.js. If the command ‘Ribbon.Permission.Manage.InheritDown’ is invoked, we make a page postback with the event target ‘inheritDown’:

__doPostBack(‘inheritDown’, ”);

we process the postback later in our WebControl. Similarly, whether the page component should handle the command ‘Ribbon.Permission.Manage.InheritDown’ is determined by the value of the permInheritEnabled variable, the value is injected by our WebControl as well (see the RegisterStartupScript in the OnInit method of the web control later).

  1. Type.registerNamespace('CustomPermission.UI');
  2.  
  3. CustomPermission.UI.PageComponent = function CustomPermissionUI_PageComponent() {
  4.     CustomPermission.UI.PageComponent.initializeBase(this);
  5. }
  6.  
  7. CustomPermission.UI.PageComponent.initialize = function () {
  8.     var ribbonPageManager = SP.Ribbon.PageManager.get_instance();
  9.     if (null !== ribbonPageManager) {
  10.         ribbonPageManager.addPageComponent(this.instance);
  11.         ribbonPageManager.get_focusManager().requestFocusForComponent(this.instance);
  12.     }
  13. }
  14. CustomPermission.UI.PageComponent.refreshRibbonStatus = function () {
  15.     SP.Ribbon.PageManager.get_instance().get_commandDispatcher().executeCommand(Commands.CommandIds.ApplicationStateChanged, null);
  16. }
  17.  
  18. CustomPermission.UI.PageComponent.prototype = {
  19.     init: function () { },
  20.     getFocusedCommands: function () {
  21.         return ['Ribbon.Permission.Manage.InheritDown'];
  22.     },
  23.  
  24.     getGlobalCommands: function () {
  25.         return ['Ribbon.Permission.Manage.InheritDown'];
  26.     },
  27.  
  28.  
  29.     canHandleCommand: function (commandId) {
  30.         if (commandId === 'Ribbon.Permission.Manage.InheritDown') {
  31.             return permInheritEnabled;
  32.         }
  33.     },
  34.  
  35.     handleCommand: function (commandId, properties, sequence) {
  36.         if (commandId === 'Ribbon.Permission.Manage.InheritDown') {
  37.             var doInherit = confirm('You are about to inherit permissions from his object to all of the child objects having custom permissions. Any custom permissions on the child objects will be lost.');
  38.             if (doInherit) {
  39.                 __doPostBack('inheritDown', '');
  40.             }
  41.         }
  42.  
  43.     },
  44.  
  45.     getId: function () {
  46.         return "CustomPermission.UI.PageComponent";
  47.     }
  48. }
  49.  
  50. CustomPermission.UI.PageComponent.registerClass('CustomPermission.UI.PageComponent', CUI.Page.PageComponent);
  51.  
  52. CustomPermission.UI.PageComponent.instance = new CustomPermission.UI.PageComponent();
  53. CustomPermission.UI.PageComponent.initialize();
  54.  
  55. SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("InheritPermissions.js");

The InheritPermissions WebControl is injected into the pages via an AdditionalPageHead delegate control.

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3.   <Control
  4.     ControlAssembly="$SharePoint.Project.AssemblyFullName$"
  5.     ControlClass="InheritPermissions.InheritPermissions"
  6.     Sequence="50" Id="AdditionalPageHead"/>
  7. </Elements>

We include our control into the Safe Control Entries:

image

We activate the functionality of the control, if the host page is a permission page (e.g. it is derived from the CBaseAclPage class).

  1. class InheritPermissions : WebControl
  2. {
  3.     bool _isInheritButtonEnabled = false;
  4.  
  5.     protected override void OnInit(EventArgs e)
  6.     {
  7.         base.OnInit(e);
  8.  
  9.         CBaseAclPage baseAclPage = this.Page as CBaseAclPage;
  10.         if (baseAclPage != null)
  11.         {
  12.             baseAclPage.ParseAclObjFromRequest();
  13.  
  14.             SPSecurableObject securable = this.Securable;
  15.  
  16.             if (securable != null)
  17.             {
  18.  
  19.                 if ((this.Page.IsPostBack) && (this.Page.Request["__EVENTTARGET"] == "inheritDown"))
  20.                 {
  21.                     InheritPermissionsDown(securable);
  22.                 }
  23.  
  24.                 _isInheritButtonEnabled = ((SPFarm.Local.CurrentUserIsAdministrator(true)) && (securable.HasSubItemWithUniquePermissions()));
  25.  
  26.                 // inject a JavaScript variable into the page to support client side PageComponent
  27.                 // see canHandleCommand method of CustomPermission.UI.PageComponent (InheritPemissions.js)
  28.                 string enabledScript = (_isInheritButtonEnabled) ? "true" : "false";
  29.                 string script = string.Format(@"<script type=""text/javascript"">
  30.                                var permInheritEnabled = {0}
  31.                             </script>", enabledScript);
  32.                 this.Page.ClientScript.RegisterStartupScript(typeof(SPRibbon), "perminherit", script, false);
  33.             }
  34.         }
  35.     }
  36.  
  37.     protected override void OnPreRender(EventArgs e)
  38.     {
  39.         if (!_isInheritButtonEnabled)
  40.         {
  41.             SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);
  42.             ribbon.TrimById("Ribbon.Permission.Manage.InheritDown");
  43.         }
  44.     }
  45.  
  46.     private SPSecurableObject Securable
  47.     {
  48.         get
  49.         {
  50.             SPSecurableObject securable = null;
  51.             UserRoles userRoles = this.Page as UserRoles;
  52.  
  53.             if (userRoles != null)
  54.             {
  55.                 securable = userRoles.m_Securable;
  56.             }
  57.  
  58.             return securable;
  59.         }
  60.     }
  61.  
  62.     // see further details in the code download…
  63.     
  64. }

We reuse the former code samples mentioned above: code details about how to inherit down permissions are discussed in this post, how to detect items with unique permission is shown in this post.

In the ParseAclObjFromRequest method we invoke the protected method with the same name of the parent page of CBaseAclPage type, thus achieve we a reference in this.Securable to the securable object (web site, list, folder, etc.) the permissions were set on.

We push the permissions down to child object if the page was posted back with the ‘inheritDown’ event target.

The value of the _isInheritButtonEnabled determines if the PageComponent implemented in InheritPermissions.js support the ‘Ribbon.Permission.Manage.InheritDown’ command. The value depends on two factors: it is enabled if the current user is a farm administrator and if the securable object has sub items with unique permissions. See my notes in the former post regarding the shortcomings of the check of unique permissions on child items. Even this check is not perfect we apply the same logic to remain consistent with the standard features / messages.

In my first attempt I invoked the InheritPermissionsDown method from the OnPreRender method and not from the OnInit method. However, the standard JavaScript functions of the permissions page were already registered by this time, so the message ‘Some content on this site has unique permissions which are not controlled from this page.’ were displayed even if we just pushed down the permissions right now. I found this behavior pretty disturbing. Including the logic in the OnInit method we push the permissions down before the standard scripts get registered (see the SetExceptionStatus method of the UserRoles class, derived from the CBaseAclPage type, invoked from InitPage invoked from OnLoad), so there is no inconsistent message displayed.

The button should be hidden if the user is no farm admin or if the item has no subitem with unique permissions (in this case the value of the _isInheritButtonEnabled  is false). We should invoke the TrimById method of the current ribbon instance, but in this case from the OnPreRender method.

Let’s see the new button in action. It is displayed on the ribbon if the current item has child items with unique permissions :

image

If the user clicks on the button, a warning is displayed. After confirmation of the action, the permissions are pushed down to child items.

image

You can download the sample solution from here.

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: