CFC Spry Rating on RIAForge
I have uploaded the project files for CFC Spry Rating to RIAForge for your enjoyment.
It should change quite a bit in the near future as I add some more customization options.
Here is an example:
Happy Coding...

Subscribe
Subscribe via RSS
Follow me on Twitter
Or, Receive daily updates via email.
Tags
adobe air ajax apple cf community cfml coldfusion examples ext flash flex google javascript max2007 max2008 misc open source programming railo software technology ui
Recent Entries
No recent entries.
Blogroll
An Architect's View
CFSilence
Rey Bango
TalkingTree

thanks for CFC Spry Rating. I have uploaded to a site I'm testing code on. It dipslays properly and you can click and vote. However when I come back to the same page, I am allowed to vote again. Also, how would I set it up to display the average vote (by stars) and total number of votes next to it.
Here is a link to what I am talking about. I am enclosing my code to the cfc. I am working with MSSql and had to make a few changes to get it to work. Some of the items where bombing out because the sql was case sensitive and I keep receiving an error on the script in line 2. Also ifnull had to be changed to isnull.
Thanks for your help.
**************CFC********************
<cfcomponent output="no">
<!---<cfscript>variables.dsn=request.dsn</cfscript>--->
<cfset variables.dsn = request.data_source />
<cffunction name="newWidget" access="public" output="true" returntype="void">
<cfargument name="ratingName" type="string" required="true" hint="This should be unique">
<cfargument name="ratingTitle" type="string" required="true" hint="Title that will appear to the user">
<cfargument name="ratingNumber" type="numeric" required="true" hint="Max number of stars">
<cfargument name="pathToSpryJS" type="string" required="true" hint="relative or absolute web path to Spry widget JS files">
<cfargument name="pathToSpryCSS" type="string" required="true" hint="relative or absolute web path to Spry widget CSS files">
<cfset rsWidget = insertOrSelect(ratingName,RatingTitle)>
<cfset uservote = getRatingForUser(rsWidget.id,"#cftoken##cfid#")>
<cfset averagevote = getRatingValue(rswidget.id)>
<cfset pathtoCFC = "/"& replace(getmetadata().name,".","/","all") & ".cfc">
<cfsavecontent variable="body">
<cfoutput>
<script language="JavaScript" type="text/javascript" src="#pathToSpryJS#SpryRating.js"></script>
<link href="#pathToSpryCSS#samples.css" rel="stylesheet" type="text/css" />
<link href="#pathToSpryCSS#SpryRating.css" rel="stylesheet" type="text/css" />
<div><h3>#rsWidget.title#</h3><span id="rating#rsWidget.id#" class="ratingContainer">
<cfloop from="1" to="#ratingNumber#" index="i">
<span class="ratingButton"></span>
</cfloop>
<input id="ratedElement" type="hidden" name="ratingField" value="" />
<span class="ratingRatedMsg">Thanks for your rating!</span>
</span>
<p> </p>
</div><script type="text/javascript">
var firstRating#replace(rsWidget.id,"-","_","all")# = new Spry.Widget.Rating("rating#rsWidget.id#", {allowMultipleRating:false,<cfif userVote gt 0> readOnly:true,</cfif>saveUrl: "#pathToCFC#?method=userRate",postData:"ratingId=#rsWidget.id#&rate=@@ratingValue@@",ratingValue:<cfoutput>#averageVote#</cfoutput>});
var myObs = {};
firstRating#replace(rsWidget.id,"-","_","all")#.addObserver(myObs);
myObs.onServerUpdate = function(obj, req){
var returnVal = parseFloat(req.xhRequest.responseText);
if (!isNaN(returnVal)){
firstRating#replace(rsWidget.id,"-","_","all")#.setValue(returnVal, true);
}
}
</script>
</cfoutput>
</cfsavecontent>
<cfoutput>#HtmlCompressFormat(body)#</cfoutput>
</cffunction>
<!--- Saves rate based on users id (default cftoken/cfid, user will have to clear out sesison cookie
to be able to vote again, you can change this to something else like user id --->
<cffunction name="userRate" access="remote" output="false" returntype="string">
<cfargument name="ratingId" type="string" required="true">
<cfargument name="rate" type="numeric" required="true">
<cfargument name="userId" type="string" required="no" default="#cftoken##cfid#">
<cfquery name="castVote" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
insert into ratingsvote (ratingId,rate,userId)
values ('#ratingid#',#rate#,'#userId#')
</cfquery>
<cfreturn getRatingValue(ratingid)>
</cffunction>
<!--- gets the average rating for specified ratings widget --->
<cffunction name="getRatingValue" access="remote" output="false" retuntype="string">
<cfargument name="ratingId" type="string" required="true">
<!--- mysql specific ifnull --->
<cfquery name="getRatingValue" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
select isnull(AVG(rate),'0') avg_rate from ratingsvote where ratingId = '#ratingid#'
</cfquery>
<cfreturn getRatingValue.avg_rate >
</cffunction>
<!--- gets the users vote to --->
<cffunction name="getRatingForUser" access="remote" output="false" returntype="string">
<cfargument name="ratingId" type="string" required="true">
<cfargument name="userID" type="string" required="true">
<cfquery name="getRatingForUser" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
select rate from ratingsvote where ratingId='#ratingId#' and userId ='#userID#'
</cfquery>
<cfif getRatingForUser.recordcount eq 0>
<cfset uservote = 0>
<cfelse>
<cfset uservote = getRatingForUser.rate>
</cfif>
<cfreturn uservote>
</cffunction>
<!--- helper function for the newwidget, will either get the details from the db if
exists or insert a new vote --->
<cffunction name="insertOrSelect" access="private" returntype="query" output="false">
<cfargument name="ratingName" type="string" required="true">
<cfargument name="ratingTitle" type="string" required="true">
<cfquery name="getRatingId" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
select id,title from rating where name = '#ratingName#'
</cfquery>
<cfif not getRatingId.recordcount>
<cfset ratingID = createUUID()>
<cfquery name="getRatingId" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
insert into rating (id,title,name)
values ('#ratingid#','#ratingTitle#','#ratingName#' )
</cfquery>
<cfset getRatingId = queryNew('id,title,name')>
<cfset newrow = queryAddRow(getRatingId,1)>
<cfset temp = querySetCell(getRatingId,'id','#ratingid#')>
<cfset temp = querySetCell(getRatingId,'title','#ratingTitle#')>
<cfset temp = querySetCell(getRatingId,'name','#ratingName#')>
</cfif>
<cfreturn getRatingId/>
</cffunction>
<cffunction name="HtmlCompressFormat" returntype="string" access="private" output="false">
<cfargument name="sInput" type="string" required="yes">
<cfset var level = 2>
<cfsilent>
<cfscript>
/**
* Replaces a huge amount of unnecessary whitespace from your HTML code.
*
* @param sInput HTML you wish to compress. (Required)
* @return Returns a string.
* @author Jordan Clark (JordanClark@Telus.net)
* @version 1, November 19, 2002
*/
if( arrayLen( arguments ) GTE 2 AND isNumeric(arguments[2]))
{
level = arguments[2];
}
// just take off the useless stuff
sInput = trim(sInput);
switch(level)
{
case "3":
{
// extra compression can screw up a few little pieces of HTML, doh
sInput = reReplace( sInput, "[[:space:]]{2,}", " ", "all" );
sInput = replace( sInput, "> <", "><", "all" );
sInput = reReplace( sInput, "<!--[^>]+>", "", "all" );
break;
}
case "2":
{
sInput = reReplace( sInput, "[[:space:]]{2,}", chr( 13 ), "all" );
break;
}
case "1":
{
// only compresses after a line break
sInput = reReplace( sInput, "(" & chr( 10 ) & "|" & chr( 13 ) & ")+[[:space:]]{2,}", chr( 13 ), "all" );
break;
}
}
</cfscript>
</cfsilent>
<cfreturn sInput/>
</cffunction>
</cfcomponent>
**************MSSQL********************************
CREATE TABLE [dbo].[rating] (
[id] [varchar](45) NOT NULL default '',
[name] [varchar](255) NOT NULL default '',
[title] [varchar](255) default NULL,
CONSTRAINT [PK_rating] PRIMARY KEY
(
[id]
)
) ON [PRIMARY]
CREATE TABLE [dbo].[ratingsvote] (
[id] [int] IDENTITY(1,1) NOT NULL,
[ratingId] [varchar](45) default NULL,
[userId] [varchar](45) default NULL,
[rate] [int] default '0',
CONSTRAINT [PK_ratingsvote] PRIMARY KEY
(
[id]
)
) ON [PRIMARY]
http://76.12.43.89/index.cfm?method=article&ar...
Click on any 'article' to get a feel for the issue.
I just checked your site with firebug and it looks like that you have the wrong URL in the spry post I am showing a 404 so it would actually never save the votes at all.
http://76.12.43.89/cfcH149293/rating.cfc?method=us... comes up with a 404 not found.
Get that problem solved first :)
Next, since you are using the session of the user to track their vote you want your session to last a bit longer so don't use session cookies use regular old cookies that last until 2037.
Or write your own cookie on the users machine that won't expire, you can simply drop in a UUID into your cookie and save that as your userid in the database.
Hope that helps
How would I got about adding in some custom fields such as a comment box and a name field?
thanks Gary...great job
I am a newbee to coldfusion. I used Duane code on MSSQL it works. I am having with trouble like Duane, when i vote and closed the browser, open the browser again, i can vote again. I guest i am use session cookies.
How do you write your own cookies?
Please help.
Many thanks
Are you sure your vote is being saved in the database? Make sure you use firebug to check that your vote is being properly submitted Duane code had an error in it which made it look like he was voting but his vote never got saved. If the vote isn't saved in the database it will look like you can vote over and over.
<cfcomponent output="no">
<cfset variables.dsn = request.data_source />
<!--- Check to see if Cookie.ratingUserId is set, if not set a value. --->
<cfif IsDefined("Cookie.ratingUserId")>
<cfset ratingUserId = Cookie.ratingUserId>
<cfelse>
<cfset ratingUserId = createUUID()>
<cfcookie name = "ratingUserId" value = "#ratingUserId#" expires = "never">
</cfif>
<cffunction name="newWidget" access="public" output="true" returntype="void">
<cfargument name="ratingName" type="string" required="true" hint="This should be unique">
<cfargument name="ratingTitle" type="string" required="true" hint="Title that will appear to the user">
<cfargument name="ratingNumber" type="numeric" required="true" hint="Max number of stars">
<cfargument name="pathToSpryJS" type="string" required="true" hint="relative or absolute web path to Spry widget JS files">
<cfargument name="pathToSpryCSS" type="string" required="true" hint="relative or absolute web path to Spry widget CSS files">
<cfset rsWidget = insertOrSelect(ratingName,RatingTitle)>
<cfset uservote = getRatingForUser(rsWidget.id,"#ratingUserId#")>
<cfset averagevote = getRatingValue(rswidget.id)>
<!---<cfset pathtoCFC = "/"& replace(getmetadata().name,".","/","all") & ".cfc">--->
<cfset pathtoCFC = "/cfc/rating.cfc"><!--- coded specifically for my application DTH --->
<cfsavecontent variable="body">
<cfoutput>
<script language="JavaScript" type="text/javascript" src="#pathToSpryJS#SpryRating.js"></script>
<link href="#pathToSpryCSS#samples.css" rel="stylesheet" type="text/css" />
<link href="#pathToSpryCSS#SpryRating.css" rel="stylesheet" type="text/css" />
<div><h3>#rsWidget.title#</h3><span id="rating#rsWidget.id#" class="ratingContainer">
<cfloop from="1" to="#ratingNumber#" index="i">
<span class="ratingButton"></span>
</cfloop>
<input id="ratedElement" type="hidden" name="ratingField" value="" />
<span class="ratingRatedMsg">Thanks for your rating!</span>
</span>
<p> </p>
</div><script type="text/javascript">
var firstRating#replace(rsWidget.id,"-","_","all")# = new Spry.Widget.Rating("rating#rsWidget.id#", {allowMultipleRating:false,<cfif userVote gt 0> readOnly:true,</cfif>saveUrl: "#pathToCFC#?method=userRate",postData:"ratingId=#rsWidget.id#&rate=@@ratingValue@@",ratingValue:<cfoutput>#averageVote#</cfoutput>});
var myObs = {};
firstRating#replace(rsWidget.id,"-","_","all")#.addObserver(myObs);
myObs.onServerUpdate = function(obj, req){
var returnVal = parseFloat(req.xhRequest.responseText);
if (!isNaN(returnVal)){
firstRating#replace(rsWidget.id,"-","_","all")#.setValue(returnVal, true);
}
}
</script>
</cfoutput>
</cfsavecontent>
<cfoutput>#HtmlCompressFormat(body)#</cfoutput>
</cffunction>
<!--- Saves rate based on users id (default cftoken/cfid, user will have to clear out sesison cookie
to be able to vote again, you can change this to something else like user id --->
<cffunction name="userRate" access="remote" output="false" returntype="string">
<cfargument name="ratingId" type="string" required="true">
<cfargument name="rate" type="numeric" required="true">
<cfargument name="userId" type="string" required="no" default="#ratingUserId#"><!--- changed from the original default="#cftoken##cfid#" --->
<cfquery name="castVote" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
insert into ratingsvote (ratingId,rate,userId)
values ('#ratingid#',#rate#,'#userId#')
</cfquery>
<cfreturn getRatingValue(ratingid)>
</cffunction>
<!--- gets the average rating for specified ratings widget --->
<cffunction name="getRatingValue" access="remote" output="false" returntype="string">
<cfargument name="ratingId" type="string" required="true">
<!--- mysql specific ifnull --->
<cfquery name="getRatingValue" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
select isnull(AVG(rate),'0') avg_rate from ratingsvote where ratingId = '#ratingid#'
</cfquery>
<cfreturn getRatingValue.avg_rate >
</cffunction>
<!--- gets the users vote to --->
<cffunction name="getRatingForUser" access="remote" output="false" returntype="string">
<cfargument name="ratingId" type="string" required="true">
<cfargument name="userID" type="string" required="true">
<cfquery name="getRatingForUser" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
select rate from ratingsvote where ratingId = '#ratingId#' and userId ='#userID#'
</cfquery>
<cfif getRatingForUser.recordcount eq 0>
<cfset uservote = 0>
<cfelse>
<cfset uservote = getRatingForUser.rate>
</cfif>
<cfreturn uservote>
</cffunction>
<!--- helper function for the newwidget, will either get the details from the db if
exists or insert a new vote --->
<cffunction name="insertOrSelect" access="private" returntype="query" output="false">
<cfargument name="ratingName" type="string" required="true">
<cfargument name="ratingTitle" type="string" required="true">
<cfquery name="getRatingId" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
select id,title from rating where name = '#ratingName#'
</cfquery>
<cfif not getRatingId.recordcount>
<cfset ratingID = createUUID()>
<cfquery name="getRatingId" datasource="#variables.dsn#" username="#request.db_un#" password="#request.db_pw#">
insert into rating (id,title,name)
values ('#ratingid#','#ratingTitle#','#ratingName#' )
</cfquery>
<cfset getRatingId = queryNew('id,title,name')>
<cfset newrow = queryAddRow(getRatingId,1)>
<cfset temp = querySetCell(getRatingId,'id','#ratingid#')>
<cfset temp = querySetCell(getRatingId,'title','#ratingTitle#')>
<cfset temp = querySetCell(getRatingId,'name','#ratingName#')>
</cfif>
<cfreturn getRatingId/>
</cffunction>
<cffunction name="HtmlCompressFormat" returntype="string" access="private" output="false">
<cfargument name="sInput" type="string" required="yes">
<cfset var level = 2>
<cfsilent>
<cfscript>
/**
* Replaces a huge amount of unnecessary whitespace from your HTML code.
*
* @param sInput HTML you wish to compress. (Required)
* @return Returns a string.
* @author Jordan Clark (JordanClark@Telus.net)
* @version 1, November 19, 2002
*/
if( arrayLen( arguments ) GTE 2 AND isNumeric(arguments[2]))
{
level = arguments[2];
}
// just take off the useless stuff
sInput = trim(sInput);
switch(level)
{
case "3":
{
// extra compression can screw up a few little pieces of HTML, doh
sInput = reReplace( sInput, "[[:space:]]{2,}", " ", "all" );
sInput = replace( sInput, "> <", "><", "all" );
sInput = reReplace( sInput, "<!--[^>]+>", "", "all" );
break;
}
case "2":
{
sInput = reReplace( sInput, "[[:space:]]{2,}", chr( 13 ), "all" );
break;
}
case "1":
{
// only compresses after a line break
sInput = reReplace( sInput, "(" & chr( 10 ) & "|" & chr( 13 ) & ")+[[:space:]]{2,}", chr( 13 ), "all" );
break;
}
}
</cfscript>
</cfsilent>
<cfreturn sInput/>
</cffunction>
</cfcomponent>
Thank you very much for this script.
I am trying to use this to rate random quotes. The problem I seem to be having is the title is not changing. It is stuck on the first quote that was loaded. Now each vote goes to that quote only, not to the one that is displayed.
I am testing it here: http://www.bloodbanktalk.com/bushisms/
That page is using an iframe and I thought that might be the problem, the source for the iframe is here: http://www.bloodbanktalk.com/bushisms/bushisms.cfm. That page has the same problem.
I changed the userId from #cftoken##cfid# to #createUUID()# because I want to be able to vote on the different quotes. I am not too worried if the user votes more than once on the same quote, it's just for fun anyhow.
Thanks for the help, Cliff
Anyhow, again, thank you for this script!