Wednesday, March 23, 2011

Make SocialCommentWebPart read only dynamically in Sharepoint

SocialCommentWebPart is one of the new features of Sharepoint 2010 which allows users add comments to the pages where this web part is added. This web part makes Sharepoint site more social and increases users communication level. One of the common requirement with this type of functionality is that not all users should be able to leave their comments on site. For example, you can decide that only registered users should be able to leave comments (in case of self-service system), or you can use membership in particular groups in order to grant possibility to leave comments for users. In both cases you will need to make SocialCommentWebPart read only (i.e. allow users only read comments, not post them) dynamically in run time. There is a property SocialCommentWebPart.WebPartPropertyAllowNewComment, but as all other properties it is assumed to be configured once by content producer (or another user with permissions to edit web parts). This is not our goal. Our goal is to allow some users to post comments, while other users should not have this possibility.

If you will check SocialCommentWebPart in reflector you will notice that all functionality is implemented in separate user control SocialCommentControl. It has property AllowNewComment and SocialCommentWebPart sets this property when add SocialCommentControl into controls collection. This property is what we need. But unfortunately it is internal, so we can’t just set it up in our code. Reflection will help us here. Suppose that we put web part on page layout directly (i.e. it is not added via UI on publishing page, but in last case code will remain the same – the only thing you will need to solve is to retrieve web part Id):

   1: <SPSWC:SocialCommentWebPart 
   2:     runat="server" 
   3:     Title="Comments" 
   4:     AllowEdit="True" 
   5:     AllowConnect="True" 
   6:     ConnectionID="00000000-0000-0000-0000-000000000000" 
   7:     IsIncluded="True" 
   8:     Dir="Default" 
   9:     PartImageLarge="" 
  10:     IsVisible="True" 
  11:     AllowMinimize="True" 
  12:     ZoneID="" 
  13:     ID="g_61df80f5_8c13_4355_aeee_a52b497e81c6" 
  14:     FrameState="Normal" 
  15:     ExportMode="All" 
  16:     AllowHide="True" 
  17:     SuppressWebPartChrome="False" 
  18:     DetailLink="" 
  19:     ChromeType="None"
  20:     DescriptionLocId="Null" 
  21:     MissingAssembly="Cannot import this Web Part." 
  22:     PartImageSmall="" 
  23:     AllowRemove="True" 
  24:     HelpMode="Modeless" 
  25:     FrameType="TitleBarOnly" 
  26:     AllowZoneChange="True" 
  27:     PartOrder="1" 
  28:     Description="Enable users to leave short, publicly-viewable notes about this page." 
  29:     HelpLink="" 
  30:     DescriptionLocIdNum="0" 
  31:     ExportControlledProperties="True" 
  32:     IsIncludedFilter="" 
  33:     __MarkupType="vsattributemarkup" 
  34:     __WebPartId="{61df80f5-8c13-4355-aeee-a52b497e81c6}" 
  35:     WebPart="true" 
  36:     Height="" 
  37:     Width="">
  38: </SPSWC:SocialCommentWebPart>

In order to make SocialCommentWebPart read only check the following code which we can add directly into page layout:

   1: <script runat="server" type="text/C#">
   1:  
   2:     protected override void OnLoad(EventArgs e)
   3:     {
   4:         base.OnLoad(e);
   5:         if (yourOwnLogic())
   6:         {
   7:             SocialCommentControl c =
   8:                 ControlFinder.FindChildControl<SocialCommentControl>(g_61df80f5_8c13_4355_aeee_a52b497e81c6);
   9:             if (c != null)
  10:             {
  11:                 ReflectionHelper.CallMethod(c, "set_AllowNewComment", false);
  12:             }
  13:         }
  14:     }
</script>

Function yourOwnLogic() here is any function which you can use in order to determine should current user have possibility to post comments or not (as I said above you can use membership in Sharepoint groups for example). Here we use 2 external components: ControlFinder and ReflectionHelper. They are not complicated. ControlFinder searches for control of specified generic type in controls collection recursively:

   1: public static class ControlFinder
   2: {
   3:     public static T FindChildControl<T>(Control startingControl) where T : Control
   4:     {
   5:         T found = null;
   6:         foreach (Control c in startingControl.Controls)
   7:         {
   8:             found = c as T;
   9:             if (found == null)
  10:             {
  11:                 found = FindChildControl<T>(c);
  12:             }
  13:             if (found != null)
  14:             {
  15:                 break;
  16:             }
  17:         }
  18:         return found;
  19:     }
  20: }

ReflectionHelper is a helper class which can be used if you need to play with internal details of Sharepoint (I understand that it can be necessary in some cases, but don’t make it too much as it non supported way to work with Sharepoint):

   1: public static class ReflectionHelper
   2: {
   3:     public static object CallMethod(object obj, string name, params object[] argv)
   4:     {
   5:         BindingFlags bf = 0 | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
   6:         MethodInfo mi = obj.GetType().FindMembers(MemberTypes.Method, bf, Type.FilterName, name)[0] as MethodInfo;
   7:         return mi.Invoke(obj, argv);
   8:     }
   9:  
  10:     public static object CallStaticMethod(Type t, string name, params object[] argv)
  11:     {
  12:         BindingFlags bf = 0 | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
  13:         MethodInfo mi = t.FindMembers(MemberTypes.Method, bf, Type.FilterName, name)[0] as MethodInfo;
  14:         return mi.Invoke(null, argv);
  15:     }
  16:  
  17:     public static object GetStaticData(Type t, string name)
  18:     {
  19:         BindingFlags bf = 0 | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
  20:         FieldInfo fi = t.FindMembers(MemberTypes.Field, bf, Type.FilterName, name)[0] as FieldInfo;
  21:         return fi.GetValue(null);
  22:     }
  23: }

After this SocialCommentWebPart will be read only for users which don’t have necessary permissions, and will allow post comments for users which have such permissions.

No comments:

Post a Comment