web staff : grid CSV file download
authorBill Erickson <berick@esilibrary.com>
Fri, 4 Apr 2014 18:19:35 +0000 (14:19 -0400)
committerBill Erickson <berick@esilibrary.com>
Fri, 4 Apr 2014 18:19:35 +0000 (14:19 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/templates/staff/parts/t_autogrid.tt2
Open-ILS/web/js/ui/default/staff/services/grid.js
Open-ILS/web/js/ui/default/staff/test/app.js

index a9726a7..f5e4868 100644 (file)
         <span class="glyphicon glyphicon-resize-small"></span>
         [% l('Hide All Columns') %]
       </a></li>
+      <li><a ng-click="grid.generateCSVExportURL()" 
+        download="{{grid.csvExportFileName}}.csv" ng-href="{{grid.csvExportURL}}">
+        <span class="glyphicon glyphicon-download"></span>
+        [% l('Download CSV') %]
+      </a></li>
       <li role="presentation" class="divider"></li>
       <li ng-repeat="col in grid.columnsProvider.columns">
         <a href='' ng-click="grid.columnsProvider.visible[col.name] = 
           ng-repeat="col in grid.columnsProvider.columns"
           style="flex:{{col.flex}}"
           ng-show="grid.columnsProvider.visible[col.name]">
-        {{grid.dataProvider.itemFieldValue(item, col) | egGridvalueFilter:col}}
+        {{grid.dataProvider.itemFieldValue(item, col) | egGridValueFilter:col}}
       </div>
     </div>
   </div>
index 034c521..c9ef91b 100644 (file)
@@ -54,8 +54,10 @@ angular.module('egGridMod',
         controller : [
                     '$scope','egIDL','egAuth','egNet',
                     'egGridFlatDataProvider','egGridColumnsProvider',
+                    '$filter','$window',
             function($scope,  egIDL,  egAuth,  egNet,  
-                    egGridFlatDataProvider,  egGridColumnsProvider) {
+                    egGridFlatDataProvider,  egGridColumnsProvider,
+                    $filter,  $window) {
 
             var grid = this;
 
@@ -313,6 +315,7 @@ angular.module('egGridMod',
                 grid.collect();
             }
 
+            // show / hide the grid configuration row
             grid.toggleConfDisplay = function() {
                 if (grid.showGridConf) {
                     grid.showGridConf = false;
@@ -323,6 +326,8 @@ angular.module('egGridMod',
                 }
             }
 
+            // called when a dragged column is dropped onto itself
+            // or any other column
             grid.onColumnDrop = function(target) {
                 if (angular.isUndefined(target)) return;
                 if (target == grid.dragColumn) return;
@@ -347,6 +352,68 @@ angular.module('egGridMod',
                 $scope.$apply(); 
             }
 
+            // prepares a string for inclusion within a CSV document
+            // by escaping commas and quotes and removing newlines.
+            grid.csvDatum = function(str) {
+                str = ''+str;
+                if (!str) return '';
+                str = str.replace(/\n/g, '');
+                if (str.match(/\,/) || str.match(/"/)) {                                     
+                    str = str.replace(/"/g, '""');
+                    str = '"' + str + '"';                                           
+                } 
+                return str;
+            }
+
+            // sets the download file name and inserts the current CSV
+            // into a Blob URL for browser download.
+            grid.generateCSVExportURL = function() {
+
+                // let the file name describe the grid
+                grid.csvExportFileName = 
+                    (grid.mainLabel || grid.persistKey || 'eg_grid_data')
+                    .replace(/\s+/g, '_') + '_' + grid.page();
+
+                // toss the CSV into a Blob and update the export URL
+                var csv = grid.generateCSV();
+                var blob = new Blob([csv], {type : 'text/plain'});
+                grid.csvExportURL = 
+                    ($window.URL || $window.webkitURL).createObjectURL(blob);
+            }
+
+            // generates CSV for the currently visible grid contents
+            grid.generateCSV = function() {
+                var csvStr = '';
+                var colCount = grid.columnsProvider.columns.length;
+
+                // columns
+                angular.forEach(grid.columnsProvider.columns,
+                    function(col, idx) {
+                        csvStr += grid.csvDatum(col.name);
+                        if (idx < colCount -1) csvStr += ',';
+                    }
+                );
+
+                csvStr += "\n";
+
+                // items
+                angular.forEach(grid.items, function(item) {
+                    angular.forEach(grid.columnsProvider.columns, 
+                        function(col, idx) {
+                            // bare value
+                            var val = grid.dataProvider.itemFieldValue(item, col);
+                            // filtered value (dates, etc.)
+                            val = $filter('egGridValueFilter')(val, col);
+                            csvStr += grid.csvDatum(val);
+                            if (idx < colCount -1) csvStr += ',';
+                        }
+                    );
+                    csvStr += "\n";
+                });
+
+                return csvStr;
+            }
+
             // asks the dataProvider for a page of data
             grid.collect = function() {
                 grid.items = [];
@@ -695,9 +762,9 @@ angular.module('egGridMod',
  *    value.  (Though we could manually translate instead..)
  * Others likely to follow...
  */
-.filter('egGridvalueFilter', ['$filter', function($filter) {                         
-    return function(value, item) {                                             
-        switch(item.datatype) {                                                
+.filter('egGridValueFilter', ['$filter', function($filter) {                         
+    return function(value, column) {                                             
+        switch(column.datatype) {                                                
             case 'bool':                                                       
                 // Browser will translate true/false for us                    
                 return Boolean(value == 't');                                  
index 27e6fe1..6d9f7e3 100644 (file)
@@ -1,9 +1,12 @@
 angular.module('egTestApp', ['ngRoute', 'ui.bootstrap', 
     'egCoreMod', 'egUiMod', 'egGridMod'])
 
-.config(function($routeProvider, $locationProvider) {
+.config(function($routeProvider, $locationProvider, $compileProvider) {
     $locationProvider.html5Mode(true);
 
+    // necessary for file download via Blob URLs (e.g. grid CSV export)
+    $compileProvider.aHrefSanitizationWhitelist(/^\s*(blob):/);
+
     var resolver = {delay : function(egStartup) { return egStartup.go() }};
 
     $routeProvider.when('/test/autogrid', {