mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-09 06:28:28 +03:00
Add Gmail XAUTH support
This commit is contained in:
parent
08333610ce
commit
32ceafa37f
16 changed files with 359 additions and 138 deletions
|
|
@ -69,6 +69,12 @@ class AbstractAjaxPromises extends AbstractBasicPromises {
|
|||
isCached = pInt(data.Time) > microtime() - start;
|
||||
}
|
||||
|
||||
if (data && data.UpdateToken) {
|
||||
if (GlobalsData.__APP__ && GlobalsData.__APP__.setClientSideToken) {
|
||||
GlobalsData.__APP__.setClientSideToken(data.UpdateToken);
|
||||
}
|
||||
}
|
||||
|
||||
// backward capability
|
||||
let type = '';
|
||||
switch (true) {
|
||||
|
|
|
|||
|
|
@ -161,6 +161,12 @@ class AbstractAjaxRemote {
|
|||
cached = pInt(oData.Time) > new window.Date().getTime() - start;
|
||||
}
|
||||
|
||||
if (oData && oData.UpdateToken) {
|
||||
if (GlobalsData.__APP__ && GlobalsData.__APP__.setClientSideToken) {
|
||||
GlobalsData.__APP__.setClientSideToken(oData.UpdateToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (action && this.oRequests[action]) {
|
||||
if (this.oRequests[action].__aborted) {
|
||||
sType = 'abort';
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class SocialAdminSettings {
|
|||
constructor() {
|
||||
this.googleEnable = SocialStore.google.enabled;
|
||||
this.googleEnableAuth = SocialStore.google.capa.auth;
|
||||
this.googleEnableAuthFast = SocialStore.google.capa.authFast;
|
||||
this.googleEnableAuthGmail = SocialStore.google.capa.authGmail;
|
||||
this.googleEnableDrive = SocialStore.google.capa.drive;
|
||||
this.googleEnablePreview = SocialStore.google.capa.preview;
|
||||
|
||||
|
|
@ -89,6 +89,7 @@ class SocialAdminSettings {
|
|||
|
||||
this.googleEnable.subscribe(Remote.saveAdminConfigHelper('GoogleEnable', boolToAjax));
|
||||
this.googleEnableAuth.subscribe(Remote.saveAdminConfigHelper('GoogleEnableAuth', boolToAjax));
|
||||
this.googleEnableAuthGmail.subscribe(Remote.saveAdminConfigHelper('GoogleEnableAuthGmail', boolToAjax));
|
||||
this.googleEnableDrive.subscribe(Remote.saveAdminConfigHelper('GoogleEnableDrive', boolToAjax));
|
||||
this.googleEnablePreview.subscribe(Remote.saveAdminConfigHelper('GoogleEnablePreview', boolToAjax));
|
||||
this.googleClientID.subscribe(Remote.saveAdminConfigHelper('GoogleClientID', trim, f5));
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ class SocialUserSettings {
|
|||
constructor() {
|
||||
this.googleEnable = SocialStore.google.enabled;
|
||||
this.googleEnableAuth = SocialStore.google.capa.auth;
|
||||
this.googleEnableAuthFast = SocialStore.google.capa.authFast;
|
||||
this.googleEnableAuthGmail = SocialStore.google.capa.authGmail;
|
||||
this.googleEnableDrive = SocialStore.google.capa.drive;
|
||||
this.googleEnablePreview = SocialStore.google.capa.preview;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,14 @@ class SocialStore {
|
|||
|
||||
this.google.capa = {};
|
||||
this.google.capa.auth = ko.observable(false);
|
||||
this.google.capa.authFast = ko.observable(false);
|
||||
this.google.capa.authGmail = ko.observable(false);
|
||||
this.google.capa.drive = ko.observable(false);
|
||||
this.google.capa.preview = ko.observable(false);
|
||||
|
||||
this.google.require = {};
|
||||
this.google.require.clientSettings = ko.computed(
|
||||
() => this.google.enabled() && (this.google.capa.auth() || this.google.capa.drive())
|
||||
() =>
|
||||
this.google.enabled() && (this.google.capa.auth() || this.google.capa.authGmail() || this.google.capa.drive())
|
||||
);
|
||||
|
||||
this.google.require.apiKeySettings = ko.computed(() => this.google.enabled() && this.google.capa.drive());
|
||||
|
|
@ -66,7 +67,7 @@ class SocialStore {
|
|||
this.google.apiKey(Settings.settingsGet('GoogleApiKey'));
|
||||
|
||||
this.google.capa.auth(!!Settings.settingsGet('AllowGoogleSocialAuth'));
|
||||
this.google.capa.authFast(!!Settings.settingsGet('AllowGoogleSocialAuthFast'));
|
||||
this.google.capa.authGmail(!!Settings.settingsGet('AllowGoogleSocialAuthGmail'));
|
||||
this.google.capa.drive(!!Settings.settingsGet('AllowGoogleSocialDrive'));
|
||||
this.google.capa.preview(!!Settings.settingsGet('AllowGoogleSocialPreview'));
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ class LoginUserView extends AbstractViewNext {
|
|||
|
||||
this.facebookLoginEnabled = ko.observable(false);
|
||||
this.googleLoginEnabled = ko.observable(false);
|
||||
this.googleFastLoginEnabled = ko.observable(false);
|
||||
this.googleGmailLoginEnabled = ko.observable(false);
|
||||
this.twitterLoginEnabled = ko.observable(false);
|
||||
|
||||
this.socialLoginEnabled = ko.computed(() => {
|
||||
|
|
@ -175,8 +175,8 @@ class LoginUserView extends AbstractViewNext {
|
|||
return true;
|
||||
}
|
||||
|
||||
@command((self) => !self.submitRequest() && self.googleFastLoginEnabled())
|
||||
googleFastCommand() {
|
||||
@command((self) => !self.submitRequest() && self.googleGmailLoginEnabled())
|
||||
googleGmailCommand() {
|
||||
window.open(socialGoogle(true), 'Google', this.windowOpenFeatures(550));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -344,8 +344,8 @@ class LoginUserView extends AbstractViewNext {
|
|||
this.googleLoginEnabled(
|
||||
!!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialAuth')
|
||||
);
|
||||
this.googleFastLoginEnabled(
|
||||
!!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialAuthFast')
|
||||
this.googleGmailLoginEnabled(
|
||||
!!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialAuthGmail')
|
||||
);
|
||||
|
||||
switch (signMe) {
|
||||
|
|
@ -375,7 +375,7 @@ class LoginUserView extends AbstractViewNext {
|
|||
this.email(AppStore.devEmail);
|
||||
this.password(AppStore.devPassword);
|
||||
|
||||
if (this.googleLoginEnabled() || this.googleFastLoginEnabled()) {
|
||||
if (this.googleLoginEnabled() || this.googleGmailLoginEnabled()) {
|
||||
window['rl_' + jsHash + '_google_login_service'] = fSocial;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,11 @@ class Actions
|
|||
*/
|
||||
private $sSpecAuthToken;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sUpdateAuthToken;
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
|
|
@ -155,6 +160,8 @@ class Actions
|
|||
$this->oPremProvider = null;
|
||||
|
||||
$this->sSpecAuthToken = '';
|
||||
$this->sUpdateAuthToken = '';
|
||||
$this->bIsAjax = false;
|
||||
|
||||
$oConfig = $this->Config();
|
||||
$this->Plugins()->RunHook('filter.application-config', array(&$oConfig));
|
||||
|
|
@ -182,6 +189,30 @@ class Actions
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sUpdateAuthToken
|
||||
*
|
||||
* @return \RainLoop\Application
|
||||
*/
|
||||
public function SetUpdateAuthToken($sUpdateAuthToken)
|
||||
{
|
||||
$this->sUpdateAuthToken = $sUpdateAuthToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $bIsAjax
|
||||
*
|
||||
* @return \RainLoop\Application
|
||||
*/
|
||||
public function SetIsAjax($bIsAjax)
|
||||
{
|
||||
$this->bIsAjax = $bIsAjax;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
|
@ -190,6 +221,22 @@ class Actions
|
|||
return $this->sSpecAuthToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetUpdateAuthToken()
|
||||
{
|
||||
return $this->sUpdateAuthToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function GetIsAjax()
|
||||
{
|
||||
return $this->bIsAjax;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
|
@ -1647,7 +1694,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
|
||||
$aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false);
|
||||
$aResult['AllowGoogleSocialAuth'] = (bool) $oConfig->Get('social', 'google_enable_auth', true);
|
||||
$aResult['AllowGoogleSocialAuthFast'] = (bool) $oConfig->Get('social', 'google_enable_auth_fast', true);
|
||||
$aResult['AllowGoogleSocialAuthGmail'] = (bool) $oConfig->Get('social', 'google_enable_auth_gmail', true);
|
||||
$aResult['AllowGoogleSocialDrive'] = (bool) $oConfig->Get('social', 'google_enable_drive', true);
|
||||
$aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', true);
|
||||
|
||||
|
|
@ -1658,7 +1705,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
'' === \trim($oConfig->Get('social', 'google_client_id', '')) || '' === \trim($oConfig->Get('social', 'google_client_secret', '')))))
|
||||
{
|
||||
$aResult['AllowGoogleSocialAuth'] = false;
|
||||
$aResult['AllowGoogleSocialAuthFast'] = false;
|
||||
$aResult['AllowGoogleSocialAuthGmail'] = false;
|
||||
$aResult['AllowGoogleSocialDrive'] = false;
|
||||
$aResult['GoogleClientID'] = '';
|
||||
$aResult['GoogleApiKey'] = '';
|
||||
|
|
@ -1670,7 +1717,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
}
|
||||
|
||||
if ($aResult['AllowGoogleSocial'] &&
|
||||
!$aResult['AllowGoogleSocialAuth'] && !$aResult['AllowGoogleSocialAuthFast'] &&
|
||||
!$aResult['AllowGoogleSocialAuth'] && !$aResult['AllowGoogleSocialAuthGmail'] &&
|
||||
!$aResult['AllowGoogleSocialDrive'] && !$aResult['AllowGoogleSocialPreview'])
|
||||
{
|
||||
$aResult['AllowGoogleSocial'] = false;
|
||||
|
|
@ -1746,7 +1793,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
|
||||
$aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false);
|
||||
$aResult['AllowGoogleSocialAuth'] = (bool) $oConfig->Get('social', 'google_enable_auth', true);
|
||||
$aResult['AllowGoogleSocialAuthFast'] = (bool) $oConfig->Get('social', 'google_enable_auth_fast', true);
|
||||
$aResult['AllowGoogleSocialAuthGmail'] = (bool) $oConfig->Get('social', 'google_enable_auth_gmail', true);
|
||||
$aResult['AllowGoogleSocialDrive'] = (bool) $oConfig->Get('social', 'google_enable_drive', true);
|
||||
$aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', true);
|
||||
|
||||
|
|
@ -1986,6 +2033,51 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
return $aResult;
|
||||
}
|
||||
|
||||
public function googleStoreTokens($oCache, $sAccessToken, $sRefreshToken)
|
||||
{
|
||||
$sCacheKey = 'tokens='.\md5($sRefreshToken);
|
||||
$oCache->Set($sCacheKey, $sAccessToken);
|
||||
$oCache->SetTimer($sCacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function googleRefreshTokenCallback($sAccessToken, $sRefreshToken)
|
||||
{
|
||||
$oAccount = $this->getAccountFromToken(false);
|
||||
if ($oAccount && $this->GetIsAjax())
|
||||
{
|
||||
$oCache = $this->Cacher($oAccount);
|
||||
$sCacheKey = 'tokens='.\md5($sRefreshToken);
|
||||
|
||||
$sCachedAccessToken = $oCache->Get($sCacheKey);
|
||||
$iTime = $oCache->GetTimer($sCacheKey);
|
||||
|
||||
if ($sCachedAccessToken === '' || $iTime === 0)
|
||||
{
|
||||
$this->googleStoreTokens($oCache, $sAccessToken, $sRefreshToken);
|
||||
}
|
||||
else if (\time() - 60 * 10 * 2 > $iTime) // 20min
|
||||
{
|
||||
$sCachedAccessToken = $this->Social()->GoogleRefreshToken($sAccessToken, $sRefreshToken);
|
||||
if ($sCachedAccessToken !== $sAccessToken) {
|
||||
$this->googleStoreTokens($oCache, $sCachedAccessToken, $sRefreshToken);
|
||||
|
||||
$oAccount->SetPassword(\RainLoop\Model\Account::GenerateTokensPassword($sCachedAccessToken, $sRefreshToken));
|
||||
$this->AuthToken($oAccount);
|
||||
$this->SetUpdateAuthToken($this->GetSpecAuthToken());
|
||||
}
|
||||
}
|
||||
|
||||
if ($sCachedAccessToken) {
|
||||
return $sCachedAccessToken;
|
||||
}
|
||||
}
|
||||
|
||||
return $sAccessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
|
@ -2059,7 +2151,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
{
|
||||
try
|
||||
{
|
||||
$oAccount->IncConnectAndLoginHelper($this->Plugins(), $this->MailClient(), $this->Config());
|
||||
$oAccount->IncConnectAndLoginHelper($this->Plugins(), $this->MailClient(), $this->Config(), array($this, 'googleRefreshTokenCallback'));
|
||||
}
|
||||
catch (\RainLoop\Exceptions\ClientException $oException)
|
||||
{
|
||||
|
|
@ -3366,7 +3458,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
*
|
||||
* @throws \MailSo\Base\Exceptions\Exception
|
||||
*/
|
||||
public function getAccountUnredCountFromHash($sHash)
|
||||
public function getAccountUnreadCountFromHash($sHash)
|
||||
{
|
||||
$iResult = 0;
|
||||
|
||||
|
|
@ -3378,7 +3470,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
$oMailClient = \MailSo\Mail\MailClient::NewInstance();
|
||||
$oMailClient->SetLogger($this->Logger());
|
||||
|
||||
$oAccount->IncConnectAndLoginHelper($this->Plugins(),$oMailClient, $this->Config());
|
||||
$oAccount->IncConnectAndLoginHelper($this->Plugins(), $oMailClient, $this->Config(), array($this, 'googleRefreshTokenCallback'));
|
||||
|
||||
$iResult = $oMailClient->InboxUnreadCount();
|
||||
|
||||
|
|
@ -3425,7 +3517,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
foreach ($mAccounts as $sEmail => $sHash)
|
||||
{
|
||||
$aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($sEmail),
|
||||
$oAccount->Email() === $sEmail ? 0 : $this->getAccountUnredCountFromHash($sHash));
|
||||
$oAccount->Email() === $sEmail ? 0 : $this->getAccountUnreadCountFromHash($sHash));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3775,7 +3867,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
|
||||
$this->setConfigFromParams($oConfig, 'GoogleEnable', 'social', 'google_enable', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'GoogleEnableAuth', 'social', 'google_enable_auth', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'GoogleEnableAuthFast', 'social', 'google_enable_auth_fast', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'GoogleEnableAuthGmail', 'social', 'google_enable_auth_gmail', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'GoogleEnableDrive', 'social', 'google_enable_drive', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'GoogleEnablePreview', 'social', 'google_enable_preview', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'GoogleClientID', 'social', 'google_client_id', 'string');
|
||||
|
|
@ -4293,7 +4385,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
$iRepTime = $this->Cacher()->GetTimer($sCacheKey);
|
||||
}
|
||||
|
||||
if ('' === $sRep || 0 === $iRepTime || time() - 3600 > $iRepTime)
|
||||
if ('' === $sRep || 0 === $iRepTime || \time() - 3600 > $iRepTime)
|
||||
{
|
||||
$iCode = 0;
|
||||
$sContentType = '';
|
||||
|
|
@ -4393,7 +4485,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
$iRepTime = $this->Cacher()->GetTimer($sCacheKey);
|
||||
}
|
||||
|
||||
if ('' === $sRep || 0 === $iRepTime || time() - 3600 > $iRepTime)
|
||||
if ('' === $sRep || 0 === $iRepTime || \time() - 3600 > $iRepTime)
|
||||
{
|
||||
$iCode = 0;
|
||||
$sContentType = '';
|
||||
|
|
@ -6172,7 +6264,9 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
$oSmtpClient = \MailSo\Smtp\SmtpClient::NewInstance()->SetLogger($this->Logger());
|
||||
$oSmtpClient->SetTimeOuts(10, (int) \RainLoop\Api::Config()->Get('labs', 'smtp_timeout', 60));
|
||||
|
||||
$bLoggined = $oAccount->OutConnectAndLoginHelper($this->Plugins(), $oSmtpClient, $this->Config(), $bUsePhpMail);
|
||||
$bLoggined = $oAccount->OutConnectAndLoginHelper(
|
||||
$this->Plugins(), $oSmtpClient, $this->Config(), array($this, 'googleRefreshTokenCallback'), $bUsePhpMail
|
||||
);
|
||||
|
||||
if ($bUsePhpMail)
|
||||
{
|
||||
|
|
@ -8315,7 +8409,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
$iExpires = $this->Config()->Get('cache', 'http_expires', 3600);
|
||||
if (0 < $iExpires)
|
||||
{
|
||||
$this->oHttp->ServerUseCache($this->etag($sKey), 1382478804, time() + $iExpires);
|
||||
$this->oHttp->ServerUseCache($this->etag($sKey), 1382478804, \time() + $iExpires);
|
||||
$bResult = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -8964,7 +9058,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
|||
|
||||
try
|
||||
{
|
||||
$oAccount->IncConnectAndLoginHelper($this->Plugins(), $this->MailClient(), $this->Config());
|
||||
$oAccount->IncConnectAndLoginHelper($this->Plugins(), $this->MailClient(), $this->Config(), array($this, 'googleRefreshTokenCallback'));
|
||||
}
|
||||
catch (\MailSo\Net\Exceptions\ConnectionException $oException)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ Examples:
|
|||
'social' => array(
|
||||
'google_enable' => array(false, 'Google'),
|
||||
'google_enable_auth' => array(false),
|
||||
'google_enable_auth_fast' => array(false),
|
||||
'google_enable_auth_gmail' => array(false),
|
||||
'google_enable_drive' => array(false),
|
||||
'google_enable_preview' => array(false),
|
||||
'google_client_id' => array(''),
|
||||
|
|
|
|||
|
|
@ -91,6 +91,35 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
return new self($sEmail, $sLogin, $sPassword, $oDomain, $sSignMeToken, $sProxyAuthUser, $sProxyAuthPassword, $sClientCert);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function GenerateTokensPassword($sAccessToken, $sRefreshToken)
|
||||
{
|
||||
return APP_GOOGLE_ACCESS_TOKEN_PREFIX.\json_encode(array($sAccessToken, $sRefreshToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function ParseTokensPassword($sPassword)
|
||||
{
|
||||
$iGatLen = \strlen(APP_GOOGLE_ACCESS_TOKEN_PREFIX);
|
||||
if ($sPassword && APP_GOOGLE_ACCESS_TOKEN_PREFIX === \substr($sPassword, 0, $iGatLen))
|
||||
{
|
||||
$aTokens = @\json_decode(\substr($sPassword, $iGatLen));
|
||||
$sAccessToken = !empty($aTokens[0]) ? $aTokens[0] : '';
|
||||
$sRefreshToken = !empty($aTokens[1]) ? $aTokens[1] : '';
|
||||
|
||||
if ($sAccessToken && $sRefreshToken)
|
||||
{
|
||||
return array($sAccessToken, $sRefreshToken);
|
||||
}
|
||||
}
|
||||
|
||||
return array('', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
|
@ -366,18 +395,18 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
public function GetAuthToken()
|
||||
{
|
||||
return \RainLoop\Utils::EncodeKeyValues(array(
|
||||
'token', // 0
|
||||
$this->sEmail, // 1
|
||||
$this->sLogin, // 2
|
||||
$this->sPassword, // 3
|
||||
'token', // 0
|
||||
$this->sEmail, // 1
|
||||
$this->sLogin, // 2
|
||||
$this->sPassword, // 3
|
||||
\RainLoop\Utils::Fingerprint(), // 4
|
||||
$this->sSignMeToken, // 5
|
||||
$this->sParentEmail, // 6
|
||||
$this->sSignMeToken, // 5
|
||||
$this->sParentEmail, // 6
|
||||
\RainLoop\Utils::GetShortToken(), // 7
|
||||
$this->sProxyAuthUser, // 8
|
||||
$this->sProxyAuthPassword, // 9
|
||||
0, // 10 // timelife
|
||||
$this->sClientCert // 11
|
||||
$this->sProxyAuthUser, // 8
|
||||
$this->sProxyAuthPassword, // 9
|
||||
0, // 10 // lifetime
|
||||
$this->sClientCert // 11
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -387,18 +416,18 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
public function GetAuthTokenQ()
|
||||
{
|
||||
return \RainLoop\Utils::EncodeKeyValuesQ(array(
|
||||
'token', // 0
|
||||
$this->sEmail, // 1
|
||||
$this->sLogin, // 2
|
||||
$this->sPassword, // 3
|
||||
'token', // 0
|
||||
$this->sEmail, // 1
|
||||
$this->sLogin, // 2
|
||||
$this->sPassword, // 3
|
||||
\RainLoop\Utils::Fingerprint(), // 4
|
||||
$this->sSignMeToken, // 5
|
||||
$this->sParentEmail, // 6
|
||||
$this->sSignMeToken, // 5
|
||||
$this->sParentEmail, // 6
|
||||
\RainLoop\Utils::GetShortToken(), // 7
|
||||
$this->sProxyAuthUser, // 8
|
||||
$this->sProxyAuthPassword, // 9
|
||||
0, // 10 // timelife
|
||||
$this->sClientCert // 11
|
||||
$this->sProxyAuthUser, // 8
|
||||
$this->sProxyAuthPassword, // 9
|
||||
0, // 10 // lifetime
|
||||
$this->sClientCert // 11
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -406,10 +435,11 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
* @param \RainLoop\Plugins\Manager $oPlugins
|
||||
* @param \MailSo\Mail\MailClient $oMailClient
|
||||
* @param \RainLoop\Application $oConfig
|
||||
* @param callback|null $refreshTokenCallback = null
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IncConnectAndLoginHelper($oPlugins, $oMailClient, $oConfig)
|
||||
public function IncConnectAndLoginHelper($oPlugins, $oMailClient, $oConfig, $refreshTokenCallback = null)
|
||||
{
|
||||
$bLogin = false;
|
||||
|
||||
|
|
@ -456,12 +486,21 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
}
|
||||
else
|
||||
{
|
||||
$iGatLen = \strlen(APP_GOOGLE_ACCESS_TOKEN_PREFIX);
|
||||
$sPassword = $aImapCredentials['Password'];
|
||||
if (APP_GOOGLE_ACCESS_TOKEN_PREFIX === \substr($sPassword, 0, $iGatLen))
|
||||
$aTokens = self::ParseTokensPassword($sPassword);
|
||||
|
||||
$sAccessToken = $aTokens[0];
|
||||
$sRefreshToken = $aTokens[1];
|
||||
|
||||
if ($sAccessToken && $sRefreshToken)
|
||||
{
|
||||
if ($refreshTokenCallback && \is_callable($refreshTokenCallback))
|
||||
{
|
||||
$sAccessToken = \call_user_func($refreshTokenCallback, $sAccessToken, $sRefreshToken);
|
||||
}
|
||||
|
||||
$oMailClient->LoginWithXOauth2(
|
||||
\base64_encode('user='.$aImapCredentials['Login']."\1".'auth=Bearer '.\substr($sPassword, $iGatLen)."\1\1"));
|
||||
\base64_encode('user='.$aImapCredentials['Login']."\1".'auth=Bearer '.$sAccessToken."\1\1"));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -482,11 +521,12 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
* @param \RainLoop\Plugins\Manager $oPlugins
|
||||
* @param \MailSo\Smtp\SmtpClient|null $oSmtpClient
|
||||
* @param \RainLoop\Application $oConfig
|
||||
* @param callback|null $refreshTokenCallback = null
|
||||
* @param bool $bUsePhpMail = false
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function OutConnectAndLoginHelper($oPlugins, $oSmtpClient, $oConfig, &$bUsePhpMail = false)
|
||||
public function OutConnectAndLoginHelper($oPlugins, $oSmtpClient, $oConfig, $refreshTokenCallback = null, &$bUsePhpMail = false)
|
||||
{
|
||||
$bLogin = false;
|
||||
|
||||
|
|
@ -526,12 +566,21 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
|
||||
if ($aSmtpCredentials['UseAuth'] && !$aSmtpCredentials['UsePhpMail'] && $oSmtpClient)
|
||||
{
|
||||
$iGatLen = \strlen(APP_GOOGLE_ACCESS_TOKEN_PREFIX);
|
||||
$sPassword = $aSmtpCredentials['Password'];
|
||||
if (APP_GOOGLE_ACCESS_TOKEN_PREFIX === \substr($sPassword, 0, $iGatLen))
|
||||
$aTokens = self::ParseTokensPassword($sPassword);
|
||||
|
||||
$sAccessToken = $aTokens[0];
|
||||
$sRefreshToken = $aTokens[1];
|
||||
|
||||
if ($sAccessToken && $sRefreshToken)
|
||||
{
|
||||
if ($refreshTokenCallback && \is_callable($refreshTokenCallback))
|
||||
{
|
||||
$sAccessToken = \call_user_func($refreshTokenCallback, $sAccessToken, $sRefreshToken);
|
||||
}
|
||||
|
||||
$oSmtpClient->LoginWithXOauth2(
|
||||
\base64_encode('user='.$aSmtpCredentials['Login']."\1".'auth=Bearer '.\substr($sPassword, $iGatLen)."\1\1"));
|
||||
\base64_encode('user='.$aSmtpCredentials['Login']."\1".'auth=Bearer '.$sAccessToken."\1\1"));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ class ServiceActions
|
|||
$sAction = $this->aPaths[2];
|
||||
}
|
||||
|
||||
$this->oActions->SetIsAjax(true);
|
||||
|
||||
try
|
||||
{
|
||||
if ($this->oHttp->IsPost() &&
|
||||
|
|
@ -211,6 +213,12 @@ class ServiceActions
|
|||
if (\is_array($aResponseItem))
|
||||
{
|
||||
$aResponseItem['Time'] = (int) ((\microtime(true) - APP_START) * 1000);
|
||||
|
||||
$sUpdateToken = $this->oActions->GetUpdateAuthToken();
|
||||
if ($sUpdateToken)
|
||||
{
|
||||
$aResponseItem['UpdateToken'] = $sUpdateToken;
|
||||
}
|
||||
}
|
||||
|
||||
$this->Plugins()->RunHook('filter.ajax-response', array($sAction, &$aResponseItem));
|
||||
|
|
@ -498,7 +506,7 @@ class ServiceActions
|
|||
if (\method_exists($this->oActions, $sMethodName))
|
||||
{
|
||||
@\header('X-Raw-Action: '.$sMethodName, true);
|
||||
@\header('Content-Security-Policy: default-src \'self\'; script-src \'none\'; style-src \'none\'; frame-src \'none\'; child-src \'none\'', true);
|
||||
@\header('Content-Security-Policy: script-src \'none\'; frame-src \'none\'; child-src \'none\'', true);
|
||||
|
||||
$sRawError = '';
|
||||
$this->oActions->SetActionParams(array(
|
||||
|
|
|
|||
|
|
@ -168,6 +168,24 @@ class Social
|
|||
return $sResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GoogleRefreshToken($sAccessToken, $sRefreshToken)
|
||||
{
|
||||
$oGoogle = $this->GoogleConnector();
|
||||
if ($oGoogle)
|
||||
{
|
||||
$aRefreshTokenResponse = $oGoogle->getAccessToken('https://accounts.google.com/o/oauth2/token', 'refresh_token', array(
|
||||
'refresh_token' => $sRefreshToken
|
||||
));
|
||||
|
||||
return !empty($aRefreshTokenResponse['result']['access_token']) ? $aRefreshTokenResponse['result']['access_token'] : '';
|
||||
}
|
||||
|
||||
return $sAccessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
|
@ -221,11 +239,11 @@ class Social
|
|||
'response_type' => 'code'
|
||||
);
|
||||
|
||||
// if ($bGmail)
|
||||
// {
|
||||
// $aParams['access_type'] = 'offline';
|
||||
// $aParams['approval_prompt'] = 'force';
|
||||
// }
|
||||
if ($bGmail)
|
||||
{
|
||||
$aParams['access_type'] = 'offline';
|
||||
// $aParams['prompt'] = 'consent';
|
||||
}
|
||||
|
||||
$sLoginUrl = $oGoogle->getAuthenticationUrl('https://accounts.google.com/o/oauth2/auth', $sRedirectUrl, $aParams);
|
||||
}
|
||||
|
|
@ -238,12 +256,17 @@ class Social
|
|||
$bLogin = !$oAccount;
|
||||
}
|
||||
|
||||
$aParams = array('code' => $this->oHttp->GetQuery('code'), 'redirect_uri' => $sRedirectUrl);
|
||||
$aAuthorizationResponse = $oGoogle->getAccessToken('https://accounts.google.com/o/oauth2/token', 'authorization_code', $aParams);
|
||||
$aAuthorizationCodeResponse = $oGoogle->getAccessToken('https://accounts.google.com/o/oauth2/token', 'authorization_code', array(
|
||||
'code' => $this->oHttp->GetQuery('code'),
|
||||
'redirect_uri' => $sRedirectUrl
|
||||
));
|
||||
|
||||
if (!empty($aAuthorizationResponse['result']['access_token']))
|
||||
$sAccessToken = !empty($aAuthorizationCodeResponse['result']['access_token']) ? $aAuthorizationCodeResponse['result']['access_token'] : '';
|
||||
$sRefreshToken = !empty($aAuthorizationCodeResponse['result']['refresh_token']) ? $aAuthorizationCodeResponse['result']['refresh_token'] : '';
|
||||
|
||||
if (!empty($sAccessToken))
|
||||
{
|
||||
$oGoogle->setAccessToken($aAuthorizationResponse['result']['access_token']);
|
||||
$oGoogle->setAccessToken($sAccessToken);
|
||||
$aUserInfoResponse = $oGoogle->fetch('https://www.googleapis.com/oauth2/v2/userinfo');
|
||||
|
||||
if (!empty($aUserInfoResponse['result']['id']))
|
||||
|
|
@ -257,7 +280,7 @@ class Social
|
|||
{
|
||||
$aUserData = array(
|
||||
'Email' => $aUserInfoResponse['result']['email'],
|
||||
'Password' => APP_GOOGLE_ACCESS_TOKEN_PREFIX.$aAuthorizationResponse['result']['access_token']
|
||||
'Password' => \RainLoop\Model\Account::GenerateTokensPassword($sAccessToken, $sRefreshToken)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ en:
|
|||
LEGEND_GOOGLE: "Google"
|
||||
LABEL_ENABLE_GOOGLE: "Enable Google Integration"
|
||||
LABEL_GOOGLE_AUTH: "Authorization"
|
||||
LABEL_GOOGLE_AUTH_GMAIL: "Authorization (Gmail)"
|
||||
LABEL_GOOGLE_DRIVE: "Google Drive Integration (Compose view)"
|
||||
LABEL_GOOGLE_PREVIEW: "Google Viewer Integration (Preview for Microsoft Word, Excel and PowerPoint files)"
|
||||
LABEL_GOOGLE_CLIENT_ID: "Client ID"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ en:
|
|||
LABEL_DONT_ASK_VERIFICATION_CODE: "Don't ask for the code for 2 weeks"
|
||||
BUTTON_SIGN_IN: "Sign In"
|
||||
TITLE_SIGN_IN_GOOGLE: "Sign In using Google"
|
||||
TITLE_SIGN_IN_GMAIL: "Sign In using Gmail"
|
||||
TITLE_SIGN_IN_FACEBOOK: "Sign In using Facebook"
|
||||
TITLE_SIGN_IN_TWITTER: "Sign In using Twitter"
|
||||
LABEL_FORGOT_PASSWORD: "Forgot password"
|
||||
|
|
@ -155,11 +156,11 @@ en:
|
|||
LINK_SAVE_TO_DROPBOX: "Save to Dropbox"
|
||||
READ_RECEIPT:
|
||||
SUBJECT: "Return Receipt (displayed) - %SUBJECT%"
|
||||
BODY: |
|
||||
This is a Return Receipt for the mail that you sent to %READ-RECEIPT%.
|
||||
|
||||
Note: "This Return Receipt only acknowledges that the message was displayed on the recipient's computer."
|
||||
There is no guarantee that the recipient has read or understood the message contents.
|
||||
BODY: |
|
||||
This is a Return Receipt for the mail that you sent to %READ-RECEIPT%.
|
||||
|
||||
Note: "This Return Receipt only acknowledges that the message was displayed on the recipient's computer."
|
||||
There is no guarantee that the recipient has read or understood the message contents.
|
||||
SUGGESTIONS:
|
||||
SEARCHING_DESC: "Searching..."
|
||||
CONTACTS:
|
||||
|
|
@ -365,16 +366,16 @@ en:
|
|||
LABEL_ARCHIVE: "Archive"
|
||||
BUTTON_CANCEL: "Cancel"
|
||||
BUTTON_CLOSE: "Close"
|
||||
NOTIFICATION_SENT: |
|
||||
You haven't selected "Sent" system folder messages are put to after sending.
|
||||
If you don't want to save sent message, please select "Do not use" option.
|
||||
NOTIFICATION_SENT: |
|
||||
You haven't selected "Sent" system folder messages are put to after sending.
|
||||
If you don't want to save sent message, please select "Do not use" option.
|
||||
NOTIFICATION_DRAFTS: "You haven't selected \"Drafts\" system folder messages are saved to while composing."
|
||||
NOTIFICATION_SPAM: |
|
||||
You haven't selected "Spam" system folder spamed messages are placed to.
|
||||
If you wish to remove messages permanently, please select "Do not use" option.
|
||||
NOTIFICATION_TRASH: |
|
||||
You haven't selected "Trash" system folder deleted messages are placed to.
|
||||
If you wish to remove messages permanently, please select "Do not use" option.
|
||||
NOTIFICATION_SPAM: |
|
||||
You haven't selected "Spam" system folder spamed messages are placed to.
|
||||
If you wish to remove messages permanently, please select "Do not use" option.
|
||||
NOTIFICATION_TRASH: |
|
||||
You haven't selected "Trash" system folder deleted messages are placed to.
|
||||
If you wish to remove messages permanently, please select "Do not use" option.
|
||||
NOTIFICATION_ARCHIVE: "You haven't selected \"Archive\" system folder achived messages are placed to."
|
||||
POPUPS_TWO_FACTOR_CFG:
|
||||
LEGEND_TWO_FACTOR_AUTH: "2-Step Verification (TOTP)"
|
||||
|
|
@ -395,13 +396,13 @@ en:
|
|||
TWO_FACTOR_REQUIRE_DESC: "Your account requires 2-Step verification configuration."
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC: "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC: "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC: >
|
||||
Import this info into your Google Authenticator client (or other TOTP client)
|
||||
using the provided QR code below or by entering the code manually.
|
||||
TWO_FACTOR_BACKUP_CODES_DESC: >
|
||||
If you can't receive codes via Google Authenticator (or other TOTP client),
|
||||
you can use backup codes to sign in. After you’ve used a backup code to sign
|
||||
in, it will become inactive.
|
||||
TWO_FACTOR_SECRET_DESC: >
|
||||
Import this info into your Google Authenticator client (or other TOTP client)
|
||||
using the provided QR code below or by entering the code manually.
|
||||
TWO_FACTOR_BACKUP_CODES_DESC: >
|
||||
If you can't receive codes via Google Authenticator (or other TOTP client),
|
||||
you can use backup codes to sign in. After you’ve used a backup code to sign
|
||||
in, it will become inactive.
|
||||
TWO_FACTOR_SECRET_TEST_BEFORE_DESC: "You can't change this setting before test."
|
||||
TITLES:
|
||||
LOADING: "Loading"
|
||||
|
|
@ -642,15 +643,15 @@ en:
|
|||
ACCESS_ERROR: "Access error"
|
||||
CONNECTION_ERROR: "Can't connect to server"
|
||||
CAPTCHA_ERROR: "Incorrect CAPTCHA."
|
||||
SOCIAL_FACEBOOK_LOGIN_ACCESS_DISABLE: >
|
||||
This social ID is not assigned for any email account yet. Log in using email
|
||||
credentials and enable this feature in account settings.
|
||||
SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE: >
|
||||
This social ID is not assigned for any email account yet. Log in using email
|
||||
credentials and enable this feature in account settings.
|
||||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE: >
|
||||
This social ID is not assigned for any email account yet. Log in using email
|
||||
credentials and enable this feature in account settings.
|
||||
SOCIAL_FACEBOOK_LOGIN_ACCESS_DISABLE: >
|
||||
This social ID is not assigned for any email account yet. Log in using email
|
||||
credentials and enable this feature in account settings.
|
||||
SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE: >
|
||||
This social ID is not assigned for any email account yet. Log in using email
|
||||
credentials and enable this feature in account settings.
|
||||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE: >
|
||||
This social ID is not assigned for any email account yet. Log in using email
|
||||
credentials and enable this feature in account settings.
|
||||
DOMAIN_NOT_ALLOWED: "Domain is not allowed"
|
||||
ACCOUNT_NOT_ALLOWED: "Account is not allowed"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED: "Two factor verification required"
|
||||
|
|
@ -700,14 +701,14 @@ en:
|
|||
PHP_EXSTENSIONS_ERROR_DESC: "Required PHP extension are not available in your PHP configuration!"
|
||||
PHP_VERSION_ERROR_DESC: "Your PHP version (%VERSION%) is lower than the minimal required 5.3.0!"
|
||||
NO_SCRIPT_TITLE: "JavaScript is required for this application."
|
||||
NO_SCRIPT_DESC: |
|
||||
JavaScript support is not available in your browser.
|
||||
Please enable JavaScript support in your browser settings and retry.
|
||||
NO_SCRIPT_DESC: |
|
||||
JavaScript support is not available in your browser.
|
||||
Please enable JavaScript support in your browser settings and retry.
|
||||
NO_COOKIE_TITLE: "Cookies support is required for this application."
|
||||
NO_COOKIE_DESC: |
|
||||
Cookies support is not available in your browser.
|
||||
Please enable Cookie support in your browser settings and retry.
|
||||
NO_COOKIE_DESC: |
|
||||
Cookies support is not available in your browser.
|
||||
Please enable Cookie support in your browser settings and retry.
|
||||
BAD_BROWSER_TITLE: "Your browser is outdated."
|
||||
BAD_BROWSER_DESC: |
|
||||
To use all the features of the application,
|
||||
download and install one of these browsers:
|
||||
BAD_BROWSER_DESC: |
|
||||
To use all the features of the application,
|
||||
download and install one of these browsers:
|
||||
|
|
|
|||
|
|
@ -24,6 +24,13 @@
|
|||
label: 'TAB_INTEGRATIONS/LABEL_GOOGLE_AUTH'
|
||||
}
|
||||
}"></div>
|
||||
<div data-bind="component: {
|
||||
name: 'Checkbox',
|
||||
params: {
|
||||
value: googleEnableAuthGmail,
|
||||
label: 'TAB_INTEGRATIONS/LABEL_GOOGLE_AUTH_GMAIL'
|
||||
}
|
||||
}"></div>
|
||||
<div data-bind="component: {
|
||||
name: 'Checkbox',
|
||||
params: {
|
||||
|
|
@ -90,7 +97,8 @@
|
|||
</div>
|
||||
<div class="legend">
|
||||
<span data-i18n="TAB_INTEGRATIONS/LEGEND_FACEBOOK"></span>
|
||||
<span style="color: #ccc; font-size: 14px;" data-bind="visible: !facebookSupported()">(<span data-i18n="HINTS/REQUIRES_PHP_54"></span>)</span>
|
||||
<span style="color: #ccc; font-size: 14px;" data-bind="visible: !facebookSupported()">(<span
|
||||
data-i18n="HINTS/REQUIRES_PHP_54"></span>)</span>
|
||||
</div>
|
||||
<div data-bind="if: facebookSupported">
|
||||
<div class="control-group">
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
<img class="logoImg" data-bind="attr: {'src': logoImg }" />
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
<div class="descWrapper thm-login-desc plugin-mark-Login-AfterLogoDescription" data-bind="visible: '' !== loginDescription">
|
||||
<div class="descWrapper thm-login-desc plugin-mark-Login-AfterLogoDescription"
|
||||
data-bind="visible: '' !== loginDescription">
|
||||
<span class="desc" data-bind="text: loginDescription"></span>
|
||||
</div>
|
||||
{{INCLUDE/AfterLogo/PLACE}}
|
||||
|
|
@ -21,17 +22,20 @@
|
|||
</div>
|
||||
<div class="wrapper-parent">
|
||||
<div class="wrapper loginWelcomeForm thm-login-text" data-bind="css: {'welcome-on': welcome}">
|
||||
<!-- ko template: { name: 'LoginWelcome' } --><!-- /ko -->
|
||||
<!-- ko template: { name: 'LoginWelcome' } -->
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
<form class="wrapper submitting-pane loginForm thm-login thm-login-text" action="#/" data-bind="submit: submitForm, css: {'errorAnimated': formError, 'welcome-off': welcome, 'submitting': submitRequest()}">
|
||||
<form class="wrapper submitting-pane loginForm thm-login thm-login-text" action="#/"
|
||||
data-bind="submit: submitForm, css: {'errorAnimated': formError, 'welcome-off': welcome, 'submitting': submitRequest()}">
|
||||
{{INCLUDE/TopControlGroup/PLACE}}
|
||||
<div class="controls plugin-mark-Login-TopControlGroup" data-bind="css: {'error': emailError, 'animated': emailErrorAnimation}">
|
||||
<div class="controls plugin-mark-Login-TopControlGroup"
|
||||
data-bind="css: {'error': emailError, 'animated': emailErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="email" class="i18n input-block-level inputEmail checkAutocomplete"
|
||||
name="RainLoopEmail" id="RainLoopEmail"
|
||||
style="padding-right: 35px;"
|
||||
autocomplete="email" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="textInput: email, hasFocus: emailFocus, disable: submitRequest" data-i18n="[placeholder]LOGIN/LABEL_EMAIL" />
|
||||
name="RainLoopEmail" id="RainLoopEmail" style="padding-right: 35px;" autocomplete="email"
|
||||
autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="textInput: email, hasFocus: emailFocus, disable: submitRequest"
|
||||
data-i18n="[placeholder]LOGIN/LABEL_EMAIL" />
|
||||
<span class="add-on">
|
||||
<i class="icon-mail"></i>
|
||||
</span>
|
||||
|
|
@ -40,11 +44,12 @@
|
|||
<div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="password" class="i18n input-block-level inputPassword checkAutocomplete"
|
||||
name="RainLoopPassword" id="RainLoopPassword"
|
||||
style="padding-right: 35px;"
|
||||
name="RainLoopPassword" id="RainLoopPassword" style="padding-right: 35px;"
|
||||
autocomplete="current-password" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="textInput: password, hasFocus: passwordFocus, disable: submitRequest" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD" />
|
||||
<span class="add-on" tabindex="-1" data-tooltip-join="left" data-bind="command: submitCommand, tooltip: 'LOGIN/BUTTON_SIGN_IN'">
|
||||
data-bind="textInput: password, hasFocus: passwordFocus, disable: submitRequest"
|
||||
data-i18n="[placeholder]LOGIN/LABEL_PASSWORD" />
|
||||
<span class="add-on" tabindex="-1" data-tooltip-join="left"
|
||||
data-bind="command: submitCommand, tooltip: 'LOGIN/BUTTON_SIGN_IN'">
|
||||
<i class="icon-key" data-bind="visible: '' === password()"></i>
|
||||
<button type="submit" class="btn-submit-icon-wrp" data-bind="visible: '' !== password()">
|
||||
<i class="icon-right-middle login-submit-icon"></i>
|
||||
|
|
@ -52,18 +57,20 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls" data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error, 'animated': additionalCode.errorAnimation}">
|
||||
<div class="controls"
|
||||
data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error, 'animated': additionalCode.errorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="text" class="i18n input-block-level inputAdditionalCode"
|
||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
style="padding-right: 35px;"
|
||||
data-bind="textInput: additionalCode, hasFocus: additionalCode.focused, disable: submitRequest" data-i18n="[placeholder]LOGIN/LABEL_VERIFICATION_CODE" />
|
||||
<input type="text" class="i18n input-block-level inputAdditionalCode" autocomplete="off"
|
||||
autocorrect="off" autocapitalize="off" spellcheck="false" style="padding-right: 35px;"
|
||||
data-bind="textInput: additionalCode, hasFocus: additionalCode.focused, disable: submitRequest"
|
||||
data-i18n="[placeholder]LOGIN/LABEL_VERIFICATION_CODE" />
|
||||
<span class="add-on">
|
||||
<i class="icon-key"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls plugin-mark-Login-BottomControlGroup" data-bind="visible: additionalCode.visibility()">
|
||||
<div class="controls plugin-mark-Login-BottomControlGroup"
|
||||
data-bind="visible: additionalCode.visibility()">
|
||||
<div class="additionalCodeSignMeLabel" data-bind="component: {
|
||||
name: 'CheckboxSimple',
|
||||
params: {
|
||||
|
|
@ -80,16 +87,24 @@
|
|||
</div>
|
||||
<div class="controls clearfix" style="margin-bottom: 10px">
|
||||
<div class="pull-right social-buttons">
|
||||
<a href="#" tabindex="-1" class="social-button" data-bind="visible: facebookLoginEnabled, command: facebookCommand, tooltip: 'LOGIN/TITLE_SIGN_IN_FACEBOOK'">
|
||||
<i class="icon-facebook-alt"></i>
|
||||
<a href="#" tabindex="-1" class="social-button"
|
||||
data-bind="visible: googleGmailLoginEnabled, command: googleGmailCommand, tooltip: 'LOGIN/TITLE_SIGN_IN_GMAIL'">
|
||||
<i class="icon-mail"></i>
|
||||
</a>
|
||||
<a href="#" tabindex="-1" class="social-button" data-bind="visible: googleLoginEnabled, command: googleCommand, tooltip: 'LOGIN/TITLE_SIGN_IN_GOOGLE'">
|
||||
<a href="#" tabindex="-1" class="social-button"
|
||||
data-bind="visible: googleLoginEnabled, command: googleCommand, tooltip: 'LOGIN/TITLE_SIGN_IN_GOOGLE'">
|
||||
<i class="icon-google"></i>
|
||||
</a>
|
||||
<a href="#" tabindex="-1" class="social-button" data-bind="visible: twitterLoginEnabled, command: twitterCommand, tooltip: 'LOGIN/TITLE_SIGN_IN_TWITTER'">
|
||||
<a href="#" tabindex="-1" class="social-button"
|
||||
data-bind="visible: facebookLoginEnabled, command: facebookCommand, tooltip: 'LOGIN/TITLE_SIGN_IN_FACEBOOK'">
|
||||
<i class="icon-facebook-alt"></i>
|
||||
</a>
|
||||
<a href="#" tabindex="-1" class="social-button"
|
||||
data-bind="visible: twitterLoginEnabled, command: twitterCommand, tooltip: 'LOGIN/TITLE_SIGN_IN_TWITTER'">
|
||||
<i class="icon-twitter"></i>
|
||||
</a>
|
||||
<a href="#" tabindex="-1" class="language-button" data-bind="visible: allowLanguagesOnLogin() && !socialLoginEnabled(), click: selectLanguage, tooltip: 'POPUPS_LANGUAGES/TITLE_LANGUAGES'">
|
||||
<a href="#" tabindex="-1" class="language-button"
|
||||
data-bind="visible: allowLanguagesOnLogin() && !socialLoginEnabled(), click: selectLanguage, tooltip: 'POPUPS_LANGUAGES/TITLE_LANGUAGES'">
|
||||
<i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -104,15 +119,20 @@
|
|||
}"></div>
|
||||
|
||||
</div>
|
||||
<div class="controls clearfix" data-bind="visible: '' !== forgotPasswordLinkUrl || '' !== registrationLinkUrl">
|
||||
<div class="forgot-link thm-forgot pull-left" data-bind="visible: '' !== forgotPasswordLinkUrl" style="text-align: center">
|
||||
<a href="#" target="_blank" class="g-ui-link" data-bind="attr: {href: forgotPasswordLinkUrl}, css: {'pull-right': '' !== registrationLinkUrl}"
|
||||
><span class="i18n" data-i18n="LOGIN/LABEL_FORGOT_PASSWORD"></span></a>
|
||||
<div class="controls clearfix"
|
||||
data-bind="visible: '' !== forgotPasswordLinkUrl || '' !== registrationLinkUrl">
|
||||
<div class="forgot-link thm-forgot pull-left" data-bind="visible: '' !== forgotPasswordLinkUrl"
|
||||
style="text-align: center">
|
||||
<a href="#" target="_blank" class="g-ui-link"
|
||||
data-bind="attr: {href: forgotPasswordLinkUrl}, css: {'pull-right': '' !== registrationLinkUrl}"><span
|
||||
class="i18n" data-i18n="LOGIN/LABEL_FORGOT_PASSWORD"></span></a>
|
||||
</div>
|
||||
|
||||
<div class="registration-link thm-registration pull-right" data-bind="visible: '' !== registrationLinkUrl" style="text-align: center">
|
||||
<a href="#" target="_blank" class="g-ui-link" data-bind="attr: {href: registrationLinkUrl}, css: {'pull-left': '' !== forgotPasswordLinkUrl}"
|
||||
><span class="i18n" data-i18n="LOGIN/LABEL_REGISTRATION"></span></a>
|
||||
<div class="registration-link thm-registration pull-right"
|
||||
data-bind="visible: '' !== registrationLinkUrl" style="text-align: center">
|
||||
<a href="#" target="_blank" class="g-ui-link"
|
||||
data-bind="attr: {href: registrationLinkUrl}, css: {'pull-left': '' !== forgotPasswordLinkUrl}"><span
|
||||
class="i18n" data-i18n="LOGIN/LABEL_REGISTRATION"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -133,11 +153,13 @@
|
|||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="e-languages thm-languages plugin-mark-Login-BottomFooter" data-bind="visible: allowLanguagesOnLogin() && socialLoginEnabled()">
|
||||
<div class="e-languages thm-languages plugin-mark-Login-BottomFooter"
|
||||
data-bind="visible: allowLanguagesOnLogin() && socialLoginEnabled()">
|
||||
<label class="flag-selector">
|
||||
<i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i>
|
||||
|
||||
<span class="flag-name" tabindex="0" data-bind="text: languageFullName, click: selectLanguage, onSpace: selectLanguage, onEnter: selectLanguage, onTab: selectLanguageOnTab"></span>
|
||||
<span class="flag-name" tabindex="0"
|
||||
data-bind="text: languageFullName, click: selectLanguage, onSpace: selectLanguage, onEnter: selectLanguage, onTab: selectLanguageOnTab"></span>
|
||||
</label>
|
||||
</div>
|
||||
{{INCLUDE/BottomFooter/PLACE}}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<div class="b-login--welcome-content">
|
||||
<i class="icon-google iconsize50" style="cursor: pointer; padding-right: 20px" data-bind="command: googleFastCommand, visible: googleFastLoginEnabled"></i>
|
||||
<i class="icon-google iconsize50" style="cursor: pointer; padding-right: 20px"
|
||||
data-bind="command: googleGmailCommand, visible: googleGmailLoginEnabled"></i>
|
||||
<i class="icon-mail iconsize50" style="cursor: pointer" data-bind="click: displayMainForm"></i>
|
||||
<br /><br />
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue