From 23ab19f6ecc45315137681d5c43d0b4a22f74560 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 25 Sep 2017 10:35:07 -0400 Subject: [PATCH] LP#1717010 Webstaff print/export full grid 1. Adds a new 'Print Full Grid' action which generates printer-friendly HTML output of all grid rows. The print content matches the display grid structure, uses the same field filters (e.g. for formatting dates) and is retrieved using the same query and sort parameters as the display grid. To render the grid as HTML, a new print HTML template is included at print_templates/t_grid_html.tt2. 2. Modifies the 'Export CSV' action to export all grid rows instead of only the visible rows. The link label is modified to 'Export Full CSV' to better indicate its intent. Signed-off-by: Bill Erickson Signed-off-by: Mike Rylander --- .../share/print_templates/t_grid_html.tt2 | 26 +++ .../src/templates/staff/share/t_autogrid.tt2 | 8 +- .../web/js/ui/default/staff/services/grid.js | 163 ++++++++++++++---- 3 files changed, 161 insertions(+), 36 deletions(-) create mode 100644 Open-ILS/src/templates/staff/share/print_templates/t_grid_html.tt2 diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_grid_html.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_grid_html.tt2 new file mode 100644 index 0000000000..f3037cee0c --- /dev/null +++ b/Open-ILS/src/templates/staff/share/print_templates/t_grid_html.tt2 @@ -0,0 +1,26 @@ + +
+ + + + + + + + + + +
{{col.label}}
{{item[col.name]}}
+
+ diff --git a/Open-ILS/src/templates/staff/share/t_autogrid.tt2 b/Open-ILS/src/templates/staff/share/t_autogrid.tt2 index 1f5500d013..e8f94d77bd 100644 --- a/Open-ILS/src/templates/staff/share/t_autogrid.tt2 +++ b/Open-ILS/src/templates/staff/share/t_autogrid.tt2 @@ -180,14 +180,14 @@ [% l('Reset Columns') %] -
  • - [% l('Download CSV') %] + [% l('Download Full CSV') %]
  • -
  • +
  • - [% l('Print CSV') %] + [% l('Print Full Grid') %]
  • diff --git a/Open-ILS/web/js/ui/default/staff/services/grid.js b/Open-ILS/web/js/ui/default/staff/services/grid.js index 1811941977..fab8679d69 100644 --- a/Open-ILS/web/js/ui/default/staff/services/grid.js +++ b/Open-ILS/web/js/ui/default/staff/services/grid.js @@ -119,8 +119,10 @@ angular.module('egGridMod', controller : [ '$scope','$q','egCore','egGridFlatDataProvider','$location', 'egGridColumnsProvider','$filter','$window','$sce','$timeout', + 'egProgressDialog', function($scope, $q , egCore, egGridFlatDataProvider , $location, - egGridColumnsProvider , $filter , $window , $sce , $timeout) { + egGridColumnsProvider , $filter , $window , $sce , $timeout, + egProgressDialog) { var grid = this; @@ -945,9 +947,35 @@ angular.module('egGridMod', return str; } - // sets the download file name and inserts the current CSV - // into a Blob URL for browser download. - $scope.generateCSVExportURL = function() { + /** Export the full data set as CSV. + * Flow of events: + * 1. User clicks the 'download csv' link + * 2. All grid data is retrieved asychronously + * 3. Once all data is all present and CSV-ized, the download + * attributes are linked to the href. + * 4. The href .click() action is prgrammatically fired again, + * telling the browser to download the data, now that the + * data is available for download. + * 5 Once downloaded, the href attributes are reset. + */ + grid.csvExportInProgress = false; + $scope.generateCSVExportURL = function($event) { + + if (grid.csvExportInProgress) { + // This is secondary href click handler. Give the + // browser a moment to start the download, then reset + // the CSV download attributes / state. + $timeout( + function() { + $scope.csvExportURL = ''; + $scope.csvExportFileName = ''; + grid.csvExportInProgress = false; + }, 500 + ); + return; + } + + grid.csvExportInProgress = true; $scope.gridColumnPickerIsOpen = false; // let the file name describe the grid @@ -956,12 +984,21 @@ angular.module('egGridMod', .replace(/\s+/g, '_') + '_' + $scope.page(); // toss the CSV into a Blob and update the export URL - var csv = grid.generateCSV(); - var blob = new Blob([csv], {type : 'text/plain'}); - $scope.csvExportURL = - ($window.URL || $window.webkitURL).createObjectURL(blob); + grid.generateCSV().then(function(csv) { + var blob = new Blob([csv], {type : 'text/plain'}); + $scope.csvExportURL = + ($window.URL || $window.webkitURL).createObjectURL(blob); + + // Fire the 2nd click event now that the browser has + // information on how to download the CSV file. + $timeout(function() {$event.target.click()}); + }); } + /* + * TODO: does this serve any purpose given we can + * print formatted HTML? If so, generateCSV() now + * returns a promise, needs light refactoring... $scope.printCSV = function() { $scope.gridColumnPickerIsOpen = false; egCore.print.print({ @@ -970,40 +1007,102 @@ angular.module('egGridMod', content_type : 'text/plain' }); } + */ + + // Given a row item and column definition, extract the + // text content for printing. Templated columns must be + // processed and parsed as HTML, then boiled down to their + // text content. + grid.getItemTextContent = function(item, col) { + var val; + if (col.template) { + val = $scope.translateCellTemplate(col, item); + if (val) { + var node = new DOMParser() + .parseFromString(val, 'text/html'); + val = $(node).text(); + } + } else { + val = grid.dataProvider.itemFieldValue(item, col); + val = $filter('egGridValueFilter')(val, col, item); + } + return val; + } + + /** + * Fetches all grid data and transates each item into a simple + * key-value pair of column name => text-value. + * Included in the response for convenience is the list of + * currently visible column definitions. + * TODO: currently fetches a maximum of 10k rows. Does this + * need to be configurable? + */ + grid.getAllItemsAsText = function() { + var text_items = []; + + // we don't know the total number of rows we're about + // to retrieve, but we can indicate the number retrieved + // so far as each item arrives. + egProgressDialog.open({value : 0}); + + var visible_cols = grid.columnsProvider.columns.filter( + function(c) { return c.visible }); + + return grid.dataProvider.get(0, 10000).then( + function() { + return {items : text_items, columns : visible_cols}; + }, + null, + function(item) { + egProgressDialog.increment(); + var text_item = {}; + angular.forEach(visible_cols, function(col) { + text_item[col.name] = + grid.getItemTextContent(item, col); + }); + text_items.push(text_item); + } + ).finally(egProgressDialog.close); + } + + // Fetch "all" of the grid data, translate it into print-friendly + // text, and send it to the printer service. + $scope.printHTML = function() { + $scope.gridColumnPickerIsOpen = false; + return grid.getAllItemsAsText().then(function(text_items) { + return egCore.print.print({ + template : 'grid_html', + scope : text_items + }); + }); + } // generates CSV for the currently visible grid contents grid.generateCSV = function() { - var csvStr = ''; - var colCount = grid.columnsProvider.columns.length; + return grid.getAllItemsAsText().then(function(text_items) { + var columns = text_items.columns; + var items = text_items.items; + var csvStr = ''; - // columns - angular.forEach(grid.columnsProvider.columns, - function(col) { - if (!col.visible) return; + // column headers + angular.forEach(columns, function(col) { csvStr += grid.csvDatum(col.label); csvStr += ','; - } - ); + }); - csvStr = csvStr.replace(/,$/,'\n'); + csvStr = csvStr.replace(/,$/,'\n'); - // items - angular.forEach($scope.items, function(item) { - angular.forEach(grid.columnsProvider.columns, - function(col) { - if (!col.visible) return; - // bare value - var val = grid.dataProvider.itemFieldValue(item, col); - // filtered value (dates, etc.) - val = $filter('egGridValueFilter')(val, col, item); - csvStr += grid.csvDatum(val); + // items + angular.forEach(items, function(item) { + angular.forEach(columns, function(col) { + csvStr += grid.csvDatum(item[col.name]); csvStr += ','; - } - ); - csvStr = csvStr.replace(/,$/,'\n'); - }); + }); + csvStr = csvStr.replace(/,$/,'\n'); + }); - return csvStr; + return csvStr; + }); } // Interpolate the value for column.linkpath within the context -- 2.43.2