4

I would like to programmatically enable "Likes" (ratings) for a list in a SharePoint 2013 solution (object model or CSOM), but have not found any public methods available that support enabling ratings from custom code. Using reflection is not an option.

I did find an internal method named EnableReputation in Microsoft.SharePoint.Portal.ReputationHelper class, which is the method SharePoint calls internally if you enable Ratings/Likes for a list using the UI (through /_layouts/15/RatingsSettings.aspx). However, I've had no luck finding anything publicly available that I can actually use. Any suggestions?

4

The Ratings setting is stored as a property of the root folder of a list. In PowerShell you can do this:

$web = Get-SPWeb http://site
$list = $web.Lists["Documents"]
$list.RootFolder.Properties["Ratings_VotingExperience"] = "Likes" 
// Value can be “Likes” or “Ratings”
$list.RootFolder.Update()

In code, you can do:

using (var site = new SPSite("http://site"))
{
    using (var web = site.OpenWeb())
    {
        var list = web.Lists["Documents"];
        list.RootFolder.Properties["Ratings_VotingExperience"] = "Likes";
        list.RootFolder.Update();
    }
}

EDIT: @Anders Austad, you were correct, this was only part of the solution. Here is the complete code to accomplish this (most extracted with reflection). Since none of the stuff to do this is publicly exposed, you either have to use reflection or recreate the method (which is what is below):

private const string EmptyIconUrl_Name = "Ratings_EmptyIconUrl";
private const string EmptyIconUrl_Value = "/_layouts/Images/RatingsEmpty.png";
private const string FeatureActivated_False_Value = "false";
private const string FeatureActivated_Name = "Ratings_FeatureActivated";
private const string FeatureActivated_True_Value = "true";
private const string ImageStripRtlUrl_Name = "Ratings_ImageStripRtlUrl";
private const string ImageStripRtlUrl_Value = "/_layouts/Images/Ratingsrtl.png";
private const string ImageStripUrl_Name = "Ratings_ImageStripUrl";
private const string ImageStripUrl_Value = "/_layouts/Images/Ratings.png";
private const string LoadingImg = @"<img src=\'/_layouts/images/loading16.gif\'> ";
private const string NewRatingIconUrl_Name = "Ratings_NewRatingIconUrl";
private const string NewRatingIconUrl_Value = "/_layouts/Images/RatingsNew.png";
private static List<KeyValuePair<string, string>> ratingImages = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("RatingsLargeStarFilled", "/_layouts/15/images/RatingsLargeStarFilled.png?rev=23"), new KeyValuePair<string, string>("RatingsSmallStarEmpty", "/_layouts/15/images/RatingsSmallStarEmpty.png?rev=23"), new KeyValuePair<string, string>("RatingsSmallStarFilled", "/_layouts/15/images/RatingsSmallStarFilled.png?rev=23"), new KeyValuePair<string, string>("RatingsSmallStarLeftHalfFilled", "/_layouts/15/images/RatingsSmallStarLeftHalfFilled.png?rev=23"), new KeyValuePair<string, string>("RatingsSmallStarRightHalfFilled", "/_layouts/15/images/RatingsSmallStarRightHalfFilled.png?rev=23"), new KeyValuePair<string, string>("RatingsSmallStarHoveroverEmpty", "/_layouts/15/images/RatingsSmallStarHoveroverEmpty.png?rev=23"), new KeyValuePair<string, string>("RatingsSmallStarHoveroverFilled", "/_layouts/15/images/RatingsSmallStarHoveroverFilled.png?rev=23") };
private const string RatingsEnabled_Name = "Ratings_Enabled";
private const string RatingsExperience_Likes_Value = "Likes";
private const string RatingsExperience_Name = "Ratings_VotingExperience";
private const string RatingsExperience_Ratings_Value = "Ratings";
private static readonly Guid RatingsFieldGuid_AverageRating = new Guid("5a14d1ab-1513-48c7-97b3-657a5ba6c742");
private static readonly Guid RatingsFieldGuid_FeatureId = new Guid("915c240e-a6cc-49b8-8b2c-0bff8b553ed3");
private static readonly Guid RatingsFieldGuid_RatedBy = new Guid("4D64B067-08C3-43DC-A87B-8B8E01673313");
private static readonly Guid RatingsFieldGuid_RatingCount = new Guid("b1996002-9167-45e5-a4df-b2c41c6723c7");
private static readonly Guid RatingsFieldGuid_Ratings = new Guid("434F51FB-FFD2-4A0E-A03B-CA3131AC67BA");
private static List<KeyValuePair<Guid, string>> reputationAllFields = new List<KeyValuePair<Guid, string>> { new KeyValuePair<Guid, string>(RatingsFieldGuid_AverageRating, "Ratings"), new KeyValuePair<Guid, string>(RatingsFieldGuid_RatingCount, "Ratings"), new KeyValuePair<Guid, string>(RatingsFieldGuid_RatedBy, "Ratings"), new KeyValuePair<Guid, string>(RatingsFieldGuid_Ratings, "Ratings"), new KeyValuePair<Guid, string>(ContentReputation_LikesCount_FieldId, "Likes"), new KeyValuePair<Guid, string>(ContentReputation_LikedBy_FieldId, "Likes") };
private static readonly Guid ContentReputation_LikedBy_FieldId = new Guid("{2CDCD5EB-846D-4f4d-9AAF-73E8E73C7312}");
private static readonly Guid ContentReputation_LikesCount_FieldId = new Guid("{6E4D832B-F610-41a8-B3E0-239608EFDA41}");

public static void SetRatings(string webUrl, string listName, bool useLikes)
{
    var experience = useLikes ? RatingsExperience_Likes_Value : RatingsExperience_Ratings_Value;
    var oldExperience = string.Empty;
    var convertExperience = false;

    // Open the site collection
    using (var site = new SPSite(webUrl))
    {
        // Open the site
        using (var web = site.OpenWeb())
        {
            // Get the list
            var list = web.Lists[listName];
            if (list != null)
            {
                // Allow unsafe updates
                var previousUnsafeUpdatesValue = web.AllowUnsafeUpdates;
                if (!previousUnsafeUpdatesValue)
                {
                    web.AllowUnsafeUpdates = true;
                    web.Update();
                }

                try
                {
                    // If the value didn't change, don't do anything
                    if (list.RootFolder.Properties.ContainsKey(RatingsExperience_Name) && list.RootFolder.Properties[RatingsExperience_Name].ToString().Equals(experience, StringComparison.OrdinalIgnoreCase))
                        return;

                    // If there was a previous value, we will convert the experience
                    if (list.RootFolder.Properties.ContainsKey(RatingsExperience_Name) && !string.IsNullOrEmpty(list.RootFolder.Properties[RatingsExperience_Name].ToString()))
                    {
                        convertExperience = true;
                        oldExperience = list.RootFolder.Properties[RatingsExperience_Name].ToString();
                    }

                    // Set the experience property
                    list.RootFolder.Properties[RatingsExperience_Name] = experience;
                    list.RootFolder.Update();

                    if (convertExperience)
                    {
                        // Convert ratings experience
                        var views = new List<SPView>();
                        if ((list.BaseTemplate == SPListTemplateType.DiscussionBoard) || (list.BaseTemplate == SPListTemplateType.Posts))
                            foreach (SPView view in list.Views)
                                views.Add(view);
                        else
                            views.Add(list.Views[list.DefaultView.ID]);
                        SPField field = null;
                        foreach (var view in views)
                        {
                            bool flag = false;
                            var internalNames = new List<string>();
                            foreach (var pair in reputationAllFields)
                            {
                                bool flag2 = false;
                                field = TryGetField(list.Fields, pair.Key);
                                if (field != null)
                                {
                                    if (((pair.Value == experience) && (((pair.Key == RatingsFieldGuid_AverageRating) || (pair.Key == RatingsFieldGuid_RatedBy)) || (((pair.Key == RatingsFieldGuid_Ratings) || (pair.Key == ContentReputation_LikesCount_FieldId)) || (pair.Key == ContentReputation_LikedBy_FieldId)))) && !view.ViewFields.Exists(field.InternalName))
                                    {
                                        view.ViewFields.Add(field.InternalName);
                                        flag = true;
                                        if ((pair.Key == ContentReputation_LikesCount_FieldId) || (pair.Key == RatingsFieldGuid_AverageRating))
                                        {
                                            if (string.IsNullOrEmpty(field.JSLink))
                                            {
                                                field.JSLink = "sp.ui.reputation.js";
                                                flag2 = true;
                                            }
                                            if (field.ShowInDisplayForm == false)
                                            {
                                                field.ShowInDisplayForm = true;
                                                flag2 = true;
                                            }
                                        }
                                        if (((field.Id == ContentReputation_LikedBy_FieldId) || (field.Id == RatingsFieldGuid_RatedBy)) || (field.Id == RatingsFieldGuid_Ratings))
                                        {
                                            internalNames.Add(field.InternalName);
                                        }
                                    }
                                    else if ((pair.Value == oldExperience) && view.ViewFields.Exists(field.InternalName))
                                    {
                                        if (((pair.Key == ContentReputation_LikesCount_FieldId) || (pair.Key == RatingsFieldGuid_AverageRating)) && (field.ShowInDisplayForm == true))
                                        {
                                            field.ShowInDisplayForm = false;
                                            flag2 = true;
                                        }
                                        view.ViewFields.Delete(field.InternalName);
                                        flag = true;
                                    }
                                    if (flag2)
                                    {
                                        field.Update();
                                    }
                                }
                            }
                            if (flag)
                            {
                                view.Update();
                            }
                            if (internalNames.Count > 0)
                            {
                                SetViewFieldAttribute(view, internalNames.ToArray(), "Explicit", bool.TrueString.ToUpperInvariant());
                                view.Update();
                            }
                        }
                        for (int i = 0; i < list.Views.Count; i++)
                        {
                            SPView view3 = list.Views[i];
                            if (view3.JSLink != null)
                            {
                                view3.JSLink = view3.JSLink + "|sp.ui.communities.js";
                            }
                            view3.Update();
                        }
                    }
                    else
                    {
                        // Enable ratings experience
                        var fields = new List<SPField>();
                        SPField item = null;
                        var fieldName = string.Empty;
                        foreach (var pair in reputationAllFields)
                        {
                            bool flag = false;
                            fieldName = EnsureField(list, pair.Key);
                            item = list.Fields.GetFieldByInternalName(fieldName);
                            if (item != null)
                            {
                                if ((pair.Value == experience) && (pair.Key != RatingsFieldGuid_RatingCount))
                                {
                                    fields.Add(item);
                                }
                                if (((pair.Key == ContentReputation_LikesCount_FieldId) || (pair.Key == RatingsFieldGuid_AverageRating)) && string.IsNullOrEmpty(item.JSLink))
                                {
                                    item.JSLink = "sp.ui.reputation.js";
                                    flag = true;
                                }
                                if (((experience == "Likes") && (pair.Key == RatingsFieldGuid_AverageRating)) || ((experience == "Ratings") && (pair.Key == ContentReputation_LikesCount_FieldId)))
                                {
                                    item.ShowInDisplayForm = false;
                                    flag = true;
                                }
                                else if (((experience == "Likes") && (pair.Key == ContentReputation_LikesCount_FieldId)) || ((experience == "Ratings") && (pair.Key == RatingsFieldGuid_AverageRating)))
                                {
                                    item.ShowInDisplayForm = true;
                                    flag = true;
                                }
                                if (flag)
                                    item.Update();
                            }
                        }
                        var views = new List<SPView>();
                        if ((list.BaseTemplate == SPListTemplateType.DiscussionBoard) || (list.BaseTemplate == SPListTemplateType.Posts))
                            foreach (SPView view in list.Views)
                                views.Add(view);
                        else
                            views.Add(list.Views[list.DefaultView.ID]);
                        foreach (var view in views)
                        {
                            bool flag2 = false;
                            var internalNames = new List<string>();
                            foreach (var field in fields)
                            {
                                if (!view.ViewFields.Exists(field.InternalName))
                                {
                                    view.ViewFields.Add(field.InternalName);
                                    flag2 = true;
                                }
                                if (((field.Id == ContentReputation_LikedBy_FieldId) || (field.Id == RatingsFieldGuid_RatedBy)) || (field.Id == RatingsFieldGuid_Ratings))
                                {
                                    internalNames.Add(field.InternalName);
                                }
                            }
                            try
                            {
                                if (flag2)
                                {
                                    view.Update();
                                    flag2 = false;
                                }
                                if (internalNames.Count > 0)
                                {
                                    SetViewFieldAttribute(view, internalNames.ToArray(), "Explicit", bool.TrueString.ToUpperInvariant());
                                    view.Update();
                                }
                            }
                            catch
                            {
                            }
                        }
                    }
                }
                catch (Exception exception)
                {
                    Console.WriteLine("Something went wrong: " + exception.ToString());
                }

                // Revert unsafe updates setting
                if (!previousUnsafeUpdatesValue)
                {
                    web.AllowUnsafeUpdates = false;
                    web.Update();
                }
            }
        }
    }
}

private static SPField TryGetField(SPFieldCollection fieldCollection, Guid fieldId)
{
    SPField field = null;
    try
    {
        field = fieldCollection[fieldId];
    }
    catch (ArgumentException)
    { }
    return field;
}



private static string EnsureField(SPList list, Guid fieldID)
{
    SPFieldCollection fields = list.Fields;
    SPFieldCollection availableFields = list.ParentWeb.AvailableFields;
    SPField field = null;
    field = availableFields[fieldID];
    if (!fields.Contains(fieldID))
    {
        return fields.AddFieldAsXml(field.SchemaXmlWithResourceTokens, false, SPAddFieldOptions.AddFieldInternalNameHint | SPAddFieldOptions.AddToAllContentTypes);
    }
    return field.InternalName;
}

private static void SetViewFieldAttribute(SPView view, string[] fieldNames, string attributeName, string attributeValue)
{
    string viewXml = string.Empty;
    if (view != null)
    {
        viewXml = view.GetViewXml();
    }
    if (!string.IsNullOrEmpty(viewXml))
    {
        XmlDocument document = new XmlDocument();
        try
        {
            using (XmlTextReader reader = new XmlTextReader(new StringReader(viewXml)))
            {
                reader.DtdProcessing = DtdProcessing.Parse;
                document.Load(reader);
            }
        }
        catch { }
        foreach (string str2 in fieldNames)
        {
            XmlNode node = document.SelectSingleNode("View/ViewFields/FieldRef[@Name=\"" + str2 + "\"]");
            if (node != null)
            {
                XmlAttribute attribute = document.CreateAttribute(attributeName);
                attribute.Value = attributeValue;
                node.Attributes.SetNamedItem(attribute);
            }
        }
        view.SetViewXml(document.OuterXml);
    }
}
4
  • Only note I would add is that there is some complexity to switching between Likes and Ratings, so I probably wouldn't attempt to do that if you don't want to use reflection. However, to set the initial setting, this works. Dec 3 '13 at 21:26
  • Thanks for replying! I tested this but it had no effect. None of the necessary fields needed for ratings were added (LikesCount, LikedBy etc) to the list and none of the views were changed. There is an internal method called "EnsureReputation" in the internal "BasicReputationHelper" class which seems to read this property and enable Ratings, but this method is never called (as far as I can see).
    – anderaus
    Dec 4 '13 at 14:19
  • 1
    I've updated the answer with a more complete solution. You were right, setting the value wasn't the whole thing. Dec 4 '13 at 15:09
  • Thanks, that looks like it should work. I was about to go down the same route, reflecting and "copying" the code I needed from the object model, but was hoping there was an easier/better way. 300 lines of extra code to enable ratings... and with no guarantee that Microsoft won't change something internally that would break everything. Anyway, thanks for the help, I'll mark this as the accepted answer.
    – anderaus
    Dec 4 '13 at 15:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.