How to add rating to blogposts

From Sense/Net Wiki
Jump to: navigation, search
  •  
  •  
  •  
  •  
  • 100%
  • 6.3
  • Enterprise
  • Community
  • Planned

Overview

Rating
Adding rating as custom functionality to a discussion forum in Sense/Net ECM is easy with a fundamental code-editor knowledge. With the use of OData API a client-side javascript can control the function. With following the steps below you can create the necessary Content Types and contents in the Content Repository to complete your new rating-function.

Steps

1. Install new CTD

Install a new CTD for rating as the child of /Root/System/Schema/ContentTypes/GenericContent/ListItem CTD with the following content:

<ContentType name="Rating" parentType="ListItem" handler="SenseNet.ContentRepository.GenericContent" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
<DisplayName>$Ctd-Rating,DisplayName</DisplayName>
<Description>$Ctd-Rating,Description</Description>
<Icon> FormItem </Icon>
<Fields>
<Field name="Rating" type="Choice">
<Configuration>
<Options>
<Option value="1">1</Option>
<Option value="2">2</Option>
<Option value="3">3</Option>
<Option value="4">4</Option>
<Option value="5">5</Option>
</Options>
</Configuration>
</Field>
</Fields>
</ContentType>

2. Modify ForumEntry CTD

Set the new Rating Content Type as AllowedChildType on ForumEntry CTD with adding the following tag right below the line of "AllowIncrementalNaming"

<AllowedChildTypes>Rating</AllowedChildTypes>

Also on ForumEntry CTD you need to add a number-field to be able to display the average rating data

<Field name="AverageRating" type="Number"></Field>


3. Modify ForumTopic view

In /Root/System/SystemPlugins/Controls/ForumTopic.ascx paste a new div in the topic-body, right after entry-title to display the average rate-value on every comment:

  <div class="sn-entry-rating"><%# Eval("AverageRating") %></div>

Into ForumTopic.ascx paste a “data-currentuser” attribute on “sn-entries” to let the script check if the user is authenticated, and not voted yet:

<div class="sn-entries" data-currentuser="<%= ((SenseNet.ContentRepository.User)(SenseNet.ContentRepository.User.Current)).Name %>">

Into ForumTopic.ascx paste a “data-path” attribute on “sn-entry-container” to be able to save the rating-value and to get existing rating content via OData:

<div class="sn-entry-container" data-path="<%# Eval("Path") %>">

At the top of ForumTopic.ascx paste the following script request, in order to use helper scripts for displaying messages (error, success, etc.):

<sn:ScriptRequest id="ScriptRequest" runat="server" Path="$skin/scripts/sn/SN.Util.js" />


4. Add rating functionality to the view with an inline javascript

You have to add javascript to the view to add rating functionality. The following script changes the average rate value to display it as stars:

$(function () {
    $('.sn-entry-rating').each(function () {
        var $this = $(this);
        var currentValue = $this.text();
        var num = currentValue.split('.');
        $this.html('');
 
        $this.append('<span class="rate-container"></span>');
        showStars($this, currentValue, num);
    });
 
    function round(num) {
        var dec = parseInt(num[1]);
        dec = dec.toFixed(2);
        var roundedDec = 0;
        if (parseInt(dec) >= 50)
            roundedDec = 5;
        return roundedDec;
    }
 
    function showStars($this, currentValue, num) {
        for (var k = 0; k < 5; k++) {
            $this.children('.rate-container').append('<span class="fa fa-star-o" title="' + (k + 1) + '"></span>');
        }
 
        if (parseInt(currentValue) > 0) {
            $this.append('<span class="star-container"></span>');
            for (var i = 0; i < parseInt(num[0]) ; i++) {
                $this.children('.star-container').append('<span class="fa fa-star" title="' + (i + 1) + '"></span>');
            }
 
            if (round(num) > 0) {
                $this.children('.star-container').append('<span class="fa fa-star-half" title="' + (parseInt(num[0]) + 1) + '"></span>');
            }
        }
 
        var $rateContainer = $('.rate-container');
        var $starContainer = $('.star-container');
 
        $rateContainer.mouseenter(function () {
            $(this).siblings('.star-container').addClass('hidden');
            $(this).addClass('shadow');
        })
        $starContainer.mouseenter(function () {
            $(this).addClass('hidden');
            $(this).siblings('.rate-container').addClass('shadow');
        })
 
        $rateContainer.mouseleave(function () {
            $(this).siblings('.star-container').removeClass('hidden');
            $(this).removeClass('shadow');
        })
        $starContainer.mouseleave(function () {
            $(this).removeClass('hidden');
            $(this).siblings('.rate-container').removeClass('shadow');
        })
 
        var $starToRate = $('.rate-container > .fa-star-o')
        $starToRate.mouseenter(function () {
            $(this).addClass('fa-selected');
            $(this).prevAll().addClass('fa-selected');
        })
        $starToRate.mouseleave(function () {
            $(this).removeClass('fa-selected');
            $(this).prevAll().removeClass('fa-selected');
        })
    }
 
});

5.Add style rules

Add the following CssRequest in ForumTopic.ascx, in order to refer to FontAwesome's icon-font.

<sn:CssRequest id="CssRequest1" runat="server" Path="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />

Add your custom style rules to the view with inline css:

.sn-entry-title {
    display: inline-block;
}
.sn-entry-rating {
	position: relative;
	margin: 10px;
    display: inline-block;
    float: right;
}
.star-container {
	position: absolute;
	left: 0;
	top: 0;
}
.hidden {
	display: none;
}
.fa-star::before {
    color: #ffd700;
}
.fa-star-half::before {
    color: #ffd700;
}
.fa-star-o::before {
    color: #000;
}
.fa-selected::before {
    font-family: FontAwesome;
	content:"\f005";
    color: #007dc2;
}
.shadow {
	box-shadow: 0 0 2px #444;
}


6.Add saving functionality

For storing your vote as a new rating content complete you javascript with the following sniplet just before closing the document-ready function. Using this code the user is examined if he/she is allowed to vote during clicking on the rating-star. Only identified users can vote, and only once for each comment.

$(".rate-container > .fa").click(function () {
        var currentRate = parseInt($(this).attr("title"));
        var path = $(this).closest('.sn-entry-container').attr('data-path');
        var $currentForumEntry = $(this).closest('.sn-entry-container');
        var currentUser = $('.sn-entries').attr('data-currentuser');
 
        if (currentUser === "Visitor") {
            console.log(currentUser);
            overlayManager.showMessage({
                type: "error",
                title: "you are not logged in yet",
                text: "to rate blogpost, pls log in"
            });
        }
 
        else {
            $.ajax({
                url: odata.dataRoot + path + "?$select=CreatedBy/Name,Rating&$expand=CreatedBy&metadata=no",
                dataType: "json",
                async: false,
                success: function (result) {
 
                    var sum = 0;
                    if (result.d.results.length === 0) {
                        saveRate(path, currentRate);
                        sum = parseInt(currentRate);
                        modifyComment(path, sum, $currentForumEntry);
                    }
                    else {
                        var saveable = true;
                        for (var d = 0; d < result.d.results.length; d++) {
                            var userName = result.d.results[d].CreatedBy.Name;
 
                            if (userName === currentUser) {
                                saveable = false;
                                overlayManager.showMessage({
                                    type: "error",
                                    title: "you have already voted",
                                    text: "saving failed"
 
                                });
                                break;
                            }
                        }
                        if (saveable) {
                            $.each(result.d.results, function (i, item) {
                                var rateValue = item.Rating;
                                sum = parseInt(sum) + parseInt(rateValue);
                            });
 
                            sum += currentRate;
                            var newAvgRate = sum / (result.d.results.length + 1);
                            saveRate(path, currentRate);
                            modifyComment(path, newAvgRate, $currentForumEntry);
                        }
                    }
                }
            });
        }
    });
 
    function saveRate(path, currentRate) {
 
        $.ajax({
            url: odata.dataRoot + odata.getItemUrl(path),
            dataType: "json",
            type: 'POST',
            data: encodeURIComponent("models=[" + JSON.stringify({ 'Rating': currentRate }) + "]"),
            success: function (result) {
                overlayManager.showMessage({
                    type: "success",
                    title: "saved successfully",
                    text: "your vote is saved"
                });
 
            },
            error: function () {
                overlayManager.showMessage({
                    type: "error",
                    title: "unexpected error",
                    text: "an error occured, your vote is not saved"
                });
            }
        });
    }
 
    function modifyComment(path, AverageRating, $currentForumEntry) {
 
        $.ajax({
            url: odata.dataRoot + odata.getItemUrl(path),
            dataType: "json",
            type: 'PATCH',
            data: encodeURIComponent("models=[" + JSON.stringify({ 'AverageRating': AverageRating }) + "]"),
            success: function () {
                $currentForumEntry.find('.sn-entry-rating').html(AverageRating);
                var currentValue = parseFloat(AverageRating).toFixed(2);
                var num = currentValue.split('.');
                var $ratingContainer = $currentForumEntry.find('.sn-entry-rating');
                $ratingContainer.html('<span class="rate-container"></span>');
                showStars($ratingContainer, currentValue, num);
            }
        });
    }


Blogposts With Rating

Related links

References