The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.
Let me first start by wrapping up what has to be done. Since we are going to rate content items we would need to associate the Rating Control with the content item being rated. For these purposes we will need to create two controls - one IScriptControl which will wrap the functionality of the RadRating control and tie it up to a content item and a custom ContentView control, which will pass the content item id and provider name to the first control. To rate content items without making post backs we would also need to create Web Service to handle this. Finally we should keep track of who has rated which item in order to prevent double rating.
For the purposes of this sample I will use the Generic Content module, however this can be easily integrated with any Generic Content based module. Lets start by adding the necessary meta fields to the meta fields collection of Generic Content module:
<add key="Generic_Content.Rating" valueType="ShortText" visible="True" searchable="True" sortable="True" defaultValue="0.0"/> |
<add key="Generic_Content.RatingSum" valueType="ShortText" visible="True" searchable="false" sortable="false" defaultValue="0.0"/> |
<add key="Generic_Content.RatingCount" valueType="Integer" visible="True" searchable="True" sortable="true" defaultValue="0"/> |
The next step is to build our custom IScript control, create a control template for it and expose content item information to our client control. Code code bellow is for the server part of our IScript control:
using System; |
using System.Collections.Generic; |
using System.Linq; |
using System.Web; |
using System.Web.Security; |
using System.Web.UI; |
using System.Web.UI.WebControls; |
using Telerik.Cms.Engine; |
using Telerik.Cms.Web.UI; |
using Telerik.Web.UI; |
namespace Telerik.Samples.WebControls |
{ |
/// <summary> |
/// Summary description for SitefinityRating |
/// </summary> |
public class SitefinityRating : SimpleControl, IScriptControl |
{ |
#region Private Properties |
private double ratingSum; |
private Guid contentID; |
private string providerName; |
//path to the client code for our IScript control |
private string jsFilePath = "~/Sitefinity/Admin/Scripts/SitefinityRating.js"; |
#endregion |
#region Public Properties |
public Guid ContentID |
{ |
get |
{ |
return this.contentID; |
} |
set |
{ |
this.contentID = value; |
} |
} |
public string ProviderName |
{ |
get |
{ |
return this.providerName; |
} |
set |
{ |
this.providerName = value; |
} |
} |
public double RatingSum |
{ |
get |
{ |
return this.ratingSum; |
} |
set |
{ |
this.ratingSum = value; |
} |
} |
public RadRating RadRating |
{ |
get |
{ |
return base.Container.GetControl<RadRating>("RadRating1",false, Telerik.Framework.Web.TraverseMethod.BreadthFirst); |
} |
set |
{ |
this.RadRating = value; |
} |
} |
public Label Rating |
{ |
get |
{ |
return base.Container.GetControl<Label>("Rating", false, Telerik.Framework.Web.TraverseMethod.BreadthFirst); |
} |
set |
{ |
this.Rating = value; |
} |
} |
public Label RatingCount |
{ |
get |
{ |
return base.Container.GetControl<Label>("RatingCount", false, Telerik.Framework.Web.TraverseMethod.BreadthFirst); |
} |
set |
{ |
this.RatingCount = value; |
} |
} |
#endregion |
#region IScriptControl Members |
//expose the necessary data to the client control |
public IEnumerable<ScriptDescriptor> GetScriptDescriptors() |
{ |
var descriptor = new ScriptBehaviorDescriptor(this.GetType().FullName, this.ClientID); |
descriptor.AddProperty("contentId", this.ContentID); |
descriptor.AddProperty("providerName", this.ProviderName); |
descriptor.AddProperty("radRatingId",this.RadRating.ClientID); |
descriptor.AddProperty("ratingId", this.Rating.ClientID); |
descriptor.AddProperty("ratingCountId", this.RatingCount.ClientID); |
descriptor.AddProperty("ratingSum", this.RatingSum); |
return new ScriptDescriptor[] { descriptor }; |
} |
//create a script reference |
public IEnumerable<ScriptReference> GetScriptReferences() |
{ |
ScriptReference reference = new ScriptReference(this.jsFilePath); |
return new ScriptReference[] { reference }; |
} |
#endregion |
#region Overriden Methods |
protected override void Render(HtmlTextWriter writer) |
{ |
if (!(this.Page == null)) |
{ |
var scriptManager = ScriptManager.GetCurrent(this.Page); |
if (scriptManager != null) |
{ |
scriptManager.RegisterScriptDescriptors(this); |
} |
} |
base.Render(writer); |
} |
//set layout template path for the control |
public override string LayoutTemplatePath |
{ |
get |
{ |
return "~/CustomControls/ControlTemplates/SitefinityRating.ascx"; |
} |
set |
{ |
base.LayoutTemplatePath = value; |
} |
} |
protected override void OnPreRender(EventArgs e) |
{ |
if (!(this.Page == null)) |
{ |
var scriptManager = ScriptManager.GetCurrent(this.Page); |
if (scriptManager != null) |
{ |
scriptManager.RegisterScriptControl<SitefinityRating>(this); |
} |
} |
//we check if user is authenticated and if user has already rated item |
//if yes then the user will not be able to rate item |
MembershipUser currentUser = Telerik.Security.UserManager.Default.GetUser(); |
if (!HttpContext.Current.User.Identity.IsAuthenticated) |
this.RadRating.ReadOnly = true; |
else |
{ |
RatingDataDataContext _db = new RatingDataDataContext(); |
IQueryable<sf_Rating> userRatings = from ratings in _db.sf_Ratings |
where ratings.UserID == (Guid)currentUser.ProviderUserKey && ratings.ContentId == this.ContentID |
select ratings; |
if (userRatings.ToList<sf_Rating>().Count > 0) |
{ |
this.RadRating.ReadOnly = true; |
} |
} |
//get necessary data which will be passed to the client control |
//we get meta field data from by instantiating a content item from the ID passed by the content view |
ContentManager contentManager = new ContentManager(ProviderName); |
IContent contentItem = contentManager.GetContent(ContentID); |
this.RatingCount.Text = contentItem.GetMetaData("RatingCount").ToString(); |
this.Rating.Text = contentItem.GetMetaData("Rating").ToString(); |
if(this.Rating.Text=="") |
this.Rating.Text="0"; |
if (contentItem.GetMetaData("RatingSum").ToString() != "") |
this.RatingSum = double.Parse(contentItem.GetMetaData("RatingSum").ToString()); |
else this.RatingSum = 0.0; |
this.RadRating.Value = RatingSum / Int32.Parse(RatingCount.Text); |
base.OnPreRender(e); |
} |
#endregion |
} |
} |
This is the markup of our control:
<%@ Control Language="C#"%> |
<%@ Register Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" TagPrefix="telerik"%> |
<telerik:RadRating ID="RadRating1" runat="server" |
Precision="Exact" ItemCount="5" Skin="Vista" |
SelectionMode="Continuous" Orientation="Horizontal" /> |
<asp:Label ID="RatingLabel" runat="server" AssociatedControlID="Rating">Rating: </asp:Label> |
<asp:Label ID="Rating" runat="server" Text=""></asp:Label> |
<br /> |
<asp:Label ID="RatingCountLabel" runat="server" AssociatedControlID="RatingCount">Times Rated: </asp:Label> |
<asp:Label ID="RatingCount" runat="server" Text=""></asp:Label> |
And lastly the client code:
// Register the namespace for client control. |
Type.registerNamespace('Telerik.Samples.WebControls'); |
// ------------------------------------------------------------------------ |
// Definition of SitefinityRating class and properties |
// ------------------------------------------------------------------------ |
Telerik.Samples.WebControls.SitefinityRating = function(element) { |
Telerik.Samples.WebControls.SitefinityRating.initializeBase(this, [element]); |
// ------------------------------------------------------------------------ |
// SitefinityRating class properties |
// ------------------------------------------------------------------------ |
this._contentId = null; |
this._providerName = null; |
this._radRatingId = null; |
this._ratingId = null; |
this._ratingCountId = null; |
this._ratingSum = null; |
} |
Telerik.Samples.WebControls.SitefinityRating.prototype = { |
// ------------------------------------------------------------------------ |
// initialization and clean-up |
// ------------------------------------------------------------------------ |
initialize: function() { |
Telerik.Samples.WebControls.SitefinityRating.callBaseMethod(this, 'initialize'); |
Sys.Application.add_load(Function.createDelegate(this, this._onLoad)); |
}, |
dispose: function() { |
Telerik.Samples.WebControls.SitefinityRating.callBaseMethod(this, 'dispose'); |
}, |
// ------------------------------------------------------------------------ |
// public functions |
// ------------------------------------------------------------------------ |
rate: function(rating) { |
this.get_radRating().set_readOnly(true); |
Telerik.Samples.WebServices.RatingService.RateContent(this.get_contentId(), this.get_providerName(), rating, Function.createDelegate(this, this._onRateSuccess), this._onRateFailure); |
}, |
// ------------------------------------------------------------------------ |
// private functions |
// ------------------------------------------------------------------------ |
_onLoad: function(sender, args) { |
var radRating = this.get_radRating(); |
if (radRating) { |
radRating.add_rating(Function.createDelegate(this, this._onRatingPreview)); |
debugger; |
} |
}, |
_onRatingPreview: function(sender, args) { |
var rating = args.get_newValue(); |
this.rate(rating); |
}, |
_onRateSuccess: function(caller, successData) { |
var rating = this.get_rating(); |
var ratingCount = this.get_ratingCount(); |
var radRating = this.get_radRating(); |
var ratingSum = this.get_ratingSum(); |
var newRatingSum = ratingSum + radRating.get_value(); |
var newRatingCount = parseInt(ratingCount.textContent) + 1; |
var newRating = newRatingSum / newRatingCount; |
radRating.set_value(newRating); |
rating.textContent = newRating; |
ratingCount.textContent = newRatingCount; |
}, |
_onRateFailure: function(errorData) { |
alert("" + errorData); |
}, |
// ------------------------------------------------------------------------ |
// property assessors |
// ------------------------------------------------------------------------ |
get_contentId: function() { |
return this._contentId; |
}, |
set_contentId: function(value) { |
if (this._contentId !== value) { |
this._contentId = value; |
this.raisePropertyChanged('contentId'); |
} |
}, |
get_providerName: function() { |
return this._providerName; |
}, |
set_providerName: function(value) { |
if (this._providerName !== value) { |
this._providerName = value; |
this.raisePropertyChanged('providerName'); |
} |
}, |
get_radRatingId: function() { |
return this._radRatingId; |
}, |
set_radRatingId: function(value) { |
if (this._radRatingId !== value) { |
this._radRatingId = value; |
this.raisePropertyChanged('radRatingId'); |
} |
}, |
get_radRating: function() { |
var rating = $find(this.get_radRatingId()); |
return rating; |
}, |
get_ratingId: function() { |
return this._ratingId; |
}, |
set_ratingId: function(value) { |
if (this._ratingId !== value) { |
this._ratingId = value; |
this.raisePropertyChanged('ratingId'); |
} |
}, |
get_rating: function() { |
var rating = $get(this._ratingId); |
return rating; |
}, |
get_ratingCountId: function() { |
return this._ratingCountId; |
}, |
set_ratingCountId: function(value) { |
if (this._ratingCountId !== value) { |
this._ratingCountId = value; |
this.raisePropertyChanged('ratingCountId'); |
} |
}, |
get_ratingCount: function() { |
var rating = $get(this.get_ratingCountId()); |
return rating; |
}, |
get_ratingSum: function() { |
return this._ratingSum; |
}, |
set_ratingSum: function(value) { |
if (this._ratingSum !== value) { |
this._ratingSum = value; |
this.raisePropertyChanged('ratingSum'); |
} |
} |
} |
Telerik.Samples.WebControls.SitefinityRating.registerClass('Telerik.Samples.WebControls.SitefinityRating', Sys.UI.Control); |
After we have created our SitefinityRating control, it is time to customize the ContentView control to pass the content item ID and the provider name to it. We have to create a control which inherits from ContentView override the LayoutTemplatePaths and point to custom templates to which we have added our SitefinityRating control, finally we should override the SetItemMetadata and OnPreRender methods. The first one is to pass id and provider name. The second one is to add a reference to the web service which is going to update the ratings:
protected override void OnPreRender(EventArgs e) |
{ |
base.OnPreRender(e); |
ScriptManager sm = ScriptManager.GetCurrent(this.Page); |
ServiceReference reference = new ServiceReference("~/Sitefinity/Admin/Services/RatingService.asmx"); |
reference.InlineScript = true; |
sm.Services.Add(reference); |
} |
protected override void SetItemMetadata(Control itemContainer, Telerik.Cms.Engine.IContent contentItem) |
{ |
base.SetItemMetadata(itemContainer, contentItem); |
SitefinityRating rating = (SitefinityRating)this.FindContentViewControl("sfRating", itemContainer); |
if (rating != null) |
{ |
rating.ContentID = contentItem.ID; |
rating.ProviderName = contentItem.ProviderName; |
} |
} |
Now we have to create the WebService and implement the RateContent WebMethod which is will update content ratings :
[WebMethod] |
public void RateContent(Guid contentID, string providerName, double ratingValue) |
{ |
MembershipUser user = Telerik.Security.UserManager.Default.GetUser(); |
if (user != null) |
{ |
//get content item being rated |
ContentManager contentManager = new ContentManager(providerName); |
IContent contentItem = contentManager.GetContent(contentID); |
//get rating sum |
double RatingSum = 0; |
if (contentItem.GetMetaData("RatingSum").ToString() != "") |
RatingSum = double.Parse(contentItem.GetMetaData("RatingSum").ToString()); |
RatingSum = RatingSum + ratingValue; |
//increase rating count |
int RatingCount = Int32.Parse(contentItem.GetMetaData("RatingCount").ToString()); |
RatingCount = RatingCount + 1; |
//calculate rating |
double Rating = RatingSum / RatingCount; |
//set new values to metafields and save item |
contentItem.SetMetaData("RatingSum", RatingSum.ToString()); |
contentItem.SetMetaData("RatingCount", RatingCount); |
contentItem.SetMetaData("Rating", Rating.ToString()); |
contentManager.SaveContent(contentItem); |
//save record that user has rated this content items |
RatingDataDataContext _db = new RatingDataDataContext(); |
sf_Rating newRating = new sf_Rating(); |
newRating.ContentId = contentID; |
newRating.UserID = (Guid)user.ProviderUserKey; |
_db.sf_Ratings.InsertOnSubmit(newRating); |
_db.SubmitChanges(); |
} |
} |
In the above code you have probably noticed that I am using LINQ to retrieve and store data about which user has rated which item. For this I have created a table in my DB to store each user's unique identifier and the content item ID. Both of those values are of type Guid. I have set those two values that I retain to be a composite primary key for this table. This table is added to my Sitefinity DB and is called sf_Ratig. Bellow is a snapshot of how my table looks in MS SQL.
Lastly you have to add hidden text boxes to the control template for editing content items and map this template, in order to prevent loosing the rating values once content item is being edited. The control template is ~/Sitefinity/Admin/ControlTemplates/Generic_Content/ContentEditView.ascx. I have added this to my template:
<sf:ContentMetaFields id="MetaFields" runat="server"> |
<ItemTemplate> |
... |
<asp:TextBox ID="Rating" runat="server" visible="false"/> |
<asp:TextBox ID="RatingSum" runat="server" visible="false"/> |
<asp:TextBox ID="RatingCount" runat="server" visible="false"/> |
<div class="bottom"><div><!-- --></div></div> |
... |
</ItemTemplate> |
</sf:ContentMetaFields> |
All you have to do now is to add the control to your controls collection:
<toolboxControls> |
<clear/> |
... |
<add name="Custom Content View" section="Custom Controls" type="Telerik.Samples.WebControls.CustomContentView, App_Code"/> |
</toolboxControls> |
You can download all sample code and other related files from the following link: SitefinityRating.
Please, note that after you have added new meta fields to the Generic Content module items which you previously had will not contain the default values for those meta fields. You will have to go through all content items and "dummy" edit and save them.
View all posts from The Progress Team on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.
Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.
Learn MoreSubscribe to get all the news, info and tutorials you need to build better business apps and sites
Progress collects the Personal Information set out in our Privacy Policy and the Supplemental Privacy notice for residents of California and other US States and uses it for the purposes stated in that policy.
You can also ask us not to share your Personal Information to third parties here: Do Not Sell or Share My Info
We see that you have already chosen to receive marketing materials from us. If you wish to change this at any time you may do so by clicking here.
Thank you for your continued interest in Progress. Based on either your previous activity on our websites or our ongoing relationship, we will keep you updated on our products, solutions, services, company news and events. If you decide that you want to be removed from our mailing lists at any time, you can change your contact preferences by clicking here.