Added counters of unread messages for additional accounts (#377)

This commit is contained in:
RainLoop Team 2015-02-02 00:46:23 +04:00
parent abddb3d828
commit 79233ad83c
19 changed files with 333 additions and 93 deletions

View file

@ -494,15 +494,23 @@
AppUser.prototype.accountsCounts = function () AppUser.prototype.accountsCounts = function ()
{ {
AccountStore.accounts.loading(true);
Remote.accountsCounts(function (sResult, oData) { Remote.accountsCounts(function (sResult, oData) {
AccountStore.accounts.loading(false);
if (Enums.StorageResultType.Success === sResult && oData.Result && oData.Result['Counts']) if (Enums.StorageResultType.Success === sResult && oData.Result && oData.Result['Counts'])
{ {
var aAcounts = AccountStore.collection(); var
sEmail = Data.accountEmail(),
aAcounts = AccountStore.accounts()
;
_.each(oData.Result['Counts'], function (oItem) { _.each(oData.Result['Counts'], function (oItem) {
var oAccount = _.find(aAcounts, function (oAccount) { var oAccount = _.find(aAcounts, function (oAccount) {
return oAccount && oItem[0] === oAccount.email; return oAccount && oItem[0] === oAccount.email && sEmail !== oAccount.email;
}); });
if (oAccount) if (oAccount)
@ -518,12 +526,12 @@
{ {
var self = this; var self = this;
AccountStore.loading(true); AccountStore.accounts.loading(true);
IdentityStore.identities.loading(true); IdentityStore.identities.loading(true);
Remote.accountsAndIdentities(function (sResult, oData) { Remote.accountsAndIdentities(function (sResult, oData) {
AccountStore.loading(false); AccountStore.accounts.loading(false);
IdentityStore.identities.loading(false); IdentityStore.identities.loading(false);
if (Enums.StorageResultType.Success === sResult && oData.Result) if (Enums.StorageResultType.Success === sResult && oData.Result)
@ -538,20 +546,26 @@
if (Utils.isArray(oData.Result['Accounts'])) if (Utils.isArray(oData.Result['Accounts']))
{ {
_.each(AccountStore.collection(), function (oAccount) { _.each(AccountStore.accounts(), function (oAccount) {
aCounts[oAccount.email] = oAccount.count(); aCounts[oAccount.email] = oAccount.count();
}); });
Utils.delegateRunOnDestroy(AccountStore.collection()); Utils.delegateRunOnDestroy(AccountStore.accounts());
AccountStore.collection(_.map(oData.Result['Accounts'], function (sValue) { AccountStore.accounts(_.map(oData.Result['Accounts'], function (sValue) {
return new AccountModel(sValue, sValue !== sParentEmail, aCounts[sValue] || 0); return new AccountModel(sValue, sValue !== sParentEmail, aCounts[sValue] || 0);
})); }));
} }
if (Utils.isUnd(bBoot) ? false : !!bBoot) if (Utils.isUnd(bBoot) ? false : !!bBoot)
{ {
_.delay(function () {
self.accountsCounts(); self.accountsCounts();
}, 1000 * 5);
Events.sub('interval.10m-after5m', function () {
self.accountsCounts();
});
} }
if (Utils.isArray(oData.Result['Identities'])) if (Utils.isArray(oData.Result['Identities']))

View file

@ -47,9 +47,12 @@
if (0 < aItems.length) if (0 < aItems.length)
{ {
if (null === this.selectedItem()) if (null === this.selectedItem())
{
if (this.selectedItem.valueHasMutated)
{ {
this.selectedItem.valueHasMutated(); this.selectedItem.valueHasMutated();
} }
}
else else
{ {
this.selectedItem(null); this.selectedItem(null);

2
dev/External/ko.js vendored
View file

@ -828,7 +828,7 @@
oTarget(''); oTarget('');
} }
} }
}) }).extend({'notify': 'always'})
; ;
oResult(oTarget()); oResult(oTarget());

View file

@ -127,7 +127,7 @@
} }
}, },
'owner': this 'owner': this
}); }).extend({'notify': 'always'});
this.messageCountUnread = ko.computed({ this.messageCountUnread = ko.computed({
'read': this.privateMessageCountUnread, 'read': this.privateMessageCountUnread,
@ -142,7 +142,7 @@
} }
}, },
'owner': this 'owner': this
}); }).extend({'notify': 'always'});
this.printableUnreadCount = ko.computed(function () { this.printableUnreadCount = ko.computed(function () {
var var

View file

@ -12,6 +12,7 @@
Events = require('Common/Events'), Events = require('Common/Events'),
Translator = require('Common/Translator'), Translator = require('Common/Translator'),
AccountStore = require('Stores/User/Account'),
SettingsStore = require('Stores/User/Settings'), SettingsStore = require('Stores/User/Settings'),
Data = require('Storage/User/Data'), Data = require('Storage/User/Data'),
@ -111,8 +112,18 @@
SettingsStore.layout.valueHasMutated(); SettingsStore.layout.valueHasMutated();
}, 50); }, 50);
Events.sub('mailbox.inbox-unread-count', function (nCount) { Events.sub('mailbox.inbox-unread-count', function (iCount) {
Data.foldersInboxUnreadCount(nCount);
Data.foldersInboxUnreadCount(iCount);
var sEmail = Data.accountEmail();
_.each(AccountStore.accounts(), function (oItem) {
if (oItem && sEmail === oItem.email)
{
oItem.count(iCount);
}
});
}); });
Data.foldersInboxUnreadCount.subscribe(function () { Data.foldersInboxUnreadCount.subscribe(function () {

View file

@ -96,7 +96,7 @@
this.contactsType.valueHasMutated(); this.contactsType.valueHasMutated();
} }
} }
}); }).extend({'notify': 'always'});
this.contactsType.subscribe(function () { this.contactsType.subscribe(function () {
this.testContactsSuccess(false); this.testContactsSuccess(false);

View file

@ -22,10 +22,10 @@
*/ */
function AccountsUserSettings() function AccountsUserSettings()
{ {
this.accounts = AccountStore.collection; this.accounts = AccountStore.accounts;
this.processText = ko.computed(function () { this.processText = ko.computed(function () {
return AccountStore.loading() ? Translator.i18n('SETTINGS_ACCOUNTS/LOADING_PROCESS') : ''; return AccountStore.accounts.loading() ? Translator.i18n('SETTINGS_ACCOUNTS/LOADING_PROCESS') : '';
}, this); }, this);
this.visibility = ko.computed(function () { this.visibility = ko.computed(function () {

View file

@ -238,8 +238,7 @@
*/ */
RemoteUserStorage.prototype.accountsCounts = function (fCallback) RemoteUserStorage.prototype.accountsCounts = function (fCallback)
{ {
return !!fCallback; // TODO this.defaultRequest(fCallback, 'AccountsCounts');
// this.defaultRequest(fCallback, 'AccountsCounts');
}; };
/** /**

View file

@ -15,28 +15,43 @@
*/ */
function AccountUserStore() function AccountUserStore()
{ {
this.loading = ko.observable(false).extend({'throttle': 100}); this.accounts = ko.observableArray([]);
this.accounts.loading = ko.observable(false).extend({'throttle': 100});
this.collection = ko.observableArray([]); this.accountsEmailNames = ko.observableArray([]).extend({'throttle': 1000});
this.collectionEmailNames = ko.observableArray([]).extend({'throttle': 1000}); this.accountsEmailNames.skipFirst = true;
this.collectionEmailNames.skipFirst = true;
this.collection.subscribe(function (aList) { this.accounts.subscribe(function (aList) {
this.collectionEmailNames(_.compact(_.map(aList, function (oItem) { this.accountsEmailNames(_.compact(_.map(aList, function (oItem) {
return oItem ? oItem.email : null; return oItem ? oItem.email : null;
}))); })));
}, this); }, this);
this.collectionEmailNames.subscribe(function (aList) { this.accountsEmailNames.subscribe(function (aList) {
if (this.collectionEmailNames.skipFirst) if (this.accountsEmailNames.skipFirst)
{ {
this.collectionEmailNames.skipFirst = false; this.accountsEmailNames.skipFirst = false;
} }
else if (aList && 1 < aList.length) else if (aList && 1 < aList.length)
{ {
Remote.accountSortOrder(null, aList); Remote.accountSortOrder(null, aList);
} }
}, this); }, this);
this.accountsUnreadCount = ko.computed(function () {
var iResult = 0;
_.each(this.accounts(), function (oItem) {
if (oItem)
{
iResult += oItem.count();
}
});
return iResult;
}, this);
} }
module.exports = new AccountUserStore(); module.exports = new AccountUserStore();

View file

@ -59,7 +59,7 @@
return iResult; return iResult;
}, this); }, this).extend({'notify': 'always'});
this.enableDesktopNotification = ko.computed({ this.enableDesktopNotification = ko.computed({
'owner': this, 'owner': this,
@ -117,7 +117,7 @@
this.allowDesktopNotification(false); this.allowDesktopNotification(false);
} }
} }
}); }).extend({'notify': 'always'});
if (!this.enableDesktopNotification.valueHasMutated) if (!this.enableDesktopNotification.valueHasMutated)
{ {
@ -150,7 +150,8 @@
this.soundNotificationIsSupported(true); this.soundNotificationIsSupported(true);
this.buzz = new buzz.sound(Links.sound('new-mail'), { this.buzz = new buzz.sound(Links.sound('new-mail'), {
formats: ['ogg', 'mp3'] 'preload': 'none',
'formats': ['mp3', 'ogg']
}); });
} }
else else

View file

@ -41,9 +41,6 @@
color: #fff; color: #fff;
text-shadow: 0 1px 0 #000; text-shadow: 0 1px 0 #000;
position: absolute;
top: 11px;
right: 70px;
display: inline-block; display: inline-block;
height: 28px; height: 28px;
max-width: 250px; max-width: 250px;
@ -55,6 +52,7 @@
text-overflow: ellipsis; text-overflow: ellipsis;
border-radius: 4px; border-radius: 4px;
font-weight: bold; font-weight: bold;
margin-right: 5px;
white-space: nowrap; white-space: nowrap;
} }

View file

@ -29,18 +29,14 @@
{ {
AbstractView.call(this, 'Right', 'SystemDropDown'); AbstractView.call(this, 'Right', 'SystemDropDown');
this.accounts = AccountStore.collection;
this.accountEmail = Data.accountEmail; this.accountEmail = Data.accountEmail;
this.accountsLoading = AccountStore.loading;
this.accounts = AccountStore.accounts;
this.accountsUnreadCount = AccountStore.accountsUnreadCount;
this.accountMenuDropdownTrigger = ko.observable(false); this.accountMenuDropdownTrigger = ko.observable(false);
this.capaAdditionalAccounts = ko.observable(Settings.capa(Enums.Capa.AdditionalAccounts)); this.capaAdditionalAccounts = ko.observable(Settings.capa(Enums.Capa.AdditionalAccounts));
this.loading = ko.computed(function () {
return this.accountsLoading();
}, this);
this.accountClick = _.bind(this.accountClick, this); this.accountClick = _.bind(this.accountClick, this);
} }
@ -50,10 +46,10 @@
{ {
if (oAccount && oEvent && !Utils.isUnd(oEvent.which) && 1 === oEvent.which) if (oAccount && oEvent && !Utils.isUnd(oEvent.which) && 1 === oEvent.which)
{ {
var self = this; AccountStore.accounts.loading(true);
this.accountsLoading(true);
_.delay(function () { _.delay(function () {
self.accountsLoading(false); AccountStore.accounts.loading(false);
}, 1000); }, 1000);
} }

View file

@ -2,7 +2,7 @@
"name": "RainLoop", "name": "RainLoop",
"title": "RainLoop Webmail", "title": "RainLoop Webmail",
"version": "1.7.3", "version": "1.7.3",
"release": "241", "release": "243",
"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

@ -337,11 +337,11 @@ class Http
} }
/** /**
* @param bool $bCheckProxy = true * @param bool $bCheckProxy = false
* *
* @return string * @return string
*/ */
public function GetClientIp($bCheckProxy = true) public function GetClientIp($bCheckProxy = false)
{ {
$sIp = ''; $sIp = '';
if ($bCheckProxy && null !== $this->GetServer('HTTP_CLIENT_IP', null)) if ($bCheckProxy && null !== $this->GetServer('HTTP_CLIENT_IP', null))

View file

@ -44,8 +44,10 @@ class Logger extends \MailSo\Base\Collection
/** /**
* @access protected * @access protected
*
* @param bool $bRegPhpErrorHandler = false
*/ */
protected function __construct() protected function __construct($bRegPhpErrorHandler = true)
{ {
parent::__construct(); parent::__construct();
@ -55,16 +57,22 @@ class Logger extends \MailSo\Base\Collection
$this->bShowSecter = false; $this->bShowSecter = false;
$this->bHideErrorNotices = false; $this->bHideErrorNotices = false;
if ($bRegPhpErrorHandler)
{
\set_error_handler(array(&$this, '__phpErrorHandler')); \set_error_handler(array(&$this, '__phpErrorHandler'));
}
\register_shutdown_function(array(&$this, '__loggerShutDown')); \register_shutdown_function(array(&$this, '__loggerShutDown'));
} }
/** /**
* @param bool $bRegPhpErrorHandler = false
*
* @return \MailSo\Log\Logger * @return \MailSo\Log\Logger
*/ */
public static function NewInstance() public static function NewInstance($bRegPhpErrorHandler = false)
{ {
return new self(); return new self($bRegPhpErrorHandler);
} }
/** /**

View file

@ -869,6 +869,24 @@ class MailClient
return self::GenerateHash($sFolderName, $iCount, $iUnseenCount, $sUidNext); return self::GenerateHash($sFolderName, $iCount, $iUnseenCount, $sUidNext);
} }
/**
* @return int
*
* @throws \MailSo\Net\Exceptions\Exception
* @throws \MailSo\Imap\Exceptions\Exception
*/
public function InboxUnreadCount()
{
$aFolderStatus = $this->oImapClient->FolderStatus('INBOX', array(
\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN
));
$iResult = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN]) ?
(int) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN] : 0;
return 0 < $iResult ? $iResult : 0;
}
/** /**
* @param string $sSearch * @param string $sSearch
* @param bool $bDetectGmail = true * @param bool $bDetectGmail = true

View file

@ -40,6 +40,11 @@ class Actions
*/ */
private $oLogger; private $oLogger;
/**
* @var \MailSo\Log\Logger
*/
private $oLoggerAuth;
/** /**
* @var \RainLoop\Social * @var \RainLoop\Social
*/ */
@ -359,60 +364,119 @@ class Actions
} }
/** /**
* @param string $sLine
* @param \RainLoop\Model\Account $oAccount = null
*
* @return string * @return string
*/ */
private function compileLogFileName() private function compileLogParams($sLine, $oAccount = null)
{ {
$sFileName = (string) $this->Config()->Get('logs', 'filename', ''); if (false !== \strpos($sLine, '{date:'))
if (false !== \strpos($sFileName, '{date:'))
{ {
$sFileName = \preg_replace_callback('/\{date:([^}]+)\}/', function ($aMatch) { $sLine = \preg_replace_callback('/\{date:([^}]+)\}/', function ($aMatch) {
return \gmdate($aMatch[1]); return \gmdate($aMatch[1]);
}, $sFileName); }, $sLine);
$sFileName = \preg_replace('/\{date:([^}]*)\}/', 'date', $sFileName); $sLine = \preg_replace('/\{date:([^}]*)\}/', 'date', $sLine);
} }
if (false !== \strpos($sFileName, '{user:')) if (false !== \strpos($sLine, '{imap:') || false !== \strpos($sLine, '{smtp:'))
{ {
if (false !== \strpos($sFileName, '{user:uid}')) if (!$oAccount)
{ {
$sFileName = \str_replace('{user:uid}', $this->ParseQueryAuthString();
$oAccount = $this->getAccountFromToken(false);
}
if ($oAccount)
{
$sLine = \str_replace('{imap:login}', $oAccount->IncLogin(), $sLine);
$sLine = \str_replace('{imap:host}', $oAccount->DomainIncHost(), $sLine);
$sLine = \str_replace('{imap:port}', $oAccount->DomainIncPort(), $sLine);
$sLine = \str_replace('{smtp:login}', $oAccount->OutLogin(), $sLine);
$sLine = \str_replace('{smtp:host}', $oAccount->DomainOutHost(), $sLine);
$sLine = \str_replace('{smtp:port}', $oAccount->DomainOutPort(), $sLine);
}
$sLine = \preg_replace('/\{imap:([^}]*)\}/i', 'imap', $sLine);
$sLine = \preg_replace('/\{smtp:([^}]*)\}/i', 'imap', $sLine);
}
if (false !== \strpos($sLine, '{request:'))
{
if (false !== \strpos($sLine, '{request:ip}'))
{
$sLine = \str_replace('{request:ip}', $this->Http()->GetClientIp(
$this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $sLine);
}
$sLine = \preg_replace('/\{request:([^}]*)\}/i', 'request', $sLine);
}
if (false !== \strpos($sLine, '{user:'))
{
if (false !== \strpos($sLine, '{user:uid}'))
{
$sLine = \str_replace('{user:uid}',
\base_convert(\sprintf('%u', \crc32(\md5(\RainLoop\Utils::GetConnectionToken()))), 10, 32), \base_convert(\sprintf('%u', \crc32(\md5(\RainLoop\Utils::GetConnectionToken()))), 10, 32),
$sFileName $sLine
); );
} }
if (false !== \strpos($sFileName, '{user:ip}')) if (false !== \strpos($sLine, '{user:ip}'))
{ {
$sFileName = \str_replace('{user:ip}', $this->Http()->GetClientIp(), $sFileName); $sLine = \str_replace('{user:ip}', $this->Http()->GetClientIp(
$this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $sLine);
} }
if (\preg_match('/\{user:(email|login|domain)\}/i', $sFileName)) if (\preg_match('/\{user:(email|login|domain)\}/i', $sLine))
{
if (!$oAccount)
{ {
$this->ParseQueryAuthString(); $this->ParseQueryAuthString();
$oAccount = $this->getAccountFromToken(false); $oAccount = $this->getAccountFromToken(false);
}
if ($oAccount) if ($oAccount)
{ {
$sEmail = $oAccount->Email(); $sEmail = $oAccount->Email();
$sFileName = \str_replace('{user:email}', $sEmail, $sFileName); $sLine = \str_replace('{user:email}', $sEmail, $sLine);
$sFileName = \str_replace('{user:login}', \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sFileName); $sLine = \str_replace('{user:login}', \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sLine);
$sFileName = \str_replace('{user:domain}', \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sFileName); $sLine = \str_replace('{user:domain}', \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sLine);
} }
} }
$sFileName = \preg_replace('/\{user:([^}]*)\}/i', 'unknown', $sFileName); $sLine = \preg_replace('/\{user:([^}]*)\}/i', 'unknown', $sLine);
} }
if (false !== \strpos($sFileName, '{labs:')) if (false !== \strpos($sLine, '{labs:'))
{ {
$sFileName = \preg_replace_callback('/\{labs:rand:([1-9])\}/', function ($aMatch) { $sLine = \preg_replace_callback('/\{labs:rand:([1-9])\}/', function ($aMatch) {
return \rand(\pow(10, $aMatch[1] - 1), \pow(10, $aMatch[1]) - 1); return \rand(\pow(10, $aMatch[1] - 1), \pow(10, $aMatch[1]) - 1);
}, $sFileName); }, $sLine);
$sFileName = \preg_replace('/\{labs:([^}]*)\}/', 'labs', $sFileName); $sLine = \preg_replace('/\{labs:([^}]*)\}/', 'labs', $sLine);
}
return $sLine;
}
/**
* @param string $sFileName
*
* @return string
*/
private function compileLogFileName($sFileName)
{
$sFileName = \trim($sFileName);
if (0 !== \strlen($sFileName))
{
$sFileName = $this->compileLogParams($sFileName);
$sFileName = \preg_replace('/[\/]+/', '/', \preg_replace('/[.]+/', '.', $sFileName));
$sFileName = \preg_replace('/[^a-zA-Z0-9@_+=\-\.\/!()\[\]]/', '', $sFileName);
} }
if (0 === \strlen($sFileName)) if (0 === \strlen($sFileName))
@ -420,9 +484,6 @@ class Actions
$sFileName = 'rainloop-log.txt'; $sFileName = 'rainloop-log.txt';
} }
$sFileName = \preg_replace('/[\/]+/', '/', \preg_replace('/[.]+/', '.', $sFileName));
$sFileName = \preg_replace('/[^a-zA-Z0-9@_+=\-\.\/!()\[\]]/', '', $sFileName);
return $sFileName; return $sFileName;
} }
@ -775,11 +836,13 @@ class Actions
{ {
$this->oLogger = \MailSo\Log\Logger::SingletonInstance(); $this->oLogger = \MailSo\Log\Logger::SingletonInstance();
if (!!$this->Config()->Get('logs', 'enable', true)) if (!!$this->Config()->Get('logs', 'enable', false))
{ {
$this->oLogger->SetShowSecter(!$this->Config()->Get('logs', 'hide_passwords', true)); $this->oLogger->SetShowSecter(!$this->Config()->Get('logs', 'hide_passwords', true));
$sLogFileFullPath = \APP_PRIVATE_DATA.'logs/'.$this->compileLogFileName(); $sLogFileFullPath = \APP_PRIVATE_DATA.'logs/'.$this->compileLogFileName(
$this->Config()->Get('logs', 'filename', ''));
$sLogFileDir = \dirname($sLogFileFullPath); $sLogFileDir = \dirname($sLogFileFullPath);
if (!@is_dir($sLogFileDir)) if (!@is_dir($sLogFileDir))
@ -804,7 +867,8 @@ class Actions
$oHttp = $this->Http(); $oHttp = $this->Http();
$this->oLogger->Write('[DATE:'.\gmdate('d.m.y').'][RL:'.APP_VERSION.'][PHP:'.PHP_VERSION.'][IP:'. $this->oLogger->Write('[DATE:'.\gmdate('d.m.y').'][RL:'.APP_VERSION.'][PHP:'.PHP_VERSION.'][IP:'.
$oHttp->GetClientIp().'][PID:'.(\MailSo\Base\Utils::FunctionExistsAndEnabled('getmypid') ? \getmypid() : 'unknown').']['. $oHttp->GetClientIp($this->Config()->Get('labs', 'http_client_ip_check_proxy', false)).'][PID:'.
(\MailSo\Base\Utils::FunctionExistsAndEnabled('getmypid') ? \getmypid() : 'unknown').']['.
$oHttp->GetServer('SERVER_SOFTWARE', '~').']['. $oHttp->GetServer('SERVER_SOFTWARE', '~').']['.
(\MailSo\Base\Utils::FunctionExistsAndEnabled('php_sapi_name') ? \php_sapi_name() : '~' ).']' (\MailSo\Base\Utils::FunctionExistsAndEnabled('php_sapi_name') ? \php_sapi_name() : '~' ).']'
); );
@ -829,6 +893,40 @@ class Actions
return $this->oLogger; return $this->oLogger;
} }
/**
* @return \MailSo\Log\Logger
*/
public function LoggerAuth()
{
if (null === $this->oLoggerAuth)
{
$this->oLoggerAuth = \MailSo\Log\Logger::NewInstance(false);
if (!!$this->Config()->Get('logs', 'auth_logging', false))
{
$sAuthLogFileFullPath = \APP_PRIVATE_DATA.'logs/'.$this->compileLogFileName(
$this->Config()->Get('logs', 'auth_logging_filename', ''));
$sLogFileDir = \dirname($sAuthLogFileFullPath);
if (!@is_dir($sLogFileDir))
{
@mkdir($sLogFileDir, 0755, true);
}
$this->oLoggerAuth->AddForbiddenType(\MailSo\Log\Enumerations\Type::MEMORY);
$this->oLoggerAuth->AddForbiddenType(\MailSo\Log\Enumerations\Type::TIME);
$this->oLoggerAuth->AddForbiddenType(\MailSo\Log\Enumerations\Type::TIME_DELTA);
$this->oLoggerAuth->Add(
\MailSo\Log\Drivers\File::NewInstance($sAuthLogFileFullPath)
);
}
}
return $this->oLoggerAuth;
}
/** /**
* @return array * @return array
*/ */
@ -1524,10 +1622,11 @@ class Actions
/** /**
* @param \RainLoop\Model\Account $oAccount * @param \RainLoop\Model\Account $oAccount
* @param bool $bAuthLog = false
* *
* @throws \RainLoop\Exceptions\ClientException * @throws \RainLoop\Exceptions\ClientException
*/ */
public function CheckMailConnection($oAccount) public function CheckMailConnection($oAccount, $bAuthLog = false)
{ {
try try
{ {
@ -1543,6 +1642,16 @@ class Actions
} }
catch (\MailSo\Imap\Exceptions\LoginBadCredentialsException $oException) catch (\MailSo\Imap\Exceptions\LoginBadCredentialsException $oException)
{ {
if ($bAuthLog)
{
$sLine = $this->Config()->Get('logs', 'auth_logging_format', '');
if (!empty($sLine))
{
$this->LoggerAuth()->Write($this->compileLogParams($sLine, $oAccount),
\MailSo\Log\Enumerations\Type::WARNING, 'IMAP');
}
}
if ($this->Config()->Get('labs', 'imap_show_login_alert', true)) if ($this->Config()->Get('labs', 'imap_show_login_alert', true))
{ {
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AuthError, throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AuthError,
@ -1724,7 +1833,7 @@ class Actions
try try
{ {
$this->CheckMailConnection($oAccount); $this->CheckMailConnection($oAccount, true);
} }
catch (\Exception $oException) catch (\Exception $oException)
{ {
@ -2373,6 +2482,40 @@ class Actions
)); ));
} }
/**
* @param string $sHash
*
* @return int
*
* @throws \MailSo\Base\Exceptions\Exception
*/
public function getAccountUnredCountFromHash($sHash)
{
$iResult = 0;
$oAccount = $this->GetAccountFromCustomToken($sHash, false);
if ($oAccount)
{
try
{
$oMailClient = \MailSo\Mail\MailClient::NewInstance();
$oMailClient->SetLogger($this->Logger());
$oAccount->IncConnectAndLoginHelper($this->Plugins(),$oMailClient, $this->Config());
$iResult = $oMailClient->InboxUnreadCount();
$oMailClient->LogoutAndDisconnect();
}
catch (\Exception $oException)
{
$this->Logger()->WriteException($oException);
}
}
return $iResult;
}
/** /**
* @return array * @return array
* *
@ -2382,13 +2525,32 @@ class Actions
{ {
$oAccount = $this->getAccountFromToken(); $oAccount = $this->getAccountFromToken();
$bComplete = true;
$aCounts = array(); $aCounts = array();
if ($this->Config()->Get('webmail', 'allow_additional_accounts', true)) if ($this->Config()->Get('webmail', 'allow_additional_accounts', true))
{ {
$iLimit = 7;
$mAccounts = $this->GetAccounts($oAccount); $mAccounts = $this->GetAccounts($oAccount);
if (\is_array($mAccounts) && 0 < \count($mAccounts))
{
if ($iLimit > \count($mAccounts))
{
$mAccounts = \array_slice($mAccounts, 0, $iLimit);
}
else
{
$bComplete = false;
}
if (0 < \count($mAccounts))
{
foreach ($mAccounts as $sEmail => $sHash) foreach ($mAccounts as $sEmail => $sHash)
{ {
$aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($sEmail), 0); $aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($sEmail),
$oAccount->Email() === $sEmail ? 0 : $this->getAccountUnredCountFromHash($sHash));
}
}
} }
} }
else else
@ -2397,7 +2559,7 @@ class Actions
} }
return $this->DefaultResponse(__FUNCTION__, array( return $this->DefaultResponse(__FUNCTION__, array(
'Complete' => true, 'Complete' => $bComplete,
'Counts' => $aCounts 'Counts' => $aCounts
)); ));
} }

View file

@ -188,12 +188,22 @@ Patterns:
{user:domain} - Replaced by user\'s domain name (the domain part of an email) {user:domain} - Replaced by user\'s domain name (the domain part of an email)
If user is not logged in, value is set to "unknown" If user is not logged in, value is set to "unknown"
{user:uid} - Replaced by user\'s UID regardless of account currently used {user:uid} - Replaced by user\'s UID regardless of account currently used
{user:ip} - Replaced by user\'s IP address
{user:ip}
{request:ip} - Replaced by user\'s IP address
Others:
{imap:login} {imap:host} {imap:port}
{smtp:login} {smtp:host} {smtp:port}
Examples: Examples:
filename = "log-{date:Y-m-d}.txt" filename = "log-{date:Y-m-d}.txt"
filename = "{date:Y-m-d}/{user:domain}/{user:email}_{user:uid}.log" filename = "{date:Y-m-d}/{user:domain}/{user:email}_{user:uid}.log"
filename = "{user:email}-{date:Y-m-d}.txt"') filename = "{user:email}-{date:Y-m-d}.txt"'),
'auth_logging' => array(false, 'Enable auth logging in a separate file (for fail2ban)'),
'auth_logging_filename' => array('fail2ban/auth-{date:Y-m-d}.txt'),
'auth_logging_format' => array('Auth failed: ip={request:ip} user={imap:login} host={imap:host} port={imap:port}')
), ),
'debug' => array( 'debug' => array(
@ -287,6 +297,7 @@ Enables caching in the system'),
'allow_external_login' => array(false), 'allow_external_login' => array(false),
'allow_external_sso' => array(false), 'allow_external_sso' => array(false),
'external_sso_key' => array(''), 'external_sso_key' => array(''),
'http_client_ip_check_proxy' => array(false),
'fast_cache_memcache_host' => array('127.0.0.1'), 'fast_cache_memcache_host' => array('127.0.0.1'),
'fast_cache_memcache_port' => array(11211), 'fast_cache_memcache_port' => array(11211),
'fast_cache_memcache_expire' => array(43200), 'fast_cache_memcache_expire' => array(43200),

View file

@ -1,11 +1,15 @@
<div class="b-system-drop-down g-ui-user-select-none"> <div class="b-system-drop-down g-ui-user-select-none">
<div class="b-toolbar"> <div class="b-toolbar">
<div class="btn-toolbar"> <div class="btn-toolbar">
<div class="accountPlace" data-bind="text: emailTitle()"></div>
<div class="btn-group pull-right dropdown colored-toggle" data-bind="registrateBootstrapDropdown: true, openDropdownTrigger: accountMenuDropdownTrigger"> <div class="btn-group pull-right dropdown colored-toggle" data-bind="registrateBootstrapDropdown: true, openDropdownTrigger: accountMenuDropdownTrigger">
<a id="top-system-dropdown-id" href="#" tabindex="-1" class="btn btn-ellipsis btn-block dropdown-toggle system-dropdown" data-toggle="dropdown"> <a id="top-system-dropdown-id" href="#" tabindex="-1" class="btn btn-ellipsis btn-block dropdown-toggle system-dropdown" data-toggle="dropdown">
<i data-bind="css: {'icon-user': !loading(), 'icon-spinner animated': loading()}" <i data-bind="css: {'icon-user': !accounts.loading(), 'icon-spinner animated': accounts.loading()}"
></i>&nbsp;&nbsp;<span class="caret"></span> ></i>
&nbsp;
<b data-bind="text: accountsUnreadCount, visible: 100 > accountsUnreadCount() && 0 < accountsUnreadCount()"></b>
<b data-bind="visible: 99 < accountsUnreadCount()">99+</b>
&nbsp;
<span class="caret"></span>
</a> </a>
<ul class="dropdown-menu g-ui-menu" tabindex="-1" role="menu" aria-labelledby="top-system-dropdown-id"> <ul class="dropdown-menu g-ui-menu" tabindex="-1" role="menu" aria-labelledby="top-system-dropdown-id">
@ -21,7 +25,7 @@
<i class="icon-ok"></i> <i class="icon-ok"></i>
<i class="icon-user"></i> <i class="icon-user"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="email-title" data-bind="text: email"></span> <span class="email-title" data-bind="text: email, attr: {title: email}"></span>
</a> </a>
</li> </li>
<!-- /ko --> <!-- /ko -->
@ -59,9 +63,9 @@
<span class="i18n" data-i18n-text="TOP_TOOLBAR/BUTTON_LOGOUT"></span> <span class="i18n" data-i18n-text="TOP_TOOLBAR/BUTTON_LOGOUT"></span>
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="accountPlace pull-right" data-bind="text: emailTitle()"></div>
</div> </div>
</div> </div>
</div> </div>