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.
One of the most requested features for Sitefinity 4 has recently been a rating control for blogs (and other content). This is the reason why I decided to show you in this post how to make one yourself. However, this post is not only about how to integrate a RadRating control with Content modules, but also how to make use of WCF Restful services, hosted in Sitefinity. In the guide, there are also various other know-hows, including - getting the value of a custom field from a template, using jquery ajax in templates and gathering data from an external resource. In the example you will also see how you can make use of Entity Framework with Sitefinity. First, I will briefly explain the plan. Here are the steps that we are going to take in order to make a working RadRating control integrated with Blog Posts: 1) Creating an extra table inside Sitefinity's database. This table will hold information about the rating. 2) Creating the model of the rating and preparing its CRUD operations inside a RatingManager. 3) Creating a WCF Restful service that makes the connection between the manager and the template. 4) Creating a template and consuming the service from inside it. As a side feature, I have shown how to put the rating data not only in the database but also in custom fields inside our Blog Posts, so that the rating information can be seen from the backend. In addition to that, I have binded the value of the custom fields inside our template. So, let's start this step by step:
1)Creating the database table There isn't much to explain here. It's just a manual adding of a table in Sitefinity's database. The easiest way would be using SQL Management Studio. Our table needs to contain the following: Two columns that will hold the ID of the content item (blog post in this case) and the ID of the user that voted for this item. This way we will always be able to keep track of who voted for what and prevent double voting. These columns are of type uniqueidentifier and are primary keys. I also added a column with the rating of the user (I called it Count). This column is of type decimal with 1 symbol after the decimal point (18,1):
Before we proceed with the next step, we must create a new project in our solution that will hold the model, the CRUD operations and the WCF service. Here's a picture of my folder structure:
2) For the model, I simply used Entity Framework, just to show you that it can be easily worked with in Sitefinity. However, you are free to use Open Access or whatever else you like. Creating the model goes the old-fashioned way (right-click on the DataModel folder, Add New Item >> ADO.NET Entity Data Model and then just go through the steps of creating your model). We should build the application here. An App.Config with the connection string to the database will be created inside your project. However, you won't need that. We already said that the service will be hosted inside Sitefinity, so you have to go to the app.config and copy the connection string inside Sitefinity's web.config (that goes directly before <system.web>):
<
connectionStrings
>
add
name
=
"First43DBEntities"
connectionString
"metadata=res://*/DataModel.RatingModel.csdl|res://*/DataModel.RatingModel.ssdl|res://*/DataModel.RatingModel.msl;provider=System.Data.SqlClient;provider connection string="data source=.\MSSQLR2;initial catalog=First43DB;integrated security=SSPI;multipleactiveresultsets=True;App=EntityFramework""
providerName
"System.Data.EntityClient"
/>
</
using
System;
System.Collections.Generic;
System.Linq;
System.Data.Entity;
System.Text;
RatingControl.DataModel;
Telerik.Sitefinity.Modules.Blogs;
namespace
RatingControl.DataManager
{
class
RatingManager
public
decimal
GetCurrentRatingOfItem(Guid contentItemId)
sum = 0;
result = 0;
List<sf_rating> controlsCount =
new
List<sf_rating>();
(First43DBEntities context =
First43DBEntities())
controlsCount = context.sf_rating.Where(r => r.ItemId == contentItemId).ToList();
}
if
(controlsCount.Count == 0)
return
-1;
(controlsCount.Count > 0)
foreach
(var item
in
controlsCount)
sum += item.Count;
result = sum / controlsCount.Count;
result;
void
UpdateRating(Guid userID,
newCount, Guid itemId)
sf_rating currentRating = FindRatingByItemIDAndUserID(userID, itemId);
currentRating.Count = newCount;
context.Attach(currentRating);
context.ObjectStateManager.ChangeObjectState(currentRating, System.Data.EntityState.Modified);
context.SaveChanges();
sf_rating FindRatingByItemIDAndUserID(Guid userID, Guid itemId)
sf_rating result =
sf_rating();
result = context.sf_rating.Where(r => r.UserId.Equals(userID) && r.ItemId.Equals(itemId)).FirstOrDefault();
Guid CreateRating(Guid itemId, Guid userID,
count)
var ratingUserId = Guid.Empty;
sf_rating rating =
rating.Count = count;
rating.ItemId = itemId;
rating.UserId = userID;
context.sf_rating.AddObject(rating);
ratingUserId = rating.UserId;
ratingUserId;
System.ServiceModel;
System.ServiceModel.Web;
System.Runtime.Serialization;
Telerik.Sitefinity.Utilities.MS.ServiceModel.Web;
Telerik.Sitefinity.Web.Services;
RatingControl.Service
[ServiceContract]
interface
IRatingService
[WebHelp(Comment =
"Requests the rating sum, passing content item ID and current user rating if such"
)]
[WebInvoke(Method =
"POST"
, UriTemplate =
"/RequestRating"
, ResponseFormat = WebMessageFormat.Json, BodyStyle=WebMessageBodyStyle.Wrapped)]
[OperationContract]
RatingSerialized RequestRating(
string
itemID,
userRating);
[DataContract]
RatingSerialized
[DataMember]
Guid RatingId {
get
;
set
; }
RateSum {
UserRate {
Guid ItemId {
Guid UserID {
blgManager.Provider.SuppressSecurityChecks =
true
System.ServiceModel.Activation;
RatingControl.DataManager;
Telerik.Sitefinity.Security;
Telerik.Sitefinity.Blogs.Model;
Telerik.Sitefinity;
Telerik.Sitefinity.Model;
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
RatingService : IRatingService
private
static
RatingManager manager =
RatingManager();
userRating)
Guid itemIdGuid = Guid.Parse(itemID);
userRatingParsed =
.Parse(userRating);
Guid currentUserId = SecurityManager.GetCurrentUser().UserId;
RatingSerialized serialized =
RatingSerialized();
(currentUserId != Guid.Empty)
sf_rating rating = manager.FindRatingByItemIDAndUserID(currentUserId, itemIdGuid);
(rating ==
null
)
manager.CreateRating(itemIdGuid, currentUserId, userRatingParsed);
rating = manager.FindRatingByItemIDAndUserID(currentUserId, itemIdGuid);
AddBlogFieldsValue(itemIdGuid, userRatingParsed);
else
UpdateBlogFieldsValue(itemIdGuid, userRatingParsed, rating.Count);
manager.UpdateRating(currentUserId, userRatingParsed, itemIdGuid);
serialized = TransformToSerialized(rating, manager.GetCurrentRatingOfItem(itemIdGuid));
serialized = TransformToSerialized(
, manager.GetCurrentRatingOfItem(itemIdGuid));
serialized;
RatingSerialized TransformToSerialized(sf_rating rating,
sum)
(rating !=
serialized.ItemId = rating.ItemId;
serialized.UserID = rating.UserId;
serialized.UserRate = rating.Count;
serialized.RateSum = sum;
UpdateBlogFieldsValue(Guid blogPostId,
ratingValue,
oldValue)
BlogsManager blgManager = BlogsManager.GetManager();
bool
securityChecksSettings = blgManager.Provider.SuppressSecurityChecks;
var post1 = blgManager.GetBlogPost(blogPostId);
var master = blgManager.GetBlogPost(post1.OriginalContentId);
ratingSum =
.Parse(DataExtensions.GetValue<
>(master,
"RatingSum"
));
int
ratingCount =
"RatingCount"
ratingSum -= oldValue;
ratingSum += ratingValue;
ratingResult = ratingSum / ratingCount;
DataExtensions.SetValue(master,
, ratingSum);
, ratingCount);
"RatingResult"
, ratingResult);
blgManager.Publish(master);
blgManager.SaveChanges();
blgManager.Provider.SuppressSecurityChecks = securityChecksSettings;
AddBlogFieldsValue(Guid blogPostId,
ratingValue)
ratingCount++;
<%@ ServiceHost Language="C#" Debug="false" Service="RatingControl.Service.RatingService" Factory="Telerik.Sitefinity.Web.Services.WcfHostFactory" %>
function
ServiceSucceeded(result) {
// Here you can add code that makes use of the returned object
All properties of the returned object that we earlier created can be accessed through result.RatingSum (for example)
<%@ Control Language="C#" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.ContentUI" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.Comments" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="sfPanel" Namespace="SitefinityWebApp.Controls" Assembly="SitefinityWebApp" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.PublicControls.BrowseAndEdit"
Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<%@ Import Namespace="Telerik.Sitefinity" %>
<%@ Import Namespace="Telerik.Sitefinity.Model" %>
<%@ Import Namespace="Telerik.Sitefinity.Blogs.Model" %>
<%@ Import Namespace="Telerik.Sitefinity.Modules.Blogs" %>
style
type
"text/css"
.label
visibility: hidden;
script
"text/javascript"
var Type;
var Url;
var Data;
var ContentType;
var DataType;
var ProcessData;
var method;
function CallService() {
$.ajax({
type: Type,
url: Url,
data: Data,
contentType: ContentType,
dataType: DataType,
processdata: ProcessData,
success: function(msg) {
ServiceSucceeded(msg);
},
error: ServiceFailed// When Service call fails
});
function ServiceSucceeded(result) {
function ServiceFailed(result) {
alert('Service call failed: ' + result.status + '' + result.statusText);
Type = null;
Url = null;
Data = null;
ContentType = null;
DataType = null;
ProcessData = null;
function CallRequestRatingService(itemId, userRating) {
Type = "Post";
Url = "http://localhost:42093/Sitefinity/Services/Rating/RatingService.svc/RequestRating";
var msg2 = {"itemID": itemId, "userRating": userRating};
Data = JSON.stringify(msg2);
ContentType = "application/json; charset=utf-8";
DataType = "json";
Processdata = true;
method = "RequestRating";
CallService();
function OnClientRated(obj, args) {
var currentValue = obj.get_value();
var ratingID = obj.get_id()
var parsedID = '#' + ratingID;
var label = $(parsedID).siblings('.label');
var id = label.text();
CallRequestRatingService(id, currentValue);
telerik:RadListView
ID
"Repeater"
ItemPlaceholderID
"ItemsContainer"
runat
"server"
EnableEmbeddedSkins
"false"
EnableEmbeddedBaseStylesheet
LayoutTemplate
sf:ContentBrowseAndEditToolbar
"MainBrowseAndEditToolbar"
Mode
"Add"
ul
"sfpostsList sfpostListTitleDateContent"
asp:PlaceHolder
ItemTemplate
li
"sfpostListItem"
h2
"sfpostTitle"
sf:DetailsViewHyperLink
"DetailsViewHyperLink1"
TextDataField
"Title"
ToolTipDataField
"Description"
div
"sfpostAuthorAndDate"
asp:Literal
"Literal2"
Text="<%$ Resources:Labels, By %>" runat="server" />
sf:PersonProfileView
"PersonProfileView1"
sf:FieldListView
"PostDate"
Format
" | {PublicationDate.ToLocal():MMM dd, yyyy}"
"PostContent"
Text
"{0}"
Properties
"Content"
WrapperTagName
"div"
WrapperTagCssClass
"sfpostContent"
sf:CommentsBox
"itemCommentsLink"
CssClass
"sfpostCommentsCount"
br
asp:Label
"Item rating"
></
telerik:RadRating
"rating"
"RadRating1"
ReadOnly
"true"
ItemCount
"7"
Skin
"Sitefinity"
Orientation
"Horizontal"
Value='<%# decimal.Parse(((BlogPost)Container.DataItem).GetValue<string>("RatingResult")) %>' >
"Your rating"
"RadRating3"
Precision
"Exact"
SelectionMode
"Continuous"
OnClientRated
"OnClientRated"
"label"
Text='<%# Eval("Id")%>'></
"BrowseAndEditToolbar"
"Edit,Delete,Unpublish"
sf:Pager
"pager"
I hope this would be useful for you guys, if you have any questions, please feel free to ask.
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.
Subscribe to get all the news, info and tutorials you need to build better business apps and sites
Copyright © 2019 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
Progress, Telerik, and certain product names used herein are trademarks or registered trademarks of Progress Software Corporation and/or one of its subsidiaries or affiliates in the U.S. and/or other countries. See Trademarks for appropriate markings.