1<!DOCTYPE html> 2<html lang=en> 3<head> 4 5 6 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> 7 Manager Portal </title> 8 <META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW"> 9 10 11
11<script type="text/javascript" src="/portal/locale/icu.js"></script>
11 12
12<script type="text/javascript" src="/portal/locale/translate.js"></script>
12 13
13<script type="text/javascript" src="/portal/locale/en_us/LC_MESSAGES/i18nJS.js"></script>
13 14
14<script type="text/javascript"> 15 //window.addEventListener('load', function(){ 16 if (i18nJSON) _.setTranslation(i18nJSON);//POTENTIAL ERROR: Removed window onload event listener because it was causing problems with validationEngine translations 17 //}); 18 </script>
18 19 <link rel="shortcut icon" type="image/x-icon" href="https://pbx.io/ns-api/?object=image&action=read&filename=favicon.gif&server=pbx.io" /> 20 <link rel="icon" type="image/x-icon" href="https://pbx.io/ns-api/?object=image&action=read&filename=favicon.gif&server=pbx.io" /> 21 <link rel="stylesheet" href="/portal/css/basicCSS.php?version=1766098087&ver=44.4.6 " type="text/css"> 22 <link rel="stylesheet" href="/portal/css/portal.php?background=%23212223&primary1=%237f223b&primary2=%23c0919e&bar1=%238c8c8c&bar2=%23cccccc" type="text/css"> 23 <link rel="stylesheet" href="/custom/custom.css" type="text/css"> 24
24<script type="text/javascript" src='/portal/js/basicJS.php?version=1766098087&ver=44.4.6 '></script>
24 25
25<script type="text/javascript"> 26 var servertime = '2026-10-08 11:49:11'; 27 </script>
27 28 29</head> 30<body> 31 <div class="page-container" style=""> 32 <div class="fixed-container"> 33 </div> 34 <div id="login-container">
vendor: 72 bytes, lines 34-38
34 35 <script src='//www.google.com/recaptcha/api.js'></script> 36 37 38
39<style> 40 #LoginForm { 41 overflow: auto; 42 margin: 0 0 8px; 43 } 44 45 .separator { 46 margin-top: 12px; 47 display: flex; 48 position: relative; 49 margin: 1rem 0; 50 text-align: center; 51 justify-content: space-between; 52 align-items: center; 53 flex: 1 54 } 55 56 .separator__content { 57 margin: 0 3rem 58 } 59 60 .separator::before, .separator::after { 61 flex: 1; 62 content: ''; 63 border-bottom: 1px solid #5f6c72 64 } 65 66 .separator--light { 67 font-weight: 700; 68 letter-spacing: 1px; 69 text-transform: uppercase; 70 color: #969ea2; 71 font-size: 1.3rem 72 } 73 74 .separator--light::before, .separator--light::after { 75 border-color: #dadfe2 76 } 77 78 .sso-container { 79 padding: 2px 0 8px; 80 text-align: center; 81 } 82 83 .sso-container:last-of-type { 84 margin-bottom: 20px; 85 } 86 87 #login-group.with-redirect-uri { 88 margin-top: 10px; 89 } 90 91</style> 92 93<div id="login-spinner" class="loading-container relative" 94 style="margin: auto; position: absolute !important;top: 0;left: 0;background: transparent; display: none;"> 95 <div class="loading-spinner la-ball-spin-clockwise"> 96 <div></div> 97 <div></div> 98 <div></div> 99 <div></div> 100 <div></div> 101 <div></div> 102 <div></div> 103 <div></div> 104 </div> 105</div> 106<div id="login-group" class=""> 107 <div id="login-box"> 108 <div id="login-logo"> 109 110 <img src="https://pbx.io/ns-api/?object=image&action=read&server=pbx.io&filename=portal_landing.png&server=pbx.io"/> 111 </div> 112 113 <form action="/portal/login/login" id="LoginForm" class="form-stacked" method="post" accept-charset="utf-8"><div style="display:none;"><input type="hidden" name="_method" value="POST" /></div> <div class='hide'> 114 <input name="data[Login][vendor]" type="text" id="SsoVendor" /><input name="data[Login][id_token]" type="text" id="SsoIdToken" /> </div> 115 116 <div id="login-fields"> 117 <div> 118 <label for="username">Login Name</label> 119 <div> 120 <input name="data[Login][username]" type="text" id="LoginUsername" /> </div> 121 </div> 122 123 <div> 124 <label for="password">Password</label> 125 <div> 126 <input type="password" name="data[Login][password]" class="hide" id="LoginPassword" /> </div> 127 </div> 128 </div> 129 130 <div id="login-submit" style="padding-bottom: 15px;"> 131 <div class="submit"><input class="btn btn-large color-primary" update="#login" complete="hideModalIf("#login");" type="submit" value="Log In" /></div> </div> 132 133 <div id="reset-container" style="margin-bottom: 12px;"> 134 <div id="reset-links"> 135 <a href="/portal/resets/forgotlogin" 136 style="padding-right: 7px;">Forgot Login Name</a>|<a 137 href="/portal/resets/forgotpassword" 138 style="padding-left: 7px;">Forgot Password</a> 139 </div> 140 </div> 141 142 <div id="ssoSeparator" class="separator separator--light" style="display: none"> 143 <span class="separator__content">OR</span> 144 </div> 145 146 147 <div class="sso-container"> 148 <div id="google-sso" style="height: 44px; overflow: hidden; display: flex; justify-content: center"></div> 149 </div> 150 151
151<script> 152 const aScript = document.createElement('script'); 153 aScript.type = 'text/javascript'; 154 aScript.src = 'https://accounts.google.com/gsi/client'; 155 document.head.appendChild(aScript); 156 157 aScript.onload = () => { 158 $('#ssoSeparator').show(); 159 160 window.google.accounts.id.initialize({ 161 client_id: '311082626465-qkkene5d077r6tjociq4sj7bfkdkhfc4.apps.googleusercontent.com', 162 callback: (res) => { 163 if (res && res.credential) { 164 $('#SsoVendor').val('google'); 165 $('#SsoIdToken').val(res.credential); 166 onSubmit(); 167 } 168 }, 169 }); 170 171 window.google.accounts.id.renderButton( 172 document.getElementById('google-sso'), 173 { 174 theme: 'outline', 175 size: 'large', 176 shape: 'rectangle', 177 width: 302, 178 logo_alignment: 'left', 179 locale: 'en_us' || 'en_US', 180 }, 181 ); 182 }; 183 </script>
183 184 185<style> 186 #appleid-signin { 187 display: none; 188 } 189</style> 190 191 <div class="sso-container"> 192 <div id="appleid-btn-custom" class="ssoLoginBtn btn btn-large"> 193 <span class="icon"><img src="/portal/img/apple-logo.png"/></span> 194 <span class="buttonText"> 195 Log in with Apple </span> 196 </div> 197 <div id="appleid-signin" class="ssoLoginBtn btn btn-large"> 198 <span class="icon"><img src="/portal/img/apple-logo.png"/></span> 199 <span class="buttonText"> 200 Log in with Apple </span> 201 </div> 202 </div> 203 204
204<script type="text/javascript" src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
204 205
205<script> 206 $('#ssoSeparator').show(); 207 208 var redirectURI = window.location.origin + '/portal/login/'; 209 210 $(document).ready(function() { 211 $('#ssoSeparator').show(); 212 213 $('#appleid-btn-custom').click(function() { 214 window.history.pushState('portal', 'Login', '/portal/login/'); // force base path so url can match allowed redirect urls 215 document.getElementById("appleid-signin").click(); 216 }); 217 218 AppleID.auth.init({ 219 clientId: 'io.pbx.sso', // 'com.netsapiens.local', 220 scope: 'name email', 221 redirectURI: redirectURI, 222 usePopup: true, 223 }); 224 225 document.addEventListener('AppleIDSignInOnSuccess', (data) => { 226 var authCode = data.detail.authorization.code; 227 var idToken = data.detail.authorization.id_token; 228 //var parsedToken = parseJwt(idToken); 229 //var email = parsedToken.email; 230 231 $('#LoginUsername').val(''); 232 $('#LoginPassword').val(''); 233 $('#SsoVendor').val('apple'); 234 $('#SsoIdToken').val(idToken); 235 236 onSubmit(); 237 }); 238 239 document.addEventListener('AppleIDSignInOnFailure', (error) => { 240 console.error('AppleIDSignInOnFailure', error) 241 }); 242 }); 243 244 function parseJwt(token) { 245 var base64Url = token.split('.')[1]; 246 var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); 247 var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) { 248 return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); 249 }).join('')); 250 251 return JSON.parse(jsonPayload); 252 }; 253 </script>
253 254 255 <div class="sso-container"> 256 <div id="officeSigninBtn" class="ssoLoginBtn btn btn-large"> 257 <span class="icon"><img src="/portal/img/office-logo.svg"/></span> 258 <span class="buttonText"> 259 Log in with Office 365 </span> 260 </div> 261 262 <div id="officeSigninLoader" style="display: none;"> 263 <span class="icon"></span> 264 <span class="buttonText"> 265 Checking Office authorization... 266 </span> 267 </div> 268 269 <div id="officeContinueBtn" class="ssoLoginBtn btn btn-large" style="display: none;"> 270 <span class="icon"><img src="/portal/img/google-logo.png"/></span> 271 <span class="buttonText"> 272 Log in with Office </span> 273 </div> 274 275 <div id="clientError" style="display: none;"> 276 Failed to initialize Office SSO application </div> 277 </div> 278 279
279<script src="https://alcdn.msauth.net/lib/1.0.0/js/msal.js"></script>
279 280 281
281<script> 282 $('#ssoSeparator').show(); 283 284 var officeIdToken = ''; 285 286 var msalConfig = { 287 auth: { 288 clientId: 'c4572491-ec47-48e6-bfc4-693208bc53e2' 289 }, 290 cache: { 291 cacheLocation: 'localStorage' 292 } 293 }; 294 295 var authenticationParameters = { 296 scopes: ["user.read"], // optional Array<string> 297 prompt: "select_account", 298 }; 299 300 //This maybe needs to be on a page load callback? 301 var msalInstance = new Msal.UserAgentApplication(msalConfig); 302 303 $('#officeSigninBtn').click( function() { 304 var thisPath = window.location.pathname; 305 window.history.pushState('portal', 'Login', '/portal/msal.php?c=c4572491-ec47-48e6-bfc4-693208bc53e2'); // force base path so url can match allowed redirect urls 306 307 msalInstance.loginPopup(authenticationParameters) 308 .then(response => { 309 msalInstance.acquireTokenSilent(authenticationParameters) 310 .then(response => { 311 window.history.pushState('portal', 'Login', thisPath); // revert path after msal roundtrip 312 313 officeIdToken = response.accessToken; 314 315 $('#LoginUsername').val(''); 316 $('#LoginPassword').val(''); 317 $('#SsoVendor').val('office'); 318 $('#SsoIdToken').val(officeIdToken); 319 320 onSubmit(); 321 }) 322 .catch(err => { 323 console.log('acquireTokenSilent fresh err',err) 324 }); 325 }) 326 .catch(err => { 327 console.log('loginPopup err',err) 328 }); 329 }); 330 </script>
330 331 332 333 334 </form> </div> 335 336 <div class="new-user-container"> 337 <a href="/portal/resets/newuser">Are you a new user?</a> 338 </div> 339 340 <div class="login-language-container"> 341 </div> 342 343 <div id="footer"> 344 345 <p>Copyright © 2017-2019 by PBX.io</p> 346 347 <p>Manager Portal: Version 44.4.6 </p> 348 </div><!-- /login-box --> 349</div><!-- /login-group --> 350 351
352<script type="text/javascript"> 353 354 var ns_t = localStorage.getItem('ns_t'); 355 var jwt = parseJwt(ns_t); 356 357 if ($.cookie('is_mask') == null && jwt) { 358 if (localStorage.getItem('jwtAutoLoginTS') == null || parseInt(localStorage.getItem('jwtAutoLoginTS')) < Date.now().getTime() - 30000) { 359 if (jwt.exp > (Date.now().getTime() / 1000)) { 360 $('#login-spinner').show(); 361 $('#login-group').hide(); 362 var username = jwt.login || jwt.sub; 363 var expires = jwt.exp; 364 localStorage.setItem('jwtAutoLoginTS', Date.now().getTime()); 365 window.location.replace("/portal/login/loginwithtoken?iframe=no&refresh_token=" + ns_t + "&username=" + username + "&avoidExpiredRedirect=true"); 366 } 367 } 368 } 369 370 function parseJwt(token) { 371 if (token) { 372 var base64Url = token.split('.')[1]; 373 var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); 374 var jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) { 375 return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); 376 }).join('')); 377 378 return JSON.parse(jsonPayload); 379 } 380 }; 381</script>
381 382
383<script type="text/javascript"> 384 //OMP-1256 385 //When auto-logged out, prevent redirect to controller if logging back in as a different user 386 loginForm = $('.form-stacked'); 387 loginURL = loginForm.attr('action'); 388 389 if (loginURL.indexOf('page=') !== -1) { 390 pageURL = loginURL; 391 noPageURL = pageURL.replace(/(&?|\?)page=[a-z]*/g, ''); 392 usernameInput = $('#LoginUsername'); 393 origUsername = "@"; 394 395 usernameInput.on("input", function () { 396 newUsername = usernameInput.val(); 397 if (newUsername != origUsername) { 398 loginForm.attr('action', noPageURL); 399 //window.history.pushState({},"", noPageURL); 400 } else { 401 loginForm.attr('action', pageURL); 402 //window.history.pushState({},"", pageURL); 403 } 404 }); 405 } 406 407 function enableLogin() { 408 $('.submit input').removeAttr("disabled"); 409 } 410 411 function onSubmit() { 412 $('form').submit(); 413 } 414</script>
414 415 </div> 416 <!-- majority of javascript at bottom so the page displays faster --> 417 </div> 418</body> 419 420</html>
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.