[Koha-patches] [PATCH] [SIGNED-OFF] Bug 5668 - star ratings in the OPAC

Nicole C. Engard nengard at bywatersolutions.com
Sun Aug 21 12:54:40 CEST 2011


From: Robin Sheat <robin at catalyst.net.nz>

This takes the work done by Mason James and tidies it up a bit to work
with master.

This patch allows OPAC users to assign star ratings to biblios, and
other users to see the average of what other people have rated.

Signed-off-by: Nicole C. Engard <nengard at bywatersolutions.com>
---
 C4/Auth.pm                                         |    6 +-
 C4/Output.pm                                       |   18 +-
 C4/Ratings.pm                                      |  145 ++++++++
 installer/data/mysql/kohastructure.sql             |   16 +
 installer/data/mysql/updatedatabase.pl             |   20 ++
 .../prog/en/modules/admin/preferences/opac.pref    |    6 +
 koha-tmpl/opac-tmpl/prog/en/css/jquery.rating.css  |   12 +
 .../prog/en/lib/jquery/plugins/jquery.rating.js    |  344 ++++++++++++++++++++
 koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt |   50 +++-
 .../opac-tmpl/prog/en/modules/opac-results.tt      |   58 ++++
 koha-tmpl/opac-tmpl/prog/images/delete.gif         |  Bin 0 -> 752 bytes
 koha-tmpl/opac-tmpl/prog/images/star.gif           |  Bin 0 -> 815 bytes
 opac/opac-detail.pl                                |   32 ++
 opac/opac-ratings.pl                               |  104 ++++++
 opac/opac-search.pl                                |   40 ++-
 15 files changed, 838 insertions(+), 13 deletions(-)
 create mode 100644 C4/Ratings.pm
 create mode 100644 koha-tmpl/opac-tmpl/prog/en/css/jquery.rating.css
 create mode 100644 koha-tmpl/opac-tmpl/prog/en/lib/jquery/plugins/jquery.rating.js
 create mode 100644 koha-tmpl/opac-tmpl/prog/images/delete.gif
 create mode 100644 koha-tmpl/opac-tmpl/prog/images/star.gif
 create mode 100755 opac/opac-ratings.pl

diff --git a/C4/Auth.pm b/C4/Auth.pm
index 16e908a..6ddf66e 100644
--- a/C4/Auth.pm
+++ b/C4/Auth.pm
@@ -347,6 +347,7 @@ sub get_template_and_user {
             LoginFirstname               => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
             LoginSurname                 => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu",
             TagsEnabled                  => C4::Context->preference("TagsEnabled"),
+            RatingsEnabled               => C4::Context->preference("RatingsEnabled"),
             hide_marc                    => C4::Context->preference("hide_marc"),
             item_level_itypes            => C4::Context->preference('item-level_itypes'),
             patronimages                 => C4::Context->preference("patronimages"),
@@ -975,9 +976,10 @@ sub checkauth {
         OpacAuthorities      => C4::Context->preference("OpacAuthorities"),
         OpacBrowser          => C4::Context->preference("OpacBrowser"),
         opacheader           => C4::Context->preference("opacheader"),
-        TagsEnabled                  => C4::Context->preference("TagsEnabled"),
-        OPACUserCSS           => C4::Context->preference("OPACUserCSS"),
         opacstylesheet       => C4::Context->preference("opacstylesheet"),
+        TagsEnabled          => C4::Context->preference("TagsEnabled"),
+        RatingsEnabled       => C4::Context->preference("RatingsEnabled"),
+        OPACUserCSS          => C4::Context->preference("OPACUserCSS"),
         intranetcolorstylesheet =>
 								C4::Context->preference("intranetcolorstylesheet"),
         intranetstylesheet => C4::Context->preference("intranetstylesheet"),
diff --git a/C4/Output.pm b/C4/Output.pm
index 98f2d7a..361de95 100644
--- a/C4/Output.pm
+++ b/C4/Output.pm
@@ -44,15 +44,15 @@ BEGIN {
     @ISA    = qw(Exporter);
 	@EXPORT_OK = qw(&is_ajax ajax_fail); # More stuff should go here instead
 	%EXPORT_TAGS = ( all =>[qw(&themelanguage &gettemplate setlanguagecookie pagination_bar
-								&output_with_http_headers &output_html_with_http_headers)],
-					ajax =>[qw(&output_with_http_headers is_ajax)],
+								&output_with_http_headers &output_ajax_with_http_headers &output_html_with_http_headers)],
+					ajax =>[qw(&output_with_http_headers &output_ajax_with_http_headers is_ajax)],
 					html =>[qw(&output_with_http_headers &output_html_with_http_headers)]
 				);
     push @EXPORT, qw(
         &themelanguage &gettemplate setlanguagecookie getlanguagecookie pagination_bar
     );
     push @EXPORT, qw(
-        &output_html_with_http_headers &output_with_http_headers FormatData FormatNumber
+        &output_html_with_http_headers &output_ajax_with_http_headers &output_with_http_headers FormatData FormatNumber
     );
 }
 
@@ -478,6 +478,18 @@ sub output_html_with_http_headers ($$$;$) {
     output_with_http_headers( $query, $cookie, $data, 'html', $status );
 }
 
+
+sub output_ajax_with_http_headers ($$) {
+    my ( $query, $js ) = @_;
+    print $query->header(
+        -type            => 'text/javascript',
+        -charset         => 'UTF-8',
+        -Pragma          => 'no-cache',
+        -'Cache-Control' => 'no-cache',
+        -expires         => '-1d',
+    ), $js;
+}
+
 sub is_ajax () {
     my $x_req = $ENV{HTTP_X_REQUESTED_WITH};
     return ( $x_req and $x_req =~ /XMLHttpRequest/i ) ? 1 : 0;
diff --git a/C4/Ratings.pm b/C4/Ratings.pm
new file mode 100644
index 0000000..ea37301
--- /dev/null
+++ b/C4/Ratings.pm
@@ -0,0 +1,145 @@
+package C4::Ratings;
+
+# Copyright 2010 KohaAloha, NZ
+# Parts copyright 2011, Catalyst IT, NZ.
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+=head1 C4::Ratings - the Koha API for dealing with star ratings for biblios
+
+This provides an interface to the ratings system, in order to allow them
+to be manipulated or queried.
+
+=cut
+
+use strict;
+use warnings;
+use Carp;
+use Exporter;
+
+use C4::Debug;
+use C4::Context;
+
+#use Smart::Comments '####';
+
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+BEGIN {
+    $VERSION = 3.00;
+    @ISA     = qw(Exporter);
+
+    @EXPORT = qw(
+      get_rating add_rating
+    );
+
+    #	%EXPORT_TAGS = ();
+}
+
+=head2 get_rating
+
+    get_rating($biblionumber, $borrowernumber)
+
+This returns the rating for the supplied biblionumber. It will also return
+the rating that the supplied user gave to the provided biblio. If a particular
+value can't be supplied, '0' is returned for that value.
+
+=head 3 RETURNS
+
+A hashref containing:
+
+=over
+
+=item total - the total number of ratings
+=item avg - the average of the ratings
+=item avgint - the integer form of the average
+=item value - the user's rating
+
+=back
+
+=cut
+
+my ($total_query_sth, $user_query_sth);
+sub get_rating {
+    my ( $biblionumber, $borrowernumber ) = @_;
+    my $dbh = C4::Context->dbh;
+
+    my $total_query = "
+	SELECT    AVG(value) AS average,COUNT(value) AS total  FROM ratings
+    WHERE       biblionumber = ?";
+    $total_query_sth = $total_query_sth || $dbh->prepare($total_query);
+
+    $total_query_sth->execute($biblionumber);
+    my $total_query_res = $total_query_sth->fetchrow_hashref();
+
+    my $user_rating = 0;
+    if ($borrowernumber) {
+        my $user_query = "
+        SELECT    value  from ratings
+        WHERE       biblionumber = ? and borrowernumber = ?";
+        $user_query_sth ||= $dbh->prepare($user_query);
+
+        $user_query_sth->execute( $biblionumber, $borrowernumber );
+        my $user_query_res = $user_query_sth->fetchrow_hashref();
+        $user_rating = $user_query_res->{value} || 0;
+    }
+    my ( $avg, $avgint ) = 0;
+    $avg = $total_query_res->{average} || 0;
+    $avgint = sprintf( "%.0f", $avg );
+
+    my %rating_hash;
+    $rating_hash{total}  = $total_query_res->{total} || 0;
+    $rating_hash{avg}    = $avg;
+    $rating_hash{avgint} = $avgint;
+    $rating_hash{value}  = $user_rating;
+    return \%rating_hash;
+}
+
+=head2 add_rating
+
+    add_rating($biblionumber, $borrowernumber, $value)
+
+This adds or updates a rating for a particular user on a biblio. If the value
+is 0, then the rating will be deleted. If the value is out of the range of
+0-5, nothing will happen.
+
+=cut
+
+my ($delete_query_sth, $insert_query_sth);
+sub add_rating {
+    my ( $biblionumber, $borrowernumber, $value ) = @_;
+    if (!defined($biblionumber) || !defined($borrowernumber) ||
+        $value < 0 || $value > 5) {
+        # Seen this happen, want to know about it if it happens again.
+        carp "Invalid input coming in to C4::Ratings::add_rating";
+        return;
+    }
+    if ($borrowernumber == 0) {
+    	carp "Attempted to add a rating for borrower number 0";
+    	return;
+    }
+    my $dbh = C4::Context->dbh;
+    my $delete_query = "DELETE FROM ratings WHERE borrowernumber = ? AND biblionumber = ? LIMIT 1";
+    my $delete_query_sth ||= $dbh->prepare($delete_query);
+    $delete_query_sth->execute( $borrowernumber, $biblionumber );
+    return if $value == 0; # We don't add a rating for zero
+
+    my $insert_query = "INSERT INTO ratings (borrowernumber,biblionumber,value)
+    VALUES (?,?,?)";
+    $insert_query_sth ||= $dbh->prepare($insert_query);
+    $insert_query_sth->execute( $borrowernumber, $biblionumber, $value );
+}
+
+1;
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index 8b84019..8a7aec0 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -2612,6 +2612,22 @@ CREATE TABLE `fieldmapping` (
   PRIMARY KEY  (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+---
+--- 'Ratings' table. This tracks the star ratings set by borrowers.
+---
+
+DROP TABLE IF EXISTS `ratings`;
+CREATE TABLE `ratings` (
+    `borrowernumber` int(11) NOT NULL, --- the borrower this rating is for
+    `biblionumber` int(11) NOT NULL, --- the biblio it's for
+    `value` tinyint(1) NOT NULL, --- the rating, from 1-5
+    `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
+    PRIMARY KEY  (`borrowernumber`,`biblionumber`),
+    KEY `ratings_borrowers_fk_1` (`borrowernumber`),
+    KEY `ratings_biblionumber_fk_1` (`biblionumber`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
 
 /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index 030897c..dbddaaa 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -4432,6 +4432,26 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
     SetVersion ($DBversion);
 }
 
+$DBversion = 'xxx';
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+    $dbh->do( qq |
+ CREATE TABLE `ratings` (
+  `borrowernumber` int(11) NOT NULL,
+  `biblionumber` int(11) NOT NULL,
+  `value` tinyint(1) NOT NULL,
+  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
+  PRIMARY KEY  (`borrowernumber`,`biblionumber`),
+  KEY `ratings_borrowers_fk_1` (`borrowernumber`),
+  KEY `ratings_biblionumber_fk_1` (`biblionumber`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 |  );
+
+    $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('RatingsEnabled','','Enabled or disables ratings feature in the OPAC',NULL,'YesNo')");
+
+    print "Upgrade to $DBversion done (Added 'ratings' table, and 'RatingsEnabled' syspref\n";
+    SetVersion ($DBversion);
+}
+
+
 =head1 FUNCTIONS
 
 =head2 DropAllForeignKeys($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref
index 4a7c652..7f6339c 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref
@@ -268,6 +268,12 @@ OPAC:
             - pref: numSearchRSSResults
               class: long
             -  search results in the RSS feed.
+        -
+            - pref: RatingsEnabled
+              choices:
+                  yes: Show
+                  no: "Don't show"
+            - star ratings
     Policy:
         -
             - pref: singleBranchMode
diff --git a/koha-tmpl/opac-tmpl/prog/en/css/jquery.rating.css b/koha-tmpl/opac-tmpl/prog/en/css/jquery.rating.css
new file mode 100644
index 0000000..e89096b
--- /dev/null
+++ b/koha-tmpl/opac-tmpl/prog/en/css/jquery.rating.css
@@ -0,0 +1,12 @@
+/* jQuery.Rating Plugin CSS - http://www.fyneworks.com/jquery/star-rating/ */
+div.rating-cancel,div.star-rating{float:left;width:15px;height:15px;text-indent:-999em;cursor:pointer;display:block;background:transparent;overflow:hidden}
+div.rating-cancel,div.rating-cancel a{background:url(../../images/delete.gif) no-repeat 0 -16px}
+div.star-rating,div.star-rating a{background:url(../../images/star.gif) no-repeat 0 0px}
+div.rating-cancel a,div.star-rating a{display:block;width:16px;height:100%;background-position:0 0px;border:0}
+div.star-rating-on a{background-position:0 -32px!important}
+div.star-rating-hover a{background-position:0 -16px}
+/* Read Only CSS */
+div.star-rating-readonly a{cursor:default !important}
+/* Partial Star CSS */
+div.star-rating{background:transparent!important;overflow:hidden!important}
+/* END jQuery.Rating Plugin CSS */
diff --git a/koha-tmpl/opac-tmpl/prog/en/lib/jquery/plugins/jquery.rating.js b/koha-tmpl/opac-tmpl/prog/en/lib/jquery/plugins/jquery.rating.js
new file mode 100644
index 0000000..5a03d3f
--- /dev/null
+++ b/koha-tmpl/opac-tmpl/prog/en/lib/jquery/plugins/jquery.rating.js
@@ -0,0 +1,344 @@
+/*
+ ### jQuery Star Rating Plugin v3.10 - 2009-03-23 ###
+ * Home: http://www.fyneworks.com/jquery/star-rating/
+ * Code: http://code.google.com/p/jquery-star-rating-plugin/
+ *
+	* Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ ###
+*/
+
+/*# AVOID COLLISIONS #*/
+;if(window.jQuery) (function($){
+/*# AVOID COLLISIONS #*/
+	
+	// IE6 Background Image Fix
+	if ($.browser.msie) try { document.execCommand("BackgroundImageCache", false, true)} catch(e) { }
+	// Thanks to http://www.visualjquery.com/rating/rating_redux.html
+	
+	// plugin initialization
+	$.fn.rating = function(options){
+		if(this.length==0) return this; // quick fail
+		
+		// Handle API methods
+		if(typeof arguments[0]=='string'){
+			// Perform API methods on individual elements
+			if(this.length>1){
+				var args = arguments;
+				return this.each(function(){
+					$.fn.rating.apply($(this), args);
+    });
+			};
+			// Invoke API method handler
+			$.fn.rating[arguments[0]].apply(this, $.makeArray(arguments).slice(1) || []);
+			// Quick exit...
+			return this;
+		};
+		
+		// Initialize options for this call
+		var options = $.extend(
+			{}/* new object */,
+			$.fn.rating.options/* default options */,
+			options || {} /* just-in-time options */
+		);
+		
+		// loop through each matched element
+		this
+		 .not('.star-rating-applied')
+			.addClass('star-rating-applied')
+		.each(function(){
+			
+			// Load control parameters / find context / etc
+			var eid = (this.name || 'unnamed-rating').replace(/\[|\]+/g, "_");
+			var context = $(this.form || document.body);
+			var input = $(this);
+			var raters = context.data('rating') || { count:0 };
+			var rater = raters[eid];
+			var control;
+			
+			// if rater is available, verify that the control still exists
+			if(rater) control = rater.data('rating');
+			
+			if(rater && control){
+				// add star to control if rater is available and the same control still exists
+				control.count++;
+				
+			}
+			else{
+				// create new control if first star or control element was removed/replaced
+				
+				// Initialize options for this raters
+				control = $.extend(
+					{}/* new object */,
+					options || {} /* current call options */,
+					($.metadata? input.metadata(): ($.meta?input.data():null)) || {}, /* metadata options */
+					{ count:0, stars: [], inputs: [] }
+				);
+				
+				// increment number of rating controls
+				control.serial = raters.count++;
+				
+				// create rating element
+				rater = $('<span class="star-rating-control"/>');
+				input.before(rater);
+				
+				// Mark element for initialization (once all stars are ready)
+				rater.addClass('rating-to-be-drawn');
+				
+				// Accept readOnly setting from 'disabled' property
+				if(input.attr('disabled')) control.readOnly = true;
+				
+				// Create 'cancel' button
+				rater.append(
+					control.cancel = $('<div class="rating-cancel"><a title="' + control.cancel + '">' + control.cancelValue + '</a></div>')
+					.mouseover(function(){
+						$(this).rating('drain');
+						$(this).addClass('star-rating-hover');
+						//$(this).rating('focus');
+					})
+					.mouseout(function(){
+						$(this).rating('draw');
+						$(this).removeClass('star-rating-hover');
+						//$(this).rating('blur');
+					})
+					.click(function(){
+					 $(this).rating('select');
+					})
+					.data('rating', control)
+				);
+				
+			}; // first element of group
+			
+			// insert rating star
+			var star = $('<div class="star-rating rater-'+ control.serial +'"><a title="' + (this.title || this.value) + '">' + this.value + '</a></div>');
+			rater.append(star);
+			
+			// inherit attributes from input element
+			if(this.id) star.attr('id', this.id);
+			if(this.className) star.addClass(this.className);
+			
+			// Half-stars?
+			if(control.half) control.split = 2;
+			
+			// Prepare division control
+			if(typeof control.split=='number' && control.split>0){
+				var stw = ($.fn.width ? star.width() : 0) || control.starWidth;
+				var spi = (control.count % control.split), spw = Math.floor(stw/control.split);
+				star
+				// restrict star's width and hide overflow (already in CSS)
+				.width(spw)
+				// move the star left by using a negative margin
+				// this is work-around to IE's stupid box model (position:relative doesn't work)
+				.find('a').css({ 'margin-left':'-'+ (spi*spw) +'px' })
+			};
+			
+			// readOnly?
+			if(control.readOnly)//{ //save a byte!
+				// Mark star as readOnly so user can customize display
+				star.addClass('star-rating-readonly');
+			//}  //save a byte!
+			else//{ //save a byte!
+			 // Enable hover css effects
+				star.addClass('star-rating-live')
+				 // Attach mouse events
+					.mouseover(function(){
+						$(this).rating('fill');
+						$(this).rating('focus');
+					})
+					.mouseout(function(){
+						$(this).rating('draw');
+						$(this).rating('blur');
+					})
+					.click(function(){
+						$(this).rating('select');
+					})
+				;
+			//}; //save a byte!
+			
+			// set current selection
+			if(this.checked)	control.current = star;
+			
+			// hide input element
+			input.hide();
+			
+			// backward compatibility, form element to plugin
+			input.change(function(){
+    $(this).rating('select');
+   });
+			
+			// attach reference to star to input element and vice-versa
+			star.data('rating.input', input.data('rating.star', star));
+			
+			// store control information in form (or body when form not available)
+			control.stars[control.stars.length] = star[0];
+			control.inputs[control.inputs.length] = input[0];
+			control.rater = raters[eid] = rater;
+			control.context = context;
+			
+			input.data('rating', control);
+			rater.data('rating', control);
+			star.data('rating', control);
+			context.data('rating', raters);
+  }); // each element
+		
+		// Initialize ratings (first draw)
+		$('.rating-to-be-drawn').rating('draw').removeClass('rating-to-be-drawn');
+		
+		return this; // don't break the chain...
+	};
+	
+	/*--------------------------------------------------------*/
+	
+	/*
+		### Core functionality and API ###
+	*/
+	$.extend($.fn.rating, {
+		
+		focus: function(){
+			var control = this.data('rating'); if(!control) return this;
+			if(!control.focus) return this; // quick fail if not required
+			// find data for event
+			var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
+   // focus handler, as requested by focusdigital.co.uk
+			if(control.focus) control.focus.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
+		}, // $.fn.rating.focus
+		
+		blur: function(){
+			var control = this.data('rating'); if(!control) return this;
+			if(!control.blur) return this; // quick fail if not required
+			// find data for event
+			var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
+   // blur handler, as requested by focusdigital.co.uk
+			if(control.blur) control.blur.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
+		}, // $.fn.rating.blur
+		
+		fill: function(){ // fill to the current mouse position.
+			var control = this.data('rating'); if(!control) return this;
+			// do not execute when control is in read-only mode
+			if(control.readOnly) return;
+			// Reset all stars and highlight them up to this element
+			this.rating('drain');
+			this.prevAll().andSelf().filter('.rater-'+ control.serial).addClass('star-rating-hover');
+		},// $.fn.rating.fill
+		
+		drain: function() { // drain all the stars.
+			var control = this.data('rating'); if(!control) return this;
+			// do not execute when control is in read-only mode
+			if(control.readOnly) return;
+			// Reset all stars
+			control.rater.children().filter('.rater-'+ control.serial).removeClass('star-rating-on').removeClass('star-rating-hover');
+		},// $.fn.rating.drain
+		
+		draw: function(){ // set value and stars to reflect current selection
+			var control = this.data('rating'); if(!control) return this;
+			// Clear all stars
+			this.rating('drain');
+			// Set control value
+			if(control.current){
+				control.current.data('rating.input').attr('checked','checked');
+				control.current.prevAll().andSelf().filter('.rater-'+ control.serial).addClass('star-rating-on');
+			}
+			else
+			 $(control.inputs).removeAttr('checked');
+			// Show/hide 'cancel' button
+			control.cancel[control.readOnly || control.required?'hide':'show']();
+			// Add/remove read-only classes to remove hand pointer
+			this.siblings()[control.readOnly?'addClass':'removeClass']('star-rating-readonly');
+		},// $.fn.rating.draw
+		
+		select: function(value){ // select a value
+			var control = this.data('rating'); if(!control) return this;
+			// do not execute when control is in read-only mode
+			if(control.readOnly) return;
+			// clear selection
+			control.current = null;
+			// programmatically (based on user input)
+			if(typeof value!='undefined'){
+			 // select by index (0 based)
+				if(typeof value=='number')
+ 			 return $(control.stars[value]).rating('select');
+				// select by literal value (must be passed as a string
+				if(typeof value=='string')
+					//return 
+					$.each(control.stars, function(){
+						if($(this).data('rating.input').val()==value) $(this).rating('select');
+					});
+			}
+			else
+				control.current = this[0].tagName=='INPUT' ? 
+				 this.data('rating.star') : 
+					(this.is('.rater-'+ control.serial) ? this : null);
+			
+			// Update rating control state
+			this.data('rating', control);
+			// Update display
+			this.rating('draw');
+			// find data for event
+			var input = $( control.current ? control.current.data('rating.input') : null );
+			// click callback, as requested here: http://plugins.jquery.com/node/1655
+			if(control.callback) control.callback.apply(input[0], [input.val(), $('a', control.current)[0]]);// callback event
+		},// $.fn.rating.select
+		
+		readOnly: function(toggle, disable){ // make the control read-only (still submits value)
+			var control = this.data('rating'); if(!control) return this;
+			// setread-only status
+			control.readOnly = toggle || toggle==undefined ? true : false;
+			// enable/disable control value submission
+			if(disable) $(control.inputs).attr("disabled", "disabled");
+			else     			$(control.inputs).removeAttr("disabled");
+			// Update rating control state
+			this.data('rating', control);
+			// Update display
+			this.rating('draw');
+		},// $.fn.rating.readOnly
+		
+		disable: function(){ // make read-only and never submit value
+			this.rating('readOnly', true, true);
+		},// $.fn.rating.disable
+		
+		enable: function(){ // make read/write and submit value
+			this.rating('readOnly', false, false);
+		}// $.fn.rating.select
+		
+ });
+	
+	/*--------------------------------------------------------*/
+	
+	/*
+		### Default Settings ###
+		eg.: You can override default control like this:
+		$.fn.rating.options.cancel = 'Clear';
+	*/
+	$.fn.rating.options = { //$.extend($.fn.rating, { options: {
+			cancel: 'Cancel Rating',   // advisory title for the 'cancel' link
+			cancelValue: '',           // value to submit when user click the 'cancel' link
+			split: 0,                  // split the star into how many parts?
+			
+			// Width of star image in case the plugin can't work it out. This can happen if
+			// the jQuery.dimensions plugin is not available OR the image is hidden at installation
+			starWidth: 16//,
+			
+			//NB.: These don't need to be pre-defined (can be undefined/null) so let's save some code!
+			//half:     false,         // just a shortcut to control.split = 2
+			//required: false,         // disables the 'cancel' button so user can only select one of the specified values
+			//readOnly: false,         // disable rating plugin interaction/ values cannot be changed
+			//focus:    function(){},  // executed when stars are focused
+			//blur:     function(){},  // executed when stars are focused
+			//callback: function(){},  // executed when a star is clicked
+ }; //} });
+	
+	/*--------------------------------------------------------*/
+	
+	/*
+		### Default implementation ###
+		The plugin will attach itself to file inputs
+		with the class 'multi' when the page loads
+	*/
+	$(function(){ $('input[type=radio].star').rating(); });
+	
+	
+	
+/*# AVOID COLLISIONS #*/
+})(jQuery);
+/*# AVOID COLLISIONS #*/
diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
index 40af8f5..3fa4f12 100644
--- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
+++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
@@ -1,6 +1,8 @@
 [% INCLUDE 'doc-head-open.inc' %][% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha Online[% END %] Catalog &rsaquo; Details for: [% title |html %][% FOREACH subtitl IN subtitle %], [% subtitl.subfield %][% END %]
 [% INCLUDE 'doc-head-close.inc' %]
 <script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.tablesorter.min.js"></script>
+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.rating.js"></script>
+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/jquery.rating.css" />
 <script type="text/JavaScript" language="JavaScript">
 //<![CDATA[
      $(document).ready(function() { 
@@ -47,7 +49,30 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
 		YAHOO.util.Event.addListener("furthersearches", "click", furthersearchesMenu.show, null, furthersearchesMenu);
 		YAHOO.widget.Overlay.windowResizeEvent.subscribe(positionfurthersearchesMenu);
  });
-	
+
+[% IF (RatingsEnabled) %]
+$(document).ready(function() { 
+
+$(".auto-submit-star").rating({
+    callback: function(value, link){
+        $.post("/cgi-bin/koha/opac-ratings.pl", 
+        {   rating: value, 
+            biblionumber: "[% biblionumber %]"  
+        },
+        function(data){
+            $("#rating_total").html('&nbsp;('+data.total+' '+ (data.total==1 ? _('vote') : _('votes'))+')');
+            if (data.value) {
+                $("#rating_user").text(_('your rating added: ')+data.value);
+            } else {
+                $("#rating_user").text('');
+            }
+        }
+        , "json");
+    }
+});
+
+});
+[% END %]
 //]]>
 </script>
 [% IF ( opacuserlogin ) %][% IF ( loggedinusername ) %][% IF ( TagsEnabled ) %]<style type="text/css">
@@ -311,6 +336,29 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
         </span>
         [% END %][% END %][% END %]
 
+ [% IF (RatingsEnabled) %]
+ <div class="results_summary">   
+    <input class="auto-submit-star" type="radio"  name="rating[% biblionumber %]"  value="1" [% IF (rating_val_1) %]checked="1"[% END %] [% IF (rating_readonly) %]disabled="disabled"[% END %]   />
+    <input class="auto-submit-star" type="radio"  name="rating[% biblionumber %]"  value="2" [% IF (rating_val_2) %]checked="1"[% END %] [% IF (rating_readonly) %]disabled="disabled"[% END %]   />
+    <input class="auto-submit-star" type="radio"  name="rating[% biblionumber %]"  value="3" [% IF (rating_val_3) %]checked="1"[% END %] [% IF (rating_readonly) %]disabled="disabled"[% END %]   />
+    <input class="auto-submit-star" type="radio"  name="rating[% biblionumber %]"  value="4" [% IF (rating_val_4) %]checked="1"[% END %] [% IF (rating_readonly) %]disabled="disabled"[% END %]   />
+    <input class="auto-submit-star" type="radio"  name="rating[% biblionumber %]"  value="5" [% IF (rating_val_5) %]checked="1"[% END %] [% IF (rating_readonly) %]disabled="disabled"[% END %]   />
+
+    <input  type="hidden" name='biblionumber'  value="[% biblionumber %]" />
+
+   <span  id="rating_total"  >
+[% IF(rating_total) %]&nbsp;([% rating_total %] [% IF (rating_total==1) %]vote[% ELSE %]votes[% END %])[% END %]
+</span>
+
+   <span id="rating_user">[% IF (rating_value) %]your rating: [% rating_value %][% END %]</span>
+    [% IF (rating_readonly) %]
+        <span id="rating_login">Log in to add your rating.</span>
+    [% END %]
+</div>
+
+[% END %]
+
+
     [% IF ( BakerTaylorContentURL ) %]
         <span class="results_summary">
         <span class="label">Enhanced Content: </span> 
diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt
index bdc4050..78928d1 100644
--- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt
+++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt
@@ -8,6 +8,10 @@
 [% INCLUDE 'doc-head-close.inc' %]
 <link rel="alternate" type="application/rss+xml" title="[% LibraryName |html %] Search RSS Feed" href="[% OPACBaseurl %]/cgi-bin/koha/opac-search.pl?[% query_cgi |html %][% limit_cgi |html %]&amp;count=[% countrss |html %]&amp;sort_by=acqdate_dsc&amp;format=rss2" />
 
+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.rating.js"></script>
+
+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/jquery.rating.css" />
+
 
 <script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
 [% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.highlight-3.js"></script>
@@ -78,6 +82,8 @@ function tagAdded() {
     KOHA.Tags.add_multitags_button(bibs, tag);
     return false;
 }[% END %][% END %]
+
+
 [% IF ( OpacHighlightedWords ) %]
 var q_array = new Array();  // holds search terms if available
 
@@ -230,7 +236,32 @@ $(document).ready(function(){
     [% END %][% END %]
     [% IF OpenLibraryCovers %]KOHA.OpenLibrary.GetCoverFromIsbn();[% END %]
     [% IF ( GoogleJackets ) %]KOHA.Google.GetCoverFromIsbn();[% END %]
+
+
+}); // end of $(document).ready
+
+[% IF ( RatingsEnabled ) %]
+$(document).ready(function() { 
+    $('.auto-submit-star').rating({
+        callback: function(value, link){
+                      var bibnum = this.name.replace(/^rating/, "");
+
+                      $.post("/cgi-bin/koha/opac-ratings.pl", {   
+                          rating: value,
+                          biblionumber: bibnum  
+                      }, function(data){
+                          $("#rating_total_"+bibnum).html("&nbsp;("+data.total+' '+ (data.total==1 ? _('vote') : _('votes'))+')');
+                            if (data.value) {
+                                $("#rating_value_"+bibnum).text(_('your rating added: ')+data.value);
+                            } else {
+                                $("#rating_value_"+bibnum).text('');
+                            }
+                      }, "json");
+                  }
+    });
 });
+[% END %]
+
 //]]>
 </script>
 </head>
@@ -504,6 +535,33 @@ $(document).ready(function(){
                                     </div>[% END %]
                                 [% END %]
                                 [% END %][% END %]
+
+[% IF ( RatingsEnabled ) %]
+<div class="results_summary">
+    <form name="ratingform[% SEARCH_RESULT.biblionumber %]" method="post" action="/cgi-bin/koha/opac-ratings.pl">
+        <input class="auto-submit-star" type="radio"  name="rating[% SEARCH_RESULT.biblionumber %]"  value="1" [% IF ( SEARCH_RESULT.rating_val_1 ) %]checked="1"[% END %] [% IF ( rating_readonly ) %]disabled="disabled"[% END %]   />
+        <input class="auto-submit-star" type="radio"  name="rating[% SEARCH_RESULT.biblionumber %]"  value="2" [% IF ( SEARCH_RESULT.rating_val_2 ) %]checked="1"[% END %] [% IF ( rating_readonly ) %]disabled="disabled"[% END %]   />
+        <input class="auto-submit-star" type="radio"  name="rating[% SEARCH_RESULT.biblionumber %]"  value="3" [% IF ( SEARCH_RESULT.rating_val_3 ) %]checked="1"[% END %] [% IF ( rating_readonly ) %]disabled="disabled"[% END %]   />
+        <input class="auto-submit-star" type="radio"  name="rating[% SEARCH_RESULT.biblionumber %]"  value="4" [% IF ( SEARCH_RESULT.rating_val_4 ) %]checked="1"[% END %] [% IF ( rating_readonly ) %]disabled="disabled"[% END %]   />
+        <input class="auto-submit-star" type="radio"  name="rating[% SEARCH_RESULT.biblionumber %]"  value="5" [% IF ( SEARCH_RESULT.rating_val_5 ) %]checked="1"[% END %] [% IF ( rating_readonly ) %]disabled="disabled"[% END %]   />
+        <input  type="hidden" name='[% SEARCH_RESULT.biblionumber %]'  value="[% SEARCH_RESULT.biblionumber %]" />
+        <span id="rating_total_[% SEARCH_RESULT.biblionumber %]">
+            [% IF (SEARCH_RESULT.rating_total) %]
+            &nbsp;([% SEARCH_RESULT.rating_total %] [% IF (SEARCH_RESULT.rating_total==1) %]vote[% ELSE %]votes[% END %])
+            [% END %]
+        </span>
+
+        <span id="rating_value_[% SEARCH_RESULT.biblionumber %]">
+            [% IF ( SEARCH_RESULT.rating_value ) %]&nbsp;your rating: [% SEARCH_RESULT.rating_value %][% END %]
+        </span>
+
+    </form>
+    <br />
+</div>
+[% END %]
+
+
+
 				[% IF ( SEARCH_RESULT.searchhighlightblob ) %]<span class="results_summary"><span class="label">Match:</span> [% SEARCH_RESULT.searchhighlightblob %]</span>[% END %]
 
 <span class="results_summary actions"><span class="label">Actions:</span>
diff --git a/koha-tmpl/opac-tmpl/prog/images/delete.gif b/koha-tmpl/opac-tmpl/prog/images/delete.gif
new file mode 100644
index 0000000000000000000000000000000000000000..43c6ca8763d79bde87bcf437e497af00c8be562d
GIT binary patch
literal 752
zcmZ?wbhEHb6kt$bc*el6GthYN-n}zt&b)vB{*)<GZZDnssVwQ*wQJ8pz3-M6ef|3N
zTw?I^BRjWl-THaiqIb<TR|-<zzkh%9=+U)n*M2*H{@2X5mq}6Aa+CK)+kfaTd)M9Y
zW&YeVXU=?m_V93u_p1$S-kmu7EY$ycOu&bh{H05m9`-kWR9EwH&eRVR>i+-#|NQy$
zABXln&Q1FL`t{#CH*SY|{oJwc*Qz;h;)0))7aYq9ewP~fc2e)_P3vAY)$9wl{IzJp
zy{ydlr;a^HiGHwZ{*SH8FZkJhE{gei`OM?;ir*_{?@ji478&@mD*tSH_`A&5 at 6Vqe
z at G<&xeCOwiq-WvY-<q?(PpbX8Zpp{q%CC#&{pc>eS5f?;vgCP8#IHGBw=+^cY}xvy
zx8c*8mDl2ff1W;kCeZrNm9vlQYJg5<pbb#`$->Caki?(^G9DBs4DA0KlA4-ZT3fl8
z8F_gb*}2-1v>0WWwY1oIg_&Emc-aM+Wn|bpRc1GF^$N-etzA1qr9X*XNNWw_j-BjG
zGEKc(x)1gUF{(82E at ad^eMXp9hUxg0Q)f<}lVRDncBb&%dq*yAR+(|<o-jL$(28?2
zo;??45^QExc`no~#IC}^-ui`UvIUER!h-n=LseJ=rYP`FI@~V6z4by;((yhm^C*W!
z21kx at DaO@p*l}R7K&xy_%z+DytsIk;xgB`2439K+b7?GbXmoN_5mIg1 at _?b4)vHb2
zW4=iz(~<TC27yT_6CAg$YSazO;JC%ee5xbs^c)q-iOvDjBs>l-D at bZwvw%g)cL7VG
z)An_f*+XqOetkIP*eg{wS7K(+qJ{-x0ue3?v&y~1d1To>MqD_2keNGNE=Pc|!BJ7b
fV_(F^#N=Z_Jo9)pKRPly^YODPxtB<AFjxZs(@{iI

literal 0
HcmV?d00001

diff --git a/koha-tmpl/opac-tmpl/prog/images/star.gif b/koha-tmpl/opac-tmpl/prog/images/star.gif
new file mode 100644
index 0000000000000000000000000000000000000000..d0948a70843bf01952d1f81dcfcdadc92976a04a
GIT binary patch
literal 815
zcmZ?wbhEHb6ksr5c*el6(A at m!(W5UnOP^`reSRqFN=wUy;^KSv?mbz*{y;*);~mc5
z-|RUW6m;#{wMW}bp6pHg_U+rN({8s`u6+0I-Q5+MAMeb%w`<p%^ObAe+`hkj`DTyO
z+d~E~k2$_RJM(N#&bNn2U+&-kax?1Vx!~u|pC6Bkx;a<%e0BBpNt3?6-n!q(>5!M#
z{q=^=j}*N*e*EQepZ7;K-(U6l^W*)qy-uI5Mx04YJ9FmD at 2@w0e|`3BzvYV!svmDJ
zy1UBt<DL4wUS5~`l~&o?KiTc|?y&dMwH9wLt^WW2|BarW%QZD$t~tCq+4%9S{-+DB
zU+=}f+^q2P-MdfMLf>A9dUx3IVz=bW<9TmSpMHPB|LHc1x92?H at 6~*NPV?1P?R#4d
zPbMV1-s|#ytJ9}D;U7+0e!n;8#jahi&$Zv4GUd&Fk1akv*Jf&ey1!wEi_7<GUO*ce
zC<BT=Ss2+FvKVwg#(?64f&G6&R#S79fN)Dk2QP1DuW+|=Z&!EEq$XuvcJ`T_!n3+(
zPg$TWFniYgMJuxwEHYhZ+HLC6v}WVFjRGxe*Yle0Y?-ll&zz2hYr8v-tu*bNVmfR6
znT~Z6o0Ux&=T2&$rVu=>gU#eYv#yB=$CE4*Ha3+f942mV(XO4k$!yUkYI4!<crrvB
zCM=xaSm|bCkl=8r*<OezWJO^_0~=?DVCaekf*U&*6uTK@`5X*vJ?{A5Yn6wikt>^v
z(7()>%8M?Gl%7kx>Gn`^RAQF96>H<Z$Y)U-PmqX3BO7nCe5XmDq6#B(gG-`HBtv>~
zqm`q-QcY~aQ7_g~!8sNO9r=2u3Mlqi#CWtc9T(=ypIRf>c$iT@(<;xV at nLFbqN;ve
z&H|wp at 2R5NS2`L#KJ=KR(rKEJaLDyUmqb%cL}by=)-=YHmH-A at k7GPXma%d?aC*qL
zh)X7I#x;cnUQ^jS^;|kOFmg at Nv0U}W!=<5%L(6DYL1B*CL?*3JjSUV#i&`dVbSiW!
SUO9NY!6jXVS5kz7!5RQ<r+B^q

literal 0
HcmV?d00001

diff --git a/opac/opac-detail.pl b/opac/opac-detail.pl
index e18e046..99b995a 100755
--- a/opac/opac-detail.pl
+++ b/opac/opac-detail.pl
@@ -37,6 +37,7 @@ use C4::XISBN qw(get_xisbns get_biblionumber_from_isbn);
 use C4::External::Amazon;
 use C4::External::Syndetics qw(get_syndetics_index get_syndetics_summary get_syndetics_toc get_syndetics_excerpt get_syndetics_reviews get_syndetics_anotes );
 use C4::Review;
+use C4::Ratings;
 use C4::Members;
 use C4::VirtualShelves;
 use C4::XSLT;
@@ -46,6 +47,8 @@ use MARC::Record;
 use MARC::Field;
 use List::MoreUtils qw/any none/;
 
+#use Smart::Comments '####';
+
 BEGIN {
 	if (C4::Context->preference('BakerTaylorEnabled')) {
 		require C4::External::BakerTaylor;
@@ -318,6 +321,20 @@ foreach ( @$reviews ) {
     $_->{userid}    = $borrowerData->{'userid'};
     $_->{cardnumber}    = $borrowerData->{'cardnumber'};
     $_->{datereviewed} = format_date($_->{datereviewed});
+
+
+
+
+#    my $value =  get_rating_by_review($_->{reviewid});
+    my $rating =  get_rating(  $biblionumber ,  $_->{borrowernumber});
+
+    $_->{"borr_rating_val_".$rating->{value}} = 1;
+    $_->{rating} = $rating->{value} ;
+
+    ####  $rating
+#### $_
+
+
     if ($borrowerData->{'borrowernumber'} eq $borrowernumber) {
 		$_->{your_comment} = 1;
 		$loggedincommenter = 1;
@@ -564,6 +581,21 @@ if (C4::Context->preference("OPACURLOpenInNewWindow")) {
     $template->param(covernewwindow => 'false');
 }
 
+if (C4::Context->preference('RatingsEnabled') ) {
+my $rating = get_rating( $biblionumber, $borrowernumber );
+$template->param(
+  RatingsShowOnDetail => 1,
+  RatingsEnabled => 1,
+  rating_value        => $rating->{'value'},
+  rating_total        => $rating->{'total'},
+  rating_avg          => $rating->{'avg'},
+  rating_avgint       => $rating->{'avgint'},
+  rating_readonly     => ( $borrowernumber ? 0 : 1 ),
+  borrowernumber      => $borrowernumber,
+  "rating_val_" . "$rating->{'avgint'}" => $rating->{'avgint'},
+  );
+}
+
 #Search for title in links
 my $marccontrolnumber   = GetMarcControlnumber   ($record, $marcflavour);
 
diff --git a/opac/opac-ratings.pl b/opac/opac-ratings.pl
new file mode 100755
index 0000000..2c8b115
--- /dev/null
+++ b/opac/opac-ratings.pl
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+
+# Copyright 2010 KohaAloha, NZ
+# Parts copyright 2011, Catalyst IT, NZ
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+# Suite 330, Boston, MA  02111-1307 USA
+
+=head1
+
+opac-ratings.pl - API endpoint for setting rating values
+
+This receives a POST containing biblionumber and rating. It
+updates rating for the logged in user.
+
+=cut
+
+use strict;
+use warnings;
+use CGI;
+use CGI::Cookie;    # need to check cookies before having CGI parse the POST request
+use JSON;
+
+use C4::Auth qw(:DEFAULT check_cookie_auth);
+use C4::Context;
+use C4::Debug;
+use C4::Output 3.02 qw(:html :ajax pagination_bar);
+use C4::Ratings;
+
+use Data::Dumper;
+
+my %ratings = ();
+my %counts  = ();
+my @errors  = ();
+
+my $is_ajax = is_ajax();
+
+my $query = ($is_ajax) ? &ajax_auth_cgi( {} ) : CGI->new();
+
+my $biblionumber   = $query->param('biblionumber');
+my $value;
+
+foreach ( $query->param ) {
+    if (/^rating(.*)/) {
+        $value = $query->param($_);
+        last;
+    }
+}
+
+my ( $template, $loggedinuser, $cookie );
+
+if ($is_ajax) {
+    $loggedinuser = C4::Context->userenv->{'number'};
+    add_rating( $biblionumber, $loggedinuser, $value );
+    my $rating = get_rating($biblionumber, $loggedinuser);
+    my $js_reply = "{total: $rating->{'total'}, value: $rating->{'value'}}";
+
+    output_ajax_with_http_headers( $query, $js_reply );
+    exit;
+} else {
+    # Future enhancements could have this have its own template to
+    # display the users' ratings, or tie in with their reading history
+    # to get them to rate things they read recently.
+    ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+        {   template_name   => "opac-user.tmpl",
+            query           => $query,
+            type            => "opac",
+            authnotrequired => 0,                  # auth required to add ratings
+            debug           => 0,
+        }
+    );
+}
+
+my $results = [];
+
+( scalar @errors ) and $template->param( ERRORS => \@errors );
+
+output_html_with_http_headers $query, $cookie, $template->output;
+
+sub ajax_auth_cgi ($) {                            # returns CGI object
+    my $needed_flags = shift;
+    my %cookies      = fetch CGI::Cookie;
+    my $input        = CGI->new;
+    my $sessid       = $cookies{'CGISESSID'}->value || $input->param('CGISESSID');
+    my ( $auth_status, $auth_sessid ) = check_cookie_auth( $sessid, $needed_flags );
+    if ( $auth_status ne "ok" ) {
+        output_ajax_with_http_headers $input, "window.alert('Your CGI session cookie ($sessid) is not current.  " . "Please refresh the page and try again.');\n";
+        exit 0;
+    }
+    return $input;
+}
+
diff --git a/opac/opac-search.pl b/opac/opac-search.pl
index 81b7ce6..9b76d12 100755
--- a/opac/opac-search.pl
+++ b/opac/opac-search.pl
@@ -36,10 +36,14 @@ use C4::Biblio;  # GetBiblioData
 use C4::Koha;
 use C4::Tags qw(get_tags);
 use C4::Branch; # GetBranches
+use C4::Ratings;
+
 use POSIX qw(ceil floor strftime);
 use URI::Escape;
 use Storable qw(thaw freeze);
 
+#use Smart::Comments '####';
+
 
 my $DisplayMultiPlaceHold = C4::Context->preference("DisplayMultiPlaceHold");
 # create a new CGI object
@@ -111,6 +115,7 @@ if (C4::Context->preference('BakerTaylorEnabled')) {
 		BakerTaylorBookstoreURL => C4::Context->preference('BakerTaylorBookstoreURL'),
 	);
 }
+
 if (C4::Context->preference('TagsEnabled')) {
 	$template->param(TagsEnabled => 1);
 	foreach (qw(TagsShowOnList TagsInputOnList)) {
@@ -118,6 +123,14 @@ if (C4::Context->preference('TagsEnabled')) {
 	}
 }
 
+if (C4::Context->preference('RatingsEnabled')) {
+####  $borrowernumber 
+	$template->param(rating_readonly => 1)  unless $borrowernumber ;
+	$template->param(borrowernumber =>  $borrowernumber );
+}
+
+
+
 ## URI Re-Writing
 # Deprecated, but preserved because it's interesting :-)
 # The same thing can be accomplished with mod_rewrite in
@@ -485,12 +498,26 @@ for (my $i=0;$i<@servers;$i++) {
 										limit=>$tag_quantity });
 			}
 		}
-                if (C4::Context->preference('COinSinOPACResults')) {
-		    foreach (@newresults) {
-		      $_->{coins} = GetCOinSBiblio($_->{'biblionumber'});
-		    }
-                }
-      
+        if (C4::Context->preference('COinSinOPACResults')) {
+            foreach (@newresults) {
+                $_->{coins} = GetCOinSBiblio($_->{'biblionumber'});
+            }
+        }
+
+        if (C4::Context->preference('RatingsEnabled')) {
+            foreach (@newresults) {
+                my $rating = get_rating( $_->{'biblionumber'}, $borrowernumber );
+
+                my $bib = $_->{'biblionumber'};
+                $_->{'rating_user'}                         = $rating->{'user'};
+                $_->{'rating_total'}                        = $rating->{'total'};
+                $_->{'rating_avg'}                          = $rating->{'avg'};
+                $_->{'rating_avgint'}                       = $rating->{'avgint'};
+                $_->{ 'rating_val_' . $rating->{'avgint'} } = $rating->{'avgint'};
+                $_->{'rating_value'}                        = $rating->{'value'};
+            }
+        }
+
 	if ($results_hashref->{$server}->{"hits"}){
 	    $total = $total + $results_hashref->{$server}->{"hits"};
 	}
@@ -609,7 +636,6 @@ for (my $i=0;$i<@servers;$i++) {
                       };
 
                 }
-                        
             }
             # now, show twenty pages, with the current one smack in the middle
             else {
-- 
1.7.2.3



More information about the Koha-patches mailing list