Friday, August 26, 2011

Working with SharePoint Ratings – Part 2

How to enable and disable SharePoint 2010 rating programmatically

If you have referred the details in the previous post on Working with the SharePoint 2010 Rating – Part 1 for theoretical explanation on what happens in the background, now let’s do something interesting in Visual Studio.
When you visit the list settings page and click on rating settings link , you gets redirected the application page called as “RatingSettings.aspx” with the query string as the list GUID and on this page you can decide the setting about enabling and disabling ratings on that list.
I didn’t see any other way to achieve this with programmatic approach in the server object model in SPList class and so for curiosity I opened up the reflector to see what Microsoft has done.
And so here are the ways to programmatically enable the ratings on list
1.       Using the reflection
typeof(Microsoft.SharePoint.Portal.RatingsSettingsPage).
GetMethod("EnableRatings", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).
Invoke(null, new object[] { _objectofSPList, true });

There is a method “EnableRatings” in class “RatingSettingsPage” and method is internal and static. So I am just invoking the method here. This method takes two parameters.
a.    object of SPList on which you need to enable the ratings
b.    a Boolean value (true/false) – keep true to prorogating rating on list items
There is one more method of this class, which allows us to disable the rating
typeof(Microsoft.SharePoint.Portal.RatingsSettingsPage).
GetMethod("DisableRatings", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).
Invoke(null, new object[] { _objectOfSPList });

This method accepts a single parameter which is as the SPList and this is the target list.
2. Using the actual code
      Rating can also be enabled without using the reflection, but in this case you write much more code than in previous method.
This method is used for enabling the rating settings on a list. You can see that there are two fields which are being add in the list.
  private void EnableRatingSetting(SPList _list)
  {
    SPFieldCollection _allfields = _list.Fields;
    SPFieldCollection _availFields = _list.ParentWeb.AvailableFields;
    if (!_allfields.Contains(FieldId.AverageRatings))
    {
       SPField field = _availFields[FieldId.AverageRatings];
_list.Fields.AddFieldAsXml(field.SchemaXmlWithResourceTokens, true, SPAddFieldOptions.AddFieldToDefaultView | SPAddFieldOptions.AddFieldInternalNameHint | SPAddFieldOptions.AddToAllContentTypes);
    }
    if (!_allfields.Contains(FieldId.RatingsCount) && _availFields.Contains(FieldId.RatingsCount))
    {
      SPField field2 = _availFields[FieldId.RatingsCount];
      _list.Fields.AddFieldAsXml(field2.SchemaXmlWithResourceTokens, false, SPAddFieldOptions.AddFieldInternalNameHint | SPAddFieldOptions.AddToAllContentTypes);
    }
      _list.Update();

   }

And this is the method to propagate the changes to the list items.
There is a method available with the SocialRatingManager class called as PropogateRating which takes care of this.
private void PropogateChanges(SPList _list)
{
SocialRatingManager _socialRatingMgr = new   SocialRatingManager(SPServiceContext.Current);
   string _baseUrl = _list.ParentWeb.Url;
   if (_baseUrl.EndsWith("/", StringComparison.OrdinalIgnoreCase))
   {
      _baseUrl = _baseUrl.TrimEnd(new char[] { '/' });
   }
   foreach (SPListItem item in _list.Items)
   {
     string _itemUrl = item.Url;
     if (_itemUrl.StartsWith("/", StringComparison.OrdinalIgnoreCase))
     {
        _itemUrl = _itemUrl.TrimStart(new char[] { '/' });
     }
     SPSecurity.RunWithElevatedPrivileges(delegate()
     {
        _socialRatingMgr.PropagateRating(new Uri(_baseUrl + "/" + _itemUrl));
     });
    }

   }

   private static SPField GetField(Guid id, SPFieldCollection fieldColl)
   {
      return fieldColl[id];
   }

This method is used to disable the rating on a list
private void DisableRatingSetting(SPList _list)
    {
       SPField _field1 = GetField(FieldId.AverageRatings, _list.Fields);
       if (_field1 != null)
       {
         _list.Fields.Delete(_field1.InternalName);
       }
       SPField _field2 = GetField(FieldId.RatingsCount, _list.Fields);
       if (_field2 != null)
       {
          _list.Fields.Delete(_field2.InternalName);
       }
        _list.Update();

    }

Thursday, August 25, 2011

Working with SharePoint Ratings – Part 1

Recently I was doing some R&D with the SharePoint Ratings and so thought to share my thoughts.
As we know that with the launch of SharePoint 2010 version, we got new social features like content tagging and content rating. For content ratings, we can rate the different contents in the list and libraries in range of 0 to 5.
For example: I have a publishing site and the pages library, so I need to have functionality such that the end users should be able to rate the pages inside the pages library.
How to do this?
For any list or library, there is a link to set the rating capability. When you go to the List and then in its settings, there you can see the link named as “Rating Settings”. After you click on that, a page opens where you can enable and disable this setting.
Ok so far so good. ? But what happens in the background?
Well, I have observed that when you enable ratings on any list or library, the two Site Columns/Fields gets added to the SharePoint list. Obviously those get added to hold this rating as a metadata.
Which are those fields?
Field names are:
    Display Name                          Internal Name                                        GUID
1. Rating (0-5)                           AverageRating               {5a14d1ab-1513-48c7-97b3-657a5ba6c742}        
2. Number of Ratings                  RatingCount                   {b1996002-9167-45e5-a4df-b2c41c6723c7}         

And here is the screen shot after enabling the rating on pages list



Rating is the asynchronous event and we talk about social features, they gets saved in social database which gets created when you have activated the user profile service application. So in short, to use rating you should have a user profile service running in your farm.
There is one timer job “User Profile Service Application – Social Rating Synchronization” job which by default runs after every hour and aggregates all the ratings.


Why ratings disappear after page refresh?
I was also puzzled by a question that when we try to rate a page in pages library and then refresh the page, ratings given were disappearing. Why?
So answer is the Timer Job. As this job runs every hour, so unless and until the job has run and aggregated the ratings, we don’t see the ratings which were given. So I changed the job schedule to run every minute to have immediate effects.
But wait, there might be the case that If you have provisioned all the sites hierarchies very fine in the production environment and now request is to enable the ratings on all the lists and libraries in each site and subsites. Of course not manually right? So How to do that?
Yes you are right, you can write a feature, or a console application or any other approach which will enable rating setting recursively on each list and library within the site.
But again question now is how to do this programmatically?
I will be posting this in part 2 of this post.

Monday, August 22, 2011

Hide Web Part’s Default Tool Part or Hiding Appearance from Web Part Properties

If the user wants to hide the default properties of a web part or some sections in the default tool part of a web part. How to do this? Here are the approaches.
I created a simple web part inheriting from SharePoint Web Part class So according to me, there can be two ways to do this.
(Microsoft.SharePoint.WebPartPages.WebPart) which has only a text box as user interface.
1.    Hide entire default tool part of a web part – this is pretty simple
2.    Hide specific sections in the default tool part. Example: hide only the appearance section in the default tool part.
And here are my approaches –
1.    Hiding the entire default tool part is very easy thing to achieve. When you override the GetToolParts method of the web part then you don’t have to add the default tool part in the array of ToolPart.
So sample can be written like:
Note that WebPartToolPart object is commented and not added in the array.
With this approach, you can only show the custom tool part for a web part.
public override Microsoft.SharePoint.WebPartPages.ToolPart[] GetToolParts()
{
  ToolPart[] _toolparts = new ToolPart[1];
  try
  {
    //WebPartToolPart _wptp = new WebPartToolPart();
    CustomToolPart _custom = new CustomToolPart();
     //_toolparts[0] = _wptp;
     _toolparts[0] = _custom;

  }
  catch (Exception ex)
  {
               
  }
  return _toolparts;
}

2.    Hiding the Specific Section of default tool part
Suppose we don’t want to hide the default tool part entrirely but need to hide specific sections like appearance.
so this approach shows that how we can achieve this.
when you are in your custom TooPart class and inside then this.Parent represents the ToolPart of a web part. If you try to do this.Parent.Controls then you can see that you get collection of controls inside the ToolPart pane. so we can easily set the visibility of those controls like
Note that you can add this code in CreateChildControls of your tool part class

if (Parent != null && Parent.Controls != null && Parent.Controls[1] != null)
{
  if (Parent.Controls[1].Controls != null)
  {
     Parent.Controls[1].Controls[0].Visible = false;
   }
}

Please Note that I have not tried these methods when web part is derived from ASP.NET Web Part class i.e. System.Web.UI.WebControls.WebParts.WebPart , If I get to know some update on that then I will update this post.

Here is my entire sample code

public class HiddenPropertiesWebPart : Microsoft.SharePoint.WebPartPages.WebPart
{

  public override Microsoft.SharePoint.WebPartPages.ToolPart[] GetToolParts()
  {
    ToolPart[] _toolparts = new ToolPart[2];
    try
    {
      WebPartToolPart _wptp = new WebPartToolPart();
      CustomToolPart _custom = new CustomToolPart();
      _toolparts[0] = _wptp;
      _toolparts[1] = _custom;

    }
    catch (Exception ex)
    {
               
    }
    return _toolparts;
 }

 protected override void CreateChildControls()
 {
   try
   {
     base.CreateChildControls();
     TextBox _txtBox = new TextBox();
     this.Controls.Add(_txtBox);
   }
   catch (Exception ex)
   {
     this.Controls.Add(new LiteralControl(string.Format("{0}-{1}", "Error", ex.Message)));
   }
  }
}

public class CustomToolPart : ToolPart
{
  protected override void CreateChildControls()
  {
    try
    {
      base.CreateChildControls();
      DropDownList _ddl = new DropDownList();
      _ddl.Items.Add("Item1");
      _ddl.Items.Add("Item2");
      this.Controls.Add(_ddl);

      //Hiding the Desired Section in the Default ToolPart

      // Parent.Controls[1] shows - default tool part
      // Parent.Controls[1].Controls[0] shows - default tool part's first control - that is Appearance
      if (Parent != null && Parent.Controls != null && Parent.Controls[1] != null)
      {
        if (Parent.Controls[1].Controls != null)
        {
          Parent.Controls[1].Controls[0].Visible = false;
        }
      }
     }
     catch (Exception ex)
     {
       this.Controls.Add(new LiteralControl(string.Format("{0}-{1}", "Error", ex.Message)));
     }
    }
}