1/** 2 * PROVIDER PAYLOAD 3 * {base_plan_id}:{email}:{name}:{company} 4 * 5 * 8:[email protected]:Chris+Woodington:XYZ+Chambers+Ltd. 6 * @example ODpjd29vZGluZ3RvbkBnbWFpbC5jb206Q2hyaXMrV29vZGluZ3RvbjpYWVorQ2hhbWJlcnMrTHRkLg== 7 * 8 * Soft invite created outside the application. Service framework is picked up from the base_plan table 9 * 10 * TRADER PAYLOAD 11 * This controller handles links provided in invites sent to traders 12 * Route (defined in app.js) is '/signup/:payload?' 13 * 14 * Payload is a URL-safe base64 encoded string allowing to link the invite from email. 15 * It consists of 10 parts, separated with colons, following this structure: 16 * 17 * id:from_orgid:from_role:to_uid:to_role:provider_workflow:to_orgid:invite_date:platform:signature 18 * 19 * @example payload NjBlMGU1NDQtNzY4Yi00M2U2LWIwNzktMjkxOTVjODBiM2MwOjc0NDo0OTU6cHJvdmlkZXI6NDk1OnRyYWRlcjpzb21lc3RyaW5nOjEyMzoxNjEyNDE1MTEyMTI6bWFya2V0cGxhY2U6c2lnbmF0dXJlc3RyaW5n 20 * corresponds to 21 * 60e0e544-768b-43e6-b079-29195c80b3c0:744:495:provider:495:trader:somestring:123:161241511212:marketplace:signaturestring 22 */ 23app.controller("signupCtrl", function ( 24 $scope, $rootScope, 25 $routeParams, 26 $location, 27 $http, 28 tbServices, 29 Data, 30 AuthService, 31 accountServices, 32 WorkflowService, 33 apiServices, 34 AclService, 35 TradeModelService 36) { 37 38 try { 39 JL('signupCtrl').debug("Entered signupCtrl"); 40 } catch (e) {} 41 42 var trader_plans = [ 43 {id: 0, description: "Free", routes: []}, 44 {id: 1, description: "Basic", routes: ["marketplace"]}, 45 {id: 2, description: "Pro", routes: ["marketplace", "invite"]}, 46 {id: 3, description: "OperatorX", routes: ["invite", "captiveX"]} 47 ]; 48 49 // this is for extra data - separate table contains this - to remove 50 var provider_plans = [ 51 {id: 0, description: "Free", routes: []}, 52 {id: 1, description: "Basic", routes: ["marketplace"]}, 53 {id: 2, description: "Pro", routes: ["marketplace", "invite"]} 54 ]; 55 56 //TODO - to replace by api lookup to plan? 57 $scope.service_provider_plans = [ 58 {base_plan_id: 6, description: "We provide transportation & logistics services"}, 59 {base_plan_id: 8, description: "We provide only customs brokerage services"}, 60 {base_plan_id: 7, description: "We provide customs consultancy"}, 61 {base_plan_id: 9, description: "We provide financial services "}, 62 ]; 63 64 $scope.available_plans = []; 65 66 /** 67 * 6 Transport provider 68 7 Customs planning 69 8 Customs agent 70 9 Finance provider 71 72 */ 73 74 var ctrl = this; 75 ctrl.view = { 76 isOpenAccess: true, 77 step2: false, 78 // logo_url: "/images/exabler-small.png" 79 }; 80 81 ctrl.invite = {}; 82 var serviceBase = "/api/v1"; 83 84 ctrl.signup = { 85 email: "", 86 name: "", 87 organisation: "", 88 operationcountry: "", 89 base_plan_id: '', 90 password: '', 91 soft_invite: false, 92 extra_data: angular.copy(OrganisationClass) 93 }; 94 95 96 let domain = $location.host(); 97 98 ctrl.isStep1Complete = function () { 99 100 if (!ctrl.signup.base_plan_id || !ctrl.signup.email || !ctrl.signup.password) return false; 101 return ctrl.signup.base_plan_id !== '' && ctrl.signup.email !== '' && ctrl.signup.password != '' 102 103 } 104 105 ctrl.clickedNext = function() { 106 try { JL('signupCtrl').info({mx_event: "Clicked Next on provider signup"}); } catch {} 107 } 108 109 ctrl.isFormComplete = function () { 110 111 return ctrl.signup.base_plan_id && ctrl.signup.email && ctrl.signup.password && ctrl.signup.name && ctrl.signup.company && ctrl.signup.operationcountry && ctrl.signup.acceptance 112 113 } 114 115 ctrl.view.payloadPresent = typeof $routeParams.payload != 'undefined'; 116 117 var onInit = function () { 118 119 try { JL('signupCtrl').debug("Initialising signupCtrl"); } catch (e) {} 120 // $scope.countries = tbServices.service('country').src(); 121 $scope.countries = TradeModelService.ordered_countries_list; 122 123 if ($location.$$path === '/provider-signup') { 124 125 try { 126 JL('signupCtrl').info("Got to provider-signup"); 127 } catch (e) { 128 console.error("Failed to log error"); 129 } 130 // provider with payload
131 if (typeof $routeParams.payload != 'undefined') { 132 133 134 // decode payload 135 ctrl.payload = atob(String($routeParams.payload)); 136 // check payload format validity 137 if (ctrl.payload.indexOf(':') <= 0) { 138 return setInvalid(); 139 } 140 // split payload into array 141 var paramsArray = ctrl.payload.split(":"); 142 // verify params count 143 if (paramsArray.length != 4) { 144 return setInvalid(); 145 } 146 147 148 ctrl.signup.soft_invite = true; 149 150 ctrl.signup.base_plan_id = paramsArray[0]; 151 ctrl.signup.email = paramsArray[1]; 152 ctrl.signup.name = decodeUrlParameter(paramsArray[2]); 153 ctrl.signup.company = decodeUrlParameter(paramsArray[3]); 154 155 156 $http.get('/data/operators.php/?p=' + ctrl.signup.base_plan_id).then((result) => { 157 158 ctrl.view.logo_url = result.data[0].logo_url; 159 ctrl.view.operator_info = result.data[0]; 160 161 if (result.data[0].owner == 'Exabler.com') { 162 ctrl.view.isOpenAccess = true; 163 } else { 164 ctrl.view.isOpenAccess = false; 165 } 166 167 168 }); 169 170 171 ctrl.signup.operationcountry = "GB"; 172 173 } else { 174 175 // independent provider signup 176 177 // todo: copy from above make standard call 178 // this one retrieves based on the traders invited plan 179 $http.get('/data/operators.php/?p=0').then((result) => { 180 181 ctrl.view.logo_url = result.data[0].logo_url; 182 ctrl.view.operator_info = result.data[0]; 183 184 if (result.data[0].owner == 'Exabler.com') { 185 ctrl.view.isOpenAccess = true; 186 } else { 187 ctrl.view.isOpenAccess = false; 188 ctrl.view.offer_details = result.data[0].offer_details; 189 } 190 191 }); 192 193 194 } 195 196 197 } 198 // check if payload is present on the trader invite 199 else if (typeof $routeParams.payload == 'undefined') { 200 201 try { JL('signupCtrl').debug("No payload. Getting operators"); } catch (e) {}// todo: copy from above make standard call 202 // this one retrieves based on the traders invited plan 203 $http.get('/data/operators.php/?p=0').then((result) => { 204 205 ctrl.view.logo_url = result.data[0].logo_url; 206 ctrl.view.operator_info = result.data[0]; 207 208 if (result.data[0].owner == 'Exabler.com') { 209 ctrl.view.isOpenAccess = true; 210 } else { 211 ctrl.view.isOpenAccess = false; 212 ctrl.view.offer_details = result.data[0].offer_details; 213 } 214 215 }); 216 217 218 try { 219 JL('signupCtrl').debug("Entered provider-signup without payload"); 220 } catch (e) { 221 console.error("Failed to log error"); 222 } 223 ctrl.invite.expired = false; 224 // ctrl.signup = { 225 // email: "", 226 // name: "", 227 // organisation: "", 228 // operationcountry: "", 229 // extra_data: angular.copy(OrganisationClass) 230 // }; 231 232 } else { 233 234 try { JL('signupCtrl').debug("Decoding payload"); } catch (e) {}// decode payload 235 ctrl.payload = atob(String($routeParams.payload)); 236 // check payload format validity 237 if (ctrl.payload.indexOf(':') <= 0) { 238 return setInvalid(); 239 } 240 // split payload into array 241 var paramsArray = ctrl.payload.split(":"); 242 // verify params count 243 if (paramsArray.length != 11) { 244 return setInvalid(); 245 } 246 247 let params = { 248 id: paramsArray[0], 249 from_orgid: paramsArray[1], 250 from_role: paramsArray[2],
251 // from_organisation: paramsArray[3], 252 to_uid: paramsArray[3], 253 to_role: paramsArray[4], 254 provider_workflow: paramsArray[5], 255 to_orgid: paramsArray[6], 256 operationcountry: paramsArray[7], 257 invite_date: paramsArray[8], 258 platform: paramsArray[9], 259 signature: paramsArray[10] 260 }; 261 // check first one is a uuid 262 if (params.id.match(/^[a-z]{3}[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i) > 1) { 263 return setInvalid(); 264 } 265 // check from_orgid format validity 266 if (isNaN(parseInt(params.from_orgid))) { 267 return setInvalid(); 268 } 269 // check signature format validity 270 // if(params.signature.toString().length!=64) { 271 // return setInvalid(); 272 // } 273 ctrl.invite = params; 274 ctrl.invite.valid = true; 275 try { JL('signupCtrl').debug("Verifying joincode"); } catch (e) {} 276 $http.post(serviceBase + '/verifyjoincode', params) 277 .then(function (response) { 278 if (response.data.status === "expired") { 279 return setInvalid(); 280 } 281 if (response.data.status === "revoked") { 282 return setInvalid(); 283 } 284 if (response.data.status != "pending" && response.data.status != "created") { 285 throw new Error("unexpected response from server: " + JSON.stringify(response.data)); 286 } 287 try { JL('signupCtrl').debug("Verified joincode"); } catch (e) {} 288 if (response.data.from_org_name) { 289 ctrl.invite.from_org_name = response.data.from_org_name; 290 } 291 if (response.data.organisation) { 292 ctrl.invite.organisation = response.data.organisation; 293 } 294 if (response.data.name) { 295 ctrl.invite.name = response.data.name; 296 } 297 if (response.data.email) { 298 ctrl.invite.email = response.data.email; 299 } 300 if (response.data.operationcountry) { 301 ctrl.invite.operationcountry = response.data.operationcountry; 302 } 303 if (response.data.available_plans) { 304 $scope.available_plans = response.data.available_plans; 305 ctrl.invite.base_plan_id = $scope.available_plans.filter((plan) => { 306 return plan.selected 307 })[0].base_plan_id; 308 } 309 if (response.data.up_chain_sponsor_orgid) { 310 ctrl.invite.up_chain_sponsor_orgid = response.data.up_chain_sponsor_orgid; 311 } 312 if (response.data.up_chain_sponsor_object_id) { 313 ctrl.invite.up_chain_sponsor_object_id = response.data.up_chain_sponsor_object_id; 314 } 315 try { 316 const log_obj = { 317 mx_event: "Invited trader visit sign up page", 318 uid: params.to_uid, 319 base_plan_id: ctrl.invite.base_plan_id 320 }; 321 JL('signupCtrl').info(log_obj); 322 } catch (e) {} 323 324 try { JL('signupCtrl').debug("Getting operators"); } catch (e) {} 325 // todo: copy from above make standard call 326 // this one retrieves based on the traders invited plan 327 $http.get('/data/operators.php/?p=' + ctrl.invite.base_plan_id).then((result) => { 328 329 try { JL('signupCtrl').debug("Got operators"); } catch (e) {} 330 ctrl.view.logo_url = result.data[0].logo_url; 331 ctrl.view.operator_info = result.data[0]; 332 333 if (result.data[0].owner == 'Exabler.com') { 334 ctrl.view.isOpenAccess = true; 335 } else { 336 ctrl.view.isOpenAccess = false;
337 ctrl.view.offer_details = result.data[0].offer_details; 338 } 339 }); 340 341 342 ctrl.invite.valid = true; 343 344 }) 345 .catch(function (error) { 346 347 console.log('Unhandled exception: ' + JSON.stringify(error)); 348 try { 349 JL('signupCtrl').error('Error - checking join code - ' + JSON.stringify(error)); 350 } catch (e) { 351 console.error("Failed to log error"); 352 } 353 // $location.path("/login"); 354 355 }); 356 357 } 358 359 }; 360 361 var setInvalid = function () { 362 ctrl.invite.expired = true; 363 ctrl.invite.valid = false; 364 } 365 366 onInit(); 367 368 369 /** 370 * Update invited tblOrganisation and tblUser 371 * Sets credentials according to given email and password 372 */ 373 $scope.signUp = function () { 374 375 try { 376 const log_obj = {mx_event: "Invited trader clicked Sign up"}; 377 JL('signupCtrl').info(log_obj); 378 } catch(e) {} 379 $scope.disableSignupButton = true; 380 $http.post(serviceBase + '/usejoincode', ctrl.invite) 381 .then(function (response) { 382 console.debug(JSON.stringify(response.data)); 383 return AuthService.login({ 384 email: ctrl.invite.email, 385 password: ctrl.invite.password 386 }); 387 }) 388 .then(function success(response) { 389 if (response.status == "success") { 390 391 // test login 392 393 $rootScope.setCurrentUser(response); 394 $rootScope.isUserLoggedin = true; 395 $rootScope.authenticated = true; 396 397 accountServices.getPlanDetails(response).then(function (plan_details) { 398 $rootScope.plan_details = plan_details; 399 WorkflowService.set_services(); 400 }); 401 402 let aclDataUser; 403 try { 404 // run api call to get roles 405 aclDataUser = JSON.parse(response.permissions); 406 } catch (e) { 407 console.log("error reading permissions"); 408 } 409 410 AclService.setAbilities(aclDataUser); 411 412 // Attach the member role to the current user (not sure of the benefit) 413 Object.keys(aclDataUser).forEach(key => { 414 AclService.attachRole(key); 415 }); 416 417 418 $location.path('/home'); 419 } else if (response.status == "error") { 420 421 try { JL('signupCtrl').error("Error - using join code - due to server error"); } catch {} 422 $scope.disableSignupButton = false; 423 // $location.path('/login'); 424 425 } 426 }) 427 .catch(function (error) { 428 $scope.disableSignupButton = false; 429 console.error("Error: " + error.message); 430 Data.toast({status: error.status, message: error.message}); 431 try { 432 JL('signupCtrl').error('Error - using join code - ' + JSON.stringify(error)); 433 } catch (e) { 434 console.error("Failed to log error"); 435 } 436 437 // $location.path('/login'); 438 }); 439 440 }; 441 442 443 /** 444 * Sign up without an invitation 445 */ 446 $scope.independentSignUp = function (data) { 447 448 $scope.disableSignupButton = true; 449 try { 450 gtag('event', 'sign_up - TradingCompany', {'event_category': 'Signup', 'event_label': 'Sign up page TradingCompany'}); 451 JL('signupCtrl').info("Independent signup"); 452 } catch(e) {} 453 454 var customer = Object.assign({}, data); 455 456 customer.type = "trader"; 457 customer.name = "<your name>"; 458 459 // legacy fields 460 customer.phone = ""; 461 customer.organisation = data.company; 462 customer.title = ""; 463 customer.jobfunction = ""; 464 465 const timestamp = moment(); 466 _.set(customer, "extra_data.created", timestamp); 467 _.set(customer, "extra_data.updated", timestamp); 468 const country_object = tbServices.service('country').getByAlpha2(data.operationcountry); 469 _.set(customer.extra_data, "registered_country", country_object); 470 _.set(customer.extra_data, "addresses.registered[0].country", country_object); 471 _.set(customer.extra_data, "addresses.trading[0].country", country_object); 472 473 customer.extra_data.id = "urg" + uuid(); 474 475 // to remve 476 customer.extra_data.plan_selection = trader_plans[0]; 477 478 // we don't know the uid for created_by or updated_by until created 479 ctrl.signup.extra_data = Object.assign({}, customer.extra_data); // update extra data object 480 481 customer.extra_data = JSON.stringify(customer.extra_data); 482 // customer.tradingAddress1 = customer.address; 483 // customer.contactName = customer.name; 484 Data.post("signUp", {customer: customer}) 485 .then(function (results) { 486 ctrl.signup.type = customer.type; 487 488 Data.toast(results); 489 490 if (results.status == "success") { 491 try { 492 JL('signupCtrl').info({mx_event: "Independent trader signup", plan: customer.extra_data.plan_selection}); 493 } catch (e) { 494 } 495 496 // Finally go to the home page 497 $location.path("/home"); 498 } else { 499 $scope.disableSignupButton = false;
500 } 501 }) 502 .catch(function (error) { 503 $scope.disableSignupButton = false; 504 console.error("Error: " + error.data.message); 505 Data.toast(error.data); 506 try { 507 JL('signupCtrl').error({mx_event: 'Error - independentSignUp', error: JSON.stringify(error)}); 508 } catch (e) { 509 console.error("Failed to log error"); 510 } 511 // $location.path('/login'); 512 }); 513 }; 514 515 516 /** 517 * Self-signup for a provider 518 */ 519 $scope.providerSelfSignUp = function (data) { 520 521 $scope.disableSignupButton = true; 522 try { 523 gtag('event', 'sign_up - ServiceProvider', {'event_category': 'Signup', 'event_label': 'Sign up page ServiceProvider'}); 524 JL('signupCtrl').info("Provider self signup"); 525 } catch(e) {} 526 527 var customer = Object.assign({}, data); 528 customer.type = "svc"; 529 530 if (!customer.soft_invite) { 531 // no longer required 532 //customer.name = "<your name>"; 533 } 534 535 // legacy fields 536 customer.phone = ""; 537 customer.organisation = data.company; 538 // customer.extra_data.registered_name = data.company; // deprecated 539 540 customer.extra_data.registered_country = tbServices.service('country').getByAlpha2(data.operationcountry); 541 customer.extra_data.addresses.registered[0].country = tbServices.service('country').getByAlpha2(data.operationcountry); 542 543 customer.operationcountry = data.operationcountry; 544 customer.extra_data.created = customer.extra_data.updated = moment(); 545 customer.extra_data.id = "urg" + uuid(); 546 547 // @ramin - not sure what this is doing 548 // plan this is in extra data and shows free 549 customer.extra_data.plan_selection = provider_plans[0]; 550 551 // we don't know the uid for created_by or updated_by until created 552 ctrl.signup.extra_data = Object.assign({}, customer.extra_data); // update extra data object 553 554 $http.post(serviceBase + "/provider-signup", {customer: customer}) 555 .then(function (results) { 556 ctrl.signup.type = customer.type; 557 558 if (results.statusText === "OK") { 559 try { 560 JL('signupCtrl').info({mx_event: "Independent SP signup", plan: customer.extra_data.plan_selection}); 561 } catch (e) { 562 } 563 564 // Finally go to the home page 565 $location.path("/home"); 566 } 567 }) 568 .catch(function (error) { 569 $scope.disableSignupButton = false; 570 console.error("Error: " + error.message); 571 Data.toast({status: error.data.status, message: error.data.message}); 572 try { 573 JL('signupCtrl').error('Error - providerSelfSignUp - ' + JSON.stringify(error.data)); 574 } catch (e) { 575 console.error("Failed to log error"); 576 } 577 // $location.path('/login'); 578 }); 579 580 581 }; 582 583 584 var strongRegularExp = new RegExp("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})"); 585 var mediumRegularExp = new RegExp("^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})"); 586 587 $scope.checkPwdStrength = { 588 "color": "white", 589 "font-size": "8pt", 590 }; 591 $scope.pwd_match = true; 592 593 $scope.validationInputPwdText = function (value) { 594 595 if (strongRegularExp.test(value)) { 596 $scope.checkPwdStrength["background-color"] = "green"; 597 $scope.feedback = 'Strong'; 598 } else if (mediumRegularExp.test(value)) { 599 $scope.checkPwdStrength["background-color"] = "orange"; 600 $scope.feedback = 'OK'; 601 } else { 602 $scope.checkPwdStrength["background-color"] = "red"; 603 $scope.feedback = "Weak"; 604 } 605 }; 606 607 $scope.matchPasswords = function (ref_pwd) { 608 if ((ref_pwd !== ctrl.password_confirm) && ref_pwd && ctrl.password_confirm) { 609 $scope.pwd_match = false;
610 } else { 611 $scope.pwd_match = true; 612 } 613 $scope.pwd_match = true; 614 615 } 616 617 618}); 619 620 621function decodeUrlParameter(str) { 622 return decodeURIComponent((str + '').replace(/\+/g, '%20')); 623} 624 625
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.