Release commit (1.7)

This commit is contained in:
RainLoop Team 2014-11-15 00:23:46 +04:00
parent d5c68d1761
commit 7b97a9f85d
25 changed files with 118 additions and 74 deletions

View file

@ -52,6 +52,14 @@
require('Knoin/Knoin').showScreenPopup(require('View/Popup/Activate')); require('Knoin/Knoin').showScreenPopup(require('View/Popup/Activate'));
}; };
/**
* @returns {boolean}
*/
LicensingAdminSettings.prototype.licenseIsUnlim = function ()
{
return 1898625600 === this.licenseExpired();
};
/** /**
* @returns {string} * @returns {string}
*/ */
@ -62,7 +70,8 @@
oDate = moment.unix(iTime) oDate = moment.unix(iTime)
; ;
return iTime && 1898625600 === iTime ? 'Never' : (oDate.format('LL') + ' (' + oDate.from(moment()) + ')'); return this.licenseIsUnlim() ? 'Never' :
(iTime && (oDate.format('LL') + ' (' + oDate.from(moment()) + ')'));
}; };
module.exports = LicensingAdminSettings; module.exports = LicensingAdminSettings;

View file

@ -1,7 +1,7 @@
.b-admin-branding { .b-admin-branding {
.disabled-form { .disabled-form {
opacity: 0.4; opacity: 0.8;
} }
} }

View file

@ -28,7 +28,6 @@
this.adminManLoadingVisibility = Data.adminManLoadingVisibility; this.adminManLoadingVisibility = Data.adminManLoadingVisibility;
this.capa = !!Settings.settingsGet('PremType'); this.capa = !!Settings.settingsGet('PremType');
this.capa = false; // TODO
kn.constructorEnd(this); kn.constructorEnd(this);
} }

View file

@ -141,6 +141,11 @@
else if (oData.ErrorCode) else if (oData.ErrorCode)
{ {
this.submitRequest(false); this.submitRequest(false);
if (-1 < Utils.inArray(oData.ErrorCode, [Enums.Notification.InvalidInputArgument]))
{
oData.ErrorCode = Enums.Notification.AuthError;
}
this.submitError(Utils.getNotification(oData.ErrorCode)); this.submitError(Utils.getNotification(oData.ErrorCode));
if ('' === this.submitError()) if ('' === this.submitError())
@ -190,7 +195,7 @@
window.open(Links.socialFacebook(), 'Facebook', window.open(Links.socialFacebook(), 'Facebook',
'left=200,top=100,width=650,height=450,menubar=no,status=no,resizable=yes,scrollbars=yes'); 'left=200,top=100,width=650,height=450,menubar=no,status=no,resizable=yes,scrollbars=yes');
return true; return true;
}, function () { }, function () {

View file

@ -1,8 +1,8 @@
{ {
"name": "RainLoop", "name": "RainLoop",
"title": "RainLoop Webmail", "title": "RainLoop Webmail",
"version": "1.6.11", "version": "1.7.0",
"release": "192", "release": "200",
"description": "Simple, modern & fast web-based email client", "description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net", "homepage": "http://rainloop.net",
"main": "gulpfile.js", "main": "gulpfile.js",

View file

@ -19,7 +19,7 @@ class ChangePasswordExampleDriver implements \RainLoop\Providers\ChangePassword\
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* *
* @return bool * @return bool
*/ */
@ -30,7 +30,7 @@ class ChangePasswordExampleDriver implements \RainLoop\Providers\ChangePassword\
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param string $sPrevPassword * @param string $sPrevPassword
* @param string $sNewPassword * @param string $sNewPassword
* *

View file

@ -8,12 +8,12 @@ class ChangeSmtpEhloMessagePlugin extends \RainLoop\Plugins\AbstractPlugin
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param array $aSmtpCredentials * @param array $aSmtpCredentials
*/ */
public function FilterSmtpCredentials($oAccount, &$aSmtpCredentials) public function FilterSmtpCredentials($oAccount, &$aSmtpCredentials)
{ {
if ($oAccount instanceof \RainLoop\Account && \is_array($aSmtpCredentials)) if ($oAccount instanceof \RainLoop\Model\Account && \is_array($aSmtpCredentials))
{ {
// Default: // Default:
// $aSmtpCredentials['Ehlo'] = \MailSo\Smtp\SmtpClient::EhloHelper(); // $aSmtpCredentials['Ehlo'] = \MailSo\Smtp\SmtpClient::EhloHelper();

View file

@ -84,7 +84,7 @@ class CpanelChangePasswordDriver implements \RainLoop\Providers\ChangePassword\C
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* *
* @return bool * @return bool
*/ */
@ -95,7 +95,7 @@ class CpanelChangePasswordDriver implements \RainLoop\Providers\ChangePassword\C
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param string $sPrevPassword * @param string $sPrevPassword
* @param string $sNewPassword * @param string $sNewPassword
* *

View file

@ -40,7 +40,7 @@ class CustomSystemFoldersPlugin extends \RainLoop\Plugins\AbstractPlugin
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param array $aSystemTypes * @param array $aSystemTypes
*/ */
public function FilterFoldersSystemTypes($oAccount, &$aSystemTypes) public function FilterFoldersSystemTypes($oAccount, &$aSystemTypes)
@ -88,7 +88,7 @@ class CustomSystemFoldersPlugin extends \RainLoop\Plugins\AbstractPlugin
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param array $aSystemFolderNames * @param array $aSystemFolderNames
*/ */
public function FilterSystemFoldersNames($oAccount, &$aSystemFolderNames) public function FilterSystemFoldersNames($oAccount, &$aSystemFolderNames)
@ -110,7 +110,7 @@ class CustomSystemFoldersPlugin extends \RainLoop\Plugins\AbstractPlugin
$this->helperFolderType($this->sArchiveFolder, \MailSo\Imap\Enumerations\FolderType::ALL, $this->helperFolderType($this->sArchiveFolder, \MailSo\Imap\Enumerations\FolderType::ALL,
$aPrepend, $aSystemFolderNames); $aPrepend, $aSystemFolderNames);
if (0 < \count($aPrepend)) if (0 < \count($aPrepend))
{ {
$aSystemFolderNames = \array_merge($aPrepend, $aSystemFolderNames); $aSystemFolderNames = \array_merge($aPrepend, $aSystemFolderNames);

View file

@ -63,7 +63,7 @@ class HmailserverChangePasswordDriver implements \RainLoop\Providers\ChangePassw
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* *
* @return bool * @return bool
*/ */
@ -74,7 +74,7 @@ class HmailserverChangePasswordDriver implements \RainLoop\Providers\ChangePassw
} }
/** /**
* @param \RainLoop\Account $oHmailAccount * @param \RainLoop\Model\Account $oHmailAccount
* @param string $sPrevPassword * @param string $sPrevPassword
* @param string $sNewPassword * @param string $sNewPassword
* *

View file

@ -70,7 +70,7 @@ class IspConfigChangePasswordDriver implements \RainLoop\Providers\ChangePasswor
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* *
* @return bool * @return bool
*/ */
@ -81,7 +81,7 @@ class IspConfigChangePasswordDriver implements \RainLoop\Providers\ChangePasswor
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param string $sPrevPassword * @param string $sPrevPassword
* @param string $sNewPassword * @param string $sNewPassword
* *

View file

@ -8,12 +8,12 @@ class OverrideSmtpCredentialsPlugin extends \RainLoop\Plugins\AbstractPlugin
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param array $aSmtpCredentials * @param array $aSmtpCredentials
*/ */
public function FilterSmtpCredentials($oAccount, &$aSmtpCredentials) public function FilterSmtpCredentials($oAccount, &$aSmtpCredentials)
{ {
if ($oAccount instanceof \RainLoop\Account && \is_array($aSmtpCredentials)) if ($oAccount instanceof \RainLoop\Model\Account && \is_array($aSmtpCredentials))
{ {
$sEmail = $oAccount->Email(); $sEmail = $oAccount->Email();

View file

@ -71,7 +71,7 @@ class ChangePasswordPoppassdDriver implements \RainLoop\Providers\ChangePassword
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* *
* @return bool * @return bool
*/ */
@ -82,7 +82,7 @@ class ChangePasswordPoppassdDriver implements \RainLoop\Providers\ChangePassword
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param string $sPrevPassword * @param string $sPrevPassword
* @param string $sNewPassword * @param string $sNewPassword
* *

View file

@ -135,7 +135,7 @@ class ChangePasswordPostfixAdminDriver implements \RainLoop\Providers\ChangePass
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* *
* @return bool * @return bool
*/ */
@ -146,7 +146,7 @@ class ChangePasswordPostfixAdminDriver implements \RainLoop\Providers\ChangePass
} }
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param string $sPrevPassword * @param string $sPrevPassword
* @param string $sNewPassword * @param string $sNewPassword
* *

View file

@ -8,11 +8,20 @@ class ProxyauthLoginExamplePlugin extends \RainLoop\Plugins\AbstractPlugin
} }
/** /**
* @param \RainLoop\Account $oAccount * @param string $sLogin
* @param string $sPassword
*/
public function isValidAccount($sLogin, $sPassword)
{
return !empty($sLogin) && !empty($sPassword);
}
/**
* @param \RainLoop\Model\Account $oAccount
*/ */
public function EventLoginPostLoginProvide(&$oAccount) public function EventLoginPostLoginProvide(&$oAccount)
{ {
if ($oAccount instanceof \RainLoop\Account) if ($oAccount instanceof \RainLoop\Model\Account)
{ {
// Verify logic // Verify logic
$bValid = isValidAccount($oAccount->Login(), $oAccount->Password()); $bValid = isValidAccount($oAccount->Login(), $oAccount->Password());
@ -23,10 +32,10 @@ class ProxyauthLoginExamplePlugin extends \RainLoop\Plugins\AbstractPlugin
* $oAccount->Password(); // IMAP password * $oAccount->Password(); // IMAP password
* $oAccount->DomainIncHost(); // IMAP host * $oAccount->DomainIncHost(); // IMAP host
* *
* @see \RainLoo\Account for more * @see \RainLoo\Model\Account for more
*/ */
if ($bValid) // if verify failed if (!$bValid) // if verify failed
{ {
// throw a Auth Error Exception // throw a Auth Error Exception
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AuthError); throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AuthError);

View file

@ -587,10 +587,10 @@ class Http
if ($oLogger) if ($oLogger)
{ {
$oLogger->Write('cUrl: URL: '.$sUrl); $oLogger->Write('cUrl: URL: '.$sUrl);
if (isset($aOptions[CURLOPT_HTTPHEADER]) && \is_array($aOptions[CURLOPT_HTTPHEADER]) && 0 < \count($aOptions[CURLOPT_HTTPHEADER])) // if (isset($aOptions[CURLOPT_HTTPHEADER]) && \is_array($aOptions[CURLOPT_HTTPHEADER]) && 0 < \count($aOptions[CURLOPT_HTTPHEADER]))
{ // {
$oLogger->Write('cUrl: Headers: '.\print_r($aOptions[CURLOPT_HTTPHEADER], true)); // $oLogger->Write('cUrl: Headers: '.\print_r($aOptions[CURLOPT_HTTPHEADER], true));
} // }
} }
\MailSo\Base\Http::DetectAndHackFollowLocationUrl($sUrl, $aOptions, $oLogger); \MailSo\Base\Http::DetectAndHackFollowLocationUrl($sUrl, $aOptions, $oLogger);

View file

@ -986,8 +986,7 @@ class Actions
static $bResult = null; static $bResult = null;
if (null === $bResult) if (null === $bResult)
{ {
// $bResult = $this->licenseParser($this->licenseHelper(false, true)); $bResult = $this->licenseParser($this->licenseHelper(false, true));
$bResult = true;
} }
return $bResult; return $bResult;
@ -2645,7 +2644,7 @@ class Actions
/** /**
* @return string * @return string
*/ */
public function licenseHelper($sForce = false, $bLongCache = false, $iFastCacheTimeInMin = 5, $iLongCacheTimeInDays = 2) public function licenseHelper($sForce = false, $bLongCache = false, $iFastCacheTimeInMin = 10, $iLongCacheTimeInDays = 3)
{ {
$sDomain = APP_SITE; $sDomain = APP_SITE;
@ -2713,16 +2712,17 @@ class Actions
/** /**
* @param string $sInput * @param string $sInput
* @param int $iExpired = 0
* *
* @return bool * @return bool
*/ */
public function licenseParser($sInput) public function licenseParser($sInput, &$iExpired = 0)
{ {
$aMatch = array(); $aMatch = array();
if (\preg_match('/^EXPIRED:([\d]+)$/', $sInput, $aMatch)) if (\preg_match('/^EXPIRED:([\d]+)$/', $sInput, $aMatch))
{ {
$iTime = (int) $aMatch[1]; $iExpired = (int) $aMatch[1];
return \time() < $iTime; return \time() < $iExpired;
} }
return false; return false;
@ -2750,15 +2750,15 @@ class Actions
\sleep(1); \sleep(1);
} }
$aMatch = array(); $iExpired = 0;
if (\preg_match('/^EXPIRED:([\d]+)$/', $sValue, $aMatch)) if ($this->licenseParser($sValue, $iExpired))
{ {
$mResult = array( $mResult = array(
'Banned' => false, 'Banned' => false,
'Expired' => (int) $aMatch[1], 'Expired' => $iExpired,
); );
} }
else if ($sValue === 'NO') else if ($sValue === 'NO' || \preg_match('/^EXPIRED:[\d]+$/', $sValue))
{ {
$iErrorCode = -1; $iErrorCode = -1;
} }

View file

@ -34,24 +34,24 @@ class ChangePassword extends \RainLoop\Providers\AbstractProvider
} }
/** /**
* @param \RainLoop\Model\Account $oAccount * @param \RainLoop\Account $oAccount
* *
* @return bool * @return bool
*/ */
public function PasswordChangePossibility($oAccount) public function PasswordChangePossibility($oAccount)
{ {
return $this->IsActive() && return $this->IsActive() &&
$oAccount instanceof \RainLoop\Model\Account && $oAccount instanceof \RainLoop\Account &&
$this->oDriver && $this->oDriver->PasswordChangePossibility($oAccount) $this->oDriver && $this->oDriver->PasswordChangePossibility($oAccount)
; ;
} }
/** /**
* @param \RainLoop\Model\Account $oAccount * @param \RainLoop\Account $oAccount
* @param string $sPrevPassword * @param string $sPrevPassword
* @param string $sNewPassword * @param string $sNewPassword
*/ */
public function ChangePassword(\RainLoop\Model\Account $oAccount, $sPrevPassword, $sNewPassword) public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword)
{ {
if ($this->oDriver instanceof \RainLoop\Providers\ChangePassword\ChangePasswordInterface && if ($this->oDriver instanceof \RainLoop\Providers\ChangePassword\ChangePasswordInterface &&
$this->PasswordChangePossibility($oAccount)) $this->PasswordChangePossibility($oAccount))

View file

@ -5,18 +5,18 @@ namespace RainLoop\Providers\ChangePassword;
interface ChangePasswordInterface interface ChangePasswordInterface
{ {
/** /**
* @param \RainLoop\Model\Account $oAccount * @param \RainLoop\Account $oAccount
* *
* @return bool * @return bool
*/ */
public function PasswordChangePossibility($oAccount); public function PasswordChangePossibility($oAccount);
/** /**
* @param \RainLoop\Model\Account $oAccount * @param \RainLoop\Account $oAccount
* @param string $sPrevPassword * @param string $sPrevPassword
* @param string $sNewPassword * @param string $sNewPassword
* *
* @return bool * @return bool
*/ */
public function ChangePassword(\RainLoop\Model\Account $oAccount, $sPrevPassword, $sNewPassword); public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword);
} }

View file

@ -20,12 +20,12 @@ class Suggestions extends \RainLoop\Providers\AbstractProvider
} }
/** /**
* @param \RainLoop\Model\Account $oAccount * @param \RainLoop\Account $oAccount
* @param string $sQuery * @param string $sQuery
* *
* @return array * @return array
*/ */
public function Process(\RainLoop\Model\Account $oAccount, $sQuery) public function Process(\RainLoop\Account $oAccount, $sQuery)
{ {
return $this->oDriver && $this->IsActive() && 0 < \strlen($sQuery) ? $this->oDriver->Process($oAccount, $sQuery) : array(); return $this->oDriver && $this->IsActive() && 0 < \strlen($sQuery) ? $this->oDriver->Process($oAccount, $sQuery) : array();
} }

View file

@ -33,9 +33,9 @@
</button> </button>
</div> </div>
</form> </form>
<!-- <div class="e-powered thm-powered" data-bind="visible: logoPowered"> <div class="e-powered thm-powered" data-bind="visible: logoPowered">
Powered by <a href="http://rainloop.net" target="_blank">RainLoop</a> Powered by <a href="http://rainloop.net" target="_blank">RainLoop</a>
</div>--> </div>
</center> </center>
</div> </div>
<div class="loginAfter"></div> <div class="loginAfter"></div>

View file

@ -1,7 +1,7 @@
<div class="b-admin-branding"> <div class="b-admin-branding">
<div class="row" data-bind="visible: !capa"> <div class="row" data-bind="visible: !capa">
<div class="alert span8" style="margin-top: 10px;"> <div class="alert span8" style="margin-top: 10px;">
Full functionality available in <strong><a href="#/licensing">Premium version</a></strong> only. This functionality is available for <strong><a href="#/licensing">Premium</a></strong> subscribers.
</div> </div>
</div> </div>
<div class="form-horizontal" data-bind="css: {'disabled-form': !capa}"> <div class="form-horizontal" data-bind="css: {'disabled-form': !capa}">
@ -77,17 +77,18 @@
}"></div> }"></div>
</div> </div>
</div> </div>
<!-- <div class="control-group"> <div class="control-group">
<div class="controls"> <div class="controls">
<div data-bind="component: { <div data-bind="component: {
name: 'Checkbox', name: 'Checkbox',
params: { params: {
label: 'Show &quot;Powered by RainLoop&quot; link', enable: capa,
label: 'Show &quot;Powered by RainLoop&quot; link (White Labeling)',
value: loginPowered value: loginPowered
} }
}"></div> }"></div>
</div> </div>
</div>--> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label"> <label class="control-label">
Custom CSS Custom CSS

View file

@ -6,17 +6,18 @@
Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)</strong> license. Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)</strong> license.
<br /> <br />
<br /> <br />
You are <strong>free</strong> to use it for your <strong>personal</strong> or <strong>non-profit</strong> projects. You are <strong>free</strong> to use it for your <strong>personal</strong> projects.
<br /> <br />
<br /> <br />
Commercial use of <strong>RainLoop Webmail</strong> requires getting a Commercial use (with additional features) of <strong>RainLoop Webmail</strong> requires getting a
<a href="http://rainloop.net/purchase/" target="_blank">permission</a>. <a href="http://rainloop.net/purchase/" target="_blank">subscription</a>.
</div> </div>
</div> </div>
<br />
<div class="form-horizontal"> <div class="form-horizontal">
<div class="control-group"> <div class="control-group">
<label class="control-label"> <label class="control-label">
Your Domain Your domain
</label> </label>
<div class="controls"> <div class="controls">
<span class="help-inline" style="padding-top: 5px;"> <span class="help-inline" style="padding-top: 5px;">
@ -25,29 +26,49 @@
</div> </div>
</div> </div>
<div class="control-group" data-bind="visible: !subscriptionEnabled()"> <div class="control-group" data-bind="visible: !subscriptionEnabled()">
<label class="control-label" style="padding-top: 20px">
Version
</label>
<div class="controls"> <div class="controls">
<div class="alert alert-block span8" style="margin-left: 0"> <div class="alert alert-block span8" style="margin-left: 0">
This domain can't be licensed for commercial use. <h4>
Basic
<span data-bind="visible: licenseIsUnlim()">&nbsp;(Unlim)</span>
</h4>
<br />
<p>
This domain can't be licensed.
</p>
</div> </div>
</div> </div>
</div> </div>
<div class="control-group" data-bind="visible: subscriptionEnabled"> <div class="control-group" data-bind="visible: subscriptionEnabled">
<label class="control-label" style="padding-top: 20px">
Version
</label>
<div class="controls"> <div class="controls">
<div data-bind="visible: licensingProcess()"> <div data-bind="visible: licensingProcess()" style="padding-top: 20px">
<i class="icon-spinner animated"></i> <i class="icon-spinner animated"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
Checking&hellip; Checking&hellip;
</div> </div>
<div data-bind="visible: !licensingProcess()"> <div data-bind="visible: !licensingProcess()">
<div class="alert alert-block span8" style="margin-left: 0" data-bind="visible: !licensing()"> <div class="alert alert-block span8" style="margin-left: 0; padding-top: 20px" data-bind="visible: !licensing()">
This domain isn't licensed for commercial use. <h4>
Basic
</h4>
<br />
<p>
This domain isn't licensed for commercial use (with additional features).
</p>
</div> </div>
<div data-bind="visible: licensing()"> <div data-bind="visible: licensing()">
<div class="alert alert-success span8" style="margin-left: 0" data-bind="visible: licenseValid() && '' === licenseError()"> <div class="alert alert-success span8" style="margin-left: 0; padding-top: 20px" data-bind="visible: licenseValid() && '' === licenseError()">
<p> <h4>
This domain is licensed for commercial use <span data-bind="visible: licenseIsUnlim()">Lifetime (Premium)</span>
</p> <span data-bind="visible: !licenseIsUnlim()">Premium</span>
<p data-bind="visible: 0 < licenseExpired()"> </h4>
<p data-bind="visible: 0 < licenseExpired()" style="padding-top: 10px">
<b>Subscription expires:</b> <b>Subscription expires:</b>
&nbsp; &nbsp;
<span data-bind="text: licenseExpiredMomentValue()"></span> <span data-bind="text: licenseExpiredMomentValue()"></span>

View file

@ -34,7 +34,7 @@
<br /> <br />
<div class="alert"> <div class="alert">
<p> <p>
After activation, commercial subscription for <strong data-bind="text: domain"></strong> will be extended. After activation, premium subscription for <strong data-bind="text: domain"></strong> will be extended.
<br /> <br />
Note that subscription key can be activated for a single domain only. Note that subscription key can be activated for a single domain only.
</p> </p>

View file

@ -84,9 +84,9 @@
</div> </div>
</div> </div>
</form> </form>
<!-- <div class="e-powered thm-powered" data-bind="visible: logoPowered"> <div class="e-powered thm-powered" data-bind="visible: logoPowered">
Powered by <a href="http://rainloop.net" target="_blank">RainLoop</a> Powered by <a href="http://rainloop.net" target="_blank">RainLoop</a>
</div>--> </div>
<div class="e-languages thm-languages" data-bind="visible: allowLanguagesOnLogin() && socialLoginEnabled()"> <div class="e-languages thm-languages" 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>