mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 10:38:45 +02:00
N°2311 - Login Page extensibility
This commit is contained in:
@@ -178,6 +178,14 @@ interface iLogoutExtension extends iLoginExtension
|
||||
public function LogoutAction();
|
||||
}
|
||||
|
||||
interface iLoginDataExtension extends iLoginExtension
|
||||
{
|
||||
/**
|
||||
* @return LoginTwigData
|
||||
*/
|
||||
public function GetLoginData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to change the behavior of the GUI for some objects.
|
||||
*
|
||||
|
||||
@@ -21,6 +21,7 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
|
||||
|
||||
protected function OnStart(&$iErrorCode)
|
||||
{
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_OK;
|
||||
// Check if proposed login mode is present and allowed
|
||||
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
|
||||
$sProposedLoginMode = utils::ReadParam('login_mode', '');
|
||||
@@ -46,22 +47,10 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
|
||||
if ($index !== false)
|
||||
{
|
||||
// Force login mode
|
||||
$_SESSION['login_mode'] = $sProposedLoginMode;
|
||||
LoginWebPage::SetLoginModeAndReload($sProposedLoginMode);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
}
|
||||
|
||||
protected function OnError(&$iErrorCode)
|
||||
{
|
||||
$_SESSION['login_error_count'] = (isset($_SESSION['login_error_count']) ? $_SESSION['login_error_count'] : 0) + 1;
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
}
|
||||
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
unset($_SESSION['login_error_count']);
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,6 +58,8 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
|
||||
*/
|
||||
class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExtension
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Must be executed after the other login plugins
|
||||
*
|
||||
@@ -81,7 +72,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
|
||||
protected function OnError(&$iErrorCode)
|
||||
{
|
||||
unset($_SESSION['login_mode']);
|
||||
self::ResetLoginSession();
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -89,7 +80,8 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']))
|
||||
{
|
||||
LoginWebPage::ResetSession();
|
||||
// If no plugin validated the user, exit
|
||||
self::ResetLoginSession();
|
||||
exit();
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
@@ -100,6 +92,19 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
*/
|
||||
public function LogoutAction()
|
||||
{
|
||||
unset($_SESSION['login_mode']);
|
||||
self::ResetLoginSession();
|
||||
}
|
||||
|
||||
// Hard reset of the session
|
||||
private static function ResetLoginSession()
|
||||
{
|
||||
LoginWebPage::ResetSession();
|
||||
foreach (array_keys($_SESSION) as $sKey)
|
||||
{
|
||||
if (utils::StartsWith($sKey, 'login_'))
|
||||
{
|
||||
unset($_SESSION[$sKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class LoginForm extends AbstractLoginFSMExtension
|
||||
class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
|
||||
{
|
||||
private $bForceFormOnError = false;
|
||||
|
||||
@@ -21,24 +21,12 @@ class LoginForm extends AbstractLoginFSMExtension
|
||||
return array('form');
|
||||
}
|
||||
|
||||
protected function OnModeDetection(&$iErrorCode)
|
||||
{
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
||||
if (!empty($sAuthUser) && !empty($sAuthPwd))
|
||||
{
|
||||
$_SESSION['login_mode'] = 'form';
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
}
|
||||
|
||||
protected function OnReadCredentials(&$iErrorCode)
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']) || ($_SESSION['login_mode'] == 'form'))
|
||||
{
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
||||
$_SESSION['login_mode'] = 'form';
|
||||
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd))
|
||||
{
|
||||
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
|
||||
@@ -51,11 +39,12 @@ class LoginForm extends AbstractLoginFSMExtension
|
||||
|
||||
// No credentials yet, display the form
|
||||
$oPage = LoginWebPage::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm('form', $this->bForceFormOnError);
|
||||
$oPage->DisplayLoginForm($this->bForceFormOnError);
|
||||
$oPage->output();
|
||||
$this->bForceFormOnError = false;
|
||||
exit;
|
||||
}
|
||||
$_SESSION['login_mode'] = 'form';
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
}
|
||||
@@ -103,4 +92,36 @@ class LoginForm extends AbstractLoginFSMExtension
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoginTwigData
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetLoginData()
|
||||
{
|
||||
|
||||
$aPostedVars = array('auth_user', 'auth_pwd');
|
||||
$oLoginData = new LoginTwigData($aPostedVars);
|
||||
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
|
||||
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
|
||||
|
||||
$aData = array(
|
||||
'sAuthUser' => $sAuthUser,
|
||||
'sAuthPwd' => $sAuthPwd,
|
||||
);
|
||||
$oLoginData->AddBlockData('login_input', new LoginBlockData('loginforminput.html.twig', $aData));
|
||||
$oLoginData->AddBlockData('login_submit', new LoginBlockData('loginformsubmit.html.twig'));
|
||||
|
||||
$bEnableResetPassword = empty(MetaModel::GetConfig()->Get('forgot_password')) ? true : MetaModel::GetConfig()->Get('forgot_password');
|
||||
$sResetPasswordUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?loginop=forgot_pwd';
|
||||
|
||||
$aData = array(
|
||||
'bEnableResetPassword' => $bEnableResetPassword,
|
||||
'sResetPasswordUrl' => $sResetPasswordUrl,
|
||||
);
|
||||
$oLoginData->AddBlockData('login_links', new LoginBlockData('loginformlinks.html.twig', $aData));
|
||||
|
||||
return $oLoginData;
|
||||
}
|
||||
}
|
||||
|
||||
222
application/logintwig.class.inc.php
Normal file
222
application/logintwig.class.inc.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
use Combodo\iTop\TwigExtension;
|
||||
|
||||
class LoginTwigData
|
||||
{
|
||||
private $aBlockData;
|
||||
private $aPostedVars;
|
||||
private $sTwigLoaderPath;
|
||||
private $sCSSFile;
|
||||
|
||||
/**
|
||||
* LoginTwigData constructor.
|
||||
*
|
||||
* @param array $aPostedVars
|
||||
* @param string $sLoaderPath
|
||||
* @param string $sCSSFile
|
||||
*/
|
||||
public function __construct($aPostedVars = array(), $sLoaderPath = null, $sCSSFile = null)
|
||||
{
|
||||
$this->aBlockData = array();
|
||||
$this->aPostedVars = $aPostedVars;
|
||||
$this->sTwigLoaderPath = $sLoaderPath;
|
||||
$this->sCSSFile = $sCSSFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sBlockName
|
||||
* @param LoginBlockData $oBlockData
|
||||
*/
|
||||
public final function AddBlockData($sBlockName, $oBlockData)
|
||||
{
|
||||
$this->aBlockData[$sBlockName] = $oBlockData;
|
||||
}
|
||||
|
||||
public final function GetBlockData($sBlockName)
|
||||
{
|
||||
/** @var LoginBlockData $oBlockData */
|
||||
$oBlockData = isset($this->aBlockData[$sBlockName]) ? $this->aBlockData[$sBlockName] : null;
|
||||
return $oBlockData;
|
||||
}
|
||||
|
||||
public final function GetPostedVars()
|
||||
{
|
||||
return $this->aPostedVars;
|
||||
}
|
||||
|
||||
public final function GetTwigLoaderPath()
|
||||
{
|
||||
return $this->sTwigLoaderPath;
|
||||
}
|
||||
|
||||
public final function GetCSSFile()
|
||||
{
|
||||
return $this->sCSSFile;
|
||||
}
|
||||
}
|
||||
|
||||
class LoginBlockData
|
||||
{
|
||||
private $sTwig;
|
||||
private $aData;
|
||||
|
||||
/**
|
||||
* LoginBlockData constructor.
|
||||
*
|
||||
* @param string $sTwig
|
||||
* @param array $aData
|
||||
*/
|
||||
public function __construct($sTwig, $aData = array())
|
||||
{
|
||||
$this->sTwig = $sTwig;
|
||||
$this->aData = $aData;
|
||||
}
|
||||
|
||||
public final function GetTwig()
|
||||
{
|
||||
return $this->sTwig;
|
||||
}
|
||||
|
||||
public final function GetData()
|
||||
{
|
||||
return $this->aData;
|
||||
}
|
||||
}
|
||||
|
||||
class LoginTwigContext
|
||||
{
|
||||
private $aLoginPluginList;
|
||||
private $aPluginFormData;
|
||||
private $aPostedVars;
|
||||
private $oTwig;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->aLoginPluginList = LoginWebPage::GetLoginPluginList('iLoginDataExtension', false);
|
||||
$this->aPluginFormData = array();
|
||||
$aTwigLoaders = array();
|
||||
$this->aPostedVars = array();
|
||||
foreach ($this->aLoginPluginList as $oLoginPlugin)
|
||||
{
|
||||
/** @var \iLoginDataExtension $oLoginPlugin */
|
||||
$oLoginData = $oLoginPlugin->GetLoginData();
|
||||
$this->aPluginFormData[] = $oLoginData;
|
||||
$sTwigLoaderPath = $oLoginData->GetTwigLoaderPath();
|
||||
if ($sTwigLoaderPath != null)
|
||||
{
|
||||
$aTwigLoaders[] = new Twig_Loader_Filesystem($sTwigLoaderPath);
|
||||
}
|
||||
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginData->GetPostedVars());
|
||||
}
|
||||
|
||||
$oCoreLoader = new Twig_Loader_Filesystem(array(), APPROOT.'templates');
|
||||
$aCoreTemplatesPaths = array('login', 'login/password');
|
||||
// Having this path declared after the plugins let the plugins replace the core templates
|
||||
$oCoreLoader->setPaths($aCoreTemplatesPaths);
|
||||
// Having the core templates accessible within a different namespace offer the possibility to extend them while replacing them
|
||||
$oCoreLoader->setPaths($aCoreTemplatesPaths, 'ItopCore');
|
||||
$aTwigLoaders[] = $oCoreLoader;
|
||||
|
||||
$oLoader = new Twig_Loader_Chain($aTwigLoaders);
|
||||
$this->oTwig = new Twig_Environment($oLoader);
|
||||
TwigExtension::RegisterTwigExtensions($this->oTwig);
|
||||
}
|
||||
|
||||
public function GetDefaultVars()
|
||||
{
|
||||
$sLogo = 'itop-logo-external.png';
|
||||
$sBrandingLogo = 'login-logo.png';
|
||||
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
}
|
||||
|
||||
$aVars = array(
|
||||
'sAppRootUrl' => utils::GetAbsoluteUrlAppRoot(),
|
||||
'aPluginFormData' => $this->GetPluginFormData(),
|
||||
'sItopVersion' => ITOP_VERSION,
|
||||
'sVersionShort' => $sVersionShort,
|
||||
'sIconUrl' => $sIconUrl,
|
||||
'sDisplayIcon' => $sDisplayIcon,
|
||||
);
|
||||
|
||||
return $aVars;
|
||||
}
|
||||
|
||||
public function Render(NiceWebPage $oPage, $sTwigFile, $aVars = array())
|
||||
{
|
||||
$sType = 'html';
|
||||
|
||||
if (preg_match('/.*\.(html|ready\.js|js)\.twig/U', $sTwigFile, $matches))
|
||||
{
|
||||
$sType = $matches[1];
|
||||
}
|
||||
|
||||
switch ($sType)
|
||||
{
|
||||
case 'html':
|
||||
$oPage->add($this->GetTwig()->render($sTwigFile, $aVars));
|
||||
// Render CSS links
|
||||
foreach ($this->aPluginFormData as $oFormData)
|
||||
{
|
||||
/** @var \LoginTwigData $oFormData */
|
||||
$sCSSFile = $oFormData->GetCSSFile();
|
||||
if (!empty($sCSSFile))
|
||||
{
|
||||
$oPage->add_linked_stylesheet($sCSSFile);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'js':
|
||||
$oPage->add_script($this->GetTwig()->render($sTwigFile, $aVars));
|
||||
break;
|
||||
case 'ready.js':
|
||||
$oPage->add_ready_script($this->GetTwig()->render($sTwigFile, $aVars));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetLoginPluginList()
|
||||
{
|
||||
return $this->aLoginPluginList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetPluginFormData()
|
||||
{
|
||||
return $this->aPluginFormData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetPostedVars()
|
||||
{
|
||||
return $this->aPostedVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Twig_Environment
|
||||
*/
|
||||
public function GetTwig()
|
||||
{
|
||||
return $this->oTwig;
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,6 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT."/application/nicewebpage.class.inc.php");
|
||||
require_once(APPROOT.'/application/portaldispatcher.class.inc.php');
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
*/
|
||||
@@ -91,6 +89,8 @@ class LoginWebPage extends NiceWebPage
|
||||
public function SetStyleSheet()
|
||||
{
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/login.css');
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/v4-shims.min.css');
|
||||
}
|
||||
|
||||
public static function SetLoginFailedMessage($sMessage)
|
||||
@@ -98,23 +98,11 @@ class LoginWebPage extends NiceWebPage
|
||||
self::$m_sLoginFailedMessage = $sMessage;
|
||||
}
|
||||
|
||||
public function EnableResetPassword()
|
||||
{
|
||||
return MetaModel::GetConfig()->Get('forgot_password');
|
||||
}
|
||||
|
||||
public function DisplayLoginHeader($bMainAppLogo = false)
|
||||
{
|
||||
if ($bMainAppLogo)
|
||||
{
|
||||
$sLogo = 'itop-logo.png';
|
||||
$sBrandingLogo = 'main-logo.png';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLogo = 'itop-logo-external.png';
|
||||
$sBrandingLogo = 'login-logo.png';
|
||||
}
|
||||
$sLogo = 'itop-logo-external.png';
|
||||
$sBrandingLogo = 'login-logo.png';
|
||||
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
@@ -125,104 +113,69 @@ class LoginWebPage extends NiceWebPage
|
||||
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8')."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
|
||||
}
|
||||
|
||||
public function DisplayLoginForm($sLoginType, $bFailedLogin = false)
|
||||
public function DisplayLoginForm($bFailedLogin = false)
|
||||
{
|
||||
switch($sLoginType)
|
||||
$oTwigContext = new LoginTwigContext();
|
||||
$aPostedVars = array_merge(array('login_mode', 'loginop'), $oTwigContext->GetPostedVars());
|
||||
|
||||
$sMessage = Dict::S('UI:Login:IdentifyYourself');
|
||||
|
||||
// Error message
|
||||
if ($bFailedLogin)
|
||||
{
|
||||
case 'form':
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
|
||||
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
|
||||
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:Welcome')."</h1>\n");
|
||||
if ($bFailedLogin)
|
||||
if (self::$m_sLoginFailedMessage != '')
|
||||
{
|
||||
if (self::$m_sLoginFailedMessage != '')
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".self::$m_sLoginFailedMessage."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectLoginPassword')."</p>\n");
|
||||
}
|
||||
$sMessage = self::$m_sLoginFailedMessage;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:Login:IdentifyYourself')."</p>\n");
|
||||
$sMessage = Dict::S('UI:Login:IncorrectLoginPassword');
|
||||
}
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$sForgotPwd = $this->EnableResetPassword() ? $this->ForgotPwdLink() : '';
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label></td><td style=\"text-align:left\"><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"pwd\">".Dict::S('UI:Login:PasswordPrompt').":</label></td><td style=\"text-align:left\"><input id=\"pwd\" type=\"password\" name=\"auth_pwd\" value=\"".htmlentities($sAuthPwd, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Login')."\" /></span></td></tr>\n");
|
||||
if (strlen($sForgotPwd) > 0)
|
||||
{
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\">$sForgotPwd</td></tr>\n");
|
||||
}
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"login\" />\n");
|
||||
|
||||
$this->add_ready_script('$("#user").focus();');
|
||||
|
||||
// Keep the OTHER parameters posted
|
||||
foreach($_POST as $sPostedKey => $postedValue)
|
||||
{
|
||||
if (!in_array($sPostedKey, array('auth_user', 'auth_pwd')))
|
||||
{
|
||||
if (is_array($postedValue))
|
||||
{
|
||||
foreach($postedValue as $sKey => $sValue)
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"".htmlentities($sPostedKey, ENT_QUOTES, 'UTF-8')."[".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"".htmlentities($sPostedKey, ENT_QUOTES, 'UTF-8')."\" value=\"".htmlentities($postedValue, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->add("</form>\n");
|
||||
$this->add(Dict::S('UI:Login:About'));
|
||||
$this->add("</div>\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return '' to disable this feature
|
||||
*/
|
||||
public function ForgotPwdLink()
|
||||
{
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?loginop=forgot_pwd';
|
||||
$sHtml = "<a href=\"$sUrl\" target=\"_blank\">".Dict::S('UI:Login:ForgotPwd')."</a>";
|
||||
return $sHtml;
|
||||
// Keep the OTHER parameters posted
|
||||
$aPreviousPostedVars = array();
|
||||
foreach($_POST as $sPostedKey => $postedValue)
|
||||
{
|
||||
if (!in_array($sPostedKey, $aPostedVars))
|
||||
{
|
||||
if (is_array($postedValue))
|
||||
{
|
||||
foreach($postedValue as $sKey => $sValue)
|
||||
{
|
||||
$sName = "{$sPostedKey}[{$sKey}]";
|
||||
$aPreviousPostedVars[$sName] = $sValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPreviousPostedVars[$sPostedKey] = $postedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aVars = array(
|
||||
'bFailedLogin' => $bFailedLogin,
|
||||
'sMessage' => $sMessage,
|
||||
'aPreviousPostedVars' => $aPreviousPostedVars,
|
||||
);
|
||||
$aVars = array_merge($aVars, $oTwigContext->GetDefaultVars());
|
||||
|
||||
$oTwigContext->Render($this, 'login.html.twig', $aVars);
|
||||
}
|
||||
|
||||
public function DisplayForgotPwdForm($bFailedToReset = false, $sFailureReason = null)
|
||||
{
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:ForgotPwdForm')."</h1>\n");
|
||||
$this->add("<p>".Dict::S('UI:Login:ForgotPwdForm+')."</p>\n");
|
||||
if ($bFailedToReset)
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".Dict::Format('UI:Login:ResetPwdFailed', htmlentities($sFailureReason, ENT_QUOTES, 'UTF-8'))."</p>\n");
|
||||
}
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center\"><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"button\" onClick=\"window.close();\" value=\"".Dict::S('UI:Button:Cancel')."\" /></span> <span class=\"btn_border\"><input type=\"submit\" value=\"".Dict::S('UI:Login:ResetPassword')."\" /></span></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"forgot_pwd_go\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div>\n");
|
||||
|
||||
$this->add_ready_script('$("#user").focus();');
|
||||
$oTwigContext = new LoginTwigContext();
|
||||
$aVars = $oTwigContext->GetDefaultVars();
|
||||
$aVars['sAuthUser'] = $sAuthUser;
|
||||
$aVars['bFailedToReset'] = $bFailedToReset;
|
||||
$aVars['sFailureReason'] = $sFailureReason;
|
||||
|
||||
$oTwigContext->Render($this, 'forgotpwdform.html.twig', $aVars);
|
||||
$oTwigContext->Render($this, 'forgotpwdform.ready.js.twig');
|
||||
}
|
||||
|
||||
protected function ForgotPwdGo()
|
||||
@@ -280,16 +233,9 @@ class LoginWebPage extends NiceWebPage
|
||||
throw new Exception(Dict::S('UI:ResetPwd-Error-Send'));
|
||||
}
|
||||
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:ForgotPwdForm')."</h1>\n");
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-EmailSent')."</p>");
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><input type=\"button\" onClick=\"window.close();\" value=\"".Dict::S('UI:Button:Done')."\" /></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div\n");
|
||||
$oTwigContext = new LoginTwigContext();
|
||||
$aVars = $oTwigContext->GetDefaultVars();
|
||||
$oTwigContext->Render($this, 'forgotpwdsent.html.twig', $aVars);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
@@ -302,59 +248,36 @@ class LoginWebPage extends NiceWebPage
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
$sToken = utils::ReadParam('token', '', false, 'raw_data');
|
||||
|
||||
$sAuthUserForDisplay = utils::HtmlEntities($sAuthUser);
|
||||
$sTokenForDisplay = utils::HtmlEntities($sToken);
|
||||
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$oUser = UserRights::GetUserObject();
|
||||
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:ResetPwd-Title')."</h1>\n");
|
||||
if ($oUser == null)
|
||||
$oTwigContext = new LoginTwigContext();
|
||||
$aVars = $oTwigContext->GetDefaultVars();
|
||||
|
||||
$aVars['sAuthUser'] = $sAuthUser;
|
||||
$aVars['sToken'] = $sToken;
|
||||
if (($oUser == null))
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUserForDisplay)."</p>\n");
|
||||
$aVars['bNoUser'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aVars['bNoUser'] = false;
|
||||
$oEncryptedToken = $oUser->Get('reset_pwd_token');
|
||||
|
||||
|
||||
if (!$oEncryptedToken->CheckPassword($sToken))
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||
$aVars['bBadToken'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUserNameForDisplay = utils::HtmlEntities($oUser->GetFriendlyName());
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $sUserNameForDisplay)."</p>\n");
|
||||
|
||||
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
function DoCheckPwd()
|
||||
{
|
||||
if ($('#new_pwd').val() != $('#retype_new_pwd').val())
|
||||
{
|
||||
alert('$sInconsistenPwdMsg');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
|
||||
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".$sAuthUserForDisplay."\" />\n");
|
||||
$this->add("<input type=\"hidden\" name=\"token\" value=\"".$sTokenForDisplay."\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div\n");
|
||||
$aVars['bBadToken'] = false;
|
||||
$aVars['sUserName'] = $oUser->GetFriendlyName();
|
||||
}
|
||||
}
|
||||
|
||||
$oTwigContext->Render($this, 'resetpwdform.html.twig', $aVars);
|
||||
$oTwigContext->Render($this, 'resetpwdform.js.twig');
|
||||
}
|
||||
|
||||
public function DoResetPassword()
|
||||
@@ -366,102 +289,61 @@ EOF
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$oUser = UserRights::GetUserObject();
|
||||
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:ResetPwd-Title')."</h1>\n");
|
||||
if ($oUser == null)
|
||||
$oTwigContext = new LoginTwigContext();
|
||||
$aVars = $oTwigContext->GetDefaultVars();
|
||||
|
||||
$aVars['sAuthUser'] = $sAuthUser;
|
||||
$aVars['sToken'] = $sToken;
|
||||
if (($oUser == null))
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
||||
$aVars['bNoUser'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oEncryptedPassword = $oUser->Get('reset_pwd_token');
|
||||
if (!$oEncryptedPassword->CheckPassword($sToken))
|
||||
$aVars['bNoUser'] = false;
|
||||
$oEncryptedToken = $oUser->Get('reset_pwd_token');
|
||||
|
||||
if (!$oEncryptedToken->CheckPassword($sToken))
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||
$aVars['bBadToken'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aVars['bBadToken'] = false;
|
||||
// Trash the token and change the password
|
||||
$oUser->Set('reset_pwd_token', '');
|
||||
$oUser->AllowWrite(true);
|
||||
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
|
||||
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
|
||||
$aVars['sUrl'] = utils::GetAbsoluteUrlAppRoot();
|
||||
}
|
||||
$this->add("</div\n");
|
||||
}
|
||||
|
||||
$oTwigContext->Render($this, 'resetpwddone.html.twig', $aVars);
|
||||
}
|
||||
|
||||
public function DisplayChangePwdForm($bFailedLogin = false)
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||
|
||||
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
||||
$this->add_script(<<<EOF
|
||||
function GoBack()
|
||||
{
|
||||
window.history.back();
|
||||
}
|
||||
|
||||
function DoCheckPwd()
|
||||
{
|
||||
if ($('#new_pwd').val() != $('#retype_new_pwd').val())
|
||||
{
|
||||
alert('$sInconsistenPwdMsg');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:ChangeYourPassword')."</h1>\n");
|
||||
if ($bFailedLogin)
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectOldPassword')."</p>\n");
|
||||
}
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"old_pwd\">".Dict::S('UI:Login:OldPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"old_pwd\" name=\"old_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"button\" onClick=\"GoBack();\" value=\"".Dict::S('UI:Button:Cancel')."\" /></span> <span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_change_pwd\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div>\n");
|
||||
$oTwigContext = new LoginTwigContext();
|
||||
$aVars = $oTwigContext->GetDefaultVars();
|
||||
$aVars['bFailedLogin'] = $bFailedLogin;
|
||||
$oTwigContext->Render($this, 'changepwdform.js.twig');
|
||||
$oTwigContext->Render($this, 'changepwdform.html.twig', $aVars);
|
||||
}
|
||||
|
||||
public function DisplayLogoutPage($bPortal, $sTitle = null)
|
||||
{
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
if ($bPortal)
|
||||
{
|
||||
$sUrl .= 'portal/';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl .= 'pages/UI.php';
|
||||
}
|
||||
|
||||
if (empty($sTitle))
|
||||
{
|
||||
$sTitle = Dict::S('UI:LogOff:ThankYou');
|
||||
}
|
||||
|
||||
$sUrl .= $bPortal ? 'portal/' : 'pages/UI.php';
|
||||
$sTitle = empty($sTitle) ? Dict::S('UI:LogOff:ThankYou') : $sTitle;
|
||||
$sMessage = Dict::S('UI:LogOff:ClickHereToLoginAgain');
|
||||
|
||||
$this->no_cache();
|
||||
$this->DisplayLoginHeader();
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>$sTitle</h1>\n");
|
||||
$oTwigContext = new LoginTwigContext();
|
||||
$aVars = $oTwigContext->GetDefaultVars();
|
||||
$aVars['sUrl'] = $sUrl;
|
||||
$aVars['sTitle'] = $sTitle;
|
||||
$aVars['sMessage'] = $sMessage;
|
||||
|
||||
$this->add("<p><a href=\"$sUrl\">$sMessage</a></p>");
|
||||
$this->add("</div>\n");
|
||||
$oTwigContext->Render($this, 'logout.html.twig', $aVars);
|
||||
$this->output();
|
||||
}
|
||||
|
||||
@@ -532,6 +414,8 @@ EOF
|
||||
IssueLog::Info("SESSION: $sSessionLog");
|
||||
}
|
||||
|
||||
$iErrorCode = self::EXIT_CODE_OK;
|
||||
|
||||
// Finite state machine loop
|
||||
while (true)
|
||||
{
|
||||
@@ -556,7 +440,6 @@ EOF
|
||||
}
|
||||
IssueLog::Info("Login: state: [$sLoginState] call: ".get_class($oLoginFSMExtensionInstance));
|
||||
}
|
||||
$iErrorCode = self::EXIT_CODE_OK;
|
||||
$iResponse = $oLoginFSMExtensionInstance->LoginAction($sLoginState, $iErrorCode);
|
||||
if ($iResponse == self::LOGIN_FSM_RETURN_OK)
|
||||
{
|
||||
@@ -597,14 +480,22 @@ EOF
|
||||
* Use the login mode to filter plugins
|
||||
*
|
||||
* @param string $sInterface 'iLoginFSMExtension' or 'iLogoutExtension'
|
||||
* @param bool $bFilterWithMode if false do not filter the plugin list with login mode
|
||||
*
|
||||
* @return array of plugins
|
||||
*/
|
||||
public static function GetLoginPluginList($sInterface = 'iLoginFSMExtension')
|
||||
public static function GetLoginPluginList($sInterface = 'iLoginFSMExtension', $bFilterWithMode = true)
|
||||
{
|
||||
$aAllPlugins = array();
|
||||
|
||||
$sCurrentLoginMode = isset($_SESSION['login_mode']) ? $_SESSION['login_mode'] : '';
|
||||
if ($bFilterWithMode)
|
||||
{
|
||||
$sCurrentLoginMode = isset($_SESSION['login_mode']) ? $_SESSION['login_mode'] : '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCurrentLoginMode = '';
|
||||
}
|
||||
|
||||
/** @var iLoginExtension $oLoginExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins($sInterface) as $oLoginExtensionInstance)
|
||||
@@ -620,6 +511,7 @@ EOF
|
||||
$aAllPlugins[$sLoginMode] = array();
|
||||
}
|
||||
$aAllPlugins[$sLoginMode][] = $oLoginExtensionInstance;
|
||||
break; // Stop here to avoid registering a plugin twice
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,6 +672,35 @@ EOF
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function SetLoginModeAndReload($sNewLoginMode)
|
||||
{
|
||||
if (isset($_SESSION['login_mode']) && ($_SESSION['login_mode'] == $sNewLoginMode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$_SESSION['login_mode'] = $sNewLoginMode;
|
||||
self::HTTPReload();
|
||||
}
|
||||
|
||||
public static function HTTPReload()
|
||||
{
|
||||
$sOriginURL = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
|
||||
if (!utils::StartsWith($sOriginURL, utils::GetAbsoluteUrlAppRoot()))
|
||||
{
|
||||
// If the found URL does not start with the configured AppRoot URL
|
||||
$sOriginURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php';
|
||||
}
|
||||
self::HTTPRedirect($sOriginURL);
|
||||
}
|
||||
|
||||
public static function HTTPRedirect($sURL)
|
||||
{
|
||||
header('HTTP/1.1 307 Temporary Redirect');
|
||||
header('Location: '.$sURL);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provisioning API: Find a User
|
||||
*
|
||||
@@ -1118,7 +1039,7 @@ EOF
|
||||
}
|
||||
self::ResetSession();
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
|
||||
$oPage->DisplayLoginForm(false /* not a failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
|
||||
126
application/twigextension.class.inc.php
Normal file
126
application/twigextension.class.inc.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop;
|
||||
|
||||
use AttributeDateTime;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use Twig_Environment;
|
||||
use Twig_SimpleFilter;
|
||||
use Twig_SimpleFunction;
|
||||
use utils;
|
||||
|
||||
class TwigExtension
|
||||
{
|
||||
/**
|
||||
* Registers Twig extensions such as filters or functions.
|
||||
* It allows us to access some stuff directly in twig.
|
||||
*
|
||||
* @param \Twig_Environment $oTwigEnv
|
||||
*/
|
||||
public static function RegisterTwigExtensions(Twig_Environment &$oTwigEnv)
|
||||
{
|
||||
// Filter to translate a string via the Dict::S function
|
||||
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_s',
|
||||
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
|
||||
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to format a string via the Dict::Format function
|
||||
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_format',
|
||||
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
|
||||
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to format output
|
||||
// example a DateTime is converted to user format
|
||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('date_format',
|
||||
function ($sDate) {
|
||||
try
|
||||
{
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate)))
|
||||
{
|
||||
return AttributeDateTime::GetFormat()->Format($sDate);
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
}
|
||||
return $sDate;
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
// Filter to format output
|
||||
// example a DateTime is converted to user format
|
||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('size_format',
|
||||
function ($sSize) {
|
||||
return utils::BytesToFriendlyFormat($sSize);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to enable base64 encode/decode
|
||||
// Usage in twig: {{ 'String to encode'|base64_encode }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
|
||||
|
||||
// Filter to enable json decode (encode already exists)
|
||||
// Usage in twig: {{ aSomeArray|json_decode }}
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('json_decode', function ($sJsonString, $bAssoc = false) {
|
||||
return json_decode($sJsonString, $bAssoc);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to add itopversion to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
$sUrl = $sUrl."?itopversion=".ITOP_VERSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = $sUrl."&itopversion=".ITOP_VERSION;
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Filter to add a module's version to an url
|
||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
|
||||
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
|
||||
|
||||
if (strpos($sUrl, '?') === false)
|
||||
{
|
||||
$sUrl = $sUrl."?moduleversion=".$sModuleVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUrl = $sUrl."&moduleversion=".$sModuleVersion;
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Function to check our current environment
|
||||
// Usage in twig: {% if is_development_environment() %}
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('is_development_environment', function()
|
||||
{
|
||||
return utils::IsDevelopmentEnvironment();
|
||||
}));
|
||||
|
||||
// Function to get configuration parameter
|
||||
// Usage in twig: {{ get_config_parameter('foo') }}
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_config_parameter', function($sParamName)
|
||||
{
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
return $oConfig->Get($sParamName);
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -2781,7 +2781,7 @@ abstract class MetaModel
|
||||
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iLoginFSMExtension', 'iLogoutExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider');
|
||||
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iLoginFSMExtension', 'iLoginDataExtension', 'iLogoutExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider');
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
self::$m_aExtensionClasses[$sInterface] = array();
|
||||
|
||||
@@ -48,14 +48,12 @@ body {
|
||||
#login table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#pwd, #user,#old_pwd, #new_pwd, #retype_new_pwd {
|
||||
#pwd, #user, #old_pwd, #new_pwd, #retype_new_pwd {
|
||||
width: 10em;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #1C94C4;
|
||||
font-size: 16pt;
|
||||
@@ -63,3 +61,8 @@ h1 {
|
||||
.v-spacer {
|
||||
padding-top: 1em;
|
||||
}
|
||||
.sso-button {
|
||||
width: 100%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,6 @@
|
||||
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
'CAS:Error:UserNotAllowed' => 'User not allowed',
|
||||
'CAS:Login:SignIn' => 'Sign in with CAS',
|
||||
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server',
|
||||
));
|
||||
|
||||
@@ -8,4 +8,6 @@
|
||||
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'CAS:Error:UserNotAllowed' => 'Utilisateur non autorisé',
|
||||
'CAS:Login:SignIn' => 'S\'identifier avec CAS',
|
||||
'CAS:Login:SignInTooltip' => 'Cliquer ici pour s\'identifier avec le serveur CAS',
|
||||
));
|
||||
|
||||
@@ -11,7 +11,10 @@ use AbstractLoginFSMExtension;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use Dict;
|
||||
use iLoginDataExtension;
|
||||
use iLogoutExtension;
|
||||
use LoginBlockData;
|
||||
use LoginTwigData;
|
||||
use LoginWebPage;
|
||||
use MetaModel;
|
||||
use phpCAS;
|
||||
@@ -23,7 +26,7 @@ use utils;
|
||||
/**
|
||||
* Class CASLoginExtension
|
||||
*/
|
||||
class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExtension
|
||||
class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExtension, iLoginDataExtension
|
||||
{
|
||||
/**
|
||||
* Return the list of supported login modes for this plugin
|
||||
@@ -45,24 +48,29 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
{
|
||||
if (!isset($_SESSION['login_mode']) || ($_SESSION['login_mode'] == 'cas'))
|
||||
{
|
||||
$_SESSION['login_mode'] = 'cas';
|
||||
static::InitCASClient();
|
||||
if (phpCAS::isAuthenticated())
|
||||
{
|
||||
$_SESSION['login_mode'] = 'cas';
|
||||
$_SESSION['auth_user'] = phpCAS::getUser();
|
||||
unset($_SESSION['login_will_redirect']);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isset($_SESSION['cas_count']))
|
||||
if ($_SESSION['login_mode'] == 'cas')
|
||||
{
|
||||
$_SESSION['cas_count'] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($_SESSION['cas_count']);
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_MISSINGLOGIN;
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_ERROR;
|
||||
if (!isset($_SESSION['login_will_redirect']))
|
||||
{
|
||||
$_SESSION['login_will_redirect'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($_SESSION['login_will_redirect']);
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_MISSINGLOGIN;
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_ERROR;
|
||||
}
|
||||
}
|
||||
$_SESSION['login_mode'] = 'cas';
|
||||
phpCAS::forceAuthentication(); // Redirect to CAS and exit
|
||||
}
|
||||
}
|
||||
@@ -110,8 +118,8 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
{
|
||||
$oLoginWebPage = new LoginWebPage();
|
||||
$oLoginWebPage->DisplayLogoutPage(false, Dict::S('CAS:Error:UserNotAllowed'));
|
||||
exit();
|
||||
}
|
||||
exit();
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_RETURN_CONTINUE;
|
||||
}
|
||||
@@ -169,18 +177,6 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
}
|
||||
}
|
||||
|
||||
public function GetSocialButtons()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'login_mode' => 'cas',
|
||||
'label' => 'Sign in with CAS',
|
||||
'tooltip' => 'Click here to authenticate yourself with the CAS server',
|
||||
'twig' => 'cas_button.twig',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private function DoUserProvisioning($sLogin)
|
||||
{
|
||||
$bCASUserSynchro = Config::Get('cas_user_synchro');
|
||||
@@ -200,6 +196,26 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
}
|
||||
CASUserProvisioning::CreateUser($sLogin, '', 'external');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoginTwigData
|
||||
*/
|
||||
public function GetLoginData()
|
||||
{
|
||||
$sPath = APPROOT.'env-'.utils::GetCurrentEnvironment().'/authent-cas/view';
|
||||
$oLoginData = new LoginTwigData(array(), $sPath);
|
||||
|
||||
$aData = array(
|
||||
'sLoginMode' => 'cas',
|
||||
'sLabel' => Dict::S('CAS:Login:SignIn'),
|
||||
'sTooltip' => Dict::S('CAS:Login:SignInTooltip'),
|
||||
);
|
||||
$oBlockData = new LoginBlockData('cas_sso_button.html.twig', $aData);
|
||||
|
||||
$oLoginData->AddBlockData('login_sso_buttons', $oBlockData);
|
||||
|
||||
return $oLoginData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
33
datamodels/2.x/authent-cas/view/cas_sso_button.html.twig
Normal file
33
datamodels/2.x/authent-cas/view/cas_sso_button.html.twig
Normal file
@@ -0,0 +1,33 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<tr>
|
||||
<td style="text-align:center" colspan="2">
|
||||
<div class="sso-button">
|
||||
<div style="width:19em; cursor: pointer; margin-left: auto; margin-right: auto; padding: 0.5em; background-color: #eee; border-radius: 0.25em;" title="{{ aData.sTooltip }}" onclick="$('#login_mode').val('{{ aData.sLoginMode }}'); $('#login_form').submit(); return false;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 336.82 167.83" class="logo" style="height: 1em; vertical-align: middle; margin-right: 1em;">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #024d71;
|
||||
}
|
||||
.cls-2 {
|
||||
fill: #74c163;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<title>{{ aData.sTooltip }}</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<path class="cls-1" d="M217.72,469.85c-17.68,1.31-38.31,1-54.18-7.87-12.59-7-23.71-18.52-28.72-32.14a82,82,0,0,1-4.49-27.49c0-36.26,16.11-64.23,40.29-80.34,16.59-11.38,36-16.83,58.54-16.83,17.3,0,30.34,4,35.08,6.64l-9.48,27.73c-4.5-2.38-15.17-5.22-28.92-5.22s-26.78,4.27-36.5,12.33c-13.27,11.13-22,29.39-22,51.66,0,25.6,14.46,42.67,42.43,42.67a161.48,161.48,0,0,0,22.38-1.19Z" transform="translate(-130.33 -305.18)"></path>
|
||||
<path class="cls-1" d="M322.77,398.75l-2.72-30.4c-.71-8.76-1.42-21.56-2.14-31.28h-.71c-4,9.72-8.53,22-12.8,31.28l-13.69,30.08-13.53,33.43-17.33,38.41h-36.5l78-159.75h43.85l18.49,159.75H327.87l-3.05-38.12Z" transform="translate(-130.33 -305.18)"></path>
|
||||
<path class="cls-1" d="M364.31,438.85c12.27,3.81,21.29,5.19,36.22,5.19,13,0,20.87-6.71,20.87-19,0-9.24-6.87-14.93-21.57-22.75-16.82-9-32.94-21.8-32.94-42.42,0-32.24,28-51.91,62.81-51.91,19.19,0,30.57,4.27,37.44,7.82l-10.66,28.44a62.94,62.94,0,0,0-29.63-6.87c-15.88,0-24.17,7.82-24.17,16.59,0,9.48,9.72,15.17,23.23,22.75,19.43,10.19,31.52,23.23,31.52,42.43,0,35.55-29.44,55.72-64.71,53.8a231.52,231.52,0,0,1-24.78-2.61Z" transform="translate(-130.33 -305.18)"></path>
|
||||
</g>
|
||||
<g id="swoosh">
|
||||
<path class="cls-2" d="M257.45,432.24a41.64,41.64,0,0,1,13.11-.32,39.17,39.17,0,0,1,16.89,6.2l0,0c10.55-15,29.11-25,50.24-25,19.75,0,37.34,8.85,48.16,22.28l1-.77c-12.85-17.31-37.44-29-65.67-29-26.92,0-50.54,10.65-63.81,26.65Z" transform="translate(-130.33 -305.18)"></path>
|
||||
</g>
|
||||
</svg>
|
||||
{{ aData.sLabel }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -526,6 +526,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'UI:Login:ForgotPwdForm+' => 'iTop can send you an email in which you will find instructions to follow to reset your account.',
|
||||
'UI:Login:ResetPassword' => 'Send now!',
|
||||
'UI:Login:ResetPwdFailed' => 'Failed to send an email: %1$s',
|
||||
'UI:Login:SeparatorOr' => 'Or',
|
||||
|
||||
'UI:ResetPwd-Error-WrongLogin' => '\'%1$s\' is not a valid login',
|
||||
'UI:ResetPwd-Error-NotPossible' => 'external accounts do not allow password reset.',
|
||||
|
||||
@@ -509,6 +509,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'UI:Login:ForgotPwdForm+' => 'Vous pouvez demander à saisir un nouveau mot de passe. Vous allez recevoir un email et vous pourrez suivre les instructions.',
|
||||
'UI:Login:ResetPassword' => 'Envoyer le message',
|
||||
'UI:Login:ResetPwdFailed' => 'Impossible de vous faire parvenir le message: %1$s',
|
||||
'UI:Login:SeparatorOr' => 'Ou',
|
||||
|
||||
'UI:ResetPwd-Error-WrongLogin' => 'le compte \'%1$s\' est inconnu.',
|
||||
'UI:ResetPwd-Error-NotPossible' => 'les comptes "externes" ne permettent pas la saisie d\'un mot de passe dans iTop.',
|
||||
|
||||
@@ -377,11 +377,11 @@ class ClassLoader
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
$search = $subPath.'\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
$length = $this->prefixLengthsPsr4[$first][$search];
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'AbstractLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'AbstractPortalUIExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'Action' => $baseDir . '/core/action.class.inc.php',
|
||||
'ActionChecker' => $baseDir . '/core/userrights.class.inc.php',
|
||||
@@ -90,7 +91,6 @@ return array(
|
||||
'BulkExportMissingParameterException' => $baseDir . '/core/bulkexport.class.inc.php',
|
||||
'BulkExportResult' => $baseDir . '/core/bulkexport.class.inc.php',
|
||||
'BulkExportResultGC' => $baseDir . '/core/bulkexport.class.inc.php',
|
||||
'CAS_SelfRegister' => $baseDir . '/core/userrights.class.inc.php',
|
||||
'CLIPage' => $baseDir . '/application/clipage.class.inc.php',
|
||||
'CMDBChange' => $baseDir . '/core/cmdbchange.class.inc.php',
|
||||
'CMDBChangeOp' => $baseDir . '/core/cmdbchangeop.class.inc.php',
|
||||
@@ -133,6 +133,7 @@ return array(
|
||||
'CheckableExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
|
||||
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\TwigExtension' => $baseDir . '/application/twigextension.class.inc.php',
|
||||
'Config' => $baseDir . '/core/config.class.inc.php',
|
||||
'ConfigException' => $baseDir . '/core/config.class.inc.php',
|
||||
'Console_Getopt' => $vendorDir . '/pear/console_getopt/Console/Getopt.php',
|
||||
@@ -257,6 +258,15 @@ return array(
|
||||
'ListExpression' => $baseDir . '/core/oql/expression.class.inc.php',
|
||||
'ListOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
|
||||
'LogAPI' => $baseDir . '/core/log.class.inc.php',
|
||||
'LoginBasic' => $baseDir . '/application/loginbasic.class.inc.php',
|
||||
'LoginBlockData' => $baseDir . '/application/logintwig.class.inc.php',
|
||||
'LoginDefaultAfter' => $baseDir . '/application/logindefault.class.inc.php',
|
||||
'LoginDefaultBefore' => $baseDir . '/application/logindefault.class.inc.php',
|
||||
'LoginExternal' => $baseDir . '/application/loginexternal.class.inc.php',
|
||||
'LoginForm' => $baseDir . '/application/loginform.class.inc.php',
|
||||
'LoginTwigContext' => $baseDir . '/application/logintwig.class.inc.php',
|
||||
'LoginTwigData' => $baseDir . '/application/logintwig.class.inc.php',
|
||||
'LoginURL' => $baseDir . '/application/loginurl.class.inc.php',
|
||||
'LoginWebPage' => $baseDir . '/application/loginwebpage.class.inc.php',
|
||||
'MatchExpression' => $baseDir . '/core/oql/expression.class.inc.php',
|
||||
'MatchOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
|
||||
@@ -274,6 +284,7 @@ return array(
|
||||
'MyHelpers' => $baseDir . '/core/MyHelpers.class.inc.php',
|
||||
'MySQLException' => $baseDir . '/core/cmdbsource.class.inc.php',
|
||||
'MySQLHasGoneAwayException' => $baseDir . '/core/cmdbsource.class.inc.php',
|
||||
'MySQLNoTransactionException' => $baseDir . '/core/cmdbsource.class.inc.php',
|
||||
'MySQLQueryHasNoResultException' => $baseDir . '/core/cmdbsource.class.inc.php',
|
||||
'NewObjectMenuNode' => $baseDir . '/application/menunode.class.inc.php',
|
||||
'NewsroomProviderBase' => $baseDir . '/application/newsroomprovider.class.inc.php',
|
||||
@@ -1885,6 +1896,10 @@ return array(
|
||||
'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php',
|
||||
'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php',
|
||||
'iDisplay' => $baseDir . '/core/dbobject.class.php',
|
||||
'iLoginDataExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iLoginExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iLogoutExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iMetricComputer' => $baseDir . '/core/computing.inc.php',
|
||||
'iNewsroomProvider' => $baseDir . '/application/newsroomprovider.class.inc.php',
|
||||
'iOnClassInitialization' => $baseDir . '/core/metamodelmodifier.inc.php',
|
||||
|
||||
@@ -24,7 +24,7 @@ class ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b', 'loadClassLoader'));
|
||||
|
||||
$includePaths = require __DIR__ . '/include_paths.php';
|
||||
$includePaths[] = get_include_path();
|
||||
array_push($includePaths, get_include_path());
|
||||
set_include_path(implode(PATH_SEPARATOR, $includePaths));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
|
||||
@@ -227,6 +227,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'AbstractLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'AbstractPortalUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'Action' => __DIR__ . '/../..' . '/core/action.class.inc.php',
|
||||
'ActionChecker' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
|
||||
@@ -311,7 +312,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'BulkExportMissingParameterException' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php',
|
||||
'BulkExportResult' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php',
|
||||
'BulkExportResultGC' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php',
|
||||
'CAS_SelfRegister' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
|
||||
'CLIPage' => __DIR__ . '/../..' . '/application/clipage.class.inc.php',
|
||||
'CMDBChange' => __DIR__ . '/../..' . '/core/cmdbchange.class.inc.php',
|
||||
'CMDBChangeOp' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
|
||||
@@ -354,6 +354,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'CheckableExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
|
||||
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\TwigExtension' => __DIR__ . '/../..' . '/application/twigextension.class.inc.php',
|
||||
'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php',
|
||||
'ConfigException' => __DIR__ . '/../..' . '/core/config.class.inc.php',
|
||||
'Console_Getopt' => __DIR__ . '/..' . '/pear/console_getopt/Console/Getopt.php',
|
||||
@@ -478,6 +479,15 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'ListExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
|
||||
'ListOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
|
||||
'LogAPI' => __DIR__ . '/../..' . '/core/log.class.inc.php',
|
||||
'LoginBasic' => __DIR__ . '/../..' . '/application/loginbasic.class.inc.php',
|
||||
'LoginBlockData' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php',
|
||||
'LoginDefaultAfter' => __DIR__ . '/../..' . '/application/logindefault.class.inc.php',
|
||||
'LoginDefaultBefore' => __DIR__ . '/../..' . '/application/logindefault.class.inc.php',
|
||||
'LoginExternal' => __DIR__ . '/../..' . '/application/loginexternal.class.inc.php',
|
||||
'LoginForm' => __DIR__ . '/../..' . '/application/loginform.class.inc.php',
|
||||
'LoginTwigContext' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php',
|
||||
'LoginTwigData' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php',
|
||||
'LoginURL' => __DIR__ . '/../..' . '/application/loginurl.class.inc.php',
|
||||
'LoginWebPage' => __DIR__ . '/../..' . '/application/loginwebpage.class.inc.php',
|
||||
'MatchExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
|
||||
'MatchOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
|
||||
@@ -495,6 +505,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'MyHelpers' => __DIR__ . '/../..' . '/core/MyHelpers.class.inc.php',
|
||||
'MySQLException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php',
|
||||
'MySQLHasGoneAwayException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php',
|
||||
'MySQLNoTransactionException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php',
|
||||
'MySQLQueryHasNoResultException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php',
|
||||
'NewObjectMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
|
||||
'NewsroomProviderBase' => __DIR__ . '/../..' . '/application/newsroomprovider.class.inc.php',
|
||||
@@ -2106,6 +2117,10 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php',
|
||||
'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php',
|
||||
'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php',
|
||||
'iLoginDataExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iLogoutExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iMetricComputer' => __DIR__ . '/../..' . '/core/computing.inc.php',
|
||||
'iNewsroomProvider' => __DIR__ . '/../..' . '/application/newsroomprovider.class.inc.php',
|
||||
'iOnClassInitialization' => __DIR__ . '/../..' . '/core/metamodelmodifier.inc.php',
|
||||
|
||||
@@ -117,8 +117,7 @@ class ApplicationInstaller
|
||||
try
|
||||
{
|
||||
$fStart = microtime(true);
|
||||
// Enter in maintenance mode
|
||||
@touch(APPROOT.'.maintenance');
|
||||
SetupUtils::EnterMaintenanceMode();
|
||||
switch ($sStep)
|
||||
{
|
||||
case '':
|
||||
@@ -341,15 +340,13 @@ class ApplicationInstaller
|
||||
'percentage-completed' => 100,
|
||||
);
|
||||
}
|
||||
// Exit maintenance mode
|
||||
@unlink(APPROOT.'.maintenance');
|
||||
SetupUtils::ExitMaintenanceMode();
|
||||
$fDuration = round(microtime(true) - $fStart, 2);
|
||||
SetupPage::log_info("##### STEP {$sStep} duration: {$fDuration}s");
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// Exit maintenance mode
|
||||
@unlink(APPROOT.'.maintenance');
|
||||
SetupUtils::ExitMaintenanceMode();
|
||||
|
||||
$aResult = array(
|
||||
'status' => self::ERROR,
|
||||
|
||||
@@ -1835,6 +1835,22 @@ EOF
|
||||
{
|
||||
return APPROOT.'log/setup-queries-'.strftime('%Y-%m-%d_%H_%M').'.sql';
|
||||
}
|
||||
|
||||
public final static function EnterMaintenanceMode($bCheckBackgroundTask = false)
|
||||
{
|
||||
@touch(APPROOT.'.maintenance');
|
||||
if ($bCheckBackgroundTask)
|
||||
{
|
||||
|
||||
// Assume database is OK but datamodel is not usable
|
||||
$iCount = CMDBSource::QueryToScalar('SELECT COUNT(*) FROM priv_backgroundtask WHERE running=1');
|
||||
}
|
||||
}
|
||||
|
||||
public final static function ExitMaintenanceMode()
|
||||
{
|
||||
@unlink(APPROOT.'.maintenance');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
17
templates/login/base.html.twig
Normal file
17
templates/login/base.html.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% block body %}
|
||||
{% block login_logo %}
|
||||
<div id="login-logo">
|
||||
<a href="{{ sIconUrl }}">
|
||||
<img title="{{ sVersionShort }}" src="{{ sDisplayIcon }}" alt="logo"/>
|
||||
</a>
|
||||
</div>
|
||||
{% endblock login_logo %}
|
||||
{% block login_content %}
|
||||
{% endblock login_content %}
|
||||
{% block login_footer %}
|
||||
<div id="login-footer"></div>
|
||||
{% endblock login_footer %}
|
||||
{% endblock body %}
|
||||
54
templates/login/login.html.twig
Normal file
54
templates/login/login.html.twig
Normal file
@@ -0,0 +1,54 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% extends "base.html.twig" %}
|
||||
|
||||
{% import "macros.twig" as Macro %}
|
||||
|
||||
{% block login_content %}
|
||||
<div id="login">
|
||||
{% block login_title %}
|
||||
<div id="login-title">
|
||||
<h1>{{ 'UI:Login:Welcome'|dict_s }}</h1>
|
||||
{% if bFailedLogin %}
|
||||
<p class="hilite">{{ sMessage }}</p>
|
||||
{% else %}
|
||||
<p>{{ sMessage }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock login_title %}
|
||||
{% block login_form %}
|
||||
<div id="login-form">
|
||||
<form id="login_form" method="post">
|
||||
<table>
|
||||
{% block login_sso_buttons %}
|
||||
{{ Macro.BlockData(aPluginFormData, 'login_sso_buttons', '<tr><td class="center" colspan="2">' ~ 'UI:Login:SeparatorOr'|dict_s ~ '</td></tr>') }}
|
||||
{% endblock login_sso_buttons %}
|
||||
{% block login_input %}
|
||||
{{ Macro.BlockData(aPluginFormData, 'login_input') }}
|
||||
{% endblock login_input %}
|
||||
{% block login_submit %}
|
||||
{{ Macro.BlockData(aPluginFormData, 'login_submit') }}
|
||||
{% endblock login_submit %}
|
||||
{% block login_additional_controls %}
|
||||
{{ Macro.BlockData(aPluginFormData, 'login_additional_controls') }}
|
||||
{% endblock login_additional_controls %}
|
||||
{% block login_links %}
|
||||
{{ Macro.BlockData(aPluginFormData, 'login_links') }}
|
||||
{% endblock login_links %}
|
||||
</table>
|
||||
<input type="hidden" id="login_mode" name="login_mode" value="form" />
|
||||
<input type="hidden" id="login_op" name="loginop" value="login" />
|
||||
{% for sName, sValue in aPreviousPostedVars %}
|
||||
<input type="hidden" name="{{ sName }}" value="{{ sValue }}" />
|
||||
{% endfor %}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock login_form %}
|
||||
</div>
|
||||
{% endblock login_content %}
|
||||
|
||||
{% block login_footer %}
|
||||
{{ Macro.BlockData(aPluginFormData, 'login_footer') }}
|
||||
{% endblock login_footer %}
|
||||
|
||||
19
templates/login/loginforminput.html.twig
Normal file
19
templates/login/loginforminput.html.twig
Normal file
@@ -0,0 +1,19 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<tr>
|
||||
<td class="v-spacer" style="text-align:right">
|
||||
<label for="user">{{ 'UI:Login:UserNamePrompt'|dict_s }}</label>
|
||||
</td>
|
||||
<td style="text-align:left">
|
||||
<input id="user" type="text" name="auth_user" value="{{ aData.sAuthUser }}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align:right">
|
||||
<label for="pwd">{{ 'UI:Login:PasswordPrompt'|dict_s }}</label>
|
||||
</td>
|
||||
<td style="text-align:left">
|
||||
<input id="pwd" type="password" name="auth_pwd" value="{{ aData.sAuthPwd }}" />
|
||||
</td>
|
||||
</tr>
|
||||
10
templates/login/loginformlinks.html.twig
Normal file
10
templates/login/loginformlinks.html.twig
Normal file
@@ -0,0 +1,10 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% if aData.bEnableResetPassword %}
|
||||
<tr>
|
||||
<td colspan="2" class="center v-spacer">
|
||||
<a href="{{ aData.sResetPasswordUrl }}" target="_blank">{{ 'UI:Login:ForgotPwd'|dict_s }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
10
templates/login/loginformsubmit.html.twig
Normal file
10
templates/login/loginformsubmit.html.twig
Normal file
@@ -0,0 +1,10 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<tr>
|
||||
<td colspan="2" class="center v-spacer">
|
||||
<span class="btn_border">
|
||||
<input type="submit" value="{{ 'UI:Button:Login'|dict_s }}" />
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
13
templates/login/logout.html.twig
Normal file
13
templates/login/logout.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% extends "base.html.twig" %}
|
||||
|
||||
{% block login_content %}
|
||||
<div id="login">
|
||||
<div id="login-title">
|
||||
<h1>{{ sTitle }}</h1>
|
||||
<p><a href="{{ sUrl }}">{{ sMessage }}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
17
templates/login/macros.twig
Normal file
17
templates/login/macros.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% macro BlockData(aPluginFormData, sBlockName, sText = '') %}
|
||||
{% set bHaveBlock = 'false' %}
|
||||
{% for oLoginData in aPluginFormData if oLoginData.GetBlockData(sBlockName) %}
|
||||
{% set oBlockData = oLoginData.GetBlockData(sBlockName) %}
|
||||
{% set bHaveBlock = 'true' %}
|
||||
{% set sTwig = oBlockData.GetTwig() %}
|
||||
{% set aData = oBlockData.GetData() %}
|
||||
{% set sContent = include(sTwig ? sTwig : '') %}
|
||||
{{ sContent|raw }}
|
||||
{% endfor %}
|
||||
{% if bHaveBlock == 'true' %}
|
||||
{{ sText|raw }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
22
templates/login/password/changepwdform.html.twig
Normal file
22
templates/login/password/changepwdform.html.twig
Normal file
@@ -0,0 +1,22 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% extends "base.html.twig" %}
|
||||
|
||||
{% block login_content %}
|
||||
<div id="login">
|
||||
<h1>{{ 'UI:Login:ChangeYourPassword'|dict_s }}</h1>
|
||||
{% if bFailedLogin %}
|
||||
<p class="hilite">{{ 'UI:Login:IncorrectOldPassword'|dict_s }}</p>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr><td style="text-align:right"><label for="old_pwd">{{ 'UI:Login:OldPasswordPrompt'|dict_s }}:</label></td><td style="text-align:left"><input type="password" id="old_pwd" name="old_pwd" value="" /></td></tr>
|
||||
<tr><td style="text-align:right"><label for="new_pwd">{{ 'UI:Login:NewPasswordPrompt'|dict_s }}:</label></td><td style="text-align:left"><input type="password" id="new_pwd" name="new_pwd" value="" /></td></tr>
|
||||
<tr><td style="text-align:right"><label for="retype_new_pwd">{{ 'UI:Login:RetypeNewPasswordPrompt'|dict_s }}:</label></td><td style="text-align:left"><input type="password" id="retype_new_pwd" name="retype_new_pwd" value="" /></td></tr>
|
||||
<tr><td colspan="2" class="center v-spacer"><span class="btn_border"><input type="button" onClick="GoBack();" value="{{ 'UI:Button:Cancel'|dict_s }}" /></span> <span class="btn_border"><input type="submit" onClick="return DoCheckPwd();" value="{{ 'UI:Button:ChangePassword'|dict_s }}" /></span></td></tr>
|
||||
</table>
|
||||
<input type="hidden" name="loginop" value="do_change_pwd" />
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
17
templates/login/password/changepwdform.js.twig
Normal file
17
templates/login/password/changepwdform.js.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
function GoBack()
|
||||
{
|
||||
window.history.back();
|
||||
}
|
||||
|
||||
function DoCheckPwd()
|
||||
{
|
||||
if ($('#new_pwd').val() != $('#retype_new_pwd').val())
|
||||
{
|
||||
alert('{{ 'UI:Login:RetypePwdDoesNotMatch'|dict_s }}');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
21
templates/login/password/forgotpwdform.html.twig
Normal file
21
templates/login/password/forgotpwdform.html.twig
Normal file
@@ -0,0 +1,21 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% extends "base.html.twig" %}
|
||||
|
||||
{% block login_content %}
|
||||
<div id="login">
|
||||
<h1>{{ 'UI:Login:ForgotPwdForm'|dict_s }}</h1>
|
||||
<p>{{ 'UI:Login:ForgotPwdForm+'|dict_s }}</p>
|
||||
{% if bFailedToReset %}
|
||||
<p class="hilite">{{ 'UI:Login:ResetPwdFailed'|dict_format(sFailureReason) }}</p>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr><td colspan="2" class="center"><label for="user">{{ 'UI:Login:UserNamePrompt'|dict_s }}:</label><input id="user" type="text" name="auth_user" value="{{ sAuthUser }}" /></td></tr>
|
||||
<tr><td colspan="2" class="center v-spacer"><span class="btn_border"><input type="button" onClick="window.close();" value="{{ 'UI:Button:Cancel'|dict_s }}" /></span> <span class="btn_border"><input type="submit" value="{{ 'UI:Login:ResetPassword'|dict_s }}" /></span></td></tr>
|
||||
</table>
|
||||
<input type="hidden" name="loginop" value="forgot_pwd_go" />
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
4
templates/login/password/forgotpwdform.ready.js.twig
Normal file
4
templates/login/password/forgotpwdform.ready.js.twig
Normal file
@@ -0,0 +1,4 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
$("#user").focus();
|
||||
18
templates/login/password/forgotpwdsent.html.twig
Normal file
18
templates/login/password/forgotpwdsent.html.twig
Normal file
@@ -0,0 +1,18 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% extends "base.html.twig" %}
|
||||
|
||||
{% block login_content %}
|
||||
<div id="login">
|
||||
<h1>{{ 'UI:Login:ForgotPwdForm'|dict_s }}</h1>
|
||||
<p>{{ 'UI:ResetPwd-EmailSent'|dict_s }}</p>
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr>
|
||||
<td colspan="2" class="center v-spacer"><input type="button" onClick="window.close();" value="{{ 'UI:Button:Done'|dict_s }}"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
18
templates/login/password/resetpwddone.html.twig
Normal file
18
templates/login/password/resetpwddone.html.twig
Normal file
@@ -0,0 +1,18 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% extends "base.html.twig" %}
|
||||
|
||||
{% block login_content %}
|
||||
<div id="login">
|
||||
<h1>{{ 'UI:ResetPwd-Title'|dict_s }}</h1>
|
||||
{% if bNoUser %}
|
||||
<p>{{ 'UI:ResetPwd-Error-WrongLogin'|dict_format(sAuthUser) }}</p>
|
||||
{% elseif bBadToken %}
|
||||
<p>{{ 'UI:ResetPwd-Error-InvalidToken'|dict_s }}</p>
|
||||
{% else %}
|
||||
<p>{{ 'UI:ResetPwd-Ready'|dict_s }}</p>
|
||||
<p><a href="{{ sUrl }}">{{ 'UI:ResetPwd-Login'|dict_s }}</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
27
templates/login/password/resetpwdform.html.twig
Normal file
27
templates/login/password/resetpwdform.html.twig
Normal file
@@ -0,0 +1,27 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
{% extends "base.html.twig" %}
|
||||
|
||||
{% block login_content %}
|
||||
<div id="login">
|
||||
<h1>{{ 'UI:ResetPwd-Title'|dict_s }}</h1>
|
||||
{% if bNoUser %}
|
||||
<p>{{ 'UI:ResetPwd-Error-WrongLogin'|dict_format(sAuthUser) }}</p>
|
||||
{% elseif bBadToken %}
|
||||
<p>{{ 'UI:ResetPwd-Error-InvalidToken'|dict_s }}</p>
|
||||
{% else %}
|
||||
<p>{{ 'UI:ResetPwd-Error-EnterPassword'|dict_format(sUserName) }}</p>
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr><td style="text-align:right"><label for="new_pwd">{{ 'UI:Login:NewPasswordPrompt'|dict_s }}:</label></td><td style="text-align:left"><input type="password" id="new_pwd" name="new_pwd" value="" /></td></tr>
|
||||
<tr><td style="text-align:right"><label for="retype_new_pwd">{{ 'UI:Login:RetypeNewPasswordPrompt'|dict_s }}:</label></td><td style="text-align:left"><input type="password" id="retype_new_pwd" name="retype_new_pwd" value="" /></td></tr>
|
||||
<tr><td colspan="2" class="center v-spacer"><span class="btn_border"><input type="submit" onClick="return DoCheckPwd();" value="{{ 'UI:Button:ChangePassword'|dict_s }}" /></span></td></tr>
|
||||
</table>
|
||||
<input type="hidden" name="loginop" value="do_reset_pwd" />
|
||||
<input type="hidden" name="auth_user" value="{{ sAuthUser }}" />
|
||||
<input type="hidden" name="token" value="{{ sToken }}" />
|
||||
</form>
|
||||
{% endif %}
|
||||
</div
|
||||
{% endblock %}
|
||||
12
templates/login/password/resetpwdform.js.twig
Normal file
12
templates/login/password/resetpwdform.js.twig
Normal file
@@ -0,0 +1,12 @@
|
||||
{# @copyright Copyright (C) 2010-2019 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
function DoCheckPwd()
|
||||
{
|
||||
if ($('#new_pwd').val() != $('#retype_new_pwd').val())
|
||||
{
|
||||
alert('{{ 'UI:Login:RetypePwdDoesNotMatch'|dict_s }}');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user