2  * ============LICENSE_START==========================================
 
   4  * ===================================================================
 
   5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
 
   6  * ===================================================================
 
   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
 
  13  *             http://www.apache.org/licenses/LICENSE-2.0
 
  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.
 
  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
 
  26  *             https://creativecommons.org/licenses/by/4.0/
 
  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.
 
  34  * ============LICENSE_END============================================
 
  39  * bulk upload role-functions controller
 
  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
 
  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 = [];
 
  54                 var appId = message.appid;
 
  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'}
 
  63                  $scope.selectedUploadType =   $scope.ngRepeatBulkUploadOptions[0];
 
  64                  $scope.UploadTypeInstruction = "Function Type, Function Instance, Function Action, Function Name";
 
  65                  $scope.changeUploadTypeInstruction = function(typeInstrc){
 
  68                                 $scope.UploadTypeInstruction = "Function Type, Function Instance, Function Action, Function Name";
 
  71                                 $scope.UploadTypeInstruction = "Role Name, Priority (Optional)";
 
  74                                 $scope.UploadTypeInstruction = "Role Name, Function Type, Function Instance, Function Action, Function Name";
 
  77                                 $scope.UploadTypeInstruction = "Global Role Name, Function Type, Function Instance, Function Action, Function Name";
 
  83                                 $log.debug('BulkRoleAndFunctionsModalCtrl::init');
 
  84                         // Angular insists on this.
 
  85                         $scope.fileModel = {};
 
  86                         // Enable modal controls
 
  89                         this.fileSelected = false;      
 
  91                         $scope.isProcessedRecords = false;
 
  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];
 
 101                     else if (aProp < bProp) 
 
 108                 // Caches the file name supplied by the event handler.
 
 109                 $scope.fileChangeHandler = (event, files) => {
 
 110                         this.fileSelected = true;
 
 111                         this.fileToRead = files[0];
 
 113                                 $log.debug("BulkRoleAndFunctionsModalCtrl::fileChangeHandler: file is ", this.fileToRead);
 
 114                 }; // file change handler
 
 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
 
 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.
 
 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);
 
 137                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
 
 139                                         $scope.progressMsg = 'Fetching & validating application roles...';
 
 141                                                         RoleService.getRoles(appId).then(function (appRoles){
 
 143                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
 
 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
 
 152                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
 
 153                                                         $scope.progressMsg = 'Done.';
 
 154                                                         $scope.isProcessing = false;
 
 155                                                         $scope.isProcessedRecords = false;
 
 157                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
 
 158                                                 $scope.isProcessing = false;
 
 159                                                 $scope.isProcessedRecords = false;
 
 161                                 } else if (typeUpload === 'roleFunctions'){
 
 162                                         $scope.uploadFile = $filter('csvToRoleFuncObj')(reader.result);
 
 164                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
 
 166                                         $scope.progressMsg = 'Fetching & validating application role functions...';
 
 167                                         //fetch app functions
 
 168                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
 
 170                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoleFunctionList returned " + JSON.stringify(appFunctions.data));
 
 171                                                                 let availableRoleFunctionsList = JSON.parse(appFunctions.data);
 
 172                                                                 appFunctionsResult = availableRoleFunctionsList.availableRoleFunctions;
 
 174                                                         RoleService.getRoles(appId).then(function (appRoles){
 
 176                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
 
 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
 
 185                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
 
 186                                                         $scope.progressMsg = 'Done.';
 
 187                                                         $scope.isProcessing = false;
 
 188                                                         $scope.isProcessedRecords = false;
 
 190                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
 
 191                                                 $scope.isProcessing = false;
 
 192                                                 $scope.isProcessedRecords = false;
 
 196                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
 
 197                                 $scope.isProcessing = false;
 
 200                                 } else if(typeUpload === 'functions'){
 
 201                                         $scope.uploadFile = $filter('csvToFuncObj')(reader.result);
 
 203                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
 
 205                                         $scope.progressMsg = 'Fetching & validating the application functions...';
 
 206                                         // fetch app functions
 
 207                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
 
 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
 
 218                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
 
 219                                         $scope.progressMsg = 'Done.';
 
 220                                         $scope.isProcessing = false;
 
 221                                         $scope.isProcessedRecords = false;
 
 224                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
 
 225                                 $scope.isProcessing = false;
 
 226                                 $scope.isProcessedRecords = false;
 
 229                                 } else if(typeUpload === 'globalRoleFunctions'){
 
 230                                         $scope.uploadFile = $filter('csvToRoleFuncObj')(reader.result);
 
 232                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile onload: data length is ' + $scope.uploadFile.length);
 
 234                                         $scope.progressMsg = 'Fetching application global role functions...';
 
 235                                         //fetch app functions
 
 236                                         RoleService.getRoleFunctionList(appId).then(function (appFunctions){
 
 238                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoleFunctionList returned " + JSON.stringify(appFunctions.data));
 
 239                                                                 let availableRoleFunctionsList = JSON.parse(appFunctions.data);
 
 240                                                                 appFunctionsResult = availableRoleFunctionsList.availableRoleFunctions;
 
 242                                                         RoleService.getRoles(appId).then(function (appRoles){
 
 244                                                         $log.debug("BulkRoleAndFunctionsModalCtrl::readValidateFile: getRoles returned " + JSON.stringify(appFunctions.data));
 
 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);
 
 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
 
 258                                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::readValidateFile inner-then ends');
 
 259                                                         $scope.progressMsg = 'Done.';
 
 260                                                         $scope.isProcessing = false;
 
 261                                                         $scope.isProcessedRecords = false;
 
 263                                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app roles info');
 
 264                                                 $scope.isProcessing = false;
 
 265                                                 $scope.isProcessedRecords = false;
 
 269                                 $log.error('BulkUserModalCtrl::readValidateFile: failed retrieving app functions info');
 
 270                                 $scope.isProcessing = false;
 
 277                         // Invoke the reader on the selected file
 
 278                         reader.readAsText(this.fileToRead);
 
 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.
 
 287                 $scope.verifyFunctions = () => {
 
 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) {
 
 295                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::verifyFunctions: skip row ' + uploadRow.line);
 
 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()) {
 
 303                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::verifyFunctions: match on function ' + uploadRow.type,
 
 304                                                                 uploadRow.instance, uploadRow.type,  uploadRow.type);
 
 309                 }; // verifyFunctions
 
 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.
 
 317                 $scope.evalAppFunctionsCheckResults = () => {
 
 319                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: uploadFile length is ' + $scope.uploadFile.length);
 
 320                         $scope.uploadFile.forEach(function (uploadRow) {
 
 321                                 if (uploadRow.status) {
 
 323                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: skip row ' + uploadRow.line);
 
 326                                 // Search for the match in the app-functions
 
 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;
 
 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;
 
 348                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppFunctionsCheckResults: new function ' 
 
 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;
 
 357                 }; // evalAppFunctionsCheckResults
 
 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.
 
 365                         $scope.evalAppRolesCheckResults = () => {
 
 367                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: uploadFile length is ' + $scope.uploadFile.length);
 
 368                                 $scope.uploadFile.forEach(function (uploadRow) {
 
 369                                         if (uploadRow.status) {
 
 371                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: skip row ' + uploadRow.line);
 
 374                                         // Search for the match in the app-roles
 
 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;
 
 383                                         }); // for each result
 
 385                                                 if(/[^a-zA-Z0-9\-\_ \.\/]/.test(uploadRow.role) ||
 
 386                                                                 uploadRow.role.toLowerCase().startsWith("global_")){
 
 387                                                         uploadRow.status = 'Invalid role!';
 
 388                                                         uploadRow.isCreate = false;
 
 391                                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRolesCheckResults: new function ' 
 
 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;
 
 400                         }; // evalAppRolesCheckResults
 
 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.
 
 408                         $scope.evalAppRoleFuncsCheckResults = (typeUpload) => {
 
 410                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRoleFuncsCheckResults: uploadFile length is ' + $scope.uploadFile.length);
 
 411                                 $scope.uploadFile.forEach(function (uploadRow) {
 
 412                                         if (uploadRow.status) {
 
 414                                                         $log.debug('BulkRoleAndFunctionsModalCtrl::evalAppRoleFuncsCheckResults: skip row ' + uploadRow.line);
 
 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()){
 
 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()) {
 
 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;
 
 445                                                 }); // for each result
 
 447                                                 // Search for the match in the app-role array
 
 448                                                 appRoleFuncsResult.forEach( function (existingRoleObj) {
 
 449                                                                         if (uploadRow.role.toUpperCase() === existingRoleObj.name.toUpperCase()) {
 
 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;
 
 461                                                 }); // for each result
 
 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!';
 
 478                                         uploadRow.status = 'Role function exists!';
 
 482                         }; // evalAppRolesCheckResults
 
 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.
 
 491                 $scope.updateFunctionsInDB = () => {
 
 492                         $scope.isProcessing = true;
 
 493                         $scope.conformMsg = '';
 
 494                         $scope.isProcessedRecords = true;
 
 495                         $scope.progressMsg = 'Sending requests to application..';
 
 497                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: request length is ' + appUserRolesRequest.length);
 
 498                         var numberFunctionsSucceeded = 0;
 
 500                         $scope.uploadFile.forEach(function(appFuncPostData) {
 
 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
 
 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 => {
 
 515                                  $log.debug('BulkRoleAndFunctionsModalCtrl::updateFunctionsInDB: updated successfully: ' + JSON.stringify(res));
 
 516                          numberFunctionsSucceeded++;
 
 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 => { });
 
 526                     promises.push(updatePromise);
 
 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 = [];
 
 536              }; // updateFunctionsInDB
 
 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.
 
 544                         $scope.updateRolesInDB = () => {
 
 545                                 $scope.isProcessing = true;
 
 546                                 $scope.conformMsg = '';
 
 547                                 $scope.isProcessedRecords = true;
 
 548                                 $scope.progressMsg = 'Sending requests to application..';
 
 550                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: request length is ' + appUserRolesRequest.length);
 
 551                                 var numberRolesSucceeded = 0;
 
 553                                 $scope.uploadFile.forEach(function(appRolePostData) {
 
 554                                         let priority = parseInt(appRolePostData.priority);
 
 556                                                 $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: appRolePostData is ' + JSON.stringify(appFuncPostData));
 
 557                                         let uplaodRolePostData = "";
 
 559                                         uplaodRolePostData = {
 
 560                                                  name: appRolePostData.role,
 
 564                                                 uplaodRolePostData = {
 
 565                                          name: appRolePostData.role, 
 
 566                                          priority: appRolePostData.priority, 
 
 571                                                                 role: uplaodRolePostData,
 
 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 => {
 
 581                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateRolesInDB: updated successfully: ' + JSON.stringify(res));
 
 582                                 numberRolesSucceeded++;
 
 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 => { });
 
 592                          promises.push(updatePromise);
 
 595                          // Run all the promises
 
 596                          $q.all(promises).then(function(){
 
 597                                 if(numberRolesSucceeded == 0){
 
 598                                  $scope.conformMsg  = 'Processed ' + numberRolesSucceeded + ' records';
 
 600                                  $scope.conformMsg  = 'Processed ' + numberRolesSucceeded + ' records. Please sync roles to reflect in portal';
 
 601                                  }                               $scope.isProcessing = false;
 
 602                                  $scope.isProcessedRecords = true;
 
 603                                  $scope.uploadFile = [];
 
 605                   }; // updateRolesInDB
 
 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.
 
 612                         $scope.updateRoleFunctionsInDB = () => {
 
 613                                 $scope.isProcessing = true;
 
 614                                 $scope.conformMsg = '';
 
 615                                 $scope.isProcessedRecords = true;
 
 616                                 $scope.progressMsg = 'Sending requests to application..';
 
 618                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: request length is ' + appUserRolesRequest.length);
 
 619                                 var numberRoleFunctionSucceeded = 0;
 
 621                                 $scope.uploadFile.forEach(function(appRoleFuncPostData) {
 
 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
 
 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 => {
 
 638                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateRoleFunctionsInDB: updated successfully: ' + JSON.stringify(res));
 
 639                                  numberRoleFunctionSucceeded++;
 
 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 => { });
 
 649                         promises.push(updatePromise);
 
 652                          // Run all the promises
 
 653                          $q.all(promises).then(function(){
 
 654                                  if(numberRoleFunctionSucceeded == 0){
 
 655                                  $scope.conformMsg  = 'Processed ' + numberRoleFunctionSucceeded + ' records';
 
 657                                  $scope.conformMsg  = 'Processed ' + numberRoleFunctionSucceeded + ' records. Please sync roles to reflect in portal';
 
 658                                  }                               $scope.isProcessing = false;
 
 659                                  $scope.isProcessedRecords = true;
 
 660                                  $scope.uploadFile = [];
 
 662                   }; // updateRoleFunctionsInDB
 
 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.
 
 669                         $scope.updateGlobalRoleFunctionsInDB = () => {
 
 670                                 $scope.isProcessing = true;
 
 671                                 $scope.conformMsg = '';
 
 672                                 $scope.isProcessedRecords = true;
 
 673                                 $scope.progressMsg = 'Sending requests to application..';
 
 675                                         $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: request length is ' + appUserRolesRequest.length);
 
 676                                 var numberGlobalRoleFunctionSucceeded = 0;
 
 678                                 $scope.uploadFile.forEach(function(appRoleFuncPostData) {
 
 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
 
 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 => {
 
 695                                          $log.debug('BulkRoleAndFunctionsModalCtrl::updateGlobalRoleFunctionsInDB: updated successfully: ' + JSON.stringify(res));
 
 696                                  numberGlobalRoleFunctionSucceeded++;
 
 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 => { });
 
 706                             promises.push(updatePromise);
 
 709                          // Run all the promises
 
 710                          $q.all(promises).then(function(){
 
 711                                  if(numberGlobalRoleFunctionSucceeded == 0){
 
 712                                          $scope.conformMsg  = 'Processed ' + numberGlobalRoleFunctionSucceeded + ' records';
 
 714                                          $scope.conformMsg  = 'Processed ' + numberGlobalRoleFunctionSucceeded + ' records. Please sync roles to reflect in portal';
 
 716                                  $scope.isProcessing = false;
 
 717                                  $scope.isProcessedRecords = true;
 
 718                                  $scope.uploadFile = [];
 
 720                        }; // updateGlobalRoleFunctionsInDB
 
 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;
 
 730              // Navigate between dialog screens using step number: 1,2,...
 
 731              $scope.navigateBack = () => {
 
 733                  this.fileSelected = false;
 
 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) => {
 
 740                 $scope.readValidateFile(typeUpload);
 
 741                 // Dialog shows progress
 
 742                 if(typeUpload === 'functions'){
 
 744                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-functions-confirm.html',
 
 746                         sizeClass: 'modal-medium', 
 
 750                 } else if(typeUpload === 'roleFunctions'){
 
 752                                 templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-role-functions-confirm.html',
 
 754                         sizeClass: 'modal-medium', 
 
 759                 } else if(typeUpload === 'roles'){
 
 761                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-roles-confirm.html',
 
 763                         sizeClass: 'modal-medium', 
 
 767                 } else if(typeUpload === 'globalRoleFunctions'){
 
 769                         templateUrl: 'app/views/role/bulk-upload-dialogs/bulk-upload-global-role-functions-confirm.html',
 
 771                         sizeClass: 'modal-medium', 
 
 778              // Invoked by the Cancel button on the confirmation dialog.
 
 779              $scope.cancelUpload = () => {
 
 786     BulkRoleAndFunctionsModalCtrl.$inject = ['$scope', '$log', '$filter', '$q',  '$modalInstance', '$modal', 'ngDialog', 'message', 'confirmBoxService', 'usersService', 'applicationsService', 'functionalMenuService', 'RoleService'];    
 
 787     angular.module('ecompApp').controller('BulkRoleAndFunctionsModalCtrl', BulkRoleAndFunctionsModalCtrl);
 
 789     angular.module('ecompApp').directive('fileChange', ['$parse', function($parse){
 
 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;
 
 801                 element[0].addEventListener('change',handler,false);
 
 806     angular.module('ecompApp').filter('csvToFuncObj',function() {
 
 807         return function(input) {
 
 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");
 
 828                         if (o.length !== 4) {
 
 829                                 // other lengths not valid for upload
 
 836                                         status: 'Failed to find 4 comma-separated values'
 
 840                                 // console.log("Valid line: ", val);
 
 847                                                 // leave status undefined, this
 
 850                                 if (o[0].toLowerCase() === 'type') {
 
 851                                         // not valid for upload, so set status
 
 852                                         entry.status = 'Header';
 
 854                                 else if (o[0].toLowerCase() === 'instance') {
 
 855                                         // not valid for upload, so set status
 
 856                                         entry.status = 'Header';
 
 858                                 else if (o[0].toLowerCase() === 'action') {
 
 859                                         // not valid for upload, so set status
 
 860                                         entry.status = 'Header';
 
 862                                 else if (o[0].toLowerCase() === 'name') {
 
 863                                         // not valid for upload, so set status
 
 864                                         entry.status = 'Header';
 
 866                                 else if (o[0].trim() == '' || o[1].trim() == '' ||  o[2].trim() == '' ||  o[3].trim() == '') {
 
 867                                         // defend against line with only a
 
 869                                         entry.status = 'Failed to find non-empty values';                                       
 
 878     angular.module('ecompApp').filter('csvToRoleFuncObj',function() {
 
 879         return function(input) {
 
 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");
 
 901                         if (o.length !== 5) {
 
 902                                 // other lengths not valid for upload
 
 910                                         status: 'Failed to find 4 comma-separated values'
 
 914                                 // console.log("Valid line: ", val);
 
 922                                                 // leave status undefined, this
 
 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';
 
 932                                 else if (o[0].toLowerCase() === 'instance') {
 
 933                                         // not valid for upload, so set status
 
 934                                         entry.status = 'Header';
 
 936                                 else if (o[0].toLowerCase() === 'action') {
 
 937                                         // not valid for upload, so set status
 
 938                                         entry.status = 'Header';
 
 940                                 else if (o[0].toLowerCase() === 'name') {
 
 941                                         // not valid for upload, so set status
 
 942                                         entry.status = 'Header';
 
 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
 
 947                                         entry.status = 'Failed to find non-empty values';                                       
 
 956     angular.module('ecompApp').filter('csvToRoleObj',function() {
 
 957         return function(input) {
 
 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");
 
 976                         if (o.length === 0 && line.length !== 0) {
 
 977                                 // other lengths not valid for upload
 
 985                                 // console.log("Valid line: ", val);
 
 990                                                 // leave status undefined, this
 
 993                                 if (o[0].toLowerCase() === 'role') {
 
 994                                         // not valid for upload, so set status
 
 995                                         entry.status = 'Header';
 
 997                                 if (o[0].toLowerCase() === 'priority') {
 
 998                                         // not valid for upload, so set status
 
 999                                         entry.status = 'Header';
 
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';