@@ -, +, @@ checked out. Hit ENTER to save/send. specified for the branch which the item was checked out from. --- C4/Circulation.pm | 43 +++++++- circ/returns.pl | 3 + .../bug_14224-add_new_issue_columns.sql | 2 + installer/data/mysql/kohastructure.sql | 2 + .../intranet-tmpl/prog/en/modules/circ/returns.tt | 10 ++ .../bootstrap/en/modules/opac-sendissuenote.tt | 21 ++++ .../opac-tmpl/bootstrap/en/modules/opac-user.tt | 45 ++++++++ opac/opac-user.pl | 120 ++++++++++++++++++++- 8 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/bug_14224-add_new_issue_columns.sql create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-sendissuenote.tt --- a/C4/Circulation.pm +++ a/C4/Circulation.pm @@ -92,6 +92,7 @@ BEGIN { &GetBranchItemRule &GetBiblioIssues &GetOpenIssue + &SetIssueNote &AnonymiseIssueHistory &CheckIfIssuedToPatron &IsItemIssued @@ -2450,7 +2451,7 @@ sub GetOpenIssue { =head2 GetIssues $issues = GetIssues({}); # return all issues! - $issues = GetIssues({ borrowernumber => $borrowernumber, biblionumber => $biblionumber }); + $issues = GetIssues({ borrowernumber => $borrowernumber, biblionumber => $biblionumber, issue_id => $issue_id }); Returns all pending issues that match given criteria. Returns a arrayref or undef if an error occurs. @@ -2465,6 +2466,8 @@ Allowed criteria are: =item * itemnumber +=item * issue_id + =back =cut @@ -2474,7 +2477,7 @@ sub GetIssues { # Build filters my @filters; - my @allowed = qw(borrowernumber biblionumber itemnumber); + my @allowed = qw(borrowernumber biblionumber itemnumber issue_id); foreach (@allowed) { if (defined $criteria->{$_}) { push @filters, { @@ -3968,6 +3971,42 @@ sub GetPendingOnSiteCheckouts { |, { Slice => {} } ); } +=head2 SetIssueNote + + &SetIssueNote($issue_id, $note); + +Sets a note to the issuenotes table for the given issue. + +=over 4 + +=item C<$issue_id> is the id of the issue for which to set the note + +=item C<$note> is the note to set + +=back + +Returns: + True on success + False on failure + +=cut + +sub SetIssueNote { + my ( $issue_id, $note) = @_; + + my $dbh = C4::Context->dbh; + + if ( ! ( $issue_id && $note ) or not $issue_id =~ /\d+/) { + return; + } + + my $query = "UPDATE issues SET notedate=NOW(),note=? WHERE issue_id=?"; + my $sth = $dbh->prepare($query); + $sth->execute( $note, $issue_id ); + return 1; +} + + __END__ =head1 AUTHOR --- a/circ/returns.pl +++ a/circ/returns.pl @@ -269,6 +269,8 @@ if ($barcode) { my $hbr = GetBranchItemRule($biblio->{'homebranch'}, $itemtype)->{'returnbranch'} || "homebranch"; my $returnbranch = $biblio->{$hbr} ; + my $issue = GetItemIssue($itemnumber); + $template->param( title => $biblio->{'title'}, homebranch => $biblio->{'homebranch'}, @@ -281,6 +283,7 @@ if ($barcode) { biblionumber => $biblio->{'biblionumber'}, borrower => $borrower, additional_materials => $biblio->{'materials'}, + issue => $issue, ); my %input = ( --- a/installer/data/mysql/atomicupdate/bug_14224-add_new_issue_columns.sql +++ a/installer/data/mysql/atomicupdate/bug_14224-add_new_issue_columns.sql @@ -0,0 +1,2 @@ +ALTER TABLE issues ADD COLUMN `note` mediumtext default NULL; -- issue note text +ALTER TABLE issues ADD COLUMN `notedate` datetime default NULL; -- datetime of issue note (yyyy-mm-dd hh:mm::ss) --- a/installer/data/mysql/kohastructure.sql +++ a/installer/data/mysql/kohastructure.sql @@ -1156,6 +1156,8 @@ CREATE TABLE `issues` ( -- information related to check outs or issues `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, -- the date and time this record was last touched `issuedate` datetime default NULL, -- date the item was checked out or issued `onsite_checkout` int(1) NOT NULL default 0, -- in house use flag + `note` mediumtext default NULL, -- issue note text + `notedate` datetime default NULL, -- datetime of issue note (yyyy-mm-dd hh:mm::ss) PRIMARY KEY (`issue_id`), KEY `issuesborridx` (`borrowernumber`), KEY `itemnumber_idx` (`itemnumber`), --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt @@ -123,6 +123,16 @@ $(document).ready(function () {
This item is part of a rotating collection and needs to be transferred to [% collectionBranch %]
[% END %] + +[% IF ( issue.note) %] +
+

Patron note

+

[% issue.notedate %]

+

[% title |html %] [% author %]

+

[% issue.note %]

+
+[% END %] + [% IF ( fines ) %]
--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-sendissuenote.tt +++ a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-sendissuenote.tt @@ -0,0 +1,21 @@ + +Issue note for [% title %] [% author %] + + +[% USE HtmlToText %] + +
+[% FILTER html2text %] +

Hi,

+

[% MEMBER.firstname %] [% MEMBER.surname %] sent you the following note related to the check out of +

[% title %] [% author %].

+[% END %] + + + + +[% FILTER html2text %] + [% note %] +[% END %] + + --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt +++ a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt @@ -98,6 +98,8 @@
[% END # / IF patron_flagged %] + + [% SET OPACMySummaryNote = Koha.Preference('OPACMySummaryNote') %] [% IF OPACMySummaryNote %][% OPACMySummaryNote %][% END %] @@ -142,6 +144,7 @@ [% IF ( OPACMySummaryHTML ) %] Links [% END %] + Note @@ -254,6 +257,9 @@ [% IF ( OPACMySummaryHTML ) %] [% ISSUE.MySummaryHTML %] [% END %] + + + [% END # /FOREACH ISSUES %] @@ -732,6 +738,45 @@ [% END %] [% END %] + $("input[name='note']").keyup(function(e){ + var self = $(this); + if(e.which == 13) { + e.preventDefault(); + if(!self.val()) return; + + var ajaxData = { + 'issue_id': $(this).data('issue_id'), + 'note': self.val() + }; + + $.ajax({ + url: '/cgi-bin/koha/opac-user.pl', + type: 'POST', + dataType: 'json', + data: ajaxData, + }) + .done(function(data) { + if(data.status == 'success') { + $("#notesaved").removeClass("alert-error"); + $("#notesaved").addClass("alert-info"); + $("#notesaved").html("Note saved and sent to library."); + } else { + $("#notesaved").removeClass("alert-info"); + $("#notesaved").addClass("alert-error"); + $("#notesaved").html(data.error); + } + }) + .fail(function(data) { + $("#notesaved").removeClass("alert-info"); + $("#notesaved").addClass("alert-error"); + $("#notesaved").html("Ajax request failed. Could not save note."); + }) + .always(function() { + $("#notesaved").show(); + }); + } + }); + $( ".suspend-until" ).datepicker({ minDate: 1 }); // Require that "until date" be in the future }); //]]> --- a/opac/opac-user.pl +++ a/opac/opac-user.pl @@ -22,18 +22,24 @@ use strict; use CGI qw ( -utf8 ); -use C4::Auth; +use Mail::Sendmail; +use MIME::QuotedPrint; +use Carp; +use C4::Auth qw(:DEFAULT check_cookie_auth); use C4::Koha; use C4::Circulation; use C4::Reserves; use C4::Members; use C4::Members::AttributeTypes; use C4::Members::Attributes qw/GetBorrowerAttributeValue/; -use C4::Output; +use CGI::Cookie; # need to check cookies before having CGI parse the POST request +use C4::Output qw(:DEFAULT :ajax); +use C4::Scrubber; use C4::Biblio; use C4::Items; use C4::Letters; use C4::Branch; # GetBranches +use Koha::Email; use Koha::DateUtils; use Koha::Borrower::Debarments qw(IsDebarred); @@ -54,6 +60,116 @@ BEGIN { } } +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; + my ($auth_status, $auth_sessid) = check_cookie_auth($sessid, $needed_flags); + if ($auth_status ne "ok") { + output_with_http_headers $input, undef, + "window.alert('Your CGI session cookie ($sessid) is not current. " . + "Please refresh the page and try again.');\n", 'js'; + exit 0; + } + return $input; +} + +my $is_ajax = is_ajax(); +my $query = ($is_ajax) ? &ajax_auth_cgi({}) : CGI->new(); +if ($is_ajax) { + my $scrubber = C4::Scrubber->new(); + my $note = $query->param('note'); + my $issue_id = $query->param('issue_id'); + my $clean_note = $scrubber->scrub($note); + my $status = "success"; + my $error = ""; + my ($error, $member, $issue); + + my ( $template, $borrowernumber, $cookie ) = get_template_and_user( + { + template_name => "opac-sendissuenote.tt", + query => $query, + type => "opac", + authnotrequired => 1, + } + ); + + # verify issue_id + if ( $issue_id =~ /\d+/ ) { + $member = GetMember(borrowernumber => $borrowernumber); + ($issue) = @{C4::Circulation::GetIssues({issue_id => $issue_id})}; + + if ( $issue->{'borrowernumber'} != $borrowernumber ) { + $status = "fail"; + $error = "Invalid issue id!"; + } + } else { + $status = "fail"; + $error = "Invalid issue id!"; + } + + if ( (not $error) && SetIssueNote($issue_id, $clean_note) ) { + my $branch = GetBranchDetail($issue->{'branchcode'}); + + if ( $branch->{'branchemail'} ) { + my $biblio = GetBiblioFromItemNumber($issue->{'itemnumber'}); + my $message = Koha::Email->new(); + my %mail = $message->create_message_headers(); + + $template->param( + author => $biblio->{'author'}, + title => $biblio->{'title'}, + MEMBER => $member, + note => $clean_note, + ); + + # Getting template result + my $template_res = $template->output(); + my $body; + + # Analysing information and getting mail properties + if ( $template_res =~ /(.*)/s ) { + $mail{subject} = $1; + $mail{subject} =~ s|\n?(.*)\n?|$1|; + } + else { $mail{'subject'} = "no subject"; } + $mail{subject} = Encode::encode("UTF-8", $mail{subject}); + + my $email_header = ""; + if ( $template_res =~ /
(.*)/s ) { + $email_header = $1; + $email_header =~ s|\n?(.*)\n?|$1|; + $email_header = encode_qp(Encode::encode("UTF-8", $email_header)); + } + + if ( $template_res =~ /(.*)/s ) { + $body = $1; + $body =~ s|\n?(.*)\n?|$1|; + $body = encode_qp(Encode::encode("UTF-8", $body)); + } + + $mail{to} = $branch->{'branchemail'}; + $mail{from} = $member->{'email'} || ($branch->{'branchemail'}=~s/^.+@/noreply@/ && $branch->{'branchemail'}); + $mail{body} = $email_header . $body; + + unless ( sendmail(%mail) ) { + $status = "fail"; + $error = "Could not send message to library. Message will still show at check in."; + carp "Error sending mail: $Mail::Sendmail::error \n"; + } + } + } else { + $status = "fail"; + $error = "Could not save note. Invalid issue id or empty note."; + } + + my $response = "{\"status\": \"$status\", \"note\": \"$clean_note\", \"issue_id\": \"$issue_id\", \"error\": \"$error\"}"; + output_with_http_headers($query, undef, $response, 'js'); + exit; +} + + my ( $template, $borrowernumber, $cookie ) = get_template_and_user( { template_name => "opac-user.tt", --