use Mojo::Base 'Mojolicious::Controller';
use Koha::Biblios;
+use Koha::Ratings;
use Koha::RecordProcessor;
use C4::Biblio qw( DelBiblio );
};
}
+=head3 set_rating
+
+Set rating for the logged in user
+
+=cut
+
+
+sub set_rating {
+ my $c = shift->openapi->valid_input or return;
+
+ my $biblio = Koha::Biblios->find( $c->validation->param('biblio_id') );
+
+ unless ($biblio) {
+ return $c->render(
+ status => 404,
+ openapi => {
+ error => "Object not found."
+ }
+ );
+ }
+
+ my $patron = $c->stash('koha.user');
+ unless ($patron) {
+ return $c->render(
+ status => 403,
+ openapi =>
+ { error => "Cannot rate. Reason: must be logged-in" }
+ );
+ }
+
+ my $body = $c->validation->param('body');
+ my $rating_value = $body->{rating};
+
+ return try {
+
+ my $rating = Koha::Ratings->find(
+ {
+ biblionumber => $biblio->biblionumber,
+ borrowernumber => $patron->borrowernumber,
+ }
+ );
+ $rating->delete if $rating;
+
+ if ( $rating_value ) { # Cannot set to 0 from the UI
+ $rating = Koha::Rating->new(
+ {
+ biblionumber => $biblio->biblionumber,
+ borrowernumber => $patron->borrowernumber,
+ rating_value => $rating_value,
+ }
+ )->store;
+ };
+ my $ratings =
+ Koha::Ratings->search( { biblionumber => $biblio->biblionumber } );
+ my $average = $ratings->get_avg_rating;
+
+ return $c->render(
+ status => 200,
+ openapi => {
+ rating => $rating && $rating->in_storage ? $rating->rating_value : undef,
+ average => $average,
+ count => $ratings->count
+ },
+ );
+ }
+ catch {
+ $c->unhandled_exception($_);
+ };
+}
+
1;
description: Under maintenance
schema:
$ref: "../swagger.yaml#/definitions/error"
+"/public/biblios/{biblio_id}/ratings":
+ post:
+ x-mojo-to: Biblios#set_rating
+ operationId: setBiblioRating
+ tags:
+ - biblios
+ summary: set biblio rating (public)
+ parameters:
+ - $ref: "../swagger.yaml#/parameters/biblio_id_pp"
+ - name: body
+ in: body
+ description: A JSON object containing rating information
+ schema:
+ type: object
+ properties:
+ rating:
+ description: the rating
+ type:
+ - integer
+ - "null"
+ required:
+ - rating
+ additionalProperties: false
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: Rating set
+ schema:
+ type: object
+ properties:
+ rating:
+ description: user's rating
+ type:
+ - number
+ - "null"
+ average:
+ description: average rating
+ type: number
+ count:
+ description: number of ratings
+ type: integer
+ additionalProperties: false
+ "401":
+ description: Authentication required
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ "403":
+ description: Access forbidden
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ "404":
+ description: Biblio not found
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ "500":
+ description: |
+ Internal server error. Possible `error_code` attribute values:
+
+ * `internal_server_error`
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ "503":
+ description: Under maintenance
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
$ref: "./paths/biblios.yaml#/~1public~1biblios~1{biblio_id}"
"/public/biblios/{biblio_id}/items":
$ref: "./paths/biblios.yaml#/~1public~1biblios~1{biblio_id}~1items"
+ "/public/biblios/{biblio_id}/ratings":
+ $ref: "./paths/biblios.yaml#/~1public~1biblios~1{biblio_id}~1ratings"
/public/libraries:
$ref: ./paths/libraries.yaml#/~1public~1libraries
"/public/libraries/{library_id}":
<script>
var Koha = {};
function _(s) { return s } // dummy function for gettext
+ const is_logged_in = [% IF logged_in_user %]true[% ELSE %]false[% END %]
</script>
[% IF lang && lang != 'en' %]
[% Asset.js(lang _ '/js/locale_data.js') | $raw %]
[% IF ( OpacStarRatings != 'disable' ) %]
<form method="post" action="/cgi-bin/koha/opac-ratings.pl">
<legend class="sr-only">Star ratings</legend>
- <div class="results_summary ratings">
+ <div class="results_summary ratings" id="rating-[% biblio.biblionumber | html %]">
[% SET rating_avg = ratings.get_avg_rating() %]
[% rating_avg_int = BLOCK %][% rating_avg | format("%.0f") %][% END %]
[% IF ( borrowernumber ) %]
- <select id="star_rating" name="rating" data-rating-enabled="1" autocomplete="off">
+ <select id="star_rating" class="star_rating" name="rating" data-biblionumber="[% biblio.biblionumber | html %]" data-context="rating-[% biblio.biblionumber | html %]" autocomplete="off">
[% ELSE %]
- <select id="star_rating" name="rating" data-rating-enabled="0" disabled="disabled" autocomplete="off">
+ <select id="star_rating" class="star_rating" name="rating" disabled="disabled" autocomplete="off">
[% END %]
[% IF ( rating_avg_int == 0 ) %]
<option value="" selected="selected"></option>
[% END %]
[% END %]
</select>
- <img id="rating-loading" style="display:none" src="[% interface | html %]/[% theme | html %]/images/spinner-small.gif" alt="" />
+ <img id="rating-loading" class="rating-loading" style="display:none" src="[% interface | html %]/[% theme | html %]/images/spinner-small.gif" alt="" />
<!-- define some hidden vars for ratings -->
<input type="hidden" name='biblionumber' value="[% biblio.biblionumber | html %]" />
- <input type="hidden" name='rating_value' id='rating_value' value="[% my_rating.rating_value | html %]" />
+ <input type="hidden" name='rating_value' id='rating_value' class="rating_value" value="[% my_rating.rating_value | html %]" />
[% UNLESS ( rating_readonly ) %] <input name="rate_button" type="submit" value="Rate me" />[% END %]
[% IF my_rating %]
- <span id="rating_value_text">Your rating: [% my_rating.rating_value | html %].</span>
- <span id="cancel_rating_text"><a href="#"><i class="fa fa-remove" aria-hidden="true"></i> Cancel rating</a>.</span>
+ <span id="rating_value_text" class="rating_value_text">Your rating: [% my_rating.rating_value | html %].</span>
+ <span id="cancel_rating_text" class="cancel_rating_text"><a href="#" data-context="star_rating"><i class="fa fa-remove" aria-hidden="true"></i> Cancel rating.</a></span>
[% ELSE %]
- <span id="rating_value_text"></span>
- <span id="cancel_rating_text" style="display:none;"><a href="#"><i class="fa fa-remove" aria-hidden="true"></i> Cancel rating</a>.</span>
+ <span id="rating_value_text" class="rating_value_text"></span>
+ <span id="cancel_rating_text" class="cancel_rating_text" style="display: none;"><a href="#" data-context="star_rating"><i class="fa fa-remove" aria-hidden="true"></i> Cancel rating.</a></span>
[% END %]
- <span id="rating_text">Average rating: [% rating_avg | html %] ([% ratings.count | html %] votes)</span>
+ <span id="rating_text" class="rating_text">Average rating: [% rating_avg | html %] ([% ratings.count | html %] votes)</span>
</div>
</form>
[% END # / IF OpacStarRatings != 'disable' %]
[% INCLUDE 'datatables.inc' %]
[% INCLUDE 'columns_settings.inc' %]
[% INCLUDE greybox.inc %]
- [% IF ( OpacStarRatings != 'disable' ) %][% Asset.js("lib/jquery/plugins/jquery.barrating.min.js") | $raw %][% END %]
+ [% IF ( OpacStarRatings != 'disable' ) %]
+ [% Asset.js("lib/jquery/plugins/jquery.barrating.min.js") | $raw %]
+ [% Asset.js("js/ratings.js") | $raw %]
+ [% END %]
[% IF ( OpacHighlightedWords ) %][% Asset.js("lib/jquery/plugins/jquery.highlight-3.js") | $raw %][% END %]
[% IF ( Koha.Preference('OPACDetailQRCode') ) %]
});
}());
[% END # /IF ( OPACShelfBrowser ) %]
-
- [% IF ( OpacStarRatings != 'disable' ) %]
- // -----------------------------------------------------
- // star-ratings code
- // -----------------------------------------------------
- // hide 'rate' button if javascript enabled
-
- $('input[name="rate_button"]').remove();
-
- var rating_enabled = ( $("#star_rating").data("rating-enabled") == "1" ) ? false : true;
- $('#star_rating').barrating({
- theme: 'fontawesome-stars',
- showSelectedRating: false,
- allowEmpty: true,
- deselectable: false,
- readonly: rating_enabled,
- onSelect: function(value, text) {
- $("#rating-loading").show();
- $.post("/cgi-bin/koha/opac-ratings-ajax.pl", {
- rating_old_value: $("#rating_value").attr("value"),
- borrowernumber: "[% borrowernumber | html %]",
- biblionumber: "[% biblio.biblionumber | html %]",
- rating_value: value,
- auth_error: value
- }, function (data) {
- $("#rating_value").val(data.rating_value);
- if (data.rating_value) {
- $("#rating_value_text").text(_("Your rating: %s, ").format(data.rating_value));
- $("#cancel_rating_text").show();
- } else {
- $("#rating_value_text").text('');
- $("#cancel_rating_text").hide();
- }
- $("#rating_text").text(_("Average rating: %s (%s votes)").format(data.rating_avg, data.rating_total));
- $("#rating-loading").hide();
- }, "json");
- }
- });
-
- $("#cancel_rating_text a").on("click", function(e){
- e.preventDefault();
- $("#star_rating").barrating("set", "");
- });
-
- [% END # / IF ( OpacStarRatings != 'disable' )%]
});
$(document).ready(function() {
}
});
});
- var borrowernumber = "[% logged_in_user.borrowernumber | html %]";
- var MSG_YOUR_RATING = _("Your rating: %s, ");
- var MSG_AVERAGE_RATING = _("Average rating: %s (%s votes)");
</script>
[% IF ( Koha.Preference('OpacStarRatings') == 'all' ) %]
[% Asset.js("lib/jquery/plugins/jquery.barrating.min.js") | $raw %]
}
var borrowernumber = "[% borrowernumber | html %]";
- var MSG_YOUR_RATING = _("Your rating: %s, ");
- var MSG_AVERAGE_RATING = _("Average rating: %s (%s votes)");
</script>
[% IF ( Koha.Preference('OpacStarRatings') == 'all' ) %]
[% Asset.js("lib/jquery/plugins/jquery.barrating.min.js") | $raw %]
$("#bootstrap-confirm-box-modal").modal('show');
}
+
+// Function to check errors from AJAX requests
+const checkError = function(response) {
+ if (response.status >= 200 && response.status <= 299) {
+ return response.json();
+ } else {
+ console.log("Server returned an error:");
+ console.log(response);
+ alert("%s (%s)".format(response.statusText, response.status));
+ }
+};
+
//Add jQuery :focusable selector
(function($) {
function visible(element) {
-/* global borrowernumber MSG_YOUR_RATING MSG_AVERAGE_RATING */
// -----------------------------------------------------
// star-ratings code
// -----------------------------------------------------
showSelectedRating: false,
allowEmpty: true,
deselectable: false,
+ readonly: !is_logged_in,
onSelect: function( value ) {
var context = $("#" + this.$elem.data("context") );
$(".rating-loading", context ).show();
- $.post("/cgi-bin/koha/opac-ratings-ajax.pl", {
- rating_old_value: $(".rating_value", context ).attr("value"),
- borrowernumber: borrowernumber,
- biblionumber: this.$elem.data('biblionumber'),
- rating_value: value,
- auth_error: value
- }, function (data) {
- $(".rating_value", context ).val(data.rating_value);
- if (data.rating_value) {
- $(".rating_value_text", context ).text( MSG_YOUR_RATING.format(data.rating_value) );
+ let biblionumber = this.$elem.data('biblionumber');
+ if ( value == "" ) value = null;
+ fetch("/api/v1/public/biblios/"+biblionumber+"/ratings", {
+ method: 'POST',
+ body: JSON.stringify({ rating: value }),
+ headers: {
+ "Content-Type": "application/json;charset=utf-8",
+ }
+ }).then(checkError)
+ .then((data) => {
+ $(".rating_value", context ).val(data.rating);
+ console.log(data);
+ console.log($(".cancel_rating_text", context ));
+ if (data.rating) {
+ console.log(data.rating);
+ $(".rating_value_text", context ).text( __("Your rating: %s.").format(data.rating) );
$(".cancel_rating_text", context ).show();
} else {
$(".rating_value_text", context ).text("");
$(".cancel_rating_text", context ).hide();
}
- $(".rating_text", context ).text( MSG_AVERAGE_RATING.format(data.rating_avg, data.rating_total) );
+ console.log($(".rating_text", context ));
+ $(".rating_text", context ).text( __("Average rating: %s (%s votes)").format(data.average, data.count) );
$(".rating-loading", context ).hide();
- }, "json");
+ });
}
});
+++ /dev/null
-#!/usr/bin/perl
-
-# Copyright 2011 KohaAloha, 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 3 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, see <http://www.gnu.org/licenses>.
-
-=head1 DESCRIPTION
-
-A script that takes an ajax json query, and then inserts or modifies a star-rating.
-
-=cut
-
-use Modern::Perl;
-
-use CGI qw ( -utf8 );
-use CGI::Cookie; # need to check cookies before having CGI parse the POST request
-
-use C4::Auth qw( get_template_and_user check_cookie_auth );
-use C4::Context;
-use C4::Output qw( is_ajax output_ajax_with_http_headers );
-
-use Koha::Ratings;
-
-use JSON;
-
-my $is_ajax = is_ajax();
-
-my ( $query, $auth_status );
-if ($is_ajax) {
- ( $query, $auth_status ) = &ajax_auth_cgi( {} );
-}
-else {
- $query = CGI->new();
-}
-
-my $biblionumber = $query->param('biblionumber');
-my $rating_value = $query->param('rating_value');
-my $rating_old_value = $query->param('rating_old_value');
-
-my ( $template, $loggedinuser, $cookie );
-if ($is_ajax) {
- $loggedinuser = C4::Context->userenv->{'number'};
-}
-else {
- ( $template, $loggedinuser, $cookie ) = get_template_and_user(
- {
- template_name => "opac-detail.tt",
- query => $query,
- type => "opac",
- authnotrequired => 0, # auth required to add tags
- }
- );
-}
-
-my $rating;
-$rating_value //= '';
-
-if ( $rating_value eq '' ) {
- my $rating = Koha::Ratings->find( { biblionumber => $biblionumber, borrowernumber => $loggedinuser } );
- $rating->delete if $rating;
-}
-
-elsif ( $rating_value and !$rating_old_value ) {
- Koha::Rating->new( { biblionumber => $biblionumber, borrowernumber => $loggedinuser, rating_value => $rating_value, })->store;
-}
-
-elsif ( $rating_value ne $rating_old_value ) {
- my $rating = Koha::Ratings->find( { biblionumber => $biblionumber, borrowernumber => $loggedinuser });
- $rating->rating_value($rating_value)->store if $rating
-}
-
-my $ratings = Koha::Ratings->search({ biblionumber => $biblionumber });
-my $my_rating = $ratings->search({ borrowernumber => $loggedinuser })->next;
-my $avg = $ratings->get_avg_rating;
-
-my %js_reply = (
- rating_total => $ratings->count,
- rating_avg => $avg,
- rating_avg_int => sprintf("%.0f", $avg),
- rating_value => $my_rating ? $my_rating->rating_value : undef,
- auth_status => $auth_status,
-
-);
-
-my $json_reply = JSON->new->encode( \%js_reply );
-
-#### $rating
-#### %js_reply
-#### $json_reply
-
-output_ajax_with_http_headers( $query, $json_reply );
-exit;
-
-# a ratings specific ajax return sub, returns CGI object, and an 'auth_success' value
-sub ajax_auth_cgi {
- my $needed_flags = shift;
- my %cookies = CGI::Cookie->fetch;
- my $input = CGI->new;
- my $sessid = $cookies{'CGISESSID'}->value || $input->param('CGISESSID');
- my ( $auth_status ) =
- check_cookie_auth( $sessid, $needed_flags );
- return $input, $auth_status;
-}
use utf8;
use Encode;
-use Test::More tests => 7;
+use Test::More tests => 8;
use Test::MockModule;
use Test::Mojo;
use Test::Warn;
$schema->storage->txn_rollback;
};
+
+subtest 'set_rating() tests' => sub {
+
+ plan tests => 12;
+
+ $schema->storage->txn_begin;
+
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ }
+ );
+ my $password = 'thePassword123';
+ $patron->set_password( { password => $password, skip_validation => 1 } );
+ $patron->discard_changes;
+ my $userid = $patron->userid;
+
+ my $biblio = $builder->build_sample_biblio();
+ $t->post_ok("/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
+ ->status_is(403);
+
+ $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
+ ->status_is(200)
+ ->json_is( '/rating', '3' )
+ ->json_is( '/average', '3' )
+ ->json_is( '/count', '1' );
+
+ $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => undef })
+ ->status_is(200)
+ ->json_is( '/rating', undef )
+ ->json_is( '/average', '0' )
+ ->json_is( '/count', '0' );
+
+ $schema->storage->txn_rollback;
+
+};