Bug 20600: (follow-up) Move template JavaScript to the footer
authorOwen Leonard <oleonard@myacpl.org>
Tue, 5 Jun 2018 14:24:00 +0000 (14:24 +0000)
committerNick Clemens <nick@bywatersolutions.com>
Tue, 19 Feb 2019 16:36:43 +0000 (16:36 +0000)
This patch moves the ILL requests template's JavaScript to the footer.

Also changed:
 - Removed unused jQuery checkboxes plugin
 - Some <input> tags have been corrected to be self-closing.
 - Title tag has been corrected
 - Table row limit of 10 has been removed in favor of accepting default
   used elsewhere.

To test, apply the patch and follow the original test plan for this bug.
Everything should work as expected.

Signed-off-by: Andrew Isherwood <andrew.isherwood@ptfs-europe.com>
Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt

index 4357eb0..60db7c3 100644 (file)
@@ -3,9 +3,9 @@
 [% USE Branches %]
 [% USE Koha %]
 [% USE KohaDates %]
-
+[% SET footerjs = 1 %]
 [% INCLUDE 'doc-head-open.inc' %]
-<title>Koha &rsaquo; ILL requests  &rsaquo;</title>
+<title>Koha &rsaquo; ILL requests</title>
 [% INCLUDE 'doc-head-close.inc' %]
 [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
 [% Asset.css("css/datatables.css") | $raw %]
                         </li>
                         <li>
                             <label for="illfilter_dateplaced_start">Date placed between:</label>
-                            <input type="text" name="illfilter_dateplaced_start" id="illfilter_dateplaced_start" class="datepicker">
+                            <input type="text" name="illfilter_dateplaced_start" id="illfilter_dateplaced_start" class="datepicker" />
                         </li>
                         <li>
                             <label for="illfilter_dateplaced_end">and:</label>
-                            <input type="text" name="illfilter_dateplaced_end" id="illfilter_dateplaced_end" class="datepicker">
+                            <input type="text" name="illfilter_dateplaced_end" id="illfilter_dateplaced_end" class="datepicker" />
                         </li>
                         <li>
                             <label for="illfilter_datemodified_start">Updated between:</label>
-                            <input type="text" name="illfilter_datemodified_start" id="illfilter_datemodified_start" class="datepicker">
+                            <input type="text" name="illfilter_datemodified_start" id="illfilter_datemodified_start" class="datepicker" />
                         </li>
                         <li>
                             <label for="illfilter_datemodified_end">and:</label>
-                            <input type="text" name="illfilter_datemodified_end" id="illfilter_datemodified_end" class="datepicker">
+                            <input type="text" name="illfilter_datemodified_end" id="illfilter_datemodified_end" class="datepicker" />
                         </li>
                         <li>
                             <label for="illfilter_branchname">Library:</label>
                         </li>
                         <li>
                             <label for="illfilter_barcode">Patron barcode:</label>
-                            <input type="text" name="illfilter_barcode" id="illfilter_barcode">
+                            <input type="text" name="illfilter_barcode" id="illfilter_barcode" />
                         </li>
                     </ol>
                     <fieldset class="action">
                                 <ol>
                                     <li>
                                         <label for="partner_filter">Filter partner libraries:</label>
-                                        <input type="text" id="partner_filter">
+                                        <input type="text" id="partner_filter" />
                                     </li>
                                     <li>
                                         <label for="partners" class="required">Select partner libraries:</label>
 [% CATCH %]
 [% END %]
 
+[% MACRO jsinclude BLOCK %]
+    [% INCLUDE 'datatables.inc' %]
+    [% INCLUDE 'calendar.inc' %]
+    <script>
+        $(document).ready(function() {
+
+            // Illview Datatable setup
+
+            var table;
+
+            // Filters that are active
+            var activeFilters = {};
+
+            // Fields we don't want to display
+            var ignore = [
+                'accessurl',
+                'backend',
+                'branchcode',
+                'completed',
+                'capabilities',
+                'cost',
+                'medium',
+                'notesopac',
+                'notesstaff',
+                'replied'
+            ];
+
+            // Fields we need to expand (flatten)
+            var expand = [
+                'metadata',
+                'patron'
+            ];
+
+            // Expanded fields
+            // This is auto populated
+            var expanded = {};
+
+            // The core fields that should be displayed first
+            var core = [
+                'metadata_Author',
+                'metadata_Title',
+                'borrowername',
+                'patron_cardnumber',
+                'biblio_id',
+                'library',
+                'status',
+                'placed',
+                'placed_formatted',
+                'updated',
+                'updated_formatted',
+                'illrequest_id',
+                'action'
+            ];
+
+            // Filterable columns
+            var filterable = {
+                status: {
+                    prep: function(tableData, oData) {
+                        var uniques = {};
+                        tableData.forEach(function(row) {
+                            var resolvedName = getStatusName(
+                                oData[0].capabilities[row.status].name
+                            );
+                            uniques[resolvedName] = 1
+                        });
+                        Object.keys(uniques).sort().forEach(function(unique) {
+                            $('#illfilter_status').append(
+                                '<option value="' + unique  +
+                                '">' + unique +  '</option>'
+                            );
+                        });
+                    },
+                    listener: function() {
+                        var me = 'status';
+                        $('#illfilter_status').change(function() {
+                            var sel = $('#illfilter_status option:selected').val();
+                            if (sel && sel.length > 0) {
+                                activeFilters[me] = function() {
+                                    table.column(6).search(sel);
+                                }
+                            } else {
+                                if (activeFilters.hasOwnProperty(me)) {
+                                    delete activeFilters[me];
+                                }
+                            }
+                        });
+                    },
+                    clear: function() {
+                        $('#illfilter_status').val('');
+                    }
+                },
+                pickupBranch: {
+                    prep: function(tableData, oData) {
+                        var uniques = {};
+                        tableData.forEach(function(row) {
+                            uniques[row.library.branchname] = 1
+                        });
+                        Object.keys(uniques).sort().forEach(function(unique) {
+                            $('#illfilter_branchname').append(
+                                '<option value="' + unique  +
+                                '">' + unique +  '</option>'
+                            );
+                        });
+                    },
+                    listener: function() {
+                        var me = 'pickupBranch';
+                        $('#illfilter_branchname').change(function() {
+                            var sel = $('#illfilter_branchname option:selected').val();
+                            if (sel && sel.length > 0) {
+                                activeFilters[me] = function() {
+                                    table.column(5).search(sel);
+                                }
+                            } else {
+                                if (activeFilters.hasOwnProperty(me)) {
+                                    delete activeFilters[me];
+                                }
+                            }
+                        });
+                    },
+                    clear: function() {
+                        $('#illfilter_branchname').val('');
+                    }
+                },
+                barcode: {
+                    listener: function() {
+                        var me = 'barcode';
+                        $('#illfilter_barcode').change(function() {
+                            var val = $('#illfilter_barcode').val();
+                            if (val && val.length > 0) {
+                                activeFilters[me] = function() {
+                                    table.column(3).search(val);
+                                }
+                            } else {
+                                if (activeFilters.hasOwnProperty(me)) {
+                                    delete activeFilters[me];
+                                }
+                            }
+                        });
+                    },
+                    clear: function() {
+                        $('#illfilter_barcode').val('');
+                    }
+                },
+                dateModified: {
+                    clear: function() {
+                        $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
+                    }
+                },
+                datePlaced: {
+                    clear: function() {
+                        $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
+                    }
+                }
+            };
+
+            // Remove any fields we're ignoring
+            var removeIgnore = function(dataObj) {
+                dataObj.forEach(function(thisRow) {
+                    ignore.forEach(function(thisIgnore) {
+                        if (thisRow.hasOwnProperty(thisIgnore)) {
+                            delete thisRow[thisIgnore];
+                        }
+                    });
+                });
+            };
+
+            // Expand any fields we're expanding
+            var expandExpand = function(row) {
+                expand.forEach(function(thisExpand) {
+                    if (row.hasOwnProperty(thisExpand)) {
+                        if (!expanded.hasOwnProperty(thisExpand)) {
+                            expanded[thisExpand] = [];
+                        }
+                        var expandObj = row[thisExpand];
+                        Object.keys(expandObj).forEach(
+                            function(thisExpandCol) {
+                                var expColName = thisExpand + '_' + thisExpandCol;
+                                // Keep a list of fields that have been expanded
+                                // so we can create toggle links for them
+                                if (expanded[thisExpand].indexOf(expColName) == -1) {
+                                    expanded[thisExpand].push(expColName);
+                                }
+                                expandObj[expColName] =
+                                    expandObj[thisExpandCol];
+                                delete expandObj[thisExpandCol];
+                            }
+                        );
+                        $.extend(true, row, expandObj);
+                        delete row[thisExpand];
+                    }
+                });
+            };
+
+            // Build a de-duped list of all column names
+            var allCols = {};
+            core.map(function(thisCore) {
+                allCols[thisCore] = 1;
+            });
+
+            // Strip the expand prefix if it exists, we do this for display
+            var stripPrefix = function(value) {
+                expand.forEach(function(thisExpand) {
+                    var regex = new RegExp(thisExpand + '_', 'g');
+                    value = value.replace(regex, '');
+                });
+                return value;
+            };
+
+            // Our 'render' function for borrowerlink
+            var createPatronLink = function(data, type, row) {
+                return '<a title="' + _("View borrower details") + '" ' +
+                    'href="/cgi-bin/koha/members/moremember.pl?' +
+                    'borrowernumber='+row.borrowernumber+'">' +
+                    row.patron_firstname + ' ' + row.patron_surname +
+                    '</a>';
+            };
+
+            // Our 'render' function for the library name
+            var createLibrary = function(data, type, row) {
+                return row.library.branchname;
+            };
+
+            // Render function for request ID
+            var createRequestId = function(data, type, row) {
+                return row.id_prefix + row.illrequest_id;
+            };
+
+            // Render function for request status
+            var createStatus = function(data, type, row, meta) {
+                var origData = meta.settings.oInit.originalData;
+                if (origData.length > 0) {
+                    var status_name = meta.settings.oInit.originalData[0].capabilities[
+                        row.status
+                    ].name;
+                    return getStatusName(status_name);
+                } else {
+                    return '';
+                }
+            };
+
+            var getStatusName = function(origName) {
+                switch( origName ) {
+                    case "New request":
+                        return _("New request");
+                    case "Requested":
+                        return _("Requested");
+                    case "Requested from partners":
+                        return _("Requested from partners");
+                    case "Request reverted":
+                        return _("Request reverted");
+                    case "Queued request":
+                        return _("Queued request");
+                    case "Cancellation requested":
+                        return _("Cancellation requested");
+                    case "Completed":
+                        return _("Completed");
+                    case "Delete request":
+                        return _("Delete request");
+                    default:
+                        return origName;
+                }
+            };
+
+            // Render function for creating a row's action link
+            var createActionLink = function(data, type, row) {
+                return '<a class="btn btn-default btn-sm" ' +
+                    'href="/cgi-bin/koha/ill/ill-requests.pl?' +
+                    'method=illview&amp;illrequest_id=' +
+                    row.illrequest_id +
+                    '">' + _("Manage request") + '</a>';
+            };
+
+            // Columns that require special treatment
+            var specialCols = {
+                action: {
+                    name: '',
+                    func: createActionLink
+                },
+                borrowername: {
+                    name: _("Patron"),
+                    func: createPatronLink
+                },
+                illrequest_id: {
+                    name: _("Request number"),
+                    func: createRequestId
+                },
+                status: {
+                    name: _("Status"),
+                    func: createStatus
+                },
+                biblio_id: {
+                    name: _("Biblio ID")
+                },
+                library: {
+                    name: _("Library"),
+                    func: createLibrary
+                },
+                updated: {
+                    name: _("Updated on"),
+                },
+                patron_cardnumber: {
+                    name: _("Patron barcode")
+                }
+            };
+
+            // Toggle request attributes in Illview
+            $('#toggle_requestattributes').on('click', function(e) {
+                e.preventDefault();
+                $('#requestattributes').toggleClass('content_hidden');
+            });
+
+            // Filter partner list
+            $('#partner_filter').keyup(function() {
+                var needle = $('#partner_filter').val();
+                $('#partners > option').each(function() {
+                    var regex = new RegExp(needle, 'i');
+                    if (
+                        needle.length == 0 ||
+                        $(this).is(':selected') ||
+                        $(this).text().match(regex)
+                    ) {
+                        $(this).show();
+                    } else {
+                        $(this).hide();
+                    }
+                });
+            });
+
+        // Display the modal containing request supplier metadata
+        $('#ill-request-display-metadata').on('click', function(e) {
+            e.preventDefault();
+            $('#dataPreview').modal({show:true});
+        });
+
+            // Get our data from the API and process it prior to passing
+            // it to datatables
+            var ajax = $.ajax(
+                '/api/v1/illrequests?embed=metadata,patron,capabilities,library'
+                ).done(function() {
+                    var data = JSON.parse(ajax.responseText);
+                    // Make a copy, we'll be removing columns next and need
+                    // to be able to refer to data that has been removed
+                    var dataCopy = $.extend(true, [], data);
+                    // Remove all columns we're not interested in
+                    removeIgnore(dataCopy);
+                    // Expand columns that need it and create an array
+                    // of all column names
+                    $.each(dataCopy, function(k, row) {
+                        expandExpand(row);
+                    });
+
+                    // Assemble an array of column definitions for passing
+                    // to datatables
+                    var colData = [];
+                    Object.keys(allCols).forEach(function(thisCol) {
+                        // Create the base column object
+                        var colObj = {
+                            name: thisCol,
+                            className: thisCol,
+                            defaultContent: ''
+                        };
+                        // We may need to process the data going in this
+                        // column, so do it if necessary
+                        if (
+                            specialCols.hasOwnProperty(thisCol) &&
+                            specialCols[thisCol].hasOwnProperty('func')
+                        ) {
+                            colObj.render = specialCols[thisCol].func;
+                        } else {
+                            colObj.data = thisCol;
+                        }
+                        colData.push(colObj);
+                    });
+
+                    // Initialise the datatable
+                    table = $('#ill-requests').DataTable($.extend(true, {}, dataTablesDefaults, {
+                        'aoColumnDefs': [
+                            { // Last column shouldn't be sortable or searchable
+                                'aTargets': [ 'actions' ],
+                                'bSortable': false,
+                                'bSearchable': false
+                            },
+                            { // Hide the two date columns we use just for sorting
+                                'aTargets': [ 'placed', 'updated' ],
+                                'bVisible': false,
+                                'bSearchable': true
+                            },
+                            { // When sorting 'placed', we want to use the
+                              // unformatted column
+                              'aTargets': [ 'placed_formatted'],
+                              'iDataSort': 7
+                            },
+                            { // When sorting 'updated', we want to use the
+                              // unformatted column
+                              'aTargets': [ 'updated_formatted'],
+                              'iDataSort': 9
+                            }
+                        ],
+                        'aaSorting': [[ 9, 'desc' ]], // Default sort, updated descending
+                        'processing': true, // Display a message when manipulating
+                        'sPaginationType': "full_numbers", // Pagination display
+                        'deferRender': true, // Improve performance on big datasets
+                        'data': dataCopy,
+                        'columns': colData,
+                        'originalData': data, // Enable render functions to access
+                                              // our original data
+                        'initComplete': function() {
+
+                            // Prepare any filter elements that need it
+                            for (var el in filterable) {
+                                if (filterable.hasOwnProperty(el)) {
+                                    if (filterable[el].hasOwnProperty('prep')) {
+                                        filterable[el].prep(dataCopy, data);
+                                    }
+                                    if (filterable[el].hasOwnProperty('listener')) {
+                                        filterable[el].listener();
+                                    }
+                                }
+                            }
+
+                        }
+                    }));
+
+                    // Custom date range filtering
+                    $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
+                        var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
+                        var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
+                        var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
+                        var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
+                        var rowPlaced = data[7] ? new Date(data[7]) : null;
+                        var rowModified = data[9] ? new Date(data[9]) : null;
+                        var placedPassed = true;
+                        var modifiedPassed = true;
+                        if (placedStart && rowPlaced && rowPlaced < placedStart) {
+                            placedPassed = false
+                        };
+                        if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
+                            placedPassed = false;
+                        }
+                        if (modifiedStart && rowModified && rowModified < modifiedStart) {
+                            modifiedPassed = false
+                        };
+                        if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
+                            modifiedPassed = false;
+                        }
+
+                        return placedPassed && modifiedPassed;
+
+                    });
+
+                }
+            );
+
+            var clearSearch = function() {
+                table.search('').columns().search('');
+                activeFilters = {};
+                for (var filter in filterable) {
+                    if (
+                        filterable.hasOwnProperty(filter) &&
+                        filterable[filter].hasOwnProperty('clear')
+                    ) {
+                        filterable[filter].clear();
+                    }
+                }
+                table.draw();
+            };
+
+            // Apply any search filters, or clear any previous
+            // ones
+            $('#illfilter_form').submit(function(event) {
+                event.preventDefault();
+                table.search('').columns().search('');
+                for (var active in activeFilters) {
+                    if (activeFilters.hasOwnProperty(active)) {
+                        activeFilters[active]();
+                    }
+                }
+                table.draw();
+            });
+
+            // Clear all filters
+            $('#clear_search').click(function() {
+                clearSearch();
+            });
+
+        });
+    </script>
+[% END %]
+
 [% INCLUDE 'intranet-bottom.inc' %]