From c496fd334f525dc906535ac3ec65078555ebae2a Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Tue, 24 Jan 2017 13:31:30 +0100 Subject: [PATCH] Bug 17981: Add a preview mode for notice templates This patch is a first step to provide a preview mode for notice templates. CHECKIN, CHECKOUT and HOLD_SLIP are supported so far. Maybe more, but I have not tested yet and the interface will not allow you to generate the preview. The idea is to provide an idea of how will render the messages generated from a notice template. A new "Preview" button is added close to each textarea on the editing notice templates view. For each notice template code (letter_code), we will need some input data to produce the preview. For instance, for CHECKIN we need an barcode. From the barcode we can guess all the other data. For CHECKOUT we will need the borrowernumber and the barcode. Note that the way to enter the data for the preview is not really user-friendly, for CHECKOUT you will have to fill 'borrowernumber|barcode', but the placeholder will help you to know how and what to fill. In the modal window, you will see 4 blocks: 1/ the content of the letter (with the placeholds << >>) 2/ the generated message (with the data filled) 3/ if the letter contained historical syntax markers, the screen will try to generate a notice template using the TT syntax 4/ the generated message from this TT syntax => You will be able to compare the 2 generated messages. What is the goal of this first patchset: - Show this first POC and get feedback from other developpers - Add a way to easily visualise the differences between the 2 syntaxes - Confort users with the TT syntax and the migration step from the historical syntax. I'd like to get opinions before going further. The possibilities: - Mock data to get fully working generated messages for any notice templates. For instance, for CHECKIN and CHECKOUT, the item is not checked in/out yet. So we cannot access the issue's information. (I have no idea how to do that) - Browse the data to get the ones we want to use for the preview (big). Signed-off-by: Kyle M Hall --- C4/Letters.pm | 2 +- .../intranet-tmpl/prog/en/modules/tools/letter.tt | 101 ++++++++- .../prog/en/modules/tools/preview_letter.tt | 52 +++++ svc/letters/preview | 248 +++++++++++++++++++++ tools/letter.pl | 4 + 5 files changed, 400 insertions(+), 7 deletions(-) create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/preview_letter.tt create mode 100644 svc/letters/preview diff --git a/C4/Letters.pm b/C4/Letters.pm index 464fcc0..c33e11d 100644 --- a/C4/Letters.pm +++ b/C4/Letters.pm @@ -881,7 +881,7 @@ sub _parseletter { my $values = $values_in ? { %$values_in } : {}; if ( $table eq 'borrowers' && $values->{'dateexpiry'} ){ - $values->{'dateexpiry'} = format_sqldatetime( $values->{'dateexpiry'} ); + $values->{'dateexpiry'} = output_pref({ str => $values->{dateexpiry}, dateonly => 1 }); } if ( $table eq 'reserves' && $values->{'waitingdate'} ) { diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt index 1e549e0..e9f33dd 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt @@ -123,6 +123,57 @@ $(document).ready(function() { $("#submit_form").click(); }); + $("body").on("click", ".preview_template", function(e){ + e.preventDefault(); + var mtt = $(this).data("mtt"); + var lang = $(this).data("lang"); + + var code = $("#code").val(); + var content = $("#content_"+mtt+"_"+lang).val(); + var title = $("#title_"+mtt+"_"+lang).val(); + + var is_html = $("#is_html_"+mtt+"_"+lang).val(); + var page = $(this).attr("href"); + var data_preview = $("#data_preview").val(); + page += '?code='+encodeURIComponent(code); + page += '&title='+encodeURIComponent(title); + page += '&content='+encodeURIComponent(content); + page += '&data_preview='+encodeURIComponent(data_preview); + page += '&is_html='+encodeURIComponent(is_html); + $("#preview_template .modal-body").load(page + " div"); + $('#preview_template').modal('show'); + $("#preview_template_button").attr("href", "/cgi-bin/koha/svc/letters/convert?module="+module+"&code="+code+"&mtt="+mtt+"&lang="+lang); + }); + $("#preview_template").on("hidden", function(){ + $("#preview_template_label").html(""); + $("#preview_template .modal-body").html("
\"\" "+_("Loading")+"
"); + }); + $("body").on("click", "#convert_template", function(e){ + e.preventDefault(); + var mtt = $(this).data("mtt"); + var lang = $(this).data("lang"); + + var code = $("#code").val(); + var content = $("#content_"+mtt+"_"+lang).val(); + var title = $("#title_"+mtt+"_"+lang).val(); + + var is_html = $("#is_html_"+mtt+"_"+lang).val(); + var page = $(this).attr("href"); + var data_preview = $("#data_preview").val(); + page += '?code='+encodeURIComponent(code); + page += '&title='+encodeURIComponent(title); + page += '&content='+encodeURIComponent(content); + page += '&data_preview='+encodeURIComponent(data_preview); + page += '&is_html='+encodeURIComponent(is_html); + $("#preview_template .modal-body").load(page + " div"); + $('#preview_template').modal('show'); + $("#preview_template_button").attr("href", "/cgi-bin/koha/svc/letters/convert?module="+module+"&code="+code+"&branchcode="+branchcode+"&mtt="+mtt+"&lang="+lang); + }); + $("#convert_template").on("hidden", function(){ + $("#convert_template_label").html(""); + $("#convert_template .modal-body").html("
\"\" "+_("Loading")+"
"); + }); + }); [% IF add_form or copy_form %] @@ -172,6 +223,24 @@ $(document).ready(function() { + + [% IF add_form or copy_form %]
[% ELSE %]
[% END %]
@@ -399,6 +468,21 @@ $(document).ready(function() { Required + [% IF code and preview_is_available%] +
  • + + [% SWITCH code %] + [% CASE 'CHECKIN' %] + + [% CASE 'CHECKOUT' %] + + [% CASE 'HOLD_SLIP' %] + + [% CASE %] + Not supported yet. + [% END %] +
  • + [% END %] [% IF Koha.Preference('TranslateNotices') %] @@ -454,23 +538,23 @@ $(document).ready(function() { [% IF letter.is_html %] - + [% ELSE %] - + [% END %]
  • - +
  • - + [% IF letter.message_transport_type == 'sms' %] [% IF letter.content && letter.content.length > 0 %][% letter.content.length %][% ELSE %]0[% END %]/160 characters [% END %] - +
    - [% FOREACH SQLfieldname IN SQLfieldnames %] [% END %] @@ -479,10 +563,15 @@ $(document).ready(function() {
  • + [% IF preview_is_available %] +
  • + Preview +
  • + [% END %] [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/preview_letter.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/preview_letter.tt new file mode 100644 index 0000000..f7aea70 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/preview_letter.tt @@ -0,0 +1,52 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Tools › Preview notice template + + + +
    + [% FOR m IN messages %] + [%# FIXME The message block does not appear at the top of the modal! %] +
    + [% SWITCH m.code %] + [% CASE 'no_data_for_preview' %]You did not specify data for preview. + [% CASE 'preview_not_available' %]Preview is not available for letters '[% m.letter_code %]'. + [% CASE 'not_checked_in_yet' %]Do not forget that the issue has not been checked in yet. + [% CASE 'not_checked_out_yet' %]Do not forget that the issue has not been checked out yet. + [% CASE %][% m.code %] + [% END %] +
    + [% END %] + [% IF rendered_message %] +
    + Original version +
    [% original_content | html %]
    +
    + +
    + Rendered message: +
    [% rendered_message.content | html %]
    +
    + [% END %] + + [% IF tt_content %] +
    + Converted version +
    [% tt_content | html %]
    +
    + +
    + Rendered message: +
    [% rendered_tt_message.content | html %]
    +
    + [% END %] +
    + + [% IF tt_content %] + [% IF messages_are_similar %] +
    The generated notices are exactly the same!
    + [% ELSE %] +
    The generated notices are different!
    + [% END %] + [% END %] + + diff --git a/svc/letters/preview b/svc/letters/preview new file mode 100644 index 0000000..d517398 --- /dev/null +++ b/svc/letters/preview @@ -0,0 +1,248 @@ +#!/usr/bin/perl + +# Copyright 2016 Koha Development Team +# +# 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 . + +use Modern::Perl; +use CGI qw( -utf8 ); +use C4::Auth; +use C4::Context; +use C4::Output; +use C4::Circulation; +use C4::Letters; +use Koha::Checkouts; +use Koha::Items; +use Koha::Patrons; + +my $input = new CGI; + +my ( $template, $borrowernumber, $cookie ) = get_template_and_user( + { + template_name => "tools/preview_letter.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { tools => 'edit_notices' }, + debug => 1, + } +); + +my @messages; +my $code = $input->param('code'); +my $content = $input->param('content'); +my $title = $input->param('title'); +my $is_html = $input->param('is_html'); +my $data_preview = $input->param('data_preview'); + +unless ( $data_preview ) { + $template->param( messages => [{ code => 'no_data_for_preview', type => 'error' }]); + output_html_with_http_headers $input, $cookie, $template->output; + exit; +} + +my $fake_letter = { content => $content, title => $title, is_html => $is_html }; + +my ( $tt_content, $fake_tt_letter ); +if ( $content =~ m/[^\n]*<<.*>>[^\n]*/so ) { + $tt_content = $content; + + my $table_mapping = { + biblio => 'biblio', + borrowers => 'borrower', + branches => 'branch', + items => 'item', + opac_news => 'news', + aqorders => 'orders', + reserves => 'hold', + serial => 'serial', + subscription => 'subscription', + suggestions => 'suggestion', + issues => 'checkout', + old_issues => 'old_checkout', + overdues => 'overdue', + borrower_modifications => 'patron_modification', + }; + + # Today + $tt_content =~ s#<>#[% today| \$KohaDates with_hours => 1 %]#sg; + + + for my $date_field ( qw( + borrowers.dateofbirth + borrowers.dateenrolled + borrowers.dateexpiry + borrowers.debarred + items.dateaccessioned + items.datelastborrowed + items.datelastseen + items.onloan + serials.planneddate + serials.publisheddate + serials.claimdate + reserves.reservedate + reserves.waitingdate + reserves.expirationdate + suggestions.suggesteddate + suggestions.manageddate + suggestions.accepteddate + suggestions.rejecteddate + aqorders.entrydate + aqorders.datereceived + aqorders.datecancellationprinted + aqorders.budgetdate + aqorders.claimed_date + ) ) { + my ( $table, $field ) = split '\.', $date_field; + my $new_field = + exists $table_mapping->{$table} + ? $table_mapping->{$table} . ".$field" + : "$table.$field"; + $tt_content =~ s#<<$table\.$field>>#[% $new_field | \$KohaDates %]#sg; + $tt_content =~ s#<<$table\.$field\s*|\s*dateonly>>#[% $new_field | \$KohaDates %]#sg; + } + + for my $datetime_field ( qw( + items.itemlost_on + items.withdrawn_on + issues.date_due + issues.returndate + issues.lastreneweddate + issues.issuedate + reserves.suspend_until + ) ) { + my ( $table, $field ) = split '\.', $datetime_field; + my $new_field = + exists $table_mapping->{$table} + ? $table_mapping->{$table} . ".$field" + : "$table.$field"; + $tt_content =~ s#<<$table\.$field>>#[% $new_field | \$KohaDates with_hours => 1 %]#sg; + $tt_content =~ s#<<$table\.$field\s*|\s*dateonly>>#[% $new_field | \$KohaDates %]#sg; + } + + + + while ( my ( $key, $value ) = each %$table_mapping ) { + $tt_content =~ s|<<$key\.|<<$value.|sg; + } + + $tt_content =~ s|<<|[% |sg; + $tt_content =~ s|>>| %]|sg; + $fake_tt_letter = + { content => $tt_content, title => $title, is_html => $is_html }; +} + +my ( $rendered_message, $rendered_tt_message ) = (q||) x 2; +my $messages_are_similar; +my $letter_params = {}; +if ( $code eq 'CHECKIN' ) { + my $item = Koha::Items->find( { barcode => $data_preview } ); + my $checkout = Koha::Checkouts->find( { itemnumber => $item->itemnumber } ); + if ($checkout) { + my $patron = Koha::Patrons->find( $checkout->borrowernumber ); + my $branchcode = + C4::Circulation::_GetCircControlBranch( $item->unblessed, + $patron->unblessed ); + $letter_params = { + tables => { + issues => $item->itemnumber, + items => $item->itemnumber, + biblio => $item->biblionumber, + biblioitems => $item->biblionumber, + issues => $patron->borrowernumber, + branches => $branchcode, + } + }; + push @messages, { code => 'not_checked_in_yet', type => 'message' }; + } + else { + warn "No checkout"; + } +} +elsif ( $code eq 'CHECKOUT' ) { + my ( $barcode, $borrowernumber ) = split '\|', $data_preview; + my $item = Koha::Items->find( { barcode => $barcode } ); + my $patron = Koha::Patrons->find( $borrowernumber ); + if ($item and $patron) { + my $branchcode = + C4::Circulation::_GetCircControlBranch( $item->unblessed, + $patron->unblessed ); + $letter_params = { + tables => { + issues => $item->itemnumber, + items => $item->itemnumber, + biblio => $item->biblionumber, + biblioitems => $item->biblionumber, + issues => $patron->borrowernumber, + branches => $branchcode, + } + }; + push @messages, { code => 'not_checked_out_yet', type => 'message' }; + } + else { + warn "No item or no patron"; + } +} +elsif ( $code eq 'HOLD_SLIP' ) { + my ( $biblionumber, $borrowernumber ) = split '\|', $data_preview; + my $hold = Koha::Holds->find( { borrowernumber => $borrowernumber, biblionumber => $biblionumber } ); + if ($hold) { + $letter_params = { + tables => { + reserves => $hold->unblessed, + branches => $hold->branchcode, + borrowers => $hold->borrowernumber, + biblio => $hold->biblionumber, + biblioitems => $hold->biblionumber, + items => $hold->itemnumber, + } + }; + } + else { + warn "No hold placed by this patron on this bibliographic record."; + } +} +else { + warn "Preview for letter code $code is not available"; + push @messages, { type => 'alert', code => 'preview_not_available', letter_code => $code, }; +} + +if ( %$letter_params ) { + # FIXME Be case here GetPreparedLetter modify $fake_letter + $rendered_message = C4::Letters::GetPreparedLetter( + letter => $fake_letter, + %$letter_params, + ); + if ($tt_content) { + $rendered_tt_message = C4::Letters::GetPreparedLetter( + letter => $fake_tt_letter, + %$letter_params, + ); + } + $messages_are_similar = + $rendered_message->{content} eq $rendered_tt_message->{content}; +} + +$template->param( + original_content => $content, + rendered_message => $rendered_message, + tt_content => $tt_content, + rendered_tt_message => $rendered_tt_message, + messages_are_similar => $messages_are_similar, + messages => \@messages, +); + +output_html_with_http_headers $input, $cookie, $template->output; diff --git a/tools/letter.pl b/tools/letter.pl index d0c52c2..68510cf 100755 --- a/tools/letter.pl +++ b/tools/letter.pl @@ -268,10 +268,14 @@ sub add_form { } } + my $preview_is_available = grep {/^$code$/} qw( + CHECKIN CHECKOUT HOLD_SLIP + ); $template->param( module => $module, SQLfieldnames => $field_selection, branchcode => $branchcode, + preview_is_available => $preview_is_available, ); return; } -- 2.10.2