Bug 4461: Manage problem reports on the staff client
authorAleisha Amohia <aleishaamohia@hotmail.com>
Wed, 11 Mar 2020 01:52:27 +0000 (01:52 +0000)
committerMartin Renvoize <martin.renvoize@ptfs-europe.com>
Mon, 6 Apr 2020 10:18:25 +0000 (11:18 +0100)
Test plan:

- Update database and upgrade schema files (if you haven't already).
Restart memcached
- Check your user's permissions and ensure the 'problem_reports'
permission is ticked. Confirm the OPACReportProblem syspref is enabled
- Log into the OPAC and submit a problem report
- Log into the staff client
- You should see a box at the bottom of the main page showing your
pending problem report
- Click the link and confirm it takes you to the new page for managing
problem reports
- Go to Administration
- Confirm you can see a link to 'OPAC problem reports' under the
'Additional parameters' heading
- Click 'OPAC problem reports'
- Confirm your problem report is showing in the table
- Open the OPAC in another tab and submit at least two more problem
reports (so you should have at least three in the table after
refreshing)
- Try the different buttons
    - selecting multiple problem reports and using the big 'mark
    viewed', 'mark closed', 'mark new' buttons. Confirm there are no
    failures and that the number of selected problem reports is correct
    - select all, clear all, hide viewed, hide closed, hide new, show
    all
    - individual 'mark viewed', 'mark closed', 'mark new' buttons for
    each problem report. Confirm the status shows and the correct button
    is disabled while others are enabled
- Confirm the problem page link works as expected

Sponsored-by: Catalyst IT
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

admin/problem-reports.pl [new file with mode: 0755]
koha-tmpl/intranet-tmpl/prog/en/modules/admin/problem-reports.tt [new file with mode: 0644]
svc/problem_reports [new file with mode: 0755]

diff --git a/admin/problem-reports.pl b/admin/problem-reports.pl
new file mode 100755 (executable)
index 0000000..659a6e0
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+
+# Copyright 2020 Aleisha Amohia <aleisha@catalyst.net.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>.
+
+use Modern::Perl;
+
+use CGI qw ( -utf8 );
+use C4::Context;
+use C4::Output;
+use C4::Auth;
+use Koha::ProblemReports;
+
+my $query = new CGI;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "admin/problem-reports.tt",
+        query           => $query,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { problem_reports => 1 },
+    }
+);
+
+my $action;
+foreach (qw( viewed closed new )) {
+    $action = $_ if ( $query->param("mark_selected-$_") );
+}
+$action ||= 'none';
+
+my @report_ids = $query->multi_param('report_ids');
+
+if ( $action eq 'viewed' ) {
+    foreach my $report_id ( @report_ids ) {
+        my $report = Koha::ProblemReports->find($report_id);
+        $report->set({ status => 'V' })->store;
+                                }
+} elsif ( $action eq 'closed' ) {
+    foreach my $report_id ( @report_ids ) {
+        my $report = Koha::ProblemReports->find($report_id);
+        $report->set({ status => 'C' })->store;
+    }
+
+} elsif ( $action eq 'new' ) {
+    foreach my $report_id ( @report_ids ) {
+        my $report = Koha::ProblemReports->find($report_id);
+        $report->set({ status => 'N' })->store;
+    }
+}
+
+my $problem_reports = Koha::ProblemReports->search();
+$template->param(
+    selected_count  => scalar(@report_ids),
+    action          => $action,
+    problem_reports => $problem_reports,
+);
+
+output_html_with_http_headers $query, $cookie, $template->output;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/problem-reports.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/problem-reports.tt
new file mode 100644 (file)
index 0000000..4d6faf0
--- /dev/null
@@ -0,0 +1,235 @@
+[% USE raw %]
+[% USE Asset %]
+[% USE Koha %]
+[% USE KohaDates %]
+[% SET footerjs = 1 %]
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Administration &rsaquo; OPAC problem reports</title>
+[% INCLUDE 'doc-head-close.inc' %]
+</head>
+
+<body id="admin_problem-reports" class="admin">
+
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'prefs-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;
+    OPAC problem reports
+</div>
+
+<div class="main container-fluid">
+    <div class="row">
+        <div class="col-sm-10 col-sm-push-2">
+            <main>
+
+            <h1>OPAC problem reports</h1>
+
+            <div class="dialog alert" id="error" style="display:none;"></div>
+
+            [% IF ( selected_count ) %]
+                <div class="dialog message">
+                    [% IF ( action == 'viewed' ) %]
+                        <span>[% selected_count | html %] problem report(s) marked as viewed.</span>
+                    [% ELSIF ( action == 'closed' ) %]
+                        <span>[% selected_count | html %] problem report(s) marked as closed.</span>
+                    [% ELSIF ( action == 'new' ) %]
+                        <span>[% selected_count | html %] problem report(s) marked as new.</span>
+                    [% ELSE %]
+                        <span>Failed to change the status of [% selected_count | html %] problem report(s).</span>
+                    [% END %]
+                </div>
+            [% END %]
+
+            [% IF ( problem_reports.count ) %]
+                <form id="mark_selected" method="post" action="/cgi-bin/koha/admin/problem-reports.pl">
+                    <div id="toolbar" class="btn-toolbar">
+                        <button type="submit" class="btn btn-default markviewed" name="mark_selected-viewed" value="viewed" disabled="disabled"><i class="fa fa-eye"></i> Mark viewed</button>
+                        <button type="submit" class="btn btn-default markclosed" name="mark_selected-closed" value="closed" disabled="disabled"><i class="fa fa-times-circle"></i> Mark closed</button>
+                        <button type="submit" class="btn btn-default marknew" name="mark_selected-new" value="new" disabled="disabled"><i class="fa fa-star"></i> Mark new</button>
+                    </div>
+
+                    <fieldset class="action" style="cursor:pointer;">
+                        <a class="SelectAll"><i class="fa fa-check"></i> Select all</a>
+                        | <a class="ClearAll"><i class="fa fa-remove"></i> Clear all</a>
+                        | <a class="HideViewed"><i class="fa fa-minus-square"></i> Hide viewed</a>
+                        | <a class="HideClosed"><i class="fa fa-minus-square"></i> Hide closed</a>
+                        | <a class="HideNew"><i class="fa fa-minus-square"></i> Hide new</a>
+                        | <a class="ShowAll"><i class="fa fa-bars"></i> Show all</a>
+                    </fieldset>
+
+                    <table id="problemreportstable">
+                        <thead>
+                            <tr>
+                                <th class="NoSort">&nbsp;</th>
+                                <th class="anti-the">Message</th>
+                                <th>Problem page</th>
+                                <th class="title-string">Created on</th>
+                                <th>Set by</th>
+                                <th>Status</th>
+                                <th class="NoSort">Actions</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            [% FOREACH report IN problem_reports %]
+                                <tr>
+                                    <td><input type="checkbox" name="report_ids" value="[% report.reportid | html %]"></td>
+                                    <td>
+                                        <b>[% report.title | html %]</b><br>
+                                        [% report.content | html %]
+                                    </td>
+                                    <td><a href="[% report.problempage | uri %]">[% report.problempage | html %]</a></td>
+                                    <td><span title="[% report.created_on | html %]">[% report.created_on | $KohaDates with_hours => 1 %]</span></td>
+                                    <td>[% INCLUDE 'patron-title.inc' patron => report.patron hide_patron_infos_if_needed=1 %]</td>
+                                    <td class="status[% report.status | html %]" name="status">
+                                        [% IF ( report.status == 'V' ) %]
+                                            <span id="status_[% report.reportid | html %]">Viewed</span>
+                                        [% ELSIF ( report.status == 'C' ) %]
+                                            <span id="status_[% report.reportid | html %]">Closed</span>
+                                        [% ELSE %]
+                                            <span id="status_[% report.reportid | html %]">New</span>
+                                        [% END %]
+                                    </td>
+                                    <td class="actions">
+                                        [% IF ( report.status == 'N' ) %]
+                                            <button name="viewed" data-report_id="[% report.reportid | html %]" class="viewed btn btn-default btn-xs"><i class="fa fa-eye"></i> Mark viewed</button> <button name="closed" data-report_id="[% report.reportid | html %]" class="closed btn btn-default btn-xs"><i class="fa fa-times-circle"></i> Mark closed</button> <button name="new" disabled="disabled" data-report_id="[% report.reportid | html %]" class="new btn btn-default btn-xs"><i class="fa fa-star"></i> Mark new</button>
+                                        [% ELSIF ( report.status == 'V' ) %]
+                                            <button name="viewed" disabled="disabled" data-report_id="[% report.reportid | html %]" class="viewed btn btn-default btn-xs"><i class="fa fa-eye"></i> Mark viewed</button> <button name="closed" data-report_id="[% report.reportid | html %]" class="closed btn btn-default btn-xs"><i class="fa fa-times-circle"></i> Mark closed</button> <button name="new" data-report_id="[% report.reportid | html %]" class="new btn btn-default btn-xs"><i class="fa fa-star"></i> Mark new</button>
+                                        [% ELSE %]
+                                            <button name="viewed" data-report_id="[% report.reportid | html %]" class="viewed btn btn-default btn-xs"><i class="fa fa-eye"></i> Mark viewed</button> <button name="closed" disabled="disabled" data-report_id="[% report.reportid | html %]" class="closed btn btn-default btn-xs"><i class="fa fa-times-circle"></i> Mark closed</button> <button name="new" data-report_id="[% report.reportid | html %]" class="new btn btn-default btn-xs"><i class="fa fa-star"></i> Mark new</button>
+                                        [% END %]
+                                    </td>
+                                </tr>
+                            [% END %]
+                        </tbody>
+                    </table>
+
+                </form>
+
+            [% ELSE %]
+                <div class="dialog message">There are currently no problem reports.</div>
+            [% END %] <!-- problem reports -->
+
+            </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("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
+    [% INCLUDE 'calendar.inc' %]
+    [% INCLUDE 'datatables.inc' %]
+    <script>
+        $(document).ready(function(){
+            $("#problemreportstable").dataTable($.extend(true, {}, dataTablesDefaults, {
+                "order": [[ 1, "asc" ]],
+                "aoColumnDefs": [
+                    { "orderable": false, "searchable": false, 'targets': [ 'NoSort' ] },
+                    { "type": "title-string", "targets" : [ "title-string" ] },
+                    { "type": "anti-the", "targets": [ "anti-the" ] }
+                ],
+                "pagingType": "full"
+            }));
+
+            $(".SelectAll").on("click", function(){
+                $("input[name='report_ids'][type='checkbox']").prop("checked", true);
+                $(".markviewed").prop("disabled", false);
+                $(".markclosed").prop("disabled", false);
+                $(".marknew").prop("disabled", false);
+            });
+
+            $(".ClearAll").on("click", function(){
+                $("input[name='report_ids'][type='checkbox']").prop("checked", false);
+                $(".markviewed").prop("disabled", true);
+                $(".markclosed").prop("disabled", true);
+                $(".marknew").prop("disabled", true);
+            });
+
+            $(".HideViewed").on("click", function(){
+                $(".statusV").parent().hide();
+            });
+
+            $(".HideClosed").on("click", function(){
+                $(".statusC").parent().hide();
+            });
+
+            $(".HideNew").on("click", function(){
+                $(".statusN").parent().hide();
+            });
+
+            $(".ShowAll").on("click", function(){
+                $("tr").show();
+            });
+
+            $("#error").hide();
+
+            $("#problemreportstable").on("change", "input[type='checkbox']", function(){
+                if ( $("input[type='checkbox']").is(":checked") ) {
+                    $(".markviewed").prop("disabled", false);
+                    $(".markclosed").prop("disabled", false);
+                    $(".marknew").prop("disabled", false);
+                } else {
+                    $(".markviewed").prop("disabled", true);
+                    $(".markclosed").prop("disabled", true);
+                    $(".marknew").prop("disabled", true);
+                }
+            });
+
+            $("#problemreportstable").on("click", "button.viewed, button.closed, button.new", function(event){
+                event.preventDefault(); // prevent form submission
+                var $action = $(this).attr("name");
+                var $report_id = $(this).data('report_id');
+                var ajaxData = {
+                    'action': $action,
+                    'report_id': $report_id,
+                };
+
+                $.ajax({
+                    url: '/cgi-bin/koha/svc/problem_reports/',
+                    type: 'POST',
+                    dataType: 'json',
+                    data: ajaxData,
+                })
+
+                .done(function(data){
+                    if (data.status == 'success'){
+                        if ( $action == 'viewed' ){
+                            $("#status_" + $report_id).text(_("Viewed"));
+                            $(event.target).parent().siblings("status").removeClass().addClass("statusV");
+                            $(event.target).siblings(".closed").prop("disabled", false);
+                            $(event.target).siblings(".new").prop("disabled", false);
+                            $(event.target).prop("disabled", true);
+                        } else if ( $action == 'new' ){
+                            $("#status_" + $report_id).text(_("New"));
+                            $(event.target).parent().siblings("status").removeClass().addClass("statusN");
+                            $(event.target).siblings(".closed").prop("disabled", false);
+                            $(event.target).siblings(".viewed").prop("disabled", false);
+                            $(event.target).prop("disabled", true);
+                        } else {
+                            $("#status_" + $report_id).text(_("Closed"));
+                            $(event.target).parent().siblings("status").removeClass().addClass("statusC");
+                            $(event.target).siblings(".viewed").prop("disabled", false);
+                            $(event.target).siblings(".new").prop("disabled", false);
+                            $(event.target).prop("disabled", true);
+                        }
+                    } else {
+                        $("#error").text(_("Unable to change status of problem report."));
+                        $("#error").show();
+                    }
+                })
+                .error(function(data){
+                    $("#error").text(_("Unable to change status of problem report."));
+                    $("#error").show();
+                });
+            });
+        });
+    </script>
+[% END %]
+
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/svc/problem_reports b/svc/problem_reports
new file mode 100755 (executable)
index 0000000..0641f97
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+
+# This file is part of Koha.
+#
+# Copyright 2020 Aleisha Amohia <aleisha@catalyst.net.nz>
+#
+# 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 JSON qw( to_json );
+use CGI;
+use C4::Service;
+use C4::Auth qw /check_cookie_auth/;
+use C4::Output qw(:DEFAULT :ajax);
+use Koha::ProblemReports;
+
+=head1 NAME
+
+svc/problem_reports - Web service for managing OPAC problem reports
+
+=head1 DESCRIPTION
+
+=cut
+
+# AJAX requests
+my $is_ajax = is_ajax();
+my $query = new CGI;
+my ( $auth_status, $sessionID ) = check_cookie_auth( $query->cookie('CGISESSID'), { problem_reports => 1 } );
+if ( $auth_status ne "ok" ) {
+    exit 0;
+}
+if ($is_ajax) {
+    my $report_id = $query->param('report_id');
+    my $report = Koha::ProblemReports->find($report_id);
+    my $action = $query->param('action');
+    my $status = 'success';
+    if ( $action eq 'viewed' ) {
+        $report->set({ status => 'V' })->store;
+        if ( $report->status ne 'V' ) {
+            $status = 'failure';
+        }
+    } elsif ( $action eq 'closed' ) {
+        $report->set({ status => 'C' })->store;
+        if ( $report->status ne 'C' ) {
+            $status = 'failure';
+        }
+    } elsif ( $action eq 'new' ) {
+        $report->set({ status => 'N' })->store;
+        if ( $report->status ne 'N' ) {
+            $status = 'failure';
+        }
+    }
+    my $json = to_json ( { status => $status } );
+    output_with_http_headers $query, undef, $json, 'js';
+    exit;
+}