722c634d1b5f2a6d19be25e31a2d6f4bcbe3917a
[portal.git] / ecomp-portal-FE-common / client / app / views / role / bulk-upload-dialogs / bulk-upload-role-functions-controller.js
1 /*-
2  * ============LICENSE_START==========================================
3  * ONAP Portal
4  * ===================================================================
5  * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ===================================================================
7  *
8  * Unless otherwise specified, all software contained herein is licensed
9  * under the Apache License, Version 2.0 (the "License");
10  * you may not use this software except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *             http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  * Unless otherwise specified, all documentation contained herein is licensed
22  * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
23  * you may not use this documentation except in compliance with the License.
24  * You may obtain a copy of the License at
25  *
26  *             https://creativecommons.org/licenses/by/4.0/
27  *
28  * Unless required by applicable law or agreed to in writing, documentation
29  * distributed under the License is distributed on an "AS IS" BASIS,
30  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31  * See the License for the specific language governing permissions and
32  * limitations under the License.
33  *
34  * ============LICENSE_END============================================
35  *
36  * 
37  */
38 /**
39  * bulk upload role-functions controller
40  */
41 'use strict';
42 (function () {
43     class BulkRoleAndFunctionsModalCtrl {
44         constructor($scope, $log, $filter, $q, $modalInstance, $modal, ngDialog, message, confirmBoxService, usersService, applicationsService, functionalMenuService, RoleService) {
45                 // Set to true for copious console output
46                 var debug = false;
47                 // Roles fetched from Role service
48                 var appRoleFuncsResult = [];
49                 // Functions fetched from Role service
50                 var appFunctionsResult = [];
51                 // Global  roles fetched from Role service
52                 var appGlobalRolesResult = [];
53                 
54                 var appId = message.appid;
55                 
56                  $scope.ngRepeatBulkUploadOptions = [
57                         {id: '1', title: 'Functions', value: 'functions'},
58                         {id: '2', title: 'Roles', value: 'roles'},
59                         {id: '3', title: 'Role Functions', value: 'roleFunctions'},
60                         {id: '4', title: 'Global Role Functions', value: 'globalRoleFunctions'}
61                     ];
62                  
63                  $scope.selectedUploadType =   $scope.ngRepeatBulkUploadOptions[0];
64                  $scope.UploadTypeInstruction = "Function Type, Function Instance, Function Action, Function Name";
65                  $scope.changeUploadTypeInstruction = function(typeInstrc){
66                          switch(typeInstrc) {
67                             case 'functions':
68                                 $scope.UploadTypeInstruction = "Function Type, Function Instance, Function Action, Function Name";
69                                 break;
70                             case 'roles':
71                                 $scope.UploadTypeInstruction = "Role Name, Priority (Optional)";
72                                 break;
73                             case 'roleFunctions':
74                                 $scope.UploadTypeInstruction = "Role Name, Function Type, Function Instance, Function Action, Function Name";
75                                 break;
76                             default:
77                                 $scope.UploadTypeInstruction = "Global Role Name, Function Type, Function Instance, Function Action, Function Name";
78                         }
79                  };
80                 
81                 let init = () => {
82                         if (debug)
83                                 $log.debug('BulkRoleAndFunctionsModalCtrl::init');
84                         // Angular insists on this.
85                         $scope.fileModel = {};
86                         // Enable modal controls
87                         this.step1 = true;
88                         
89                         this.fileSelected = false;      
90                         
91                         $scope.isProcessedRecords = false;
92                 }; // init
93                 
94                 // Answers a function that compares properties with the specified name.
95                 let getSortOrder = (prop, foldCase) => {
96                 return function(a, b) {
97                         let aProp = foldCase ? a[prop].toLowerCase() : a[prop];
98                         let bProp = foldCase ? b[prop].toLowerCase() : b[prop];
99                     if (aProp > bProp)
100                         return 1;
101                     else if (aProp < bProp) 
102                         return -1;
103                     else
104                         return 0;
105                 }
106             }
107                 
108                 // Caches the file name supplied by the event handler.
109                 $scope.fileChangeHandler = (event, files) => {
110                         this.fileSelected = true;
111                         this.fileToRead = files[0];
112                         if (debug)
113                                 $log.debug("BulkRoleAndFunctionsModalCtrl::fileChangeHandler: file is ", this.fileToRead);
114                 }; // file change handler
115                 
116                 /**
117                  * Reads the contents of the file, calls portal endpoints to
118                  * validate roles, userIds and existing role assignments;
119                  * ultimately builds array of requests to be sent. Creates scope
120                  * variable with input file contents for communication with
121                  * functions.
122                  * 
123                  * This function performs a synchronous step-by-step process
124                  * using asynchronous promises. The code could all be inline
125                  * here but the nesting becomes unwieldy.
126                  */
127                 $scope.readValidateFile = (typeUpload) => {
128                         $scope.isProcessing = true;
129                         $scope.conformMsg = '';
130                         $scope.isProcessedRecords = true;
131                         $scope.progressMsg = 'Reading upload file...';
132                         var reader = new FileReader();
133                         reader.onload = function(event) {
134                                 if(typeUpload === 'roles'){
135                                         $scope.uploadFile = $filter('csvToRoleObj')(reader.result);
136                                         if (debug){
137                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
138                                         }
139                                         $scope.progressMsg = 'Fetching & validating application roles...';
140                                                                 // fetch app roles
141                                                         RoleService.getRoles(appId).then(function (appRoles){
142                                                                 if (debug){
143                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
144                                                                 }
145                                                                         let availableRolesList = JSON.parse(appRoles.data);
146                                                                         appRoleFuncsResult = availableRolesList.availableRoles;
147                                                                 $scope.evalAppRolesCheckResults();
148                                                                 // Re sort by line for the confirmation dialog
149                                                         $scope.uploadFile.sort(getSortOrder('line', false));
150                                                         // We're done, confirm box may show the  table
151                                                         if (debug)
152                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
153                                                         $scope.progressMsg = 'Done.';
154                                                         $scope.isProcessing = false;
155                                                         $scope.isProcessedRecords = false;
156                                                         }, function(error) {
157                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
158                                                 $scope.isProcessing = false;
159                                                 $scope.isProcessedRecords = false;
160                                         });
161                                 } else if (typeUpload === 'roleFunctions'){
162                                         $scope.uploadFile = $filter('csvToRoleFuncObj')(reader.result);
163                                         if (debug){
164                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
165                                         }
166                                         $scope.progressMsg = 'Fetching & validating application role functions...';
167                                         //fetch app functions
168                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
169                                                 if (debug)
170                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoleFunctionList returned " + JSON.stringify(appFunctions.data));
171                                                                 let availableRoleFunctionsList = JSON.parse(appFunctions.data);
172                                                                 appFunctionsResult = availableRoleFunctionsList.availableRoleFunctions;
173                                                                 // fetch app roles
174                                                         RoleService.getRoles(appId).then(function (appRoles){
175                                                                 if (debug){
176                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
177                                                                 }
178                                                                         let availableRolesList = JSON.parse(appRoles.data);
179                                                                         appRoleFuncsResult = availableRolesList.availableRoles;
180                                                                 $scope.evalAppRoleFuncsCheckResults();
181                                                                 // Re sort by line for the confirmation dialog
182                                                         $scope.uploadFile.sort(getSortOrder('line', false));
183                                                         // We're done, confirm box may show the  table
184                                                         if (debug)
185                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
186                                                         $scope.progressMsg = 'Done.';
187                                                         $scope.isProcessing = false;
188                                                         $scope.isProcessedRecords = false;
189                                                         }, function(error) {
190                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
191                                                 $scope.isProcessing = false;
192                                                 $scope.isProcessedRecords = false;
193                                         });
194                                         },
195                         function(error) {
196                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
197                                 $scope.isProcessing = false;
198                         }
199                         );
200                                 } else if(typeUpload === 'functions'){
201                                         $scope.uploadFile = $filter('csvToFuncObj')(reader.result);
202                                         if (debug){
203                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
204                                         }
205                                         $scope.progressMsg = 'Fetching & validating the application functions...';
206                                         // fetch app functions
207                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
208                                                 if (debug)
209                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoleFunctionList returned " + JSON.stringify(appFunctions.data));
210                                                                 let availableRoleFunctionsList = JSON.parse(appFunctions.data);
211                                                                 appFunctionsResult = availableRoleFunctionsList.availableRoleFunctions;
212                                                 $scope.verifyFunctions();
213                                                 $scope.evalAppFunctionsCheckResults();
214                                         // Re sort by line for the confirmation dialog
215                                         $scope.uploadFile.sort(getSortOrder('line', false));
216                                         // We're done, confirm box may show the  table
217                                         if (debug)
218                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
219                                         $scope.progressMsg = 'Done.';
220                                         $scope.isProcessing = false;
221                                         $scope.isProcessedRecords = false;
222                                         },
223                         function(error) {
224                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
225                                 $scope.isProcessing = false;
226                                 $scope.isProcessedRecords = false;
227                         }
228                         );
229                                 } else if(typeUpload === 'globalRoleFunctions'){
230                                         $scope.uploadFile = $filter('csvToRoleFuncObj')(reader.result);
231                                         if (debug){
232                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
233                                         }
234                                         $scope.progressMsg = 'Fetching application global role functions...';
235                                         //fetch app functions
236                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
237                                                         if (debug)
238                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoleFunctionList returned " + JSON.stringify(appFunctions.data));
239                                                                 let availableRoleFunctionsList = JSON.parse(appFunctions.data);
240                                                                 appFunctionsResult = availableRoleFunctionsList.availableRoleFunctions;
241                                                                 // fetch app roles
242                                                         RoleService.getRoles(appId).then(function (appRoles){
243                                                                 if (debug){
244                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
245                                                                 }
246                                                                         let availableRolesList = JSON.parse(appRoles.data);
247                                                                         appRoleFuncsResult = availableRolesList.availableRoles;
248                                                                         appRoleFuncsResult.forEach(function(appRole) {
249                                                                                 if(appRole.name.toLowerCase().startsWith("global_")){
250                                                                                         appGlobalRolesResult.push(appRole);
251                                                                                 }
252                                                                                 });
253                                                                 $scope.evalAppRoleFuncsCheckResults(typeUpload);
254                                                                 // Re sort by line for the confirmation dialog
255                                                         $scope.uploadFile.sort(getSortOrder('line', false));
256                                                         // We're done, confirm box may show the  table
257                                                         if (debug)
258                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
259                                                         $scope.progressMsg = 'Done.';
260                                                         $scope.isProcessing = false;
261                                                         $scope.isProcessedRecords = false;
262                                                         }, function(error) {
263                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
264                                                 $scope.isProcessing = false;
265                                                 $scope.isProcessedRecords = false;
266                                         });
267                                                 },
268                             function(error) {
269                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
270                                 $scope.isProcessing = false;
271                             }
272                             );
273                                 }
274
275                         } // onload
276                         
277                         // Invoke the reader on the selected file
278                         reader.readAsText(this.fileToRead);
279                 }; 
280                 
281                 /**
282                  * Evaluates the result set returned by the role service.
283                  * Sets an uploadFile array element status if a functions is not
284                  * defined. Reads and writes scope variable uploadFile. Reads
285                  * closure variable appFunctionsResult.
286                  */
287                 $scope.verifyFunctions = () => {
288                         if (debug)
289                                 $log.debug('BulkRoleAndFunctionsModalCtrl::verifyFunctions: appFunctions is ' + JSON.stringify(appFunctionsResult));
290                         // check functions in upload file against defined app functions
291                         $scope.uploadFile.forEach( function (uploadRow) {
292                                 // skip rows that already have a defined status: headers etc.
293                                 if (uploadRow.status) {
294                                         if (debug)
295                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::verifyFunctions: skip row ' + uploadRow.line);
296                                         return;
297                                 }
298                                 for (var i=0; i < appFunctionsResult.length; i++) {
299                                         if (uploadRow.type.toUpperCase() === appFunctionsResult[i].type.toUpperCase()
300                                                 && uploadRow.instance.toUpperCase() === appFunctionsResult[i].code.toUpperCase()
301                                                 && uploadRow.action.toUpperCase() === appFunctionsResult[i].action.toUpperCase()) {
302                                                 if (debug)
303                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::verifyFunctions: match on function ' + uploadRow.type,
304                                                                 uploadRow.instance, uploadRow.type,  uploadRow.type);
305                                                 break;
306                                         }
307                                 }
308                         }); // foreach
309                 }; // verifyFunctions
310                 
311         /**
312                  * Evaluates the result set of existing functions returned by 
313                  * the Roleservice and list of functions found in the upload file. 
314                  * Reads and writes scope variable uploadFile. 
315                  * Reads closure variable appFunctionsResult.
316                  */
317                 $scope.evalAppFunctionsCheckResults = () => {
318                         if (debug)
319                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: uploadFile length is ' + $scope.uploadFile.length);
320                         $scope.uploadFile.forEach(function (uploadRow) {
321                                 if (uploadRow.status) {
322                                         if (debug)
323                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: skip row ' + uploadRow.line);
324                                         return;
325                                 }
326                                 // Search for the match in the app-functions
327                                 // array
328                                 let isFunctionExist = false;
329                                 appFunctionsResult.forEach( function (exixtingFuncObj) {
330                                                         if (uploadRow.type.toUpperCase() === exixtingFuncObj.type.toUpperCase()
331                                                                 && uploadRow.instance.toUpperCase() === exixtingFuncObj.code.toUpperCase()
332                                                                 && uploadRow.action.toUpperCase() === exixtingFuncObj.action.toUpperCase()) {
333                                                                 uploadRow.status = 'Function exits!';
334                                                                 uploadRow.isCreate = false;
335                                                                 isFunctionExist = true;
336                                                         }
337                                 }); // for each result
338                                 if(!isFunctionExist) {
339                                         if(/[^a-zA-Z0-9\-\.\_]/.test(uploadRow.type) 
340                                                         || (uploadRow.action !== '*' 
341                                                         && /[^a-zA-Z0-9\-\.\_]/.test(uploadRow.action))
342                                                         || /[^a-zA-Z0-9\-\:\_\./*]/.test(uploadRow.instance)
343                                                         || /[^a-zA-Z0-9\-\_ \.]/.test(uploadRow.name)){
344                                                 uploadRow.status = 'Invalid function';
345                                                 uploadRow.isCreate = false;
346                                         } else {
347                                                 if (debug){
348                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: new function ' 
349                                                                         + uploadRow);
350                                                 }
351                                                         // After much back-and-forth I decided a clear  indicator is better than blank in the table  status column.
352                                                 uploadRow.status = 'Create';
353                                                 uploadRow.isCreate = true;
354                                         }
355                                         }
356                         }); // for each row
357                 }; // evalAppFunctionsCheckResults
358                 
359                 /**
360                  * Evaluates the result set of existing roles returned by 
361                  * the Roleservice and list of roles found in the upload file. 
362                  * Reads and writes scope variable uploadFile. 
363                  * Reads closure variable appRolesResult.
364                  */
365                         $scope.evalAppRolesCheckResults = () => {
366                                 if (debug)
367                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: uploadFile length is ' + $scope.uploadFile.length);
368                                 $scope.uploadFile.forEach(function (uploadRow) {
369                                         if (uploadRow.status) {
370                                                 if (debug)
371                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: skip row ' + uploadRow.line);
372                                                 return;
373                                         }
374                                         // Search for the match in the app-roles
375                                 // array
376                                         let isRoleExist = false;
377                                         appRoleFuncsResult.forEach( function (existingRoleObj) {
378                                                                 if (uploadRow.role.toUpperCase() === existingRoleObj.name.toUpperCase()) {
379                                                                 uploadRow.status = 'Role exits!';
380                                                                 uploadRow.isCreate = false;
381                                                                 isRoleExist = true;
382                                                                 }
383                                         }); // for each result
384                                         if(!isRoleExist) {
385                                                 if(/[^a-zA-Z0-9\-\_ \.\/]/.test(uploadRow.role) ||
386                                                                 uploadRow.role.toLowerCase().startsWith("global_")){
387                                                         uploadRow.status = 'Invalid role!';
388                                                         uploadRow.isCreate = false;
389                                                 } else {
390                                                         if (debug){
391                                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: new function ' 
392                                                                         + uploadRow);
393                                                         }
394                                                         // After much back-and-forth I decided a clear  indicator is better than blank in the table  status column.
395                                                         uploadRow.status = 'Create';
396                                                         uploadRow.isCreate = true;
397                                                 }
398                                         }
399                                 }); // for each row
400                         }; // evalAppRolesCheckResults
401                         
402                         /**
403                          * Evaluates the result set of existing roles returned by 
404                          * the Roleservice and list of roles found in the upload file. 
405                          * Reads and writes scope variable uploadFile. 
406                          * Reads closure variable appRolesResult.
407                          */
408                         $scope.evalAppRoleFuncsCheckResults = (typeUpload) => {
409                                 if (debug)
410                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRoleFuncsCheckResults: uploadFile length is ' + $scope.uploadFile.length);
411                                 $scope.uploadFile.forEach(function (uploadRow) {
412                                         if (uploadRow.status) {
413                                                 if (debug)
414                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRoleFuncsCheckResults: skip row ' + uploadRow.line);
415                                                 return;
416                                         }
417                                         // Search for the match in the app-functions array
418                                         let isValidFunc = false;
419                                         appFunctionsResult.forEach(function (existingFuncObj){
420                                                 if(uploadRow.type.toUpperCase() === existingFuncObj.type.toUpperCase()
421                                                                         && uploadRow.instance.toUpperCase() === existingFuncObj.code.toUpperCase()
422                                                                         && uploadRow.action.toUpperCase() === existingFuncObj.action.toUpperCase()
423                                                                         && uploadRow.name.toUpperCase() === existingFuncObj.name.toUpperCase()){
424                                                         isValidFunc = true;
425                                                         }
426                                         });
427                                         
428                                         let isValidRole = false;
429                                         let isRoleFuncExist = false;
430                                         if(typeUpload === 'globalRoleFunctions'){
431                                                 // Search for the match in the app-role array
432                                                 appGlobalRolesResult.forEach( function (existingRoleObj) {
433                                                                         if (uploadRow.role.toUpperCase() === existingRoleObj.name.toUpperCase()) {
434                                                                                 isValidRole = true;
435                                                                                 if(isValidFunc){
436                                                                                         existingRoleObj.roleFunctions.forEach(function (existingRoleFuncObj){
437                                                                                                 if(uploadRow.type.toUpperCase() === existingRoleFuncObj.type.toUpperCase()
438                                                                                                 && uploadRow.instance.toUpperCase() === existingRoleFuncObj.code.toUpperCase()
439                                                                                                 && uploadRow.action.toUpperCase() === existingRoleFuncObj.action.toUpperCase()){
440                                                                                                         isRoleFuncExist = true;
441                                                                                         }
442                                                                                         });
443                                                                                 }
444                                                                         }
445                                                 }); // for each result
446                                         } else {
447                                                 // Search for the match in the app-role array
448                                                 appRoleFuncsResult.forEach( function (existingRoleObj) {
449                                                                         if (uploadRow.role.toUpperCase() === existingRoleObj.name.toUpperCase()) {
450                                                                                 isValidRole = true;
451                                                                                 if(isValidFunc){
452                                                                                         existingRoleObj.roleFunctions.forEach(function (existingRoleFuncObj){
453                                                                                                 if(uploadRow.type.toUpperCase() === existingRoleFuncObj.type.toUpperCase()
454                                                                                                 && uploadRow.instance.toUpperCase() === existingRoleFuncObj.code.toUpperCase()
455                                                                                                 && uploadRow.action.toUpperCase() === existingRoleFuncObj.action.toUpperCase()){
456                                                                                                         isRoleFuncExist = true;
457                                                                                         }
458                                                                                         });
459                                                                                 }       
460                                                                         }
461                                                 }); // for each result
462                                         }
463                                         
464                                 uploadRow.isCreate = false;
465                                 if(typeUpload === 'globalRoleFunctions' && (!isValidRole || !isValidFunc)){
466                                         uploadRow.status = 'Invalid global role function!';
467                                 } else if(typeUpload !== 'globalRoleFunctions' && (!isValidRole || !isValidFunc)){
468                                         uploadRow.status = 'Invalid role function!';
469                                 } else if(typeUpload === 'globalRoleFunctions' && !isRoleFuncExist) {
470                                         uploadRow.status = 'Add global role function!';
471                                                 uploadRow.isCreate = true;
472                                 } else if(typeUpload !== 'globalRoleFunctions' && !isRoleFuncExist){
473                                         uploadRow.status = 'Add role function!';
474                                         uploadRow.isCreate = true;
475                                 } else if(typeUpload === 'globalRoleFunctions'){
476                                         uploadRow.status = 'Global role function exists!';
477                                 } else {
478                                         uploadRow.status = 'Role function exists!';
479                                 }
480                                         
481                                 }); // for each row
482                         }; // evalAppRolesCheckResults
483                         
484                 
485         /**
486          * Sends requests to Portal BE requesting application functions assignment.
487                  * That endpoint handles creation of the application functions in the 
488                  * external auth system if necessary. Reads closure variable appFunctionsResult.
489                  * Invoked by the Next button on the confirmation dialog.
490                  */
491                 $scope.updateFunctionsInDB = () => {
492                         $scope.isProcessing = true;
493                         $scope.conformMsg = '';
494                         $scope.isProcessedRecords = true;
495                         $scope.progressMsg = 'Sending requests to application..';
496                         if (debug)
497                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: request length is ' + appUserRolesRequest.length);
498                         var numberFunctionsSucceeded = 0;
499                         let promises = [];
500                         $scope.uploadFile.forEach(function(appFuncPostData) {
501                                 if (debug) 
502                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: appFuncPostData is ' + JSON.stringify(appFuncPostData));
503                                 let updateFunctionsFinalPostData = {
504                                          type: appFuncPostData.type, 
505                                          code: appFuncPostData.instance, 
506                                          action: appFuncPostData.action,
507                                          name: appFuncPostData.name
508                         };
509                      if (debug)
510                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: updateFunctionsFinalPostData is ' + JSON.stringify(updateFunctionsFinalPostData));
511                      let updatePromise = {};
512                      if(appFuncPostData.isCreate){
513                      updatePromise = functionalMenuService.saveBulkFunction(appId, updateFunctionsFinalPostData).promise().then(res => {
514                          if (debug)
515                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: updated successfully: ' + JSON.stringify(res));
516                          numberFunctionsSucceeded++;
517                      }).catch(err => {
518                          // What to do if one of many fails??
519                          $log.error('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB failed: ', err);
520                          confirmBoxService.showInformation(
521                                          'Failed to update the application functions. ' +
522                                          'Error: ' + err.status).then(isConfirmed => { });
523                      }).finally( () => {
524                      });
525                         }
526                     promises.push(updatePromise);
527                  }); // for each
528                         
529                  // Run all the promises
530                  $q.all(promises).then(function(){
531                          $scope.conformMsg  = 'Processed ' + numberFunctionsSucceeded + ' records.';
532                          $scope.isProcessing = false;
533                          $scope.isProcessedRecords = true;
534                          $scope.uploadFile = [];
535                  });
536              }; // updateFunctionsInDB
537              
538              /**
539                  * Sends requests to Portal BE requesting application functions assignment.
540                  * That endpoint handles creation of the application role in the 
541                  * external auth system if necessary. Reads closure variable appRoleFuncResult.
542                  * Invoked by the Next button on the confirmation dialog.
543                  */
544                         $scope.updateRolesInDB = () => {
545                                 $scope.isProcessing = true;
546                                 $scope.conformMsg = '';
547                                 $scope.isProcessedRecords = true;
548                                 $scope.progressMsg = 'Sending requests to application..';
549                                 if (debug)
550                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: request length is ' + appUserRolesRequest.length);
551                                 var numberRolesSucceeded = 0;
552                                 let promises = [];
553                                 $scope.uploadFile.forEach(function(appRolePostData) {
554                                         let priority = parseInt(appRolePostData.priority);
555                                         if (debug) 
556                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: appRolePostData is ' + JSON.stringify(appFuncPostData));
557                                         let uplaodRolePostData = "";
558                                         if(isNaN(priority)){
559                                         uplaodRolePostData = {
560                                                  name: appRolePostData.role,
561                                                  active: true,
562                                                         }
563                                         } else {
564                                                 uplaodRolePostData = {
565                                          name: appRolePostData.role, 
566                                          priority: appRolePostData.priority, 
567                                          active: true,
568                                                 }
569                                         }
570                                                 var postData = {
571                                                                 role: uplaodRolePostData,
572                                                                 roleFunctions: [],
573                                                                 childRoles: []
574                                                         }
575                           if (debug)
576                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: uplaodRoleFinalPostData is ' + JSON.stringify(uplaodRoleFinalPostData));
577                           let updatePromise = {};
578                           if(appRolePostData.isCreate){
579                           updatePromise = functionalMenuService.saveBulkRole(appId, JSON.stringify(postData)).promise().then(res => {
580                                  if (debug)
581                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: updated successfully: ' + JSON.stringify(res));
582                                 numberRolesSucceeded++;
583                           }).catch(err => {
584                                  // What to do if one of many fails??
585                                  $log.error('BulkRoleAndFunctionsModalCtrl::updateRolesInDB failed: ', err);
586                                  confirmBoxService.showInformation(
587                                                  'Failed to update the application role. ' +
588                                                  'Error: ' + err.status).then(isConfirmed => { });
589                           }).finally( () => {
590                           });
591                         }
592                          promises.push(updatePromise);
593                          }); // for each
594                                 
595                          // Run all the promises
596                          $q.all(promises).then(function(){
597                                 if(numberRolesSucceeded == 0){
598                                  $scope.conformMsg  = 'Processed ' + numberRolesSucceeded + ' records';
599                                  }else{
600                                  $scope.conformMsg  = 'Processed ' + numberRolesSucceeded + ' records. Please sync roles to reflect in portal';
601                                  }                               $scope.isProcessing = false;
602                                  $scope.isProcessedRecords = true;
603                                  $scope.uploadFile = [];
604                          });
605                   }; // updateRolesInDB
606                   
607                 /**
608                  * Sends requests to Portal BE requesting role function assignment.
609                  * That endpoint handles adding role function in the external auth system
610                  * if necessary.Invoked by the Next button on the confirmation dialog.
611                  */
612                         $scope.updateRoleFunctionsInDB = () => {
613                                 $scope.isProcessing = true;
614                                 $scope.conformMsg = '';
615                                 $scope.isProcessedRecords = true;
616                                 $scope.progressMsg = 'Sending requests to application..';
617                                 if (debug)
618                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: request length is ' + appUserRolesRequest.length);
619                                 var numberRoleFunctionSucceeded = 0;
620                                 let promises = [];
621                                 $scope.uploadFile.forEach(function(appRoleFuncPostData) {
622                                         if (debug) 
623                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: appRoleFuncPostData is ' + JSON.stringify(appFuncPostData));
624                                         let updateRoleFunctionFinalPostData = {
625                                                  roleName: appRoleFuncPostData.role,
626                                                  type: appRoleFuncPostData.type, 
627                                                  instance: appRoleFuncPostData.instance, 
628                                                  action: appRoleFuncPostData.action,
629                                                  name: appRoleFuncPostData.name,
630                                                  isGlobalRolePartnerFunc: false
631                             };
632                          if (debug)
633                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: updateRoleFunctionFinalPostData is ' + JSON.stringify(updateFunctionsFinalPostData));
634                          let updatePromise = {};
635                          if(appRoleFuncPostData.isCreate){
636                          updatePromise = functionalMenuService.updateBulkRoleFunction(appId, updateRoleFunctionFinalPostData).promise().then(res => {
637                                  if (debug)
638                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: updated successfully: ' + JSON.stringify(res));
639                                  numberRoleFunctionSucceeded++;
640                          }).catch(err => {
641                                  // What to do if one of many fails??
642                                  $log.error('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB failed: ', err);
643                                  confirmBoxService.showInformation(
644                                                  'Failed to update the application role function. ' +
645                                                  'Error: ' + err.status).then(isConfirmed => { });
646                          }).finally( () => {
647                          });
648                         }
649                         promises.push(updatePromise);
650                          }); // for each
651                                 
652                          // Run all the promises
653                          $q.all(promises).then(function(){
654                                  if(numberRoleFunctionSucceeded == 0){
655                                  $scope.conformMsg  = 'Processed ' + numberRoleFunctionSucceeded + ' records';
656                                  }else{
657                                  $scope.conformMsg  = 'Processed ' + numberRoleFunctionSucceeded + ' records. Please sync roles to reflect in portal';
658                                  }                               $scope.isProcessing = false;
659                                  $scope.isProcessedRecords = true;
660                                  $scope.uploadFile = [];
661                          });
662                   }; // updateRoleFunctionsInDB
663                   
664                 /**
665                          * Sends requests to Portal requesting global role functions assignment.
666                          * That endpoint handles updating global role functions in the external auth system
667                          * if necessary. Invoked by the Next button on the confirmation dialog.
668                          */
669                         $scope.updateGlobalRoleFunctionsInDB = () => {
670                                 $scope.isProcessing = true;
671                                 $scope.conformMsg = '';
672                                 $scope.isProcessedRecords = true;
673                                 $scope.progressMsg = 'Sending requests to application..';
674                                 if (debug)
675                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: request length is ' + appUserRolesRequest.length);
676                                 var numberGlobalRoleFunctionSucceeded = 0;
677                                 let promises = [];
678                                 $scope.uploadFile.forEach(function(appRoleFuncPostData) {
679                                         if (debug) 
680                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: appRoleFuncPostData is ' + JSON.stringify(appFuncPostData));
681                                         let updateGlobalRoleFunctionFinalPostData = {
682                                                  roleName: appRoleFuncPostData.role,
683                                                  type: appRoleFuncPostData.type, 
684                                                  instance: appRoleFuncPostData.instance, 
685                                                  action: appRoleFuncPostData.action,
686                                                  name: appRoleFuncPostData.name,
687                                                  isGlobalRolePartnerFunc: true
688                                 };
689                              if (debug)
690                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: updateRoleFunctionFinalPostData is ' + JSON.stringify(updateFunctionsFinalPostData));
691                              let updatePromise = {};
692                              if(appRoleFuncPostData.isCreate){
693                              updatePromise = functionalMenuService.updateBulkRoleFunction(appId, updateGlobalRoleFunctionFinalPostData).promise().then(res => {
694                                  if (debug)
695                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: updated successfully: ' + JSON.stringify(res));
696                                  numberGlobalRoleFunctionSucceeded++;
697                              }).catch(err => {
698                                  // What to do if one of many fails??
699                                  $log.error('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB failed: ', err);
700                                  confirmBoxService.showInformation(
701                                                  'Failed to update the global role partner function. ' +
702                                                  'Error: ' + err.status).then(isConfirmed => { });
703                              }).finally( () => {
704                              });
705                                 }
706                             promises.push(updatePromise);
707                          }); // for each
708                                 
709                          // Run all the promises
710                          $q.all(promises).then(function(){
711                                  if(numberGlobalRoleFunctionSucceeded == 0){
712                                          $scope.conformMsg  = 'Processed ' + numberGlobalRoleFunctionSucceeded + ' records';
713                                  }else{
714                                          $scope.conformMsg  = 'Processed ' + numberGlobalRoleFunctionSucceeded + ' records. Please sync roles to reflect in portal';
715                                  }
716                                  $scope.isProcessing = false;
717                                  $scope.isProcessedRecords = true;
718                                  $scope.uploadFile = [];
719                          });
720                        }; // updateGlobalRoleFunctionsInDB
721                        
722                 // Sets the variable that hides/reveals the user controls
723                 $scope.step2 = () => {
724                         this.fileSelected = false;
725                         $scope.selectedFile = null;
726                         $scope.fileModel = null;
727                         this.step1 = false;                     
728                 }
729                 
730              // Navigate between dialog screens using step number: 1,2,...
731              $scope.navigateBack = () => {
732                  this.step1 = true;
733                  this.fileSelected = false;
734              };
735              
736              // Opens a dialog to show the data to be uploaded.
737              // Invoked by the upload button on the bulk user dialog.
738              $scope.confirmUpload = (typeUpload) => {
739                 // Start the process
740                 $scope.readValidateFile(typeUpload);
741                 // Dialog shows progress
742                 if(typeUpload === 'functions'){
743                         $modal.open({
744                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-functions-confirm.html',
745                         controller: '',
746                         sizeClass: 'modal-medium', 
747                         resolve:'',
748                         scope: $scope
749                     })
750                 } else if(typeUpload === 'roleFunctions'){
751                         $modal.open({
752                                 templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-role-functions-confirm.html',
753                         controller: '',
754                         sizeClass: 'modal-medium', 
755                         resolve:'',
756                         scope: $scope
757                     })
758                         
759                 } else if(typeUpload === 'roles'){
760                         $modal.open({
761                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-roles-confirm.html',
762                         controller: '',
763                         sizeClass: 'modal-medium', 
764                         resolve:'',
765                         scope: $scope
766                     })
767                 } else if(typeUpload === 'globalRoleFunctions'){
768                         $modal.open({
769                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-global-role-functions-confirm.html',
770                         controller: '',
771                         sizeClass: 'modal-medium', 
772                         resolve:'',
773                         scope: $scope
774                     })
775                 }
776              };
777
778              // Invoked by the Cancel button on the confirmation dialog.
779              $scope.cancelUpload = () => {
780                  ngDialog.close();
781              };
782              
783              init();
784         } // constructor
785     } // class
786     BulkRoleAndFunctionsModalCtrl.$inject = ['$scope', '$log', '$filter', '$q',  '$modalInstance', '$modal', 'ngDialog', 'message', 'confirmBoxService', 'usersService', 'applicationsService', 'functionalMenuService', 'RoleService'];    
787     angular.module('ecompApp').controller('BulkRoleAndFunctionsModalCtrl', BulkRoleAndFunctionsModalCtrl);
788
789     angular.module('ecompApp').directive('fileChange', ['$parse', function($parse){
790         return {
791                 require: 'ngModel',
792             restrict: 'A',
793             link : function($scope, element, attrs, ngModel) {
794                 var attrHandler = $parse(attrs['fileChange']);
795                 var handler=function(e) {
796                         $scope.$apply(function() {
797                                 attrHandler($scope, { $event:e, files:e.target.files } );
798                                 $scope.selectedFile = e.target.files[0].name;
799                         });
800                 };
801                 element[0].addEventListener('change',handler,false);
802            }
803         }
804     }]);
805     
806     angular.module('ecompApp').filter('csvToFuncObj',function() {
807         return function(input) {
808             var result = [];
809             var len, i, line, o;
810                 var lines = input.split('\n');
811             // Need 1-based index below
812             for (len = lines.length, i = 1; i <= len; ++i) {
813                 // Use 0-based index for array
814                 line = lines[i - 1].trim();
815                         if (line.length == 0) {
816                                 // console.log("Skipping blank line");
817                                 result.push({
818                                         line: i,
819                                         type: '',
820                                         instance: '',
821                                         action: '',
822                                         name: '',
823                                         status: 'Blank line'
824                                 });
825                                 continue;
826                         }
827                         o = line.split(',');
828                         if (o.length !== 4) {
829                                 // other lengths not valid for upload
830                                 result.push({
831                                         line: i,
832                                         type: o[0],
833                                         instance: o[1],
834                                         action: o[2],
835                                         name: '',
836                                         status: 'Failed to find 4 comma-separated values'
837                                 });
838                         }
839                         else {
840                                 // console.log("Valid line: ", val);
841                                 let entry = {
842                                                 line: i,
843                                                 type: o[0],
844                                                 instance: o[1],
845                                                 action: o[2],
846                                                 name: o[3]
847                                                 // leave status undefined, this
848                                                 // could be valid.
849                                 };
850                                 if (o[0].toLowerCase() === 'type') {
851                                         // not valid for upload, so set status
852                                         entry.status = 'Header';
853                                 }
854                                 else if (o[0].toLowerCase() === 'instance') {
855                                         // not valid for upload, so set status
856                                         entry.status = 'Header';
857                                 }
858                                 else if (o[0].toLowerCase() === 'action') {
859                                         // not valid for upload, so set status
860                                         entry.status = 'Header';
861                                 }
862                                 else if (o[0].toLowerCase() === 'name') {
863                                         // not valid for upload, so set status
864                                         entry.status = 'Header';
865                                 }
866                                 else if (o[0].trim() == '' || o[1].trim() == '' ||  o[2].trim() == '' ||  o[3].trim() == '') {
867                                         // defend against line with only a
868                                         // single comma etc.
869                                         entry.status = 'Failed to find non-empty values';                                       
870                                 }
871                                 result.push(entry);
872                         } // len 2
873             } // for
874             return result;
875         };
876     });
877     
878     angular.module('ecompApp').filter('csvToRoleFuncObj',function() {
879         return function(input) {
880             var result = [];
881             var len, i, line, o;
882                 var lines = input.split('\n');
883             // Need 1-based index below
884             for (len = lines.length, i = 1; i <= len; ++i) {
885                 // Use 0-based index for array
886                 line = lines[i - 1].trim();
887                         if (line.length == 0) {
888                                 // console.log("Skipping blank line");
889                                 result.push({
890                                         line: i,
891                                         role:'',
892                                         type: '',
893                                         instance: '',
894                                         action: '',
895                                         name: '',
896                                         status: 'Blank line'
897                                 });
898                                 continue;
899                         }
900                         o = line.split(',');
901                         if (o.length !== 5) {
902                                 // other lengths not valid for upload
903                                 result.push({
904                                         line: i,
905                                         role: o[0],
906                                         type: o[1],
907                                         instance: o[2],
908                                         action: o[3],
909                                         name: '',
910                                         status: 'Failed to find 4 comma-separated values'
911                                 });
912                         }
913                         else {
914                                 // console.log("Valid line: ", val);
915                                 let entry = {
916                                                 line: i,
917                                                 role: o[0],
918                                                 type: o[1],
919                                                 instance: o[2],
920                                                 action: o[3],
921                                                 name: o[4]
922                                                 // leave status undefined, this
923                                                 // could be valid.
924                                 };
925                                 if (o[0].toLowerCase() === 'role') {
926                                         // not valid for upload, so set status
927                                         entry.status = 'Header';
928                                 } else if (o[0].toLowerCase() === 'type') {
929                                         // not valid for upload, so set status
930                                         entry.status = 'Header';
931                                 }
932                                 else if (o[0].toLowerCase() === 'instance') {
933                                         // not valid for upload, so set status
934                                         entry.status = 'Header';
935                                 }
936                                 else if (o[0].toLowerCase() === 'action') {
937                                         // not valid for upload, so set status
938                                         entry.status = 'Header';
939                                 }
940                                 else if (o[0].toLowerCase() === 'name') {
941                                         // not valid for upload, so set status
942                                         entry.status = 'Header';
943                                 }
944                                 else if (o[0].trim() == '' || o[1].trim() == '' ||  o[2].trim() == '' ||  o[3].trim() == '' || o[4].trim() == '') {
945                                         // defend against line with only a
946                                         // single comma etc.
947                                         entry.status = 'Failed to find non-empty values';                                       
948                                 }
949                                 result.push(entry);
950                         } // len 2
951             } // for
952             return result;
953         };
954     });
955     
956     angular.module('ecompApp').filter('csvToRoleObj',function() {
957         return function(input) {
958             var result = [];
959             var len, i, line, o;
960                 var lines = input.split('\n');
961             // Need 1-based index below
962             for (len = lines.length, i = 1; i <= len; ++i) {
963                 // Use 0-based index for array
964                 line = lines[i - 1].trim();
965                         if (line.length == 0) {
966                                 // console.log("Skipping blank line");
967                                 result.push({
968                                         line: i,
969                                         role:'',
970                                         priority: '',
971                                         status: 'Blank line'
972                                 });
973                                 continue;
974                         }
975                         o = line.split(',');
976                         if (o.length === 0 && line.length !== 0) {
977                                 // other lengths not valid for upload
978                                 result.push({
979                                         line: i,
980                                         role: o[0],
981                                         priority:null
982                                 });
983                         }
984                         else {
985                                 // console.log("Valid line: ", val);
986                                 let entry = {
987                                                 line: i,
988                                                 role: o[0],
989                                                 priority: o[1]
990                                                 // leave status undefined, this
991                                                 // could be valid.
992                                 };
993                                 if (o[0].toLowerCase() === 'role') {
994                                         // not valid for upload, so set status
995                                         entry.status = 'Header';
996                                 }
997                                 if (o[0].toLowerCase() === 'priority') {
998                                         // not valid for upload, so set status
999                                         entry.status = 'Header';
1000                                 }
1001                                 else if (o[0].trim() == '') {
1002                                         // defend against line with only a
1003                                         // single comma etc.
1004                                         entry.status = 'Failed to find non-empty values';                                       
1005                                 }
1006                                 result.push(entry);
1007                         } // len 2
1008             } // for
1009             return result;
1010         };
1011     });
1012     
1013 })();