diff --git a/dev/Model/Attachment.js b/dev/Model/Attachment.js index 00ebb212d..3d995ad58 100644 --- a/dev/Model/Attachment.js +++ b/dev/Model/Attachment.js @@ -75,8 +75,8 @@ var bResult = false; if (oJsonAttachment && 'Object/Attachment' === oJsonAttachment['@Object']) { - this.mimeType = (oJsonAttachment.MimeType || '').toLowerCase(); - this.fileName = oJsonAttachment.FileName; + this.mimeType = Utils.trim((oJsonAttachment.MimeType || '').toLowerCase()); + this.fileName = Utils.trim(oJsonAttachment.FileName); this.estimatedSize = Utils.pInt(oJsonAttachment.EstimatedSize); this.isInline = !!oJsonAttachment.IsInline; this.isLinked = !!oJsonAttachment.IsLinked; @@ -122,8 +122,8 @@ */ AttachmentModel.prototype.isText = function () { - return 'text/' === this.mimeType.substr(0, 5) && - -1 === Utils.inArray(this.mimeType, ['text/html']); + return -1 < Utils.inArray(this.mimeType, ['application/pgp-signature']) || + ('text/' === this.mimeType.substr(0, 5) && -1 === Utils.inArray(this.mimeType, ['text/html'])); }; /** @@ -267,9 +267,12 @@ */ AttachmentModel.staticIconClassHelper = function (sMimeType) { + sMimeType = Utils.trim(sMimeType).toLowerCase(); + var - aParts = sMimeType.toLocaleString().split('/'), - sClass = 'icon-file' + sText = '', + sClass = 'icon-file', + aParts = sMimeType.split('/') ; if (aParts && aParts[1]) @@ -295,17 +298,24 @@ { sClass = 'icon-file-zip'; } - // else if (-1 < Utils.inArray(aParts[1], - // ['pdf', 'x-pdf'])) - // { - // sClass = 'icon-file-pdf'; - // } + else if (-1 < Utils.inArray(aParts[1], + ['pdf', 'x-pdf'])) + { + sText = 'pdf' + sClass = 'icon-none'; + } // else if (-1 < Utils.inArray(aParts[1], [ // 'exe', 'x-exe', 'x-winexe', 'bat' // ])) // { // sClass = 'icon-console'; // } + else if (-1 < Utils.inArray(sMimeType, [ + 'application/pgp-signature' + ])) + { + sClass = 'icon-file-certificate'; + } else if (-1 < Utils.inArray(aParts[1], [ 'rtf', 'msword', 'vnd.msword', 'vnd.openxmlformats-officedocument.wordprocessingml.document', 'vnd.openxmlformats-officedocument.wordprocessingml.template', @@ -342,7 +352,7 @@ } } - return sClass; + return [sClass, sText]; }; /** @@ -350,7 +360,15 @@ */ AttachmentModel.prototype.iconClass = function () { - return AttachmentModel.staticIconClassHelper(this.mimeType); + return AttachmentModel.staticIconClassHelper(this.mimeType)[0]; + }; + + /** + * @returns {string} + */ + AttachmentModel.prototype.iconText = function () + { + return AttachmentModel.staticIconClassHelper(this.mimeType)[1]; }; module.exports = AttachmentModel; diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 1ae8201f6..49bfa31f4 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -91,6 +91,9 @@ case 'doc': sClass = 'icon-file-text'; break; + case 'certificate': + sClass = 'icon-file-certificate'; + break; // case 'pdf': // sClass = 'icon-file-pdf'; // break; @@ -628,6 +631,10 @@ { aResult.push('focused'); } + if (this.isImportant()) + { + aResult.push('important'); + } if (this.hasAttachments()) { aResult.push('withAttachments'); diff --git a/dev/Storage/AbstractData.js b/dev/Storage/AbstractData.js index bd3148ef4..e71032cc9 100644 --- a/dev/Storage/AbstractData.js +++ b/dev/Storage/AbstractData.js @@ -75,7 +75,7 @@ { this.layout(mLayout); } - + this.facebookSupported(!!Settings.settingsGet('SupportedFacebookSocial')); this.facebookEnable(!!Settings.settingsGet('AllowFacebookSocial')); this.facebookAppID(Settings.settingsGet('FacebookAppID')); diff --git a/dev/Styles/MessageList.less b/dev/Styles/MessageList.less index ae3e96d0f..7131f3f4c 100644 --- a/dev/Styles/MessageList.less +++ b/dev/Styles/MessageList.less @@ -242,6 +242,17 @@ html.rl-no-preview-pane { background-color: #ccc !important; } + .importantMark { + display: none; + + color:red; + margin-right:5px + } + + &.important .importantMark { + display: inline; + } + &.e-single-line { height: 35px; } diff --git a/dev/Styles/MessageView.less b/dev/Styles/MessageView.less index aea2a97b5..50816c77b 100644 --- a/dev/Styles/MessageView.less +++ b/dev/Styles/MessageView.less @@ -309,6 +309,21 @@ html.rl-no-preview-pane { color: #aaa; } + .attachmentIconText { + display: inline-block; + font-size: 28px; + width: 60px; + height: 56px; + color: #aaa; + line-height: 56px; + text-align: center; + font-style: normal; + } + + .attachmentIcon.icon-none { + display: none; + } + .attachmentIconParent.hasPreview:hover { .iconPreview { display: inline-block; diff --git a/dev/Styles/_FontasticToBoot.less b/dev/Styles/_FontasticToBoot.less index 4f06b2711..8a07f72f6 100644 --- a/dev/Styles/_FontasticToBoot.less +++ b/dev/Styles/_FontasticToBoot.less @@ -119,6 +119,15 @@ animation: rotation .8s infinite linear; } + + &.big { + + height: 13px; + width: 13px; + + margin-top: -2px; + margin-left: -2px; + } } html.no-cssanimations .icon-spinner { diff --git a/dev/View/Popup/Plugin.js b/dev/View/Popup/Plugin.js index 4b4ba4529..e4fe9c48c 100644 --- a/dev/View/Popup/Plugin.js +++ b/dev/View/Popup/Plugin.js @@ -48,6 +48,7 @@ 'placement': 'top', 'trigger': 'hover', 'title': 'About', + 'container': 'body', 'content': function () { return self.readme(); } diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index e135689cb..9ddf849c7 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -900,6 +900,13 @@ }) ; + this.dragOver.subscribe(function (bValue) { + if (bValue) + { + this.selector.scrollToTop(); + } + }, this); + oJua .on('onDragEnter', _.bind(function () { this.dragOverEnter(true); diff --git a/gulpfile.js b/gulpfile.js index 5011a3d9e..7b4f29e90 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -159,6 +159,7 @@ cfg.paths.js = { 'vendors/routes/hasher.min.js', 'vendors/routes/crossroads.min.js', 'vendors/knockout/knockout-3.2.0.js', +// 'vendors/knockout-punches/knockout.punches.min.js', 'vendors/knockout-projections/knockout-projections-1.0.0.min.js', 'vendors/knockout-sortable/knockout-sortable.min.js', 'vendors/ssm/ssm.min.js', diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 38c3ed618..a6619b7d6 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -608,7 +608,8 @@ class ImapClient extends \MailSo\Net\NetClient /** * @param array $aResult - * @param bool $bIsSubscribeList + * @param string $sStatus + * @param bool $bUseListStatus = false * * @return array */ diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php index 4dbe12823..51def4efc 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php @@ -196,6 +196,14 @@ class Attachment return $this->oBodyStructure ? $this->oBodyStructure->IsDoc() : false; } + /** + * @return bool + */ + public function IsPgpSignature() + { + return $this->oBodyStructure ? $this->oBodyStructure->IsPgpSignature() : false; + } + /** * @return \MailSo\Mail\Attachment */ diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/AttachmentCollection.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/AttachmentCollection.php index 7fd51d20b..0a603170a 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/AttachmentCollection.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/AttachmentCollection.php @@ -104,4 +104,16 @@ class AttachmentCollection extends \MailSo\Base\Collection return \is_array($aList) ? \count($aList) : 0; } + + /** + * @return int + */ + public function CertificateCount() + { + $aList = $this->FilterList(function ($oAttachment) { + return $oAttachment && $oAttachment->IsPgpSignature(); + }); + + return \is_array($aList) ? \count($aList) : 0; + } } diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/FolderCollection.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/FolderCollection.php index 3aaffbbb6..bfb3d202a 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/FolderCollection.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/FolderCollection.php @@ -32,6 +32,11 @@ class FolderCollection extends \MailSo\Base\Collection */ public $IsThreadsSupported; + /** + * @var bool + */ + public $Optimized; + /** * @var array */ @@ -48,6 +53,7 @@ class FolderCollection extends \MailSo\Base\Collection $this->FoldersHash = ''; $this->SystemFolders = array(); $this->IsThreadsSupported = false; + $this->Optimized = false; } /** diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index 9a0d4ee03..efca3e5c8 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -1914,7 +1914,8 @@ class MailClient if (1 < $iMessageCount) { - if ($bMessageListOptimization || 0 === \MailSo\Config::$MessageListDateFilter) + if (0 === \MailSo\Config::$MessageListDateFilter && + ($bMessageListOptimization || !$bUseSortIfSupported)) { $aIndexOrUids = \array_reverse(\range(1, $iMessageCount)); } @@ -2013,19 +2014,140 @@ class MailClient return \is_array($aUids) && 1 === \count($aUids) && \is_numeric($aUids[0]) ? (int) $aUids[0] : null; } + /** + * @param array $aMailFoldersHelper + * @param int $iOptimizationLimit = 0 + * + * @return array + */ + public function folerListOptimization($aMailFoldersHelper, $iOptimizationLimit = 0) + { + // optimization + if (10 < $iOptimizationLimit && $iOptimizationLimit < \count($aMailFoldersHelper)) + { + if ($this->oLogger) + { + $this->oLogger->Write('Start optimization (limit:'.$iOptimizationLimit.') for '.\count($aMailFoldersHelper).' folders'); + } + + $iForeachLimit = 1; + + $aFilteredNames = array( + 'inbox', + 'sent', 'outbox', 'sentmail', + 'drafts', + 'junk', 'spam', + 'trash', 'bin', + 'archive', 'allmail', 'all', + 'starred', 'flagged', 'important' + ); + + $aNewMailFoldersHelper = array(); + + $iCountLimit = $iForeachLimit; + foreach ($aMailFoldersHelper as $iIndex => /* @var $oImapFolder \MailSo\Mail\Folder */ $oFolder) + { + // normal and subscribed only + if ($oFolder && ($oFolder->IsSubscribed() || \in_array(\strtolower($oFolder->NameRaw()), $aFilteredNames))) + { + $aNewMailFoldersHelper[] = $oFolder; + + $aMailFoldersHelper[$iIndex] = null; + $iCountLimit--; + } + + if (0 > $iCountLimit) + { + if ($iOptimizationLimit < \count($aNewMailFoldersHelper)) + { + break; + } + else + { + $iCountLimit = $iForeachLimit; + } + } + } + + $iCountLimit = $iForeachLimit; + if ($iOptimizationLimit >= \count($aNewMailFoldersHelper)) + { + // name filter + foreach ($aMailFoldersHelper as $iIndex => /* @var $oImapFolder \MailSo\Mail\Folder */ $oFolder) + { + if ($oFolder && !\preg_match('/[{}\[\]]/', $oFolder->NameRaw())) + { + $aNewMailFoldersHelper[] = $oFolder; + + $aMailFoldersHelper[$iIndex] = null; + $iCountLimit--; + } + + if (0 > $iCountLimit) + { + if ($iOptimizationLimit < \count($aNewMailFoldersHelper)) + { + break; + } + else + { + $iCountLimit = $iForeachLimit; + } + } + } + } + + $iCountLimit = $iForeachLimit; + if ($iOptimizationLimit >= \count($aNewMailFoldersHelper)) + { + // other + foreach ($aMailFoldersHelper as $iIndex => /* @var $oImapFolder \MailSo\Mail\Folder */ $oFolder) + { + if ($oFolder) + { + $aNewMailFoldersHelper[] = $oFolder; + + $aMailFoldersHelper[$iIndex] = null; + $iCountLimit--; + } + + if (0 > $iCountLimit) + { + if ($iOptimizationLimit < \count($aNewMailFoldersHelper)) + { + break; + } + else + { + $iCountLimit = $iForeachLimit; + } + } + } + } + + $aMailFoldersHelper = $aNewMailFoldersHelper; + + if ($this->oLogger) + { + $this->oLogger->Write('Result optimization: '.\count($aMailFoldersHelper).' folders'); + } + } + + return $aMailFoldersHelper; + } + /** * @param string $sParent = '' * @param string $sListPattern = '*' * @param bool $bUseListSubscribeStatus = false + * @param int $iOptimizationLimit = 0 * * @return \MailSo\Mail\FolderCollection|false */ - public function Folders($sParent = '', $sListPattern = '*', $bUseListSubscribeStatus = true) + public function Folders($sParent = '', $sListPattern = '*', $bUseListSubscribeStatus = true, $iOptimizationLimit = 0) { $oFolderCollection = false; - $aFolders = $this->oImapClient->FolderList($sParent, $sListPattern); - $aSubscribedFolders = null; if ($bUseListSubscribeStatus) { @@ -2046,7 +2168,11 @@ class MailClient } } + $aFolders = $this->oImapClient->FolderList($sParent, $sListPattern); + + $bOptimized = false; $aMailFoldersHelper = null; + if (\is_array($aFolders)) { $aMailFoldersHelper = array(); @@ -2058,12 +2184,19 @@ class MailClient $oImapFolder->IsInbox() ); } + + $iCount = \count($aMailFoldersHelper); + $aMailFoldersHelper = $this->folerListOptimization($aMailFoldersHelper, $iOptimizationLimit); + + $bOptimized = $iCount !== \count($aMailFoldersHelper); } if (\is_array($aMailFoldersHelper)) { $oFolderCollection = FolderCollection::NewInstance(); $oFolderCollection->InitByUnsortedMailFolderArray($aMailFoldersHelper); + + $oFolderCollection->Optimized = $bOptimized; } if ($oFolderCollection) diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php index ff9150adc..773f09b76 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php @@ -4334,7 +4334,8 @@ class Actions if (null === $oFolderCollection) { $oFolderCollection = $this->MailClient()->Folders('', '*', - !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true) + !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true), + (int) $this->Config()->Get('labs', 'imap_folder_list_limit', 200) ); } @@ -4694,7 +4695,7 @@ class Actions */ public function DoMessageList() { -// sleep(2); +// \sleep(2); // throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantGetMessageList); $sFolder = ''; @@ -4708,7 +4709,7 @@ class Actions $sRawKey = $this->GetActionParam('RawKey', ''); $aValues = $this->getDecodedClientRawKeyValue($sRawKey, 9); - if (is_array($aValues) && 9 === count($aValues)) + if (\is_array($aValues) && 9 === \count($aValues)) { $sFolder =(string) $aValues[0]; $iOffset = (int) $aValues[1]; @@ -4753,7 +4754,7 @@ class Actions $aExpandedThreadUid = \array_map(function ($sValue) { $sValue = \trim($sValue); - return is_numeric($sValue) ? (int) $sValue : 0; + return \is_numeric($sValue) ? (int) $sValue : 0; }, $aExpandedThreadUid); $aExpandedThreadUid = \array_filter($aExpandedThreadUid, function ($iValue) { @@ -8115,6 +8116,9 @@ class Actions case $iAttachmentsCount === $oAttachments->DocCount(): $mResult['AttachmentsMainType'] = 'doc'; break; + case $iAttachmentsCount === $oAttachments->CertificateCount(): + $mResult['AttachmentsMainType'] = 'certificate'; + break; } } @@ -8382,6 +8386,7 @@ class Actions 'Folder' => $mResponse->FolderName, 'FolderHash' => $mResponse->FolderHash, 'UidNext' => $mResponse->UidNext, + 'Optimized' => $mResponse->Optimized, 'NewMessages' => $this->responseObject($mResponse->NewMessages), 'LastCollapsedThreadUids' => $mResponse->LastCollapsedThreadUids, 'Offset' => $mResponse->Offset, diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Config/Application.php b/rainloop/v/0.0.0/app/src/RainLoop/Config/Application.php index ab442c3a5..5ea1888a1 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/Config/Application.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/Config/Application.php @@ -35,7 +35,7 @@ class Application extends \RainLoop\Config\AbstractConfig $sConfigPassword = (string) $this->Get('security', 'admin_password', ''); return 0 < \strlen($sPassword) && - ($sPassword === $sConfigPassword || \md5(APP_SALT.$sPassword.APP_SALT) === $sConfigPassword); + (($sPassword === $sConfigPassword && '12345' === $sConfigPassword) || \md5(APP_SALT.$sPassword.APP_SALT) === $sConfigPassword); } /** @@ -193,11 +193,6 @@ Examples: 'enable' => array(false, 'Special option required for development purposes') ), - 'version' => array( - 'current' => array(''), - 'saved' => array('') - ), - 'social' => array( 'google_enable' => array(false, 'Google'), 'google_enable_auth' => array(false), @@ -271,6 +266,7 @@ Enables caching in the system'), 'imap_message_list_count_limit_trigger' => array(0), 'imap_message_list_date_filter' => array(0), 'imap_large_thread_limit' => array(100), + 'imap_folder_list_limit' => array(200), 'smtp_show_server_errors' => array(false), 'curl_proxy' => array(''), 'curl_proxy_auth' => array(''), @@ -287,6 +283,11 @@ Enables caching in the system'), 'use_local_proxy_for_external_images' => array(false), 'dev_email' => array(''), 'dev_password' => array('') + ), + + 'version' => array( + 'current' => array(''), + 'saved' => array('') ) ); } diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html index 8fa820c69..e046ed550 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html @@ -37,7 +37,7 @@