mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +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;
|
isCached = pInt(data.Time) > microtime() - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data && data.UpdateToken) {
|
||||||
|
if (GlobalsData.__APP__ && GlobalsData.__APP__.setClientSideToken) {
|
||||||
|
GlobalsData.__APP__.setClientSideToken(data.UpdateToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// backward capability
|
// backward capability
|
||||||
let type = '';
|
let type = '';
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,12 @@ class AbstractAjaxRemote {
|
||||||
cached = pInt(oData.Time) > new window.Date().getTime() - start;
|
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 (action && this.oRequests[action]) {
|
||||||
if (this.oRequests[action].__aborted) {
|
if (this.oRequests[action].__aborted) {
|
||||||
sType = 'abort';
|
sType = 'abort';
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class SocialAdminSettings {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.googleEnable = SocialStore.google.enabled;
|
this.googleEnable = SocialStore.google.enabled;
|
||||||
this.googleEnableAuth = SocialStore.google.capa.auth;
|
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.googleEnableDrive = SocialStore.google.capa.drive;
|
||||||
this.googleEnablePreview = SocialStore.google.capa.preview;
|
this.googleEnablePreview = SocialStore.google.capa.preview;
|
||||||
|
|
||||||
|
|
@ -89,6 +89,7 @@ class SocialAdminSettings {
|
||||||
|
|
||||||
this.googleEnable.subscribe(Remote.saveAdminConfigHelper('GoogleEnable', boolToAjax));
|
this.googleEnable.subscribe(Remote.saveAdminConfigHelper('GoogleEnable', boolToAjax));
|
||||||
this.googleEnableAuth.subscribe(Remote.saveAdminConfigHelper('GoogleEnableAuth', boolToAjax));
|
this.googleEnableAuth.subscribe(Remote.saveAdminConfigHelper('GoogleEnableAuth', boolToAjax));
|
||||||
|
this.googleEnableAuthGmail.subscribe(Remote.saveAdminConfigHelper('GoogleEnableAuthGmail', boolToAjax));
|
||||||
this.googleEnableDrive.subscribe(Remote.saveAdminConfigHelper('GoogleEnableDrive', boolToAjax));
|
this.googleEnableDrive.subscribe(Remote.saveAdminConfigHelper('GoogleEnableDrive', boolToAjax));
|
||||||
this.googleEnablePreview.subscribe(Remote.saveAdminConfigHelper('GoogleEnablePreview', boolToAjax));
|
this.googleEnablePreview.subscribe(Remote.saveAdminConfigHelper('GoogleEnablePreview', boolToAjax));
|
||||||
this.googleClientID.subscribe(Remote.saveAdminConfigHelper('GoogleClientID', trim, f5));
|
this.googleClientID.subscribe(Remote.saveAdminConfigHelper('GoogleClientID', trim, f5));
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ class SocialUserSettings {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.googleEnable = SocialStore.google.enabled;
|
this.googleEnable = SocialStore.google.enabled;
|
||||||
this.googleEnableAuth = SocialStore.google.capa.auth;
|
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.googleEnableDrive = SocialStore.google.capa.drive;
|
||||||
this.googleEnablePreview = SocialStore.google.capa.preview;
|
this.googleEnablePreview = SocialStore.google.capa.preview;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,14 @@ class SocialStore {
|
||||||
|
|
||||||
this.google.capa = {};
|
this.google.capa = {};
|
||||||
this.google.capa.auth = ko.observable(false);
|
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.drive = ko.observable(false);
|
||||||
this.google.capa.preview = ko.observable(false);
|
this.google.capa.preview = ko.observable(false);
|
||||||
|
|
||||||
this.google.require = {};
|
this.google.require = {};
|
||||||
this.google.require.clientSettings = ko.computed(
|
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());
|
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.apiKey(Settings.settingsGet('GoogleApiKey'));
|
||||||
|
|
||||||
this.google.capa.auth(!!Settings.settingsGet('AllowGoogleSocialAuth'));
|
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.drive(!!Settings.settingsGet('AllowGoogleSocialDrive'));
|
||||||
this.google.capa.preview(!!Settings.settingsGet('AllowGoogleSocialPreview'));
|
this.google.capa.preview(!!Settings.settingsGet('AllowGoogleSocialPreview'));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ class LoginUserView extends AbstractViewNext {
|
||||||
|
|
||||||
this.facebookLoginEnabled = ko.observable(false);
|
this.facebookLoginEnabled = ko.observable(false);
|
||||||
this.googleLoginEnabled = ko.observable(false);
|
this.googleLoginEnabled = ko.observable(false);
|
||||||
this.googleFastLoginEnabled = ko.observable(false);
|
this.googleGmailLoginEnabled = ko.observable(false);
|
||||||
this.twitterLoginEnabled = ko.observable(false);
|
this.twitterLoginEnabled = ko.observable(false);
|
||||||
|
|
||||||
this.socialLoginEnabled = ko.computed(() => {
|
this.socialLoginEnabled = ko.computed(() => {
|
||||||
|
|
@ -175,8 +175,8 @@ class LoginUserView extends AbstractViewNext {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@command((self) => !self.submitRequest() && self.googleFastLoginEnabled())
|
@command((self) => !self.submitRequest() && self.googleGmailLoginEnabled())
|
||||||
googleFastCommand() {
|
googleGmailCommand() {
|
||||||
window.open(socialGoogle(true), 'Google', this.windowOpenFeatures(550));
|
window.open(socialGoogle(true), 'Google', this.windowOpenFeatures(550));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -344,8 +344,8 @@ class LoginUserView extends AbstractViewNext {
|
||||||
this.googleLoginEnabled(
|
this.googleLoginEnabled(
|
||||||
!!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialAuth')
|
!!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialAuth')
|
||||||
);
|
);
|
||||||
this.googleFastLoginEnabled(
|
this.googleGmailLoginEnabled(
|
||||||
!!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialAuthFast')
|
!!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialAuthGmail')
|
||||||
);
|
);
|
||||||
|
|
||||||
switch (signMe) {
|
switch (signMe) {
|
||||||
|
|
@ -375,7 +375,7 @@ class LoginUserView extends AbstractViewNext {
|
||||||
this.email(AppStore.devEmail);
|
this.email(AppStore.devEmail);
|
||||||
this.password(AppStore.devPassword);
|
this.password(AppStore.devPassword);
|
||||||
|
|
||||||
if (this.googleLoginEnabled() || this.googleFastLoginEnabled()) {
|
if (this.googleLoginEnabled() || this.googleGmailLoginEnabled()) {
|
||||||
window['rl_' + jsHash + '_google_login_service'] = fSocial;
|
window['rl_' + jsHash + '_google_login_service'] = fSocial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,11 @@ class Actions
|
||||||
*/
|
*/
|
||||||
private $sSpecAuthToken;
|
private $sSpecAuthToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sUpdateAuthToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
|
|
@ -155,6 +160,8 @@ class Actions
|
||||||
$this->oPremProvider = null;
|
$this->oPremProvider = null;
|
||||||
|
|
||||||
$this->sSpecAuthToken = '';
|
$this->sSpecAuthToken = '';
|
||||||
|
$this->sUpdateAuthToken = '';
|
||||||
|
$this->bIsAjax = false;
|
||||||
|
|
||||||
$oConfig = $this->Config();
|
$oConfig = $this->Config();
|
||||||
$this->Plugins()->RunHook('filter.application-config', array(&$oConfig));
|
$this->Plugins()->RunHook('filter.application-config', array(&$oConfig));
|
||||||
|
|
@ -182,6 +189,30 @@ class Actions
|
||||||
return $this;
|
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
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
@ -190,6 +221,22 @@ class Actions
|
||||||
return $this->sSpecAuthToken;
|
return $this->sSpecAuthToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function GetUpdateAuthToken()
|
||||||
|
{
|
||||||
|
return $this->sUpdateAuthToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function GetIsAjax()
|
||||||
|
{
|
||||||
|
return $this->bIsAjax;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
@ -1647,7 +1694,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
|
|
||||||
$aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false);
|
$aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false);
|
||||||
$aResult['AllowGoogleSocialAuth'] = (bool) $oConfig->Get('social', 'google_enable_auth', true);
|
$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['AllowGoogleSocialDrive'] = (bool) $oConfig->Get('social', 'google_enable_drive', true);
|
||||||
$aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', 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', '')))))
|
'' === \trim($oConfig->Get('social', 'google_client_id', '')) || '' === \trim($oConfig->Get('social', 'google_client_secret', '')))))
|
||||||
{
|
{
|
||||||
$aResult['AllowGoogleSocialAuth'] = false;
|
$aResult['AllowGoogleSocialAuth'] = false;
|
||||||
$aResult['AllowGoogleSocialAuthFast'] = false;
|
$aResult['AllowGoogleSocialAuthGmail'] = false;
|
||||||
$aResult['AllowGoogleSocialDrive'] = false;
|
$aResult['AllowGoogleSocialDrive'] = false;
|
||||||
$aResult['GoogleClientID'] = '';
|
$aResult['GoogleClientID'] = '';
|
||||||
$aResult['GoogleApiKey'] = '';
|
$aResult['GoogleApiKey'] = '';
|
||||||
|
|
@ -1670,7 +1717,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($aResult['AllowGoogleSocial'] &&
|
if ($aResult['AllowGoogleSocial'] &&
|
||||||
!$aResult['AllowGoogleSocialAuth'] && !$aResult['AllowGoogleSocialAuthFast'] &&
|
!$aResult['AllowGoogleSocialAuth'] && !$aResult['AllowGoogleSocialAuthGmail'] &&
|
||||||
!$aResult['AllowGoogleSocialDrive'] && !$aResult['AllowGoogleSocialPreview'])
|
!$aResult['AllowGoogleSocialDrive'] && !$aResult['AllowGoogleSocialPreview'])
|
||||||
{
|
{
|
||||||
$aResult['AllowGoogleSocial'] = false;
|
$aResult['AllowGoogleSocial'] = false;
|
||||||
|
|
@ -1746,7 +1793,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
|
|
||||||
$aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false);
|
$aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false);
|
||||||
$aResult['AllowGoogleSocialAuth'] = (bool) $oConfig->Get('social', 'google_enable_auth', true);
|
$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['AllowGoogleSocialDrive'] = (bool) $oConfig->Get('social', 'google_enable_drive', true);
|
||||||
$aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', true);
|
$aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', true);
|
||||||
|
|
||||||
|
|
@ -1986,6 +2033,51 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
return $aResult;
|
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
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
@ -2059,7 +2151,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
{
|
{
|
||||||
try
|
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)
|
catch (\RainLoop\Exceptions\ClientException $oException)
|
||||||
{
|
{
|
||||||
|
|
@ -3366,7 +3458,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
*
|
*
|
||||||
* @throws \MailSo\Base\Exceptions\Exception
|
* @throws \MailSo\Base\Exceptions\Exception
|
||||||
*/
|
*/
|
||||||
public function getAccountUnredCountFromHash($sHash)
|
public function getAccountUnreadCountFromHash($sHash)
|
||||||
{
|
{
|
||||||
$iResult = 0;
|
$iResult = 0;
|
||||||
|
|
||||||
|
|
@ -3378,7 +3470,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
$oMailClient = \MailSo\Mail\MailClient::NewInstance();
|
$oMailClient = \MailSo\Mail\MailClient::NewInstance();
|
||||||
$oMailClient->SetLogger($this->Logger());
|
$oMailClient->SetLogger($this->Logger());
|
||||||
|
|
||||||
$oAccount->IncConnectAndLoginHelper($this->Plugins(),$oMailClient, $this->Config());
|
$oAccount->IncConnectAndLoginHelper($this->Plugins(), $oMailClient, $this->Config(), array($this, 'googleRefreshTokenCallback'));
|
||||||
|
|
||||||
$iResult = $oMailClient->InboxUnreadCount();
|
$iResult = $oMailClient->InboxUnreadCount();
|
||||||
|
|
||||||
|
|
@ -3425,7 +3517,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
foreach ($mAccounts as $sEmail => $sHash)
|
foreach ($mAccounts as $sEmail => $sHash)
|
||||||
{
|
{
|
||||||
$aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($sEmail),
|
$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, 'GoogleEnable', 'social', 'google_enable', 'bool');
|
||||||
$this->setConfigFromParams($oConfig, 'GoogleEnableAuth', 'social', 'google_enable_auth', '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, 'GoogleEnableDrive', 'social', 'google_enable_drive', 'bool');
|
||||||
$this->setConfigFromParams($oConfig, 'GoogleEnablePreview', 'social', 'google_enable_preview', 'bool');
|
$this->setConfigFromParams($oConfig, 'GoogleEnablePreview', 'social', 'google_enable_preview', 'bool');
|
||||||
$this->setConfigFromParams($oConfig, 'GoogleClientID', 'social', 'google_client_id', 'string');
|
$this->setConfigFromParams($oConfig, 'GoogleClientID', 'social', 'google_client_id', 'string');
|
||||||
|
|
@ -4293,7 +4385,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
$iRepTime = $this->Cacher()->GetTimer($sCacheKey);
|
$iRepTime = $this->Cacher()->GetTimer($sCacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('' === $sRep || 0 === $iRepTime || time() - 3600 > $iRepTime)
|
if ('' === $sRep || 0 === $iRepTime || \time() - 3600 > $iRepTime)
|
||||||
{
|
{
|
||||||
$iCode = 0;
|
$iCode = 0;
|
||||||
$sContentType = '';
|
$sContentType = '';
|
||||||
|
|
@ -4393,7 +4485,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
$iRepTime = $this->Cacher()->GetTimer($sCacheKey);
|
$iRepTime = $this->Cacher()->GetTimer($sCacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('' === $sRep || 0 === $iRepTime || time() - 3600 > $iRepTime)
|
if ('' === $sRep || 0 === $iRepTime || \time() - 3600 > $iRepTime)
|
||||||
{
|
{
|
||||||
$iCode = 0;
|
$iCode = 0;
|
||||||
$sContentType = '';
|
$sContentType = '';
|
||||||
|
|
@ -6172,7 +6264,9 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
$oSmtpClient = \MailSo\Smtp\SmtpClient::NewInstance()->SetLogger($this->Logger());
|
$oSmtpClient = \MailSo\Smtp\SmtpClient::NewInstance()->SetLogger($this->Logger());
|
||||||
$oSmtpClient->SetTimeOuts(10, (int) \RainLoop\Api::Config()->Get('labs', 'smtp_timeout', 60));
|
$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)
|
if ($bUsePhpMail)
|
||||||
{
|
{
|
||||||
|
|
@ -8315,7 +8409,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
$iExpires = $this->Config()->Get('cache', 'http_expires', 3600);
|
$iExpires = $this->Config()->Get('cache', 'http_expires', 3600);
|
||||||
if (0 < $iExpires)
|
if (0 < $iExpires)
|
||||||
{
|
{
|
||||||
$this->oHttp->ServerUseCache($this->etag($sKey), 1382478804, time() + $iExpires);
|
$this->oHttp->ServerUseCache($this->etag($sKey), 1382478804, \time() + $iExpires);
|
||||||
$bResult = true;
|
$bResult = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8964,7 +9058,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack
|
||||||
|
|
||||||
try
|
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)
|
catch (\MailSo\Net\Exceptions\ConnectionException $oException)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ Examples:
|
||||||
'social' => array(
|
'social' => array(
|
||||||
'google_enable' => array(false, 'Google'),
|
'google_enable' => array(false, 'Google'),
|
||||||
'google_enable_auth' => array(false),
|
'google_enable_auth' => array(false),
|
||||||
'google_enable_auth_fast' => array(false),
|
'google_enable_auth_gmail' => array(false),
|
||||||
'google_enable_drive' => array(false),
|
'google_enable_drive' => array(false),
|
||||||
'google_enable_preview' => array(false),
|
'google_enable_preview' => array(false),
|
||||||
'google_client_id' => array(''),
|
'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 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
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
@ -376,7 +405,7 @@ class Account extends \RainLoop\Account // for backward compatibility
|
||||||
\RainLoop\Utils::GetShortToken(), // 7
|
\RainLoop\Utils::GetShortToken(), // 7
|
||||||
$this->sProxyAuthUser, // 8
|
$this->sProxyAuthUser, // 8
|
||||||
$this->sProxyAuthPassword, // 9
|
$this->sProxyAuthPassword, // 9
|
||||||
0, // 10 // timelife
|
0, // 10 // lifetime
|
||||||
$this->sClientCert // 11
|
$this->sClientCert // 11
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +426,7 @@ class Account extends \RainLoop\Account // for backward compatibility
|
||||||
\RainLoop\Utils::GetShortToken(), // 7
|
\RainLoop\Utils::GetShortToken(), // 7
|
||||||
$this->sProxyAuthUser, // 8
|
$this->sProxyAuthUser, // 8
|
||||||
$this->sProxyAuthPassword, // 9
|
$this->sProxyAuthPassword, // 9
|
||||||
0, // 10 // timelife
|
0, // 10 // lifetime
|
||||||
$this->sClientCert // 11
|
$this->sClientCert // 11
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -406,10 +435,11 @@ class Account extends \RainLoop\Account // for backward compatibility
|
||||||
* @param \RainLoop\Plugins\Manager $oPlugins
|
* @param \RainLoop\Plugins\Manager $oPlugins
|
||||||
* @param \MailSo\Mail\MailClient $oMailClient
|
* @param \MailSo\Mail\MailClient $oMailClient
|
||||||
* @param \RainLoop\Application $oConfig
|
* @param \RainLoop\Application $oConfig
|
||||||
|
* @param callback|null $refreshTokenCallback = null
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function IncConnectAndLoginHelper($oPlugins, $oMailClient, $oConfig)
|
public function IncConnectAndLoginHelper($oPlugins, $oMailClient, $oConfig, $refreshTokenCallback = null)
|
||||||
{
|
{
|
||||||
$bLogin = false;
|
$bLogin = false;
|
||||||
|
|
||||||
|
|
@ -456,12 +486,21 @@ class Account extends \RainLoop\Account // for backward compatibility
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$iGatLen = \strlen(APP_GOOGLE_ACCESS_TOKEN_PREFIX);
|
|
||||||
$sPassword = $aImapCredentials['Password'];
|
$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(
|
$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
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -482,11 +521,12 @@ class Account extends \RainLoop\Account // for backward compatibility
|
||||||
* @param \RainLoop\Plugins\Manager $oPlugins
|
* @param \RainLoop\Plugins\Manager $oPlugins
|
||||||
* @param \MailSo\Smtp\SmtpClient|null $oSmtpClient
|
* @param \MailSo\Smtp\SmtpClient|null $oSmtpClient
|
||||||
* @param \RainLoop\Application $oConfig
|
* @param \RainLoop\Application $oConfig
|
||||||
|
* @param callback|null $refreshTokenCallback = null
|
||||||
* @param bool $bUsePhpMail = false
|
* @param bool $bUsePhpMail = false
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function OutConnectAndLoginHelper($oPlugins, $oSmtpClient, $oConfig, &$bUsePhpMail = false)
|
public function OutConnectAndLoginHelper($oPlugins, $oSmtpClient, $oConfig, $refreshTokenCallback = null, &$bUsePhpMail = false)
|
||||||
{
|
{
|
||||||
$bLogin = false;
|
$bLogin = false;
|
||||||
|
|
||||||
|
|
@ -526,12 +566,21 @@ class Account extends \RainLoop\Account // for backward compatibility
|
||||||
|
|
||||||
if ($aSmtpCredentials['UseAuth'] && !$aSmtpCredentials['UsePhpMail'] && $oSmtpClient)
|
if ($aSmtpCredentials['UseAuth'] && !$aSmtpCredentials['UsePhpMail'] && $oSmtpClient)
|
||||||
{
|
{
|
||||||
$iGatLen = \strlen(APP_GOOGLE_ACCESS_TOKEN_PREFIX);
|
|
||||||
$sPassword = $aSmtpCredentials['Password'];
|
$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(
|
$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
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,8 @@ class ServiceActions
|
||||||
$sAction = $this->aPaths[2];
|
$sAction = $this->aPaths[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->oActions->SetIsAjax(true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ($this->oHttp->IsPost() &&
|
if ($this->oHttp->IsPost() &&
|
||||||
|
|
@ -211,6 +213,12 @@ class ServiceActions
|
||||||
if (\is_array($aResponseItem))
|
if (\is_array($aResponseItem))
|
||||||
{
|
{
|
||||||
$aResponseItem['Time'] = (int) ((\microtime(true) - APP_START) * 1000);
|
$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));
|
$this->Plugins()->RunHook('filter.ajax-response', array($sAction, &$aResponseItem));
|
||||||
|
|
@ -498,7 +506,7 @@ class ServiceActions
|
||||||
if (\method_exists($this->oActions, $sMethodName))
|
if (\method_exists($this->oActions, $sMethodName))
|
||||||
{
|
{
|
||||||
@\header('X-Raw-Action: '.$sMethodName, true);
|
@\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 = '';
|
$sRawError = '';
|
||||||
$this->oActions->SetActionParams(array(
|
$this->oActions->SetActionParams(array(
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,24 @@ class Social
|
||||||
return $sResult;
|
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
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
@ -221,11 +239,11 @@ class Social
|
||||||
'response_type' => 'code'
|
'response_type' => 'code'
|
||||||
);
|
);
|
||||||
|
|
||||||
// if ($bGmail)
|
if ($bGmail)
|
||||||
// {
|
{
|
||||||
// $aParams['access_type'] = 'offline';
|
$aParams['access_type'] = 'offline';
|
||||||
// $aParams['approval_prompt'] = 'force';
|
// $aParams['prompt'] = 'consent';
|
||||||
// }
|
}
|
||||||
|
|
||||||
$sLoginUrl = $oGoogle->getAuthenticationUrl('https://accounts.google.com/o/oauth2/auth', $sRedirectUrl, $aParams);
|
$sLoginUrl = $oGoogle->getAuthenticationUrl('https://accounts.google.com/o/oauth2/auth', $sRedirectUrl, $aParams);
|
||||||
}
|
}
|
||||||
|
|
@ -238,12 +256,17 @@ class Social
|
||||||
$bLogin = !$oAccount;
|
$bLogin = !$oAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aParams = array('code' => $this->oHttp->GetQuery('code'), 'redirect_uri' => $sRedirectUrl);
|
$aAuthorizationCodeResponse = $oGoogle->getAccessToken('https://accounts.google.com/o/oauth2/token', 'authorization_code', array(
|
||||||
$aAuthorizationResponse = $oGoogle->getAccessToken('https://accounts.google.com/o/oauth2/token', 'authorization_code', $aParams);
|
'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');
|
$aUserInfoResponse = $oGoogle->fetch('https://www.googleapis.com/oauth2/v2/userinfo');
|
||||||
|
|
||||||
if (!empty($aUserInfoResponse['result']['id']))
|
if (!empty($aUserInfoResponse['result']['id']))
|
||||||
|
|
@ -257,7 +280,7 @@ class Social
|
||||||
{
|
{
|
||||||
$aUserData = array(
|
$aUserData = array(
|
||||||
'Email' => $aUserInfoResponse['result']['email'],
|
'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"
|
LEGEND_GOOGLE: "Google"
|
||||||
LABEL_ENABLE_GOOGLE: "Enable Google Integration"
|
LABEL_ENABLE_GOOGLE: "Enable Google Integration"
|
||||||
LABEL_GOOGLE_AUTH: "Authorization"
|
LABEL_GOOGLE_AUTH: "Authorization"
|
||||||
|
LABEL_GOOGLE_AUTH_GMAIL: "Authorization (Gmail)"
|
||||||
LABEL_GOOGLE_DRIVE: "Google Drive Integration (Compose view)"
|
LABEL_GOOGLE_DRIVE: "Google Drive Integration (Compose view)"
|
||||||
LABEL_GOOGLE_PREVIEW: "Google Viewer Integration (Preview for Microsoft Word, Excel and PowerPoint files)"
|
LABEL_GOOGLE_PREVIEW: "Google Viewer Integration (Preview for Microsoft Word, Excel and PowerPoint files)"
|
||||||
LABEL_GOOGLE_CLIENT_ID: "Client ID"
|
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"
|
LABEL_DONT_ASK_VERIFICATION_CODE: "Don't ask for the code for 2 weeks"
|
||||||
BUTTON_SIGN_IN: "Sign In"
|
BUTTON_SIGN_IN: "Sign In"
|
||||||
TITLE_SIGN_IN_GOOGLE: "Sign In using Google"
|
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_FACEBOOK: "Sign In using Facebook"
|
||||||
TITLE_SIGN_IN_TWITTER: "Sign In using Twitter"
|
TITLE_SIGN_IN_TWITTER: "Sign In using Twitter"
|
||||||
LABEL_FORGOT_PASSWORD: "Forgot password"
|
LABEL_FORGOT_PASSWORD: "Forgot password"
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,13 @@
|
||||||
label: 'TAB_INTEGRATIONS/LABEL_GOOGLE_AUTH'
|
label: 'TAB_INTEGRATIONS/LABEL_GOOGLE_AUTH'
|
||||||
}
|
}
|
||||||
}"></div>
|
}"></div>
|
||||||
|
<div data-bind="component: {
|
||||||
|
name: 'Checkbox',
|
||||||
|
params: {
|
||||||
|
value: googleEnableAuthGmail,
|
||||||
|
label: 'TAB_INTEGRATIONS/LABEL_GOOGLE_AUTH_GMAIL'
|
||||||
|
}
|
||||||
|
}"></div>
|
||||||
<div data-bind="component: {
|
<div data-bind="component: {
|
||||||
name: 'Checkbox',
|
name: 'Checkbox',
|
||||||
params: {
|
params: {
|
||||||
|
|
@ -90,7 +97,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="legend">
|
<div class="legend">
|
||||||
<span data-i18n="TAB_INTEGRATIONS/LEGEND_FACEBOOK"></span>
|
<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>
|
||||||
<div data-bind="if: facebookSupported">
|
<div data-bind="if: facebookSupported">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
<img class="logoImg" data-bind="attr: {'src': logoImg }" />
|
<img class="logoImg" data-bind="attr: {'src': logoImg }" />
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
<!-- /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>
|
<span class="desc" data-bind="text: loginDescription"></span>
|
||||||
</div>
|
</div>
|
||||||
{{INCLUDE/AfterLogo/PLACE}}
|
{{INCLUDE/AfterLogo/PLACE}}
|
||||||
|
|
@ -21,17 +22,20 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper-parent">
|
<div class="wrapper-parent">
|
||||||
<div class="wrapper loginWelcomeForm thm-login-text" data-bind="css: {'welcome-on': welcome}">
|
<div class="wrapper loginWelcomeForm thm-login-text" data-bind="css: {'welcome-on': welcome}">
|
||||||
<!-- ko template: { name: 'LoginWelcome' } --><!-- /ko -->
|
<!-- ko template: { name: 'LoginWelcome' } -->
|
||||||
|
<!-- /ko -->
|
||||||
</div>
|
</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}}
|
{{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">
|
<div class="input-append">
|
||||||
<input type="email" class="i18n input-block-level inputEmail checkAutocomplete"
|
<input type="email" class="i18n input-block-level inputEmail checkAutocomplete"
|
||||||
name="RainLoopEmail" id="RainLoopEmail"
|
name="RainLoopEmail" id="RainLoopEmail" style="padding-right: 35px;" autocomplete="email"
|
||||||
style="padding-right: 35px;"
|
autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
autocomplete="email" autocorrect="off" autocapitalize="off" spellcheck="false"
|
data-bind="textInput: email, hasFocus: emailFocus, disable: submitRequest"
|
||||||
data-bind="textInput: email, hasFocus: emailFocus, disable: submitRequest" data-i18n="[placeholder]LOGIN/LABEL_EMAIL" />
|
data-i18n="[placeholder]LOGIN/LABEL_EMAIL" />
|
||||||
<span class="add-on">
|
<span class="add-on">
|
||||||
<i class="icon-mail"></i>
|
<i class="icon-mail"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -40,11 +44,12 @@
|
||||||
<div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
|
<div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
|
||||||
<div class="input-append">
|
<div class="input-append">
|
||||||
<input type="password" class="i18n input-block-level inputPassword checkAutocomplete"
|
<input type="password" class="i18n input-block-level inputPassword checkAutocomplete"
|
||||||
name="RainLoopPassword" id="RainLoopPassword"
|
name="RainLoopPassword" id="RainLoopPassword" style="padding-right: 35px;"
|
||||||
style="padding-right: 35px;"
|
|
||||||
autocomplete="current-password" autocorrect="off" autocapitalize="off" spellcheck="false"
|
autocomplete="current-password" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
data-bind="textInput: password, hasFocus: passwordFocus, disable: submitRequest" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD" />
|
data-bind="textInput: password, hasFocus: passwordFocus, disable: submitRequest"
|
||||||
<span class="add-on" tabindex="-1" data-tooltip-join="left" data-bind="command: submitCommand, tooltip: 'LOGIN/BUTTON_SIGN_IN'">
|
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>
|
<i class="icon-key" data-bind="visible: '' === password()"></i>
|
||||||
<button type="submit" class="btn-submit-icon-wrp" data-bind="visible: '' !== password()">
|
<button type="submit" class="btn-submit-icon-wrp" data-bind="visible: '' !== password()">
|
||||||
<i class="icon-right-middle login-submit-icon"></i>
|
<i class="icon-right-middle login-submit-icon"></i>
|
||||||
|
|
@ -52,18 +57,20 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="input-append">
|
||||||
<input type="text" class="i18n input-block-level inputAdditionalCode"
|
<input type="text" class="i18n input-block-level inputAdditionalCode" autocomplete="off"
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
autocorrect="off" autocapitalize="off" spellcheck="false" style="padding-right: 35px;"
|
||||||
style="padding-right: 35px;"
|
data-bind="textInput: additionalCode, hasFocus: additionalCode.focused, disable: submitRequest"
|
||||||
data-bind="textInput: additionalCode, hasFocus: additionalCode.focused, disable: submitRequest" data-i18n="[placeholder]LOGIN/LABEL_VERIFICATION_CODE" />
|
data-i18n="[placeholder]LOGIN/LABEL_VERIFICATION_CODE" />
|
||||||
<span class="add-on">
|
<span class="add-on">
|
||||||
<i class="icon-key"></i>
|
<i class="icon-key"></i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</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: {
|
<div class="additionalCodeSignMeLabel" data-bind="component: {
|
||||||
name: 'CheckboxSimple',
|
name: 'CheckboxSimple',
|
||||||
params: {
|
params: {
|
||||||
|
|
@ -80,16 +87,24 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="controls clearfix" style="margin-bottom: 10px">
|
<div class="controls clearfix" style="margin-bottom: 10px">
|
||||||
<div class="pull-right social-buttons">
|
<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'">
|
<a href="#" tabindex="-1" class="social-button"
|
||||||
<i class="icon-facebook-alt"></i>
|
data-bind="visible: googleGmailLoginEnabled, command: googleGmailCommand, tooltip: 'LOGIN/TITLE_SIGN_IN_GMAIL'">
|
||||||
|
<i class="icon-mail"></i>
|
||||||
</a>
|
</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>
|
<i class="icon-google"></i>
|
||||||
</a>
|
</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>
|
<i class="icon-twitter"></i>
|
||||||
</a>
|
</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>
|
<i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -104,15 +119,20 @@
|
||||||
}"></div>
|
}"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="controls clearfix" data-bind="visible: '' !== forgotPasswordLinkUrl || '' !== registrationLinkUrl">
|
<div class="controls clearfix"
|
||||||
<div class="forgot-link thm-forgot pull-left" data-bind="visible: '' !== forgotPasswordLinkUrl" style="text-align: center">
|
data-bind="visible: '' !== forgotPasswordLinkUrl || '' !== registrationLinkUrl">
|
||||||
<a href="#" target="_blank" class="g-ui-link" data-bind="attr: {href: forgotPasswordLinkUrl}, css: {'pull-right': '' !== registrationLinkUrl}"
|
<div class="forgot-link thm-forgot pull-left" data-bind="visible: '' !== forgotPasswordLinkUrl"
|
||||||
><span class="i18n" data-i18n="LOGIN/LABEL_FORGOT_PASSWORD"></span></a>
|
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>
|
||||||
|
|
||||||
<div class="registration-link thm-registration pull-right" data-bind="visible: '' !== registrationLinkUrl" style="text-align: center">
|
<div class="registration-link thm-registration pull-right"
|
||||||
<a href="#" target="_blank" class="g-ui-link" data-bind="attr: {href: registrationLinkUrl}, css: {'pull-left': '' !== forgotPasswordLinkUrl}"
|
data-bind="visible: '' !== registrationLinkUrl" style="text-align: center">
|
||||||
><span class="i18n" data-i18n="LOGIN/LABEL_REGISTRATION"></span></a>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -133,11 +153,13 @@
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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">
|
<label class="flag-selector">
|
||||||
<i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i>
|
<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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{{INCLUDE/BottomFooter/PLACE}}
|
{{INCLUDE/BottomFooter/PLACE}}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<div class="b-login--welcome-content">
|
<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>
|
<i class="icon-mail iconsize50" style="cursor: pointer" data-bind="click: displayMainForm"></i>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
</div>
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue