As the title describes, I am going to share about the Rating functionality (GetRating and SetRating) of a SharePoint 2013 List Item by using SocialRatingManager Class programmatically.
Before starting to that, there are certain basic settings, we need to do before proceeding to the manage rating programmatically.
Enabling the Rating Settings
· This needs to be done on the List level. Before doing any rating, we need to enable the rating settings on the List Settings.
· Click on the List Settings.
· Click on the Rating Settings and Enable the Rating Functionality
Permissions on the User Profile Service Application
· Need to Give permissions on the UserProfile Service Application.
· On the CentralAdministration, Select the UserProfile Service Application.
· Click on the Administrators
· Add the User and give him full permission.
· Come back to Permissions Section.
· Give the user Full Control.
Timer Jobs Responsible for the List Item to reflect the Rating.
There are 2 timer jobs, which are responsible to reflect the Rating value on the List Item through the UI. i.e., When we do a Rating for a particular item through code, it will not get reflected immediately on the SharePoint UI. It will get reflect only after the timer gets executed. The timer jobs are as follows.
1. Social Rating Synchronization Job
2. Social Data Manager Timer Job.
These 2 jobs can be viewed on by going to Central Administration -> Monitoring -> Review Job definitions
The frequency of the Job, by default would be 1 hour. Hence, whenever, we made any Rating, this will be reflected after an hour only. To avoid the delay, we can modify the frequency of the timer as one minute. But even then, I couldn’t find any alternate way that, the rating will get reflected immediately. Please do let me know, if someone finds a way for that.
With this fundamental settings, coming to our core topic J .
Basically, I faced a requirement like, we need to rate an item from the Provider Hosted Application. As all of us are well aware that, from PHA, only the Client Side Object Model can be used to talk with SharePoint. At that time, I was searching for a Client Side Object for SocialRatingManager. UnFortunately, there is no CSOM object for SocialRatingManager. Hence Planned to develop a WCF Service and Host it in the SharePoint Farm. By making use of the REST call, access this WCF Service and call the GetRating Method from the Provider Hosted Application. The below image can express requirement and the solution clearly.
To achieve this transanction, from PHA, only CSOM is allowed. But there is no CSOM Object for SocialRatingManager. Hence the solution would be Introducing a WCF Service.
How to Create the WCF Service and How to Invoke the Service from the PHA has been already discussed in the previous articles. (https://www.sharepointpals.com/post/Step-by-Step-Procedures-to-create-a-WCF-Service-Application-for-SharePoint-2013 and https://www.sharepointpals.com/post/Making-a-REST-Call-to-a-Custom-WCF-Service-from-SharePoint-2013-Provider-Hosted-Application )
With the introduction, let us go to the SetRating method. The below lines of code are self explanatory.
/// <summary>
/// This will return the User Name in the correct format
/// </summary>
/// <param name="spUser"></param>
/// <returns></returns>
private static string GetUpn(string spUser)
{
string[] userName = spUser.Split('\');
System.DirectoryServices.ActiveDirectory.Domain domain =
System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain();
return userName[1] + "@" + domain.Name;
}
/// <summary>
/// This will take the required parameter from the PHA and start the rating functionality
/// </summary>
/// <param name="ColumnName"></param>
/// <param name="Value"></param>
/// <param name="ListName"></param>
/// <param name="RatingValue"></param>
/// <param name="RatingTitle"></param>
/// <returns></returns>
public string SetRatings(string ColumnName, string Value, string ListName, string RatingValue, string RatingTitle)
{
try
{
//Getting the values of the Sitecollection url and user in the format of domainUserName from the Query string, as it cannot be passed as a parameter to the WebService Method
string SiteURL = Convert.ToString(System.Web.HttpContext.Current.Request.QueryString["URL"]);
string ImpersonatedUser = Convert.ToString(System.Web.HttpContext.Current.Request.QueryString["USER"]);
ULSLog.LogDebug("Entering into SetRatings Method");
bool bSetRating = false;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(SiteURL))
{
SPWeb web = site.AllWebs["MYSite in which the List resides"];
SPList list = web.Lists[ListName];
SPQuery qry = new SPQuery();
string selectCmd = "<Where><Eq><FieldRef Name='" + ColumnName + "'/><Value Type='Text'>" + Value + "</Value></Eq></Where>";
qry.Query = selectCmd;
qry.RowLimit = 1;
SPListItemCollection lstitmcol = list.GetItems(qry);
if (lstitmcol.Count > 0)
{
SPListItem item = lstitmcol[0];
web.AllowUnsafeUpdates = true;
//This is the Actual SetRating Method.
SetRatingForListItem(item, Convert.ToInt32(RatingValue), RatingTitle, ImpersonatedUser);
web.AllowUnsafeUpdates = false;
bSetRating = true;
}
else
bSetRating = false;
}
});
return bSetRating == true? "Success" : "Failure";
}
catch (Exception ex)
{
ULSLog.LogError(ex);
return ex.Message;
}
}
/// <summary>
/// This is the one which is doing the actual rating functionality.
/// </summary>
/// <param name="listItem"></param>
/// <param name="ratingValue"></param>
/// <param name="ratingTitle"></param>
/// <param name="user"></param>
private void SetRatingForListItem(SPListItem listItem, int ratingValue, String ratingTitle, string user)
{
//We are Impersonating the User context with the current logged user. This is because, the rating should happen on behalf of the person, who is rating. When the same person rates the same item again, then the rating count will not increased. It will be Updated for the same entry.
IPrincipal impersonationPrincipal = new WindowsPrincipal(new WindowsIdentity(GetUpn(user)));
HttpRequest request =
new HttpRequest(string.Empty, listItem.Web.Url, string.Empty);
HttpResponse response =
new HttpResponse(
new System.IO.StreamWriter(new System.IO.MemoryStream()));
HttpContext impersonatedContext =
new HttpContext(request, response);
impersonatedContext.User = impersonationPrincipal;
impersonatedContext.Items["HttpHandlerSPWeb"] = listItem.Web;
HttpContext.Current = impersonatedContext;
SPServiceContext serviceContext = SPServiceContext.GetContext(impersonatedContext);
Uri uri = new Uri(String.Format("{0}/{1}", listItem.Web.Url, listItem.Url));
SocialRatingManager ratingManager = new SocialRatingManager(serviceContext);
ratingManager.SetRating(uri, ratingValue, ratingTitle);
//After setting the SetRating method, we need to call the PropogateRating method. This will avoid the delay in the reflecting time. (i.e., waiting for the timer to get executed). But unfortunately, I could not find any impact with this method.
ULSLog.LogDebug("SetRating Completed");
}
Now we were able to SetRating successfully. Now we need to fetch the rated information from the ListItem. To achieve that, the following piece of self explanatory code will be helpful.
public class Rating
{
public float floatAverage{get;set;}
public int ratingCount { get; set; }
}
/// <summary>
/// This method is being called from the PHA.
/// </summary>
/// <param name="ColumnName"></param>
/// <param name="Value"></param>
/// <param name="ListName"></param>
/// <returns></returns>
public Rating GetRatings(string ColumnName, string Value, string ListName)
{
try
{
Rating socialRating = new Rating();
string SiteURL = Convert.ToString(System.Web.HttpContext.Current.Request.QueryString["URL"]);
string ImpersonatedUser = Convert.ToString(System.Web.HttpContext.Current.Request.QueryString["USER"]);
ULSLog.LogDebug("Entering into GetRatings Method");
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(SiteURL))
{
SPWeb web = site.AllWebs["My Site "];
SPList list = web.Lists[ListName];
SPQuery qry = new SPQuery();
string selectCmd = "<Where><Eq><FieldRef Name='" + ColumnName + "'/><Value Type='Text'>" + Value + "</Value></Eq></Where>";
qry.Query = selectCmd;
qry.RowLimit = 1;
SPListItemCollection lstitmcol = list.GetItems(qry);
if (lstitmcol.Count > 0)
{
SPListItem item = lstitmcol[0];
socialRating = GetRatingsForListItem(item, ImpersonatedUser);
}
else
{
ULSLog.LogDebug("No Items Found");
socialRating = null;
}
}
});
ULSLog.LogDebug("Leaving GetRating Method");
return socialRating;
}
catch (Exception ex)
{
ULSLog.LogError(ex);
return null;
}
}
/// <summary>
/// This is the actual method which will be used to Fetch the rating information from the RatingManager
/// </summary>
/// <param name="listItem"></param>
/// <param name="ImpersonatedUser"></param>
private Rating GetRatingsForListItem(SPListItem listItem, string ImpersonatedUser)
{
ULSLog.LogDebug("Entering GetRatingsForListItem Method");
Rating rating = new Rating();
string ItemURL = String.Format("{0}/{1}", listItem.Web.Url, listItem.Url);
IPrincipal impersonationPrincipal = new WindowsPrincipal(new WindowsIdentity(GetUpn(ImpersonatedUser)));
HttpRequest request =
new HttpRequest(string.Empty, listItem.Web.Url, string.Empty);
HttpResponse response =
new HttpResponse(
new System.IO.StreamWriter(new System.IO.MemoryStream()));
HttpContext impersonatedContext =
new HttpContext(request, response);
impersonatedContext.User = impersonationPrincipal;
impersonatedContext.Items["HttpHandlerSPWeb"] = listItem.Web;
HttpContext.Current = impersonatedContext;
Uri uri = new Uri(String.Format("{0}/{1}", listItem.Web.Url, listItem.Url));
SPServiceContext serviceContext = SPServiceContext.GetContext(impersonatedContext);
SocialRatingManager ratingManager = new SocialRatingManager(serviceContext);
SocialRatingAverage average = ratingManager.GetAverage(uri);
rating.floatAverage = average.Average;
rating.ratingCount = ratingManager.GetCount(uri);
if (rating.ratingCount == 0)
rating.floatAverage = 0;
ULSLog.LogDebug("Leaving GetRatingsForListItem Method");
return rating;
}
With this we are done with the GetRating and SetRating.
The issue which I faced during the development was,
Rating.floatAverage = average.Average; -> This line will be throwing null. But after giving the permission on the Permissions tab in Central Administration, I was able to get the RatingAverage.
Leave a comment