From 22c83e10fcac1537a5f492efe86334192fc60f54 Mon Sep 17 00:00:00 2001 From: Andrew Isherwood <andrew.isherwood@ptfs-europe.com> Date: Wed, 2 Oct 2019 16:41:07 +0100 Subject: [PATCH] Bug 23681: Add management UI This patch adds UI to allow CRUD operations on restriction types --- admin/restrictions.pl | 112 +++++++++++++ .../intranet-tmpl/prog/css/src/staff-global.scss | 6 + .../prog/en/modules/admin/restrictions.tt | 174 +++++++++++++++++++++ .../intranet-tmpl/prog/js/restrictiontypes.js | 54 +++++++ 4 files changed, 346 insertions(+) create mode 100755 admin/restrictions.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/restrictions.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/js/restrictiontypes.js diff --git a/admin/restrictions.pl b/admin/restrictions.pl new file mode 100755 index 0000000000..e865259fe6 --- /dev/null +++ b/admin/restrictions.pl @@ -0,0 +1,112 @@ +#!/usr/bin/perl + +# Copyright 2019 PTFS Europe +# +# 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>. + +use Modern::Perl; + +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Output; +use Koha::RestrictionTypes; + +my $input = new CGI; +my $op = $input->param('op') // 'list'; +my $code = uc $input->param('code'); +my @messages = (); + + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "admin/restrictions.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { parameters => 'manage_patron_restrictions' }, + debug => 1, + } +); + +if ( $op eq 'add_form') { + # Get all existing restrictions, so we can do client-side validation + $template->param( + existing => scalar Koha::RestrictionTypes->search() + ); + if ($code) { + $template->param( + restriction => scalar Koha::RestrictionTypes->find($code) + ); + } +} elsif ( $op eq 'add_validate' ) { + + my $display_text = $input->param('display_text'); + my $is_a_modif = $input->param("is_a_modif"); + + if ($is_a_modif) { + # Check whether another restriction already has this display text + my $dupe = Koha::RestrictionTypes->find({ + display_text => $display_text + }); + if ($dupe) { + push @messages, { + type => 'error', code => 'duplicate_display_text' + }; + } else { + my $restriction = Koha::RestrictionTypes->find($code); + $restriction->display_text($display_text); + $restriction->store; + } + } else { + # Check whether another restriction already has this code + my $dupe = Koha::RestrictionTypes->find($code); + if ($dupe) { + push @messages, { + type => 'error', code => 'duplicate_code' + }; + } else { + my $restriction = Koha::RestrictionType->new({ + code => $code, + display_text => $display_text + }); + $restriction->store; + } + } + $op = 'list'; +} elsif ( $op eq 'delete_confirm' ) { + $template->param( + restriction => scalar Koha::RestrictionTypes->find($code) + ); +} elsif ( $op eq 'delete_confirmed' ) { + Koha::RestrictionTypes->find($code)->delete; + $op = 'list'; +} + +$template->param( + messages => \@messages, + op => $op +); + +if ( $op eq 'list' ) { + my $restrictions = Koha::RestrictionTypes->search(); + $template->param( + restrictions => $restrictions, + ) +} + +output_html_with_http_headers $input, $cookie, $template->output; + +exit 0; diff --git a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss index 1e6c23282a..d5b7ad3270 100644 --- a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss +++ b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss @@ -3843,6 +3843,12 @@ input.renew { background-color: rgba(0, 0, 0, 0.1); } +#restriction_form { + .type_input { + text-transform: uppercase; + } +} + #stage_list_headings { font-weight: bold; span { diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/restrictions.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/restrictions.tt new file mode 100644 index 0000000000..6e2353b5a9 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/restrictions.tt @@ -0,0 +1,174 @@ +[% USE raw %] +[% USE Asset %] +[% USE Koha %] +[% SET footerjs = 1 %] +[% INCLUDE 'doc-head-open.inc' %] +<title>Koha › Administration › Patron restrictions › [% IF op == 'add_form' %][% IF ( restriction ) %]Modify restriction '[% restriction.display_text | html %]'[% ELSE %]New restriction[% END %][% END %] +[% IF op == 'delete_confirm' %]Confirm deletion of restriction '[% restriction.display_text | html %]'[% END %] +</title> +[% INCLUDE 'doc-head-close.inc' %] +</head> + +<body id="admin_restrictions" class="admin"> +[% INCLUDE 'header.inc' %] +[% INCLUDE 'patrons-admin-search.inc' %] + +<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> › [% IF op == 'add_form' %] <a href="/cgi-bin/koha/admin/restrictions.pl">Patron restrictions</a> › [% IF ( restriction ) %]Modify restriction '[% restriction.display_text | html %]'[% ELSE %]New restriction[% END %][% END %] +[% IF op == 'delete_confirm' %] <a href="/cgi-bin/koha/admin/restrictions.pl">Patron restrictions</a> › Confirm deletion of restriction '[% restriction.display_text | html %]'[% END %] +[% IF op == 'delete_confirmed' %] <a href="/cgi-bin/koha/admin/categories.pl">Patron restrictions</a> › Restriction deleted[% END %] +[% IF op == 'list' %]Patron restrictions[% END %]</div> + +<div class="main container-fluid"> + <div class="row"> + <div class="col-sm-10 col-sm-push-2"> + <main> + +[% FOR m IN messages %] + <div class="dialog [% m.type | html %]"> + [% SWITCH m.code %] + [% CASE 'duplicate_display_text' %] + Another restriction already has this label + [% CASE 'duplicate_code' %] + Another restriction already has this code + [% CASE %] + [% m.code | html %] + [% END %] + </div> +[% END %] + +[% IF op == 'add_form' %] + <form id="restriction_form" action="/cgi-bin/koha/admin/restrictions.pl" method="post"> + <input type="hidden" name="op" value="add_validate" /> + <input type="hidden" name="checked" value="0" /> + [% IF restriction %] + <h1>Modify restriction [% restriction.display_text | html %]</h1> + <input type="hidden" name="is_a_modif" value="1" /> + [% ELSE %] + <h1>New restriction</h1> + [% END %] + <fieldset class="rows"> + <ol> + [% IF restriction %] + <li> + <span class="label">Code: </span>[% restriction.code | html %] + <input type="hidden" name="code" value="[% restriction.code | html %]" /> + </li> + <li> + <label for="display_text" class="required">Label: </label> + <input type="text" value="[% restriction.display_text | html %]" name="display_text" id="display_text" size="50" maxlength="50" class="required" required="required" /> + <span class="required">Required</span> + </li> + [% ELSE %] + <li> + <label for="code" class="required">Code: </label> + <input type="text" name="code" id="code" size="50" maxlength="50" class="required type_input " required="required" /> + <span class="required">Required</span> + </li> + <li> + <label for="display_text" class="required">Label: </label> + <input type="text" name="display_text" id="display_text" size="50" maxlength="50" class="required" required="required" /> + <span class="required">Required</span> + </li> + [% END %] + </ol> + </fieldset> + + <fieldset class="action"> + <input type="submit" value="Save" /> + <a href="/cgi-bin/koha/admin/restrictions.pl" class="cancel">Cancel</a> + </fieldset> + </form> +[% END %] + +[% IF op == 'delete_confirm' %] + <form action="/cgi-bin/koha/admin/restrictions.pl" method="post"> + <fieldset> + <legend> + Confirm restriction deletion + </legend> + + <p>Are you sure you want to delete "[% restriction.display_text | html %]"</p> + + <fieldset class="action"> + <input type="hidden" name="op" value="delete_confirmed" /> + <input type="hidden" name="code" value="[% restriction.code | html %]" /> + <input type="submit" value="Delete this restriction" /> + <a class="cancel" href="/cgi-bin/koha/admin/restrictions.pl">Cancel</a> + </fieldset> + </fieldset> + </form> +[% END %] + +[% IF op == 'list' %] + + <div id="toolbar" class="btn-toolbar"> + <a class="btn btn-default" id="newrestriction" href="/cgi-bin/koha/admin/restrictions.pl?op=add_form"><i class="fa fa-plus"></i> New restriction</a> + </div> + + <h2>Patron restrictions</h2> + [% IF searchfield %] + You Searched for [% searchfield | html %]</span> + [% END %] + [% IF restrictions %] + <table id="restriction_types"> + <thead> + <tr> + <th scope="col">Code</th> + <th scope="col">Label</th> + <th scope="col">Default</th> + <th scope="col">Actions</th> + </tr> + </thead> + <tbody> + [% FOREACH restriction IN restrictions %] + <tr> + <td> + [% restriction.code | html %] + </td> + <td> + [% restriction.display_text | html %] + </td> + <td> + [% IF restriction.dflt %]Yes[% END %] + </td> + <td class="actions"> + <a class="btn btn-default btn-xs" href="/cgi-bin/koha/admin/restrictions.pl?op=add_form&code=[% restriction.code | uri %]"><i class="fa fa-pencil"></i> Edit</a> + [% IF !restriction.ronly %] + <a class="btn btn-default btn-xs" href="/cgi-bin/koha/admin/restrictions.pl?op=delete_confirm&code=[% restriction.code | uri %]"><i class="fa fa-trash"></i> Delete</a> + [% END %] + </td> + </tr> + [% END %] + </tbody> + </table> + [% ELSE %] + <div class="dialog alert">No restrictions have been defined. <a href="/cgi-bin/koha/admin/restrictions.pl?op=add_form">Create a new restriction</a>.</div> + [% END %] +[% END %] + + </main> + </div> <!-- /.col-sm-10.col-sm-push-2 --> + + <div class="col-sm-2 col-sm-pull-10"> + <aside> + [% INCLUDE 'admin-menu.inc' %] + </aside> + </div> <!-- /.col-sm-2.col-sm-pull-10 --> + </div> <!-- /.row --> + +[% MACRO jsinclude BLOCK %] + [% Asset.js("js/admin-menu.js") | $raw %] + [% INCLUDE 'datatables.inc' %] + [% INCLUDE 'columns_settings.inc' %] + <script> + var MSG_DUPLICATE_CODE = _("Restriction code is already in use"); + var MSG_DUPLICATE_DISPLAY_TEXT = _("Label is already in use"); + var existing = { + [% FOREACH ex IN existing %] + [% ex.code | $raw %]: '[% ex.display_text | $raw %]', + [% END %] + }; + </script> + [% Asset.js("js/restrictiontypes.js") | $raw %] +[% END %] +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/js/restrictiontypes.js b/koha-tmpl/intranet-tmpl/prog/js/restrictiontypes.js new file mode 100644 index 0000000000..d1a4e5383e --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/restrictiontypes.js @@ -0,0 +1,54 @@ +jQuery.validator.addMethod( "restrictionCode", function(value){ + var ex = Object.keys(existing); + return (value.length > 0 && ex.indexOf(value.toUpperCase()) > -1) ? + false : + true; +}, MSG_DUPLICATE_CODE); + +jQuery.validator.addMethod( "restrictionDisplayText", function(value){ + var ex = Object.values(existing).map(function(el) { + return el.toLowerCase(); + }); + return (value.length > 0 && ex.indexOf(value.toLowerCase()) > -1) ? + false : + true; +}, MSG_DUPLICATE_DISPLAY_TEXT); + +$(document).ready(function() { + KohaTable("restriction_types", { + "aoColumnDefs": [{ + "aTargets": [-1], + "bSortable": false, + "bSearchable": false + }, { + "aTargets": [0, 1], + "sType": "natural" + }, ], + "aaSorting": [ + [1, "asc"] + ], + "sPaginationType": "full", + "exportColumns": [0,1], + }); + + $("#restriction_form").validate({ + rules: { + code: { + required: true, + restrictionCode: true + }, + display_text: { + required: true, + restrictionDisplayText: true + } + }, + messages: { + code: { + restrictionCode: MSG_DUPLICATE_CODE + }, + display_text: { + restrictionDisplayText: MSG_DUPLICATE_DISPLAY_TEXT + } + } + }); +}); -- 2.11.0