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 &rsaquo; Administration &rsaquo; Patron restrictions &rsaquo; [% 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> &rsaquo; <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> &rsaquo; [% IF op == 'add_form' %] <a href="/cgi-bin/koha/admin/restrictions.pl">Patron restrictions</a> &rsaquo; [% 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> &rsaquo; Confirm deletion of restriction '[% restriction.display_text | html %]'[% END %]
+[% IF op == 'delete_confirmed' %] <a href="/cgi-bin/koha/admin/categories.pl">Patron restrictions</a> &rsaquo; 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 &quot;[% restriction.display_text | html %]&quot;</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&amp;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&amp;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