03.03.2010 Updated code for the file “RatingForSharePoint.js” due to a bug occurring when the ID column was hidden, but not placed far right in the view.
I got this request after enabling “rating” of the blog comments:
…How can we implement the thumbs up or down functionality you have on your site to a sharepoint list?
Tony
I have made a solution that uses a separate list to hold all the ratings. There will be written one line in this list for each rating of an item (list item or document).
This solution allows for one rating per item, per user, per session (browser session – new window = new session). There are an option to restrict or allow multiple ratings per user in different sessions.
The rating of the items are available in a list view (plain type, no boxed style), and in grouped plain list views. It is also available in DispForm.
To enable rating of items, you add a calculated column to your list with this code:
=""
Yes, only “equals” and two double quotes. You then assures that this new calculated column and the ID column is visible in the list view (there is an option in the script to hide the ID column).
Then you create a custom list for the ratings, with the name: RatingForSharePoint, and add these fields:
- Plus The type of information in this column is: Number
- Minus The type of information in this column is: Number
- ListUrl The type of information in this column is: Single line of text
- ItemID The type of information in this column is: Single line of text
- ListGuid The type of information in this column is: Single line of text
- ListName The type of information in this column is: Single line of text
As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.
The scripts “interaction.js” and “stringBuffer.js” is created by Erucy and published on CodePlex.
The file “sessvars.js” is found here.
The sourcecode for the file “RatingForSharePoint.js” is found below.
Add a CEWP below the listView and add this code:
<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script> <script type="text/javascript" src="/test/English/Javascript/interaction.js"></script> <script type="text/javascript" src="/test/English/Javascript/stringBuffer.js"></script> <script type="text/javascript" src="/test/English/Javascript/sessvars.js"></script> <script type="text/javascript" src="/test/English/Javascript/RatingForSharePoint.js"></script> <script type="text/javascript"> ratingForSharePoint('Rating',true,true); </script>
Parameters explained:
- FieldInternalName: FieldInternalName of the calculated column created above.
- hideIdCol: true to hide the ID column, false to let it be visible.
- rateOnlyOnce: true to allow only one rating per user, false to allow multiple ratings (in different sessions)
Add a CEWP below your DispForm, and add this code:
<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script> <script type="text/javascript" src="/test/English/Javascript/interaction.js"></script> <script type="text/javascript" src="/test/English/Javascript/stringBuffer.js"></script> <script type="text/javascript" src="/test/English/Javascript/sessvars.js"></script> <script type="text/javascript" src="/test/English/Javascript/RatingForSharePoint.js"></script> <script type="text/javascript"> // You must edit this GUID to match THIS list's GUID - this is NOT the GUID of the "RatingForSharePoint" thisListsGuid = '{77C35652-1D4D-4B09-B58C-D941068D251E}'; ratingForSharePointDispForm('Rating',true); </script>
Parameters explained:
- thisListsGuid: This list’s GUID – this is NOT the GUID of the “RatingForSharePoint”
- FieldInternalName: FieldInternalName of the calculated column created above
- rateOnlyOnce: true to allow only one rating per user, false to allow multiple ratings (in different sessions)
The sourcecode for the file “RatingForSharePoint.js” looks like this:
/* Rating for SharePoint lists * --------------------------------------------- * Created by Alexander Bautz * alexander.bautz@gmail.com * https://spjsblog.com * v1.1 * LastMod: 03.03.2010 * --------------------------------------------- * Must include reference to: * jQuery - http://jquery.com * interaction.js - http://spjslib.codeplex.com * stringBuffer.js - http://spjslib.codeplex.com * sessvars.js - http://www.thomasfrank.se * RatingForSharePoint.js - This file * --------------------------------------------- */ // If anonymous the SharePoint variable "_spUserId" does not exist if(typeof(_spUserId)=='undefined'){ _spUserId = ''; } /************************************************************ *********************** ListView Code *********************** ************************************************************/ function ratingForSharePoint(FieldInternalName,hideIdCol,rateOnlyOnce){ if(typeof(ctx)=='object'){ var thisListsGuid=ctx.listName; } if(typeof(currentRatingObj)=='undefined'){ currentRatingObj = getRating(); } if(typeof(FieldInternalName)!='undefined'){ intName = FieldInternalName; hideId = hideIdCol; rateOnce = rateOnlyOnce; // Fiend index of "Rating" column $(".ms-viewheadertr th").each(function(){ if($(this).find('table:first').attr('name')==intName){ colIndex = $(this).attr('cellIndex'); displayName = $(this).find('table:first').attr('displayname'); // Remove filtering possibility $(this).removeClass('ms-vh2').addClass('ms-vh2-nograd').html(displayName); } }); // Find index of ID column $(".ms-viewheadertr th").each(function(){ if($(this).find('table:first').attr('name')=='ID'){ IdColIndex = $(this).attr('cellIndex'); // Hide ID column if(hideId){ $(this).remove(); } } }); if(typeof(colIndex)=='undefined' || typeof(IdColIndex)=='undefined'){ alert("Both the column with FieldInternalName ""+FieldInternalName+"" and the ID-column must be in the view."); return false; } } $("table.ms-listviewtable tbody:not([id^='aggr']) >tr[beenthere!=1]:has(td.ms-vb2)").each(function(){ $(this).attr('beenthere',1); var itemAlreadyRate = false; var thisTd = $(this).find("td[cellIndex=" + colIndex + "]"); var thisIdColumn = $(this).find("td[cellIndex=" + IdColIndex + "]"); var thisId = thisIdColumn.text() if(hideId){ thisIdColumn.remove(); } var rPos = 0; var rNeg = 0; if(currentRatingObj[thisId]!=undefined){ // Previously rated by current user if(rateOnce && currentRatingObj[thisId]['ratedByMe']==true){ itemAlreadyRate = true; } rPos = currentRatingObj[thisId]['Plus']; rNeg = currentRatingObj[thisId]['Minus']; } // Rated in this session if(sessvars[thisListsGuid+thisId]==1 && currentRatingObj!=false){ itemAlreadyRate = true; } var str = "<div style='color:gray'>"; str += "<span><img "; if(!itemAlreadyRate){ str += "onclick='javascript:rateMe("up","+thisId+")' title='Rate Up' "; str += "style='vertical-align:middle;cursor:pointer' "; }else{ str += "title='You have already rated this item "+currentRatingObj[thisId]['ratedByMeVal']+"' "; str += "style='vertical-align:middle;cursor:no-drop' "; } str += "src='/_layouts/images/arrupi.gif'></span>"; str += "<span style='font-weight:bold;' id='rateUp_"+thisId+"'>"+rPos+"</span>"; str += "<span><img "; if(!itemAlreadyRate){ str += "onclick='javascript:rateMe("down","+thisId+")' title='Rate Down' "; str += "style='vertical-align:middle;cursor:pointer' "; }else{ str += "title='You have already rated this item "+currentRatingObj[thisId]['ratedByMeVal']+"' "; str += "style='vertical-align:middle;cursor:no-drop' "; } str += "src='/_layouts/images/arrdowni.gif'></span>"; str += "<span style='font-weight:bold;' id='rateDown_"+thisId+"'>"+rNeg+"</span>"; str += "</div>"; // Write HTML thisTd.html(str); }); } function getRating(){ // Path to webservices wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/'; var query = "<Where><Eq><FieldRef Name='ListGuid' /><Value Type='Text'>"+ctx.listName+"</Value></Eq></Where>"; var rating = queryItems('RatingForSharePoint',query,['Plus','Minus','ItemID','Author']); if(rating.count==-1){ alert("An error occured in the query:n"+query); }else if(rating.count>0){ ratingObj = {}; $.each(rating.items,function(i,item){ var authorIdRaw = item['Author']; var authorId = authorIdRaw.substring(0,authorIdRaw.indexOf(';#')); var plusVal = item['Plus']; var minusVal = item['Minus']; if(plusVal==null)plusVal=0; if(minusVal==null)minusVal=0; if(ratingObj[item['ItemID']]==undefined){ ratingObj[item['ItemID']]={'Plus':parseInt(plusVal),'Minus':parseInt(minusVal)}; }else{ ratingObj[item['ItemID']]['Plus']+=parseInt(plusVal); ratingObj[item['ItemID']]['Minus']+=parseInt(minusVal); } // Rated by current user if(_spUserId==authorId){ ratingObj[item['ItemID']]['ratedByMe']=true; if(item['Plus']>0){ ratingObj[item['ItemID']]['ratedByMeVal']="+1"; }else{ ratingObj[item['ItemID']]['ratedByMeVal']="-1"; } } }); return ratingObj; }else{ return false; } } /************************************************************ *********************** DispForm Code *********************** ************************************************************/ function ratingForSharePointDispForm(FieldInternalName,rateOnlyOnce){ var queryStr = getQueryParameters(); if(typeof(fields)=='undefined')fields = init_fields(); var itemAlreadyRate = false; var thisId = queryStr.ID; var currentRatingObj = getRatingDispForm(thisId); if(currentRatingObj[thisId]==undefined){ rPos = 0; rNeg = 0; }else{ // Previously rated by current user if(rateOnlyOnce && currentRatingObj[thisId]['ratedByMe']==true){ itemAlreadyRate = true; } rPos = currentRatingObj[thisId]['Plus']; rNeg = currentRatingObj[thisId]['Minus']; } // Rated in this session if(sessvars[thisListsGuid+thisId]==1 && currentRatingObj!=false){ itemAlreadyRate = true; } var thisTd = $(fields[FieldInternalName]).find('.ms-formbody'); var str = "<div style='color:gray'>"; str += "<img "; if(!itemAlreadyRate){ str += "onclick='javascript:rateMe("up","+thisId+")' title='Rate Up' "; str += "style='vertical-align:middle;cursor:pointer' "; }else{ str += "title='You have already rated this item "+currentRatingObj[thisId]['ratedByMeVal']+"' "; str += "style='vertical-align:middle;cursor:no-drop' "; } str += "src='/_layouts/images/arrupi.gif'>"; str += "<span style='font-weight:bold;' id='rateUp_"+thisId+"'>"+rPos+"</span>"; str += "<img "; if(!itemAlreadyRate){ str += "onclick='javascript:rateMe("down","+thisId+")' title='Rate Down' "; str += "style='vertical-align:middle;cursor:pointer' "; }else{ str += "title='You have already rated this item "+currentRatingObj[thisId]['ratedByMeVal']+"' "; str += "style='vertical-align:middle;cursor:no-drop' "; } str += "src='/_layouts/images/arrdowni.gif'>"; str += "<span style='font-weight:bold;' id='rateDown_"+thisId+"'>"+rNeg+"</span>"; str += "</div>"; // Write HTML thisTd.html(str); } function getRatingDispForm(itemID){ // Path to webservices wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/'; var query = "<Where><And><Eq><FieldRef Name='ListGuid' /><Value Type='Text'>"+thisListsGuid+"</Value></Eq>"+ "<Eq><FieldRef Name='ItemID' /><Value Type='Text'>"+itemID+"</Value></Eq></And></Where>"; var rating = queryItems('RatingForSharePoint',query,['Plus','Minus','ItemID','Author']); if(rating.count==-1){ alert("An error occured in the query:n"+query); }else if(rating.count>0){ ratingObj = {}; $.each(rating.items,function(i,item){ var authorIdRaw = item['Author']; var authorId = authorIdRaw.substring(0,authorIdRaw.indexOf(';#')); var plusVal = item['Plus']; var minusVal = item['Minus']; if(plusVal==null)plusVal=0; if(minusVal==null)minusVal=0; if(ratingObj[item['ItemID']]==undefined){ ratingObj[item['ItemID']]={'Plus':parseInt(plusVal),'Minus':parseInt(minusVal)}; }else{ ratingObj[item['ItemID']]['Plus']+=parseInt(plusVal); ratingObj[item['ItemID']]['Minus']+=parseInt(minusVal); } // Rated by current user if(_spUserId==authorId){ ratingObj[item['ItemID']]['ratedByMe']=true; if(item['Plus']>0){ ratingObj[item['ItemID']]['ratedByMeVal']="+1"; }else{ ratingObj[item['ItemID']]['ratedByMeVal']="-1"; } } }); return ratingObj; }else{ return false; } } /************************************************************ *********************** Shared Code *********************** ************************************************************/ function rateMe(upORdown,id){ if(typeof(ctx)=='object'){ var listGuid=ctx.listName; var listTitle = $.trim($("td.ms-pagetitle").text()); }else{ var listGuid=thisListsGuid; var listTitle = $.trim($("td.ms-pagetitle a").text()); } // Only rate item if not already rated in this session if(sessvars[listGuid+id]==undefined){ plus=''; minus=''; if(upORdown=='up'){ sessvars[listGuid+id]=1; currVal = parseInt($("#rateUp_"+id).text()); $("#rateUp_"+id).text(currVal+1) .prev().css({'background-color':'#C5E3BF'}) .parent().find('img').attr({'onclick':'','title':'Rated +1'}); // Prepare for writing rating plus=1; }else{ sessvars[listGuid+id]=1; currVal = parseInt($("#rateDown_"+id).text()); $("#rateDown_"+id).text(currVal+1) //.parent().css({'background-color':'#FFE4E1'}) .prev().css({'background-color':'#FFE4E1'}) .parent().find('img').attr({'onclick':'','title':'Rated -1'}); // Prepare for writing rating minus=1; } // Get url var urlDir = location.pathname; // Path to webservices wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/'; // Add item var res = addItem('RatingForSharePoint', {'Title':"Rating for: " + listTitle, 'Plus':plus, 'Minus':minus, 'ListUrl':urlDir, 'ListGuid':listGuid, 'ListName':listTitle, 'ItemID':id}); } } // Attaches a call to the function to the "expand grouped elements function" for it to function in grouped listview's function ExpGroupRenderData(htmlToRender, groupName, isLoaded){ var tbody=document.getElementById("tbod"+groupName+"_"); var wrapDiv=document.createElement("DIV"); wrapDiv.innerHTML="<TABLE><TBODY id="tbod"+groupName+"_" isLoaded=""+isLoaded+"">"+htmlToRender+"</TBODY></TABLE>"; tbody.parentNode.replaceChild(wrapDiv.firstChild.firstChild,tbody); ratingForSharePoint(); } // Function to separate each url search string parameters function getQueryParameters(){ qObj = {}; var urlSearch = window.location.search; if(urlSearch.length>0){ var qpart = urlSearch.substring(1).split('&'); $.each(qpart,function(i,item){ var splitAgain = item.split('='); qObj[splitAgain[0]] = splitAgain[1]; }); } return qObj; } function init_fields(){ var res = {}; $("td.ms-formbody").each(function(){ if($(this).html().indexOf('FieldInternalName="')<0) return; var start = $(this).html().indexOf('FieldInternalName="')+19; var stopp = $(this).html().indexOf('FieldType="')-7; var nm = $(this).html().substring(start,stopp); res[nm] = this.parentNode; }); return res; }
Save as “RatingForSharePoint.js”, mind the file extension, and upload to the scriptlibrary as shown above.
Ask is something is unclear, and please report any bugs.
Regards
Alexander