mirror of
https://github.com/the-djmaze/snappymail.git
synced 2025-03-04 18:53:42 +08:00
Activated search subfolders as request by #154
This commit is contained in:
parent
b1038667ee
commit
6c797c34f8
51 changed files with 1024 additions and 874 deletions
|
@ -840,7 +840,7 @@ class AppUser extends AbstractApp {
|
|||
if (getFolderInboxName() !== cF) {
|
||||
this.folderInformation(cF);
|
||||
}
|
||||
FolderUserStore.listStatusSupported() || this.folderInformationMultiply(true);
|
||||
FolderUserStore.hasCapability('LIST-STATUS') || this.folderInformationMultiply(true);
|
||||
}, 1000);
|
||||
|
||||
setTimeout(() => Remote.appDelayStart(()=>0), 35000);
|
||||
|
|
|
@ -37,13 +37,11 @@ export class FolderCollectionModel extends AbstractCollectionModel
|
|||
super();
|
||||
this.CountRec
|
||||
this.FoldersHash
|
||||
this.IsMetadataSupported
|
||||
this.IsThreadsSupported
|
||||
this.IsSortSupported
|
||||
this.IsExtendedSupported
|
||||
this.Namespace;
|
||||
this.Optimized
|
||||
this.SystemFolders
|
||||
this.Capabilities
|
||||
}
|
||||
*/
|
||||
|
||||
|
@ -146,11 +144,9 @@ export class FolderCollectionModel extends AbstractCollectionModel
|
|||
AppUserStore.threadsAllowed(!!(Settings.app('useImapThread') && this.IsThreadsSupported));
|
||||
|
||||
FolderUserStore.folderListOptimized(!!this.Optimized);
|
||||
FolderUserStore.sortSupported(!!this.IsSortSupported);
|
||||
FolderUserStore.metadataSupported(!!this.IsMetadataSupported);
|
||||
FolderUserStore.listStatusSupported(!!this.IsListStatusSupported);
|
||||
FolderUserStore.quotaUsage(this.quotaUsage);
|
||||
FolderUserStore.quotaLimit(this.quotaLimit);
|
||||
FolderUserStore.capabilities(this.Capabilities);
|
||||
|
||||
FolderUserStore.sentFolder(normalizeFolder(SettingsGet('SentFolder')));
|
||||
FolderUserStore.draftFolder(normalizeFolder(SettingsGet('DraftFolder')));
|
||||
|
|
|
@ -26,7 +26,7 @@ const folderForDeletion = ko.observable(null).deleteAccessHelper();
|
|||
|
||||
export class FoldersUserSettings /*extends AbstractViewSettings*/ {
|
||||
constructor() {
|
||||
this.showKolab = Settings.capa(Capa.Kolab) && FolderUserStore.metadataSupported();
|
||||
this.showKolab = ko.computed(() => FolderUserStore.hasCapability('METADATA') && Settings.capa(Capa.Kolab));
|
||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||
this.kolabTypeOptions = ko.observableArray();
|
||||
let i18nFilter = key => i18n('SETTINGS_FOLDERS/TYPE_' + key);
|
||||
|
|
|
@ -9,7 +9,8 @@ import { Settings, SettingsGet } from 'Common/Globals';
|
|||
|
||||
export const FolderUserStore = new class {
|
||||
constructor() {
|
||||
addObservablesTo(this, {
|
||||
const self = this;
|
||||
addObservablesTo(self, {
|
||||
/**
|
||||
* To use "checkable" option in /#/settings/folders
|
||||
* When true, getNextFolderNames only lists system and "checkable" folders
|
||||
|
@ -19,12 +20,6 @@ export const FolderUserStore = new class {
|
|||
*/
|
||||
displaySpecSetting: false,
|
||||
|
||||
/**
|
||||
* If the IMAP server supports SORT, METADATA
|
||||
*/
|
||||
sortSupported: false,
|
||||
metadataSupported: false,
|
||||
listStatusSupported: false,
|
||||
// sortMode: '',
|
||||
|
||||
quotaLimit: 0,
|
||||
|
@ -47,40 +42,42 @@ export const FolderUserStore = new class {
|
|||
foldersInboxUnreadCount: 0
|
||||
});
|
||||
|
||||
this.sortMode = ko.observable('').extend({ limitedList: Object.values(FolderSortMode) });
|
||||
self.sortMode = ko.observable('').extend({ limitedList: Object.values(FolderSortMode) });
|
||||
|
||||
this.namespace = '';
|
||||
self.namespace = '';
|
||||
|
||||
this.folderList = ko.observableArray();
|
||||
self.folderList = ko.observableArray();
|
||||
|
||||
this.currentFolder = ko.observable(null).extend({ toggleSubscribeProperty: [this, 'selected'] });
|
||||
self.capabilities = ko.observableArray();
|
||||
|
||||
this.sieveAllowFileintoInbox = !!SettingsGet('SieveAllowFileintoInbox');
|
||||
self.currentFolder = ko.observable(null).extend({ toggleSubscribeProperty: [self, 'selected'] });
|
||||
|
||||
addComputablesTo(this, {
|
||||
self.sieveAllowFileintoInbox = !!SettingsGet('SieveAllowFileintoInbox');
|
||||
|
||||
draftFolderNotEnabled: () => !this.draftFolder() || UNUSED_OPTION_VALUE === this.draftFolder(),
|
||||
addComputablesTo(self, {
|
||||
|
||||
currentFolderFullNameRaw: () => (this.currentFolder() ? this.currentFolder().fullNameRaw : ''),
|
||||
draftFolderNotEnabled: () => !self.draftFolder() || UNUSED_OPTION_VALUE === self.draftFolder(),
|
||||
|
||||
currentFolderFullName: () => (this.currentFolder() ? this.currentFolder().fullName : ''),
|
||||
currentFolderFullNameHash: () => (this.currentFolder() ? this.currentFolder().fullNameHash : ''),
|
||||
currentFolderFullNameRaw: () => (self.currentFolder() ? self.currentFolder().fullNameRaw : ''),
|
||||
|
||||
currentFolderFullName: () => (self.currentFolder() ? self.currentFolder().fullName : ''),
|
||||
currentFolderFullNameHash: () => (self.currentFolder() ? self.currentFolder().fullNameHash : ''),
|
||||
|
||||
foldersChanging: () =>
|
||||
this.foldersLoading() | this.foldersCreating() | this.foldersDeleting() | this.foldersRenaming(),
|
||||
self.foldersLoading() | self.foldersCreating() | self.foldersDeleting() | self.foldersRenaming(),
|
||||
|
||||
folderListSystemNames: () => {
|
||||
const list = [getFolderInboxName()],
|
||||
others = [this.sentFolder(), this.draftFolder(), this.spamFolder(), this.trashFolder(), this.archiveFolder()];
|
||||
others = [self.sentFolder(), self.draftFolder(), self.spamFolder(), self.trashFolder(), self.archiveFolder()];
|
||||
|
||||
this.folderList().length &&
|
||||
self.folderList().length &&
|
||||
others.forEach(name => name && UNUSED_OPTION_VALUE !== name && list.push(name));
|
||||
|
||||
return list;
|
||||
},
|
||||
|
||||
folderListSystem: () =>
|
||||
this.folderListSystemNames().map(name => getFolderFromCacheList(name)).filter(v => v)
|
||||
self.folderListSystemNames().map(name => getFolderFromCacheList(name)).filter(v => v)
|
||||
});
|
||||
|
||||
const
|
||||
|
@ -93,13 +90,13 @@ export const FolderUserStore = new class {
|
|||
folder && folder.type(type);
|
||||
};
|
||||
|
||||
this.sentFolder.subscribe(fRemoveSystemFolderType(this.sentFolder), this, 'beforeChange');
|
||||
this.draftFolder.subscribe(fRemoveSystemFolderType(this.draftFolder), this, 'beforeChange');
|
||||
this.spamFolder.subscribe(fRemoveSystemFolderType(this.spamFolder), this, 'beforeChange');
|
||||
this.trashFolder.subscribe(fRemoveSystemFolderType(this.trashFolder), this, 'beforeChange');
|
||||
this.archiveFolder.subscribe(fRemoveSystemFolderType(this.archiveFolder), this, 'beforeChange');
|
||||
self.sentFolder.subscribe(fRemoveSystemFolderType(self.sentFolder), self, 'beforeChange');
|
||||
self.draftFolder.subscribe(fRemoveSystemFolderType(self.draftFolder), self, 'beforeChange');
|
||||
self.spamFolder.subscribe(fRemoveSystemFolderType(self.spamFolder), self, 'beforeChange');
|
||||
self.trashFolder.subscribe(fRemoveSystemFolderType(self.trashFolder), self, 'beforeChange');
|
||||
self.archiveFolder.subscribe(fRemoveSystemFolderType(self.archiveFolder), self, 'beforeChange');
|
||||
|
||||
addSubscribablesTo(this, {
|
||||
addSubscribablesTo(self, {
|
||||
sentFolder: fSetSystemFolderType(FolderType.Sent),
|
||||
draftFolder: fSetSystemFolderType(FolderType.Drafts),
|
||||
spamFolder: fSetSystemFolderType(FolderType.Spam),
|
||||
|
@ -107,12 +104,19 @@ export const FolderUserStore = new class {
|
|||
archiveFolder: fSetSystemFolderType(FolderType.Archive)
|
||||
});
|
||||
|
||||
this.quotaPercentage = ko.computed(() => {
|
||||
const quota = this.quotaLimit(), usage = this.quotaUsage();
|
||||
self.quotaPercentage = ko.computed(() => {
|
||||
const quota = self.quotaLimit(), usage = self.quotaUsage();
|
||||
return 0 < quota ? Math.ceil((usage / quota) * 100) : 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If the IMAP server supports SORT, METADATA
|
||||
*/
|
||||
hasCapability(name) {
|
||||
return this.capabilities().includes(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array}
|
||||
*/
|
||||
|
|
|
@ -6,6 +6,7 @@ import { MessageUserStore } from 'Stores/User/Message';
|
|||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
|
||||
class AdvancedSearchPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
|
@ -17,12 +18,15 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
|
|||
subject: '',
|
||||
text: '',
|
||||
selectedDateValue: -1,
|
||||
selectedTreeValue: '',
|
||||
|
||||
hasAttachment: false,
|
||||
starred: false,
|
||||
unseen: false
|
||||
});
|
||||
|
||||
this.showMultisearch = ko.computed(() => FolderUserStore.hasCapability('MULTISEARCH'));
|
||||
|
||||
let prefix = 'SEARCH/LABEL_ADV_DATE_';
|
||||
this.selectedDates = ko.computed(() => {
|
||||
translatorTrigger();
|
||||
|
@ -37,6 +41,16 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
|
|||
];
|
||||
});
|
||||
|
||||
prefix = 'SEARCH/LABEL_ADV_SUBFOLDERS_';
|
||||
this.selectedTree = ko.computed(() => {
|
||||
translatorTrigger();
|
||||
return [
|
||||
{ id: '', name: i18n(prefix + 'NONE') },
|
||||
{ id: 'subtree-one', name: i18n(prefix + 'SUBTREE_ONE') },
|
||||
{ id: 'subtree', name: i18n(prefix + 'SUBTREE') }
|
||||
];
|
||||
});
|
||||
|
||||
decorateKoCommands(this, {
|
||||
searchCommand: 1
|
||||
});
|
||||
|
@ -69,65 +83,54 @@ class AdvancedSearchPopupView extends AbstractViewPopup {
|
|||
});
|
||||
}
|
||||
|
||||
buildSearchStringValue(value) {
|
||||
if (value.includes(' ')) {
|
||||
value = '"' + value + '"';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
buildSearchString() {
|
||||
const result = [],
|
||||
from_ = this.from().trim(),
|
||||
to = this.to().trim(),
|
||||
subject = this.subject().trim(),
|
||||
text = this.text().trim(),
|
||||
isPart = [],
|
||||
hasPart = [];
|
||||
const
|
||||
result = new FormData();
|
||||
|
||||
if (from_) {
|
||||
result.push('from:' + this.buildSearchStringValue(from_));
|
||||
let value = this.from().trim();
|
||||
if (value) {
|
||||
result.set('from', value);
|
||||
}
|
||||
|
||||
if (to) {
|
||||
result.push('to:' + this.buildSearchStringValue(to));
|
||||
value = this.to().trim();
|
||||
if (value) {
|
||||
result.set('to', value);
|
||||
}
|
||||
|
||||
if (subject) {
|
||||
result.push('subject:' + this.buildSearchStringValue(subject));
|
||||
value = this.subject().trim();
|
||||
if (value) {
|
||||
result.set('subject', value);
|
||||
}
|
||||
|
||||
if (this.hasAttachment()) {
|
||||
hasPart.push('attachment');
|
||||
result.set('has', 'attachment');
|
||||
}
|
||||
|
||||
if (this.unseen()) {
|
||||
isPart.push('unseen');
|
||||
result.set('is[]', 'unseen');
|
||||
}
|
||||
|
||||
if (this.starred()) {
|
||||
isPart.push('flagged');
|
||||
}
|
||||
|
||||
if (hasPart.length) {
|
||||
result.push('has:' + hasPart.join(','));
|
||||
}
|
||||
|
||||
if (isPart.length) {
|
||||
result.push('is:' + isPart.join(','));
|
||||
result.set('is[]', 'flagged');
|
||||
}
|
||||
|
||||
if (-1 < this.selectedDateValue()) {
|
||||
let d = new Date();
|
||||
d.setDate(d.getDate() - this.selectedDateValue());
|
||||
result.push('date:' + d.format('Y.m.d') + '/');
|
||||
result.set('date', d.format('Y.m.d') + '/');
|
||||
}
|
||||
|
||||
if (text) {
|
||||
result.push('text:' + this.buildSearchStringValue(text));
|
||||
value = this.selectedTreeValue();
|
||||
if (value) {
|
||||
result.set('in', value);
|
||||
}
|
||||
|
||||
return result.join(' ').trim();
|
||||
value = this.text().trim();
|
||||
if (value) {
|
||||
result.set('text', value);
|
||||
}
|
||||
|
||||
return new URLSearchParams(result).toString();
|
||||
}
|
||||
|
||||
clearPopup() {
|
||||
|
|
|
@ -64,7 +64,9 @@ export class MailMessageList extends AbstractViewRight {
|
|||
|
||||
this.messageList = MessageUserStore.list;
|
||||
|
||||
this.sortSupported = FolderUserStore.sortSupported;
|
||||
this.sortSupported = ko.computed(() =>
|
||||
FolderUserStore.hasCapability('SORT') | FolderUserStore.hasCapability('ESORT')
|
||||
);
|
||||
|
||||
this.composeInEdit = AppUserStore.composeInEdit;
|
||||
this.leftPanelDisabled = leftPanelDisabled;
|
||||
|
|
|
@ -452,7 +452,7 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
if ($this->IsSupported('ESEARCH')) {
|
||||
$aResult = $oFolderInfo->getStatusItems();
|
||||
// SELECT or EXAMINE command then UNSEEN is the message sequence number of the first unseen message
|
||||
$aResult['UNSEEN'] = $this->simpleESearchOrESortHelper(false, 'UNSEEN', ['COUNT'])['COUNT'];
|
||||
$aResult['UNSEEN'] = $this->MessageSimpleESearch('UNSEEN', ['COUNT'])['COUNT'];
|
||||
return $aResult;
|
||||
}
|
||||
*/
|
||||
|
@ -778,10 +778,6 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
*/
|
||||
public function MessageSimpleSort(array $aSortTypes, string $sSearchCriterias = 'ALL', bool $bReturnUid = true) : array
|
||||
{
|
||||
$sCommandPrefix = ($bReturnUid) ? 'UID ' : '';
|
||||
$sSearchCriterias = !\strlen(\trim($sSearchCriterias)) || '*' === $sSearchCriterias
|
||||
? 'ALL' : $sSearchCriterias;
|
||||
|
||||
if (!$aSortTypes)
|
||||
{
|
||||
$this->writeLogException(
|
||||
|
@ -795,6 +791,10 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
\MailSo\Log\Enumerations\Type::ERROR, true);
|
||||
}
|
||||
|
||||
$sCommandPrefix = ($bReturnUid) ? 'UID ' : '';
|
||||
$sSearchCriterias = !\strlen(\trim($sSearchCriterias)) || '*' === $sSearchCriterias
|
||||
? 'ALL' : $sSearchCriterias;
|
||||
|
||||
$aRequest = array();
|
||||
$aRequest[] = $aSortTypes;
|
||||
$aRequest[] = \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? 'US-ASCII' : 'UTF-8';
|
||||
|
@ -811,72 +811,15 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
private function simpleESearchOrESortHelper(bool $bSort = false, string $sSearchCriterias = 'ALL', array $aSearchOrSortReturn = null, bool $bReturnUid = true, string $sLimit = '', string $sCharset = '', array $aSortTypes = null) : array
|
||||
public function MessageSimpleESearch(string $sSearchCriterias = 'ALL', array $aSearchReturn = null, bool $bReturnUid = true, string $sCharset = '', string $sLimit = '') : array
|
||||
{
|
||||
$sCommandPrefix = ($bReturnUid) ? 'UID ' : '';
|
||||
$sSearchCriterias = !\strlen($sSearchCriterias) || '*' === $sSearchCriterias
|
||||
? 'ALL' : $sSearchCriterias;
|
||||
|
||||
$sCmd = $bSort ? 'SORT': 'SEARCH';
|
||||
if ($bSort && (!$aSortTypes || !$this->IsSupported('SORT')))
|
||||
{
|
||||
$this->writeLogException(
|
||||
new \MailSo\Base\Exceptions\InvalidArgumentException,
|
||||
\MailSo\Log\Enumerations\Type::ERROR, true);
|
||||
}
|
||||
|
||||
if (!$this->IsSupported($bSort ? 'ESORT' : 'ESEARCH'))
|
||||
{
|
||||
$this->writeLogException(
|
||||
new \MailSo\Base\Exceptions\InvalidArgumentException,
|
||||
\MailSo\Log\Enumerations\Type::ERROR, true);
|
||||
}
|
||||
|
||||
if (!$aSearchOrSortReturn)
|
||||
{
|
||||
// ALL OR COUNT | MIN | MAX
|
||||
$aSearchOrSortReturn = array('ALL');
|
||||
}
|
||||
|
||||
$aRequest = array();
|
||||
|
||||
if ($bSort)
|
||||
{
|
||||
$aRequest[] = 'RETURN';
|
||||
$aRequest[] = $aSearchOrSortReturn;
|
||||
|
||||
$aRequest[] = $aSortTypes;
|
||||
$aRequest[] = \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? 'US-ASCII' : 'UTF-8';
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
TODO: https://github.com/the-djmaze/snappymail/issues/154
|
||||
https://datatracker.ietf.org/doc/html/rfc6237
|
||||
$sCmd = 'ESEARCH';
|
||||
$aReques[] = 'IN';
|
||||
$aReques[] = ['mailboxes', '"folder1"', 'subtree', '"folder2"'];
|
||||
$aReques[] = ['mailboxes', '"folder1"', 'subtree-one', '"folder2"'];
|
||||
*/
|
||||
|
||||
if (\strlen($sCharset))
|
||||
{
|
||||
$aRequest[] = 'CHARSET';
|
||||
$aRequest[] = \strtoupper($sCharset);
|
||||
}
|
||||
|
||||
$aRequest[] = 'RETURN';
|
||||
$aRequest[] = $aSearchOrSortReturn;
|
||||
}
|
||||
|
||||
$aRequest[] = $sSearchCriterias;
|
||||
|
||||
if (\strlen($sLimit))
|
||||
{
|
||||
$aRequest[] = $sLimit;
|
||||
}
|
||||
|
||||
return $this->SendRequestGetResponse($sCommandPrefix.$sCmd, $aRequest)
|
||||
$oESearch = new Requests\ESEARCH($this);
|
||||
$oESearch->sCriterias = $sSearchCriterias;
|
||||
$oESearch->aReturn = $aSearchReturn;
|
||||
$oESearch->bUid = $bReturnUid;
|
||||
$oESearch->sLimit = $sLimit;
|
||||
$oESearch->sCharset = $sCharset;
|
||||
return $oESearch->SendRequestGetResponse()
|
||||
->getSimpleESearchOrESortResult($this->getCurrentTag(), $bReturnUid);
|
||||
}
|
||||
|
||||
|
@ -885,19 +828,16 @@ class ImapClient extends \MailSo\Net\NetClient
|
|||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
public function MessageSimpleESearch(string $sSearchCriterias = 'ALL', array $aSearchReturn = null, bool $bReturnUid = true, string $sLimit = '', string $sCharset = '') : array
|
||||
public function MessageSimpleESort(array $aSortTypes, string $sSearchCriterias = 'ALL', array $aSearchReturn = ['ALL'], bool $bReturnUid = true, string $sLimit = '') : array
|
||||
{
|
||||
return $this->simpleESearchOrESortHelper(false, $sSearchCriterias, $aSearchReturn, $bReturnUid, $sLimit, $sCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \MailSo\Base\Exceptions\InvalidArgumentException
|
||||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
public function MessageSimpleESort(array $aSortTypes, string $sSearchCriterias = 'ALL', array $aSearchReturn = null, bool $bReturnUid = true, string $sLimit = '') : array
|
||||
{
|
||||
return $this->simpleESearchOrESortHelper(true, $sSearchCriterias, $aSearchReturn, $bReturnUid, $sLimit, '', $aSortTypes);
|
||||
$oSort = new Requests\SORT($this);
|
||||
$oSort->sCriterias = $sSearchCriterias;
|
||||
$oSort->aReturn = $aSearchReturn ?: ['ALL'];
|
||||
$oSort->bUid = $bReturnUid;
|
||||
$oSort->sLimit = $sLimit;
|
||||
$oSort->aSortTypes = $aSortTypes;
|
||||
return $oSort->SendRequestGetResponse()
|
||||
->getSimpleESearchOrESortResult($this->getCurrentTag(), $bReturnUid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of MailSo.
|
||||
*
|
||||
* (c) 2014 Usenko Timur
|
||||
* (c) 2021 DJMaze
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MailSo\Imap\Requests;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Imap
|
||||
*/
|
||||
class ESEARCH extends Request
|
||||
{
|
||||
public
|
||||
$sCriterias = 'ALL',
|
||||
$aReturn = [],
|
||||
$bUid = true,
|
||||
$sLimit = '',
|
||||
$sCharset = '',
|
||||
$aMailboxes = [],
|
||||
$aSubtrees = [],
|
||||
$aSubtreesOne = [];
|
||||
|
||||
public function SendRequestGetResponse() : \MailSo\Imap\ResponseCollection
|
||||
{
|
||||
if (!$this->oImapClient->IsSupported('ESEARCH')) {
|
||||
$this->oImapClient->writeLogException(
|
||||
new \MailSo\Base\Exceptions\InvalidArgumentException,
|
||||
\MailSo\Log\Enumerations\Type::ERROR, true);
|
||||
}
|
||||
|
||||
$sCmd = 'SEARCH';
|
||||
$aRequest = array();
|
||||
|
||||
// TODO: https://github.com/the-djmaze/snappymail/issues/154
|
||||
// https://datatracker.ietf.org/doc/html/rfc7377
|
||||
$aFolders = [];
|
||||
if ($this->aMailboxes) {
|
||||
$aFolders[] = 'mailboxes';
|
||||
$aFolders[] = $this->aMailboxes;
|
||||
}
|
||||
if ($this->aSubtrees) {
|
||||
$aFolders[] = 'subtree';
|
||||
$aFolders[] = $this->aSubtrees;
|
||||
}
|
||||
if ($this->aSubtreesOne) {
|
||||
$aFolders[] = 'subtree-one';
|
||||
$aFolders[] = $this->aSubtreesOne;
|
||||
}
|
||||
if ($aFolders && $this->oImapClient->IsSupported('MULTISEARCH')) {
|
||||
$sCmd = 'ESEARCH';
|
||||
$aReques[] = 'IN';
|
||||
$aReques[] = $aFolders;
|
||||
}
|
||||
|
||||
if (\strlen($this->sCharset)) {
|
||||
$aRequest[] = 'CHARSET';
|
||||
$aRequest[] = \strtoupper($this->sCharset);
|
||||
}
|
||||
|
||||
$aRequest[] = 'RETURN';
|
||||
if ($this->aReturn) {
|
||||
$aRequest[] = $this->aReturn;
|
||||
} else {
|
||||
// ALL OR COUNT | MIN | MAX
|
||||
$aRequest[] = array('ALL');
|
||||
}
|
||||
|
||||
$aRequest[] = !\strlen($this->sCriterias) || '*' === $this->sCriterias
|
||||
? 'ALL' : $this->sCriterias;
|
||||
|
||||
if (\strlen($this->sLimit)) {
|
||||
$aRequest[] = $this->sLimit;
|
||||
}
|
||||
|
||||
return $this->oImapClient->SendRequestGetResponse(
|
||||
($this->bUid ? 'UID ' : '') . $sCmd,
|
||||
$aRequest
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of MailSo.
|
||||
*
|
||||
* (c) 2021 DJMaze
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MailSo\Imap\Requests;
|
||||
|
||||
abstract class Request
|
||||
{
|
||||
protected
|
||||
$oImapClient;
|
||||
|
||||
function __construct(\MailSo\Imap\ImapClient $oImapClient)
|
||||
{
|
||||
$this->oImapClient = $oImapClient;
|
||||
}
|
||||
|
||||
final public function getName()
|
||||
{
|
||||
$name = \explode('\\', \get_class($this));
|
||||
return \end($name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of MailSo.
|
||||
*
|
||||
* (c) 2014 Usenko Timur
|
||||
* (c) 2021 DJMaze
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MailSo\Imap\Requests;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Imap
|
||||
*/
|
||||
class SORT extends Request
|
||||
{
|
||||
public
|
||||
$sCriterias = 'ALL',
|
||||
$bUid = true,
|
||||
$sLimit = '',
|
||||
$aSortTypes = [],
|
||||
// rfc5267
|
||||
$aReturn = [
|
||||
/*
|
||||
MIN
|
||||
Return the message number/UID of the lowest sorted message
|
||||
satisfying the search criteria.
|
||||
|
||||
MAX
|
||||
Return the message number/UID of the highest sorted message
|
||||
satisfying the search criteria.
|
||||
|
||||
ALL
|
||||
Return all message numbers/UIDs which match the search criteria,
|
||||
in the requested sort order, using a sequence-set.
|
||||
|
||||
COUNT
|
||||
As in [ESEARCH].
|
||||
*/
|
||||
];
|
||||
|
||||
public function SendRequestGetResponse() : \MailSo\Imap\ResponseCollection
|
||||
{
|
||||
if (!$this->aSortTypes) {
|
||||
$this->oImapClient->writeLogException(
|
||||
new \MailSo\Base\Exceptions\InvalidArgumentException,
|
||||
\MailSo\Log\Enumerations\Type::ERROR, true);
|
||||
}
|
||||
|
||||
$aRequest = array();
|
||||
|
||||
if ($this->aReturn) {
|
||||
if (!$this->oImapClient->IsSupported('ESORT')) {
|
||||
$this->oImapClient->writeLogException(
|
||||
new \MailSo\Base\Exceptions\InvalidArgumentException,
|
||||
\MailSo\Log\Enumerations\Type::ERROR, true);
|
||||
}
|
||||
$aRequest[] = 'RETURN';
|
||||
$aRequest[] = $this->aReturn;
|
||||
}
|
||||
|
||||
$aRequest[] = $this->aSortTypes;
|
||||
|
||||
$sSearchCriterias = (\strlen($this->sCriterias) && '*' !== $this->sCriterias) ? $this->sCriterias : 'ALL';
|
||||
$aRequest[] = \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? 'US-ASCII' : 'UTF-8';
|
||||
$aRequest[] = $sSearchCriterias;
|
||||
|
||||
if (\strlen($this->sLimit)) {
|
||||
$aRequest[] = $this->sLimit;
|
||||
}
|
||||
|
||||
return $this->oImapClient->SendRequestGetResponse(
|
||||
($this->bUid ? 'UID SORT' : 'SORT'),
|
||||
$aRequest
|
||||
);
|
||||
}
|
||||
}
|
|
@ -227,7 +227,7 @@ class ResponseCollection extends \MailSo\Base\Collection
|
|||
if (Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType
|
||||
&& ($sStatus === $oResponse->StatusOrIndex || $iOffset)
|
||||
&& \is_array($oResponse->ResponseList)
|
||||
&& 2 < count($oResponse->ResponseList))
|
||||
&& 2 < \count($oResponse->ResponseList))
|
||||
{
|
||||
$iLen = \count($oResponse->ResponseList);
|
||||
for ($iIndex = 2 + $iOffset; $iIndex < $iLen; ++$iIndex) {
|
||||
|
@ -270,12 +270,13 @@ class ResponseCollection extends \MailSo\Base\Collection
|
|||
$iLen = \count($oResponse->ResponseList);
|
||||
for ($iIndex = 2 + $iOffset; $iIndex < $iLen; ++$iIndex) {
|
||||
$aNewValue = $this->validateThreadItem($oResponse->ResponseList[$iIndex]);
|
||||
if (false !== $aNewValue) {
|
||||
if (\is_array($aNewValue)) {
|
||||
$aReturn[] = $aNewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aReturn;
|
||||
}
|
||||
|
||||
|
@ -374,27 +375,24 @@ class ResponseCollection extends \MailSo\Base\Collection
|
|||
$aResult = array();
|
||||
foreach ($this as $oResponse) {
|
||||
if (Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType
|
||||
&& ('ESEARCH' === $oResponse->StatusOrIndex || 'ESORT' === $oResponse->StatusOrIndex)
|
||||
&& ('ESEARCH' === $oResponse->StatusOrIndex || 'SORT' === $oResponse->StatusOrIndex)
|
||||
&& \is_array($oResponse->ResponseList)
|
||||
&& isset($oResponse->ResponseList[2], $oResponse->ResponseList[2][0], $oResponse->ResponseList[2][1])
|
||||
&& isset($oResponse->ResponseList[2][1])
|
||||
&& 'TAG' === $oResponse->ResponseList[2][0] && $sRequestTag === $oResponse->ResponseList[2][1]
|
||||
&& (!$bReturnUid || ($bReturnUid && !empty($oResponse->ResponseList[3]) && 'UID' === $oResponse->ResponseList[3]))
|
||||
&& (!$bReturnUid || (!empty($oResponse->ResponseList[3]) && 'UID' === $oResponse->ResponseList[3]))
|
||||
)
|
||||
{
|
||||
$iStart = 3;
|
||||
foreach ($oResponse->ResponseList as $iIndex => $mItem) {
|
||||
if ($iIndex >= $iStart) {
|
||||
switch ($mItem)
|
||||
{
|
||||
case 'ALL':
|
||||
case 'MAX':
|
||||
case 'MIN':
|
||||
case 'COUNT':
|
||||
if (isset($oResponse->ResponseList[$iIndex + 1])) {
|
||||
$aResult[$mItem] = $oResponse->ResponseList[$iIndex + 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
$i = \count($oResponse->ResponseList) - 1;
|
||||
while (3 < --$i) {
|
||||
$sItem = $oResponse->ResponseList[$i];
|
||||
switch ($sItem)
|
||||
{
|
||||
case 'ALL':
|
||||
case 'MAX':
|
||||
case 'MIN':
|
||||
case 'COUNT':
|
||||
$aResult[$sItem] = $oResponse->ResponseList[$i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
449
snappymail/v/0.0.0/app/libraries/MailSo/Imap/SearchCriterias.php
Normal file
449
snappymail/v/0.0.0/app/libraries/MailSo/Imap/SearchCriterias.php
Normal file
|
@ -0,0 +1,449 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of MailSo.
|
||||
*
|
||||
* (c) 2014 Usenko Timur
|
||||
* (c) 2021 DJMaze
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MailSo\Imap;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Mail
|
||||
*/
|
||||
class SearchCriterias
|
||||
{
|
||||
/**
|
||||
https://datatracker.ietf.org/doc/html/rfc3501#section-6.4.4
|
||||
|
||||
✔ ALL
|
||||
All messages in the mailbox; the default initial key for
|
||||
ANDing.
|
||||
|
||||
☐ BCC <string>
|
||||
Messages that contain the specified string in the envelope
|
||||
structure's BCC field.
|
||||
|
||||
✔ BEFORE <date>
|
||||
Messages whose internal date (disregarding time and timezone)
|
||||
is earlier than the specified date.
|
||||
|
||||
✔ BODY <string>
|
||||
Messages that contain the specified string in the body of the
|
||||
message.
|
||||
|
||||
✔ CC <string>
|
||||
Messages that contain the specified string in the envelope
|
||||
structure's CC field.
|
||||
|
||||
✔ FROM <string>
|
||||
Messages that contain the specified string in the envelope
|
||||
structure's FROM field.
|
||||
|
||||
✔ HEADER <field-name> <string>
|
||||
Messages that have a header with the specified field-name (as
|
||||
defined in [RFC-2822]) and that contains the specified string
|
||||
in the text of the header (what comes after the colon). If the
|
||||
string to search is zero-length, this matches all messages that
|
||||
have a header line with the specified field-name regardless of
|
||||
the contents.
|
||||
|
||||
☐ KEYWORD <flag>
|
||||
Messages with the specified keyword flag set.
|
||||
|
||||
✔ LARGER <n>
|
||||
Messages with an [RFC-2822] size larger than the specified
|
||||
number of octets.
|
||||
|
||||
☐ NOT <search-key>
|
||||
Messages that do not match the specified search key.
|
||||
|
||||
☐ ON <date>
|
||||
Messages whose internal date (disregarding time and timezone)
|
||||
is within the specified date.
|
||||
|
||||
✔ OR <search-key1> <search-key2>
|
||||
Messages that match either search key.
|
||||
|
||||
☐ SENTBEFORE <date>
|
||||
Messages whose [RFC-2822] Date: header (disregarding time and
|
||||
timezone) is earlier than the specified date.
|
||||
|
||||
☐ SENTON <date>
|
||||
Messages whose [RFC-2822] Date: header (disregarding time and
|
||||
timezone) is within the specified date.
|
||||
|
||||
☐ SENTSINCE <date>
|
||||
Messages whose [RFC-2822] Date: header (disregarding time and
|
||||
timezone) is within or later than the specified date.
|
||||
|
||||
✔ SINCE <date>
|
||||
Messages whose internal date (disregarding time and timezone)
|
||||
is within or later than the specified date.
|
||||
|
||||
✔ SMALLER <n>
|
||||
Messages with an [RFC-2822] size smaller than the specified
|
||||
number of octets.
|
||||
|
||||
✔ SUBJECT <string>
|
||||
Messages that contain the specified string in the envelope
|
||||
structure's SUBJECT field.
|
||||
|
||||
✔ TEXT <string>
|
||||
Messages that contain the specified string in the header or
|
||||
body of the message.
|
||||
|
||||
✔ TO <string>
|
||||
Messages that contain the specified string in the envelope
|
||||
structure's TO field.
|
||||
|
||||
☐ UID <sequence set>
|
||||
Messages with unique identifiers corresponding to the specified
|
||||
unique identifier set. Sequence set ranges are permitted.
|
||||
|
||||
☐ UNKEYWORD <flag>
|
||||
Messages that do not have the specified keyword flag set.
|
||||
|
||||
✔ FLAGGED
|
||||
✔ UNFLAGGED
|
||||
✔ SEEN
|
||||
✔ UNSEEN
|
||||
☐ ANSWERED
|
||||
☐ UNANSWERED
|
||||
☐ DELETED
|
||||
☐ UNDELETED
|
||||
☐ DRAFT
|
||||
☐ UNDRAFT
|
||||
X NEW
|
||||
X OLD
|
||||
X RECENT
|
||||
*/
|
||||
|
||||
public static function fromString(\MailSo\Imap\ImapClient $oImapClient, string $sFolderName, string $sSearch, int $iTimeZoneOffset = 0, bool &$bUseCache = true) : string
|
||||
{
|
||||
$bUseCache = true;
|
||||
$iTimeFilter = 0;
|
||||
$aCriteriasResult = array();
|
||||
|
||||
if (0 < \MailSo\Config::$MessageListDateFilter) {
|
||||
$iD = \time() - 3600 * 24 * 30 * \MailSo\Config::$MessageListDateFilter;
|
||||
$iTimeFilter = \gmmktime(1, 1, 1, \gmdate('n', $iD), 1, \gmdate('Y', $iD));
|
||||
}
|
||||
|
||||
if (\strlen(\trim($sSearch))) {
|
||||
$aLines = static::parseQueryString($sSearch);
|
||||
// $aLines = static::parseSearchString($sSearch);
|
||||
|
||||
if (!$aLines) {
|
||||
$sValue = static::escapeSearchString($oImapClient, $sSearch);
|
||||
|
||||
if (\MailSo\Config::$MessageListFastSimpleSearch) {
|
||||
$aCriteriasResult[] = 'OR OR OR';
|
||||
$aCriteriasResult[] = 'FROM';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'TO';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'CC';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'SUBJECT';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
} else {
|
||||
$aCriteriasResult[] = 'TEXT';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
}
|
||||
} else {
|
||||
if (isset($aLines['IN']) && $oImapClient->IsSupported('MULTISEARCH') && \in_array($aLines['IN'], ['subtree','subtree-one','mailboxes'])) {
|
||||
$aCriteriasResult[] = "IN ({$aLines['IN']} \"{$sFolderName}\")";
|
||||
}
|
||||
|
||||
if (isset($aLines['EMAIL'])) {
|
||||
$sValue = static::escapeSearchString($oImapClient, $aLines['EMAIL']);
|
||||
|
||||
$aCriteriasResult[] = 'OR OR';
|
||||
$aCriteriasResult[] = 'FROM';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'TO';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'CC';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
|
||||
unset($aLines['EMAIL']);
|
||||
}
|
||||
|
||||
if (isset($aLines['TO'])) {
|
||||
$sValue = static::escapeSearchString($oImapClient, $aLines['TO']);
|
||||
|
||||
$aCriteriasResult[] = 'OR';
|
||||
$aCriteriasResult[] = 'TO';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'CC';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
|
||||
unset($aLines['TO']);
|
||||
}
|
||||
|
||||
foreach ($aLines as $sName => $sRawValue) {
|
||||
if ('' === \trim($sRawValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sValue = static::escapeSearchString($oImapClient, $sRawValue);
|
||||
switch ($sName) {
|
||||
case 'FROM':
|
||||
case 'SUBJECT':
|
||||
$aCriteriasResult[] = $sName;
|
||||
$aCriteriasResult[] = $sValue;
|
||||
break;
|
||||
|
||||
case 'BODY':
|
||||
// $sMainText = \trim(\MailSo\Base\Utils::StripSpaces($sMainText), '"');
|
||||
$aCriteriasResult[] = 'BODY';
|
||||
$aCriteriasResult[] = static::escapeSearchString($oImapClient, $sRawValue);
|
||||
break;
|
||||
|
||||
case 'HAS':
|
||||
$aValue = \explode(',', \strtolower($sRawValue));
|
||||
$aValue = \array_map('trim', $aValue);
|
||||
$aCompareArray = array('file', 'files', 'attachment', 'attachments');
|
||||
if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue))) {
|
||||
// Simple, is not detailed search (Sometimes doesn't work)
|
||||
$aCriteriasResult[] = 'OR OR OR';
|
||||
$aCriteriasResult[] = 'HEADER Content-Type application/';
|
||||
$aCriteriasResult[] = 'HEADER Content-Type multipart/m';
|
||||
$aCriteriasResult[] = 'HEADER Content-Type multipart/signed';
|
||||
$aCriteriasResult[] = 'HEADER Content-Type multipart/report';
|
||||
}
|
||||
|
||||
case 'IS':
|
||||
$aValue = \explode(',', \strtolower($sRawValue));
|
||||
if (\in_array('flagged', $aValue)) {
|
||||
$aCriteriasResult[] = 'FLAGGED';
|
||||
$bUseCache = false;
|
||||
} else if (\in_array('unflagged', $aValue)) {
|
||||
$aCriteriasResult[] = 'UNFLAGGED';
|
||||
$bUseCache = false;
|
||||
}
|
||||
if (\in_array('seen', $aValue)) {
|
||||
$aCriteriasResult[] = 'SEEN';
|
||||
$bUseCache = false;
|
||||
} else if (\in_array('unseen', $aValue)) {
|
||||
$aCriteriasResult[] = 'UNSEEN';
|
||||
$bUseCache = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'LARGER':
|
||||
case 'SMALLER':
|
||||
$aCriteriasResult[] = $sName;
|
||||
$aCriteriasResult[] = static::parseFriendlySize($sRawValue);
|
||||
break;
|
||||
|
||||
case 'DATE':
|
||||
$iDateStampFrom = $iDateStampTo = 0;
|
||||
$sDate = $sRawValue;
|
||||
$aDate = \explode('/', $sDate);
|
||||
if (2 === \count($aDate)) {
|
||||
if (\strlen($aDate[0])) {
|
||||
$iDateStampFrom = static::parseSearchDate($aDate[0], $iTimeZoneOffset);
|
||||
}
|
||||
|
||||
if (\strlen($aDate[1])) {
|
||||
$iDateStampTo = static::parseSearchDate($aDate[1], $iTimeZoneOffset);
|
||||
$iDateStampTo += 60 * 60 * 24;
|
||||
}
|
||||
} else {
|
||||
if (\strlen($sDate)) {
|
||||
$iDateStampFrom = static::parseSearchDate($sDate, $iTimeZoneOffset);
|
||||
$iDateStampTo = $iDateStampFrom + 60 * 60 * 24;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < $iDateStampFrom) {
|
||||
$aCriteriasResult[] = 'SINCE';
|
||||
$aCriteriasResult[] = \gmdate('j-M-Y', $iTimeFilter > $iDateStampFrom ?
|
||||
$iTimeFilter : $iDateStampFrom);
|
||||
|
||||
$iTimeFilter = 0;
|
||||
}
|
||||
|
||||
if (0 < $iDateStampTo) {
|
||||
$aCriteriasResult[] = 'BEFORE';
|
||||
$aCriteriasResult[] = \gmdate('j-M-Y', $iDateStampTo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim(\implode(' ', $aCriteriasResult));
|
||||
|
||||
if (0 < $iTimeFilter) {
|
||||
$sCriteriasResult .= ' SINCE '.\gmdate('j-M-Y', $iTimeFilter);
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
if (\MailSo\Config::$MessageListUndeletedOnly) {
|
||||
$sCriteriasResult = \trim($sCriteriasResult.' UNDELETED');
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
if (\MailSo\Config::$MessageListPermanentFilter) {
|
||||
$sCriteriasResult = \trim($sCriteriasResult.' '.\MailSo\Config::$MessageListPermanentFilter);
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
|
||||
return $sCriteriasResult ?: 'ALL';
|
||||
}
|
||||
|
||||
private static function escapeSearchString(\MailSo\Imap\ImapClient $oImapClient, string $sSearch) : string
|
||||
{
|
||||
return !\MailSo\Base\Utils::IsAscii($sSearch)
|
||||
? '{'.\strlen($sSearch).'}'."\r\n".$sSearch : $oImapClient->EscapeString($sSearch);
|
||||
}
|
||||
|
||||
private static function parseSearchDate(string $sDate, int $iTimeZoneOffset) : int
|
||||
{
|
||||
if (\strlen($sDate)) {
|
||||
$oDateTime = \DateTime::createFromFormat('Y.m.d', $sDate, \MailSo\Base\DateTimeHelper::GetUtcTimeZoneObject());
|
||||
return $oDateTime ? $oDateTime->getTimestamp() - $iTimeZoneOffset : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static function parseFriendlySize(string $sSize) : int
|
||||
{
|
||||
$sSize = \preg_replace('/[^0-9KM]/', '', \strtoupper($sSize));
|
||||
$iResult = \intval($sSize);
|
||||
switch (\substr($sSize, -1)) {
|
||||
case 'K':
|
||||
$iResult *= 1024;
|
||||
case 'M':
|
||||
$iResult *= 1024;
|
||||
}
|
||||
return $iResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* SnappyMail search like: 'from=foo&to=test&is[]=unseen&is[]=flagged'
|
||||
*/
|
||||
private static function parseQueryString(string $sSearch) : array
|
||||
{
|
||||
$aResult = array();
|
||||
$aParams = array();
|
||||
\parse_str($sSearch, $aParams);
|
||||
foreach ($aParams as $sName => $mValue) {
|
||||
if (\is_array($mValue)) {
|
||||
$mValue = \implode(',', $mValue);
|
||||
}
|
||||
if (\strlen($mValue)) {
|
||||
$sName = \strtoupper($sName);
|
||||
if ('MAIL' === $sName) {
|
||||
$sName = 'EMAIL';
|
||||
} else if ('TEXT' === $sName) {
|
||||
$sName = 'BODY';
|
||||
} else if ('SIZE' === $sName || 'BIGGER' === $sName || 'MINSIZE' === $sName) {
|
||||
$sName = 'LARGER';
|
||||
} else if ('MAXSIZE' === $sName) {
|
||||
$sName = 'SMALLER';
|
||||
}
|
||||
switch ($sName) {
|
||||
case 'BODY':
|
||||
case 'EMAIL':
|
||||
case 'FROM':
|
||||
case 'TO':
|
||||
case 'SUBJECT':
|
||||
case 'IS':
|
||||
case 'IN':
|
||||
case 'HAS':
|
||||
case 'SMALLER':
|
||||
case 'LARGER':
|
||||
case 'DATE':
|
||||
$aResult[$sName] = $mValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* RainLoop search like: 'from:"foo" to:"test" is:unseen,flagged'
|
||||
*/
|
||||
private static function parseSearchString(string $sSearch) : array
|
||||
{
|
||||
$aResult = array();
|
||||
|
||||
$aCache = array();
|
||||
|
||||
$sReg = 'in|e?mail|from|to|subject|has|is|date|text|body|size|larger|bigger|smaller|maxsize|minsize';
|
||||
|
||||
$sSearch = \MailSo\Base\Utils::StripSpaces($sSearch);
|
||||
$sSearch = \trim(\preg_replace('/('.$sReg.'): /i', '\\1:', $sSearch));
|
||||
|
||||
$mMatch = array();
|
||||
if (\preg_match_all('/".*?(?<!\\\)"/', $sSearch, $mMatch)) {
|
||||
foreach ($mMatch[0] as $sItem) {
|
||||
do {
|
||||
$sKey = \MailSo\Base\Utils::Sha1Rand();
|
||||
}
|
||||
while (isset($aCache[$sKey]));
|
||||
|
||||
$aCache[$sKey] = \stripcslashes($sItem);
|
||||
$sSearch = \str_replace($sItem, $sKey, $sSearch);
|
||||
}
|
||||
}
|
||||
|
||||
if (\preg_match_all('/\'.*?(?<!\\\)\'/', $sSearch, $mMatch)) {
|
||||
foreach ($mMatch[0] as $sItem) {
|
||||
do {
|
||||
$sKey = \MailSo\Base\Utils::Sha1Rand();
|
||||
}
|
||||
while (isset($aCache[$sKey]));
|
||||
|
||||
$aCache[$sKey] = \stripcslashes($sItem);
|
||||
$sSearch = \str_replace($sItem, $sKey, $sSearch);
|
||||
}
|
||||
}
|
||||
|
||||
if (\preg_match_all('/('.$sReg.'):([^\s]*)/i', $sSearch, $mMatch)) {
|
||||
if (\is_array($mMatch[0])) {
|
||||
foreach ($mMatch[0] as $sToken) {
|
||||
$sSearch = \str_replace($sToken, '', $sSearch);
|
||||
}
|
||||
|
||||
$sSearch = \MailSo\Base\Utils::StripSpaces($sSearch);
|
||||
}
|
||||
|
||||
foreach ($mMatch[1] as $iIndex => $sName) {
|
||||
if (isset($mMatch[2][$iIndex]) && \strlen($mMatch[2][$iIndex])) {
|
||||
$sName = \strtoupper($sName);
|
||||
if ('MAIL' === $sName) {
|
||||
$sName = 'EMAIL';
|
||||
} else if ('TEXT' === $sName) {
|
||||
$sName = 'BODY';
|
||||
} else if ('SIZE' === $sName || 'BIGGER' === $sName || 'MINSIZE' === $sName) {
|
||||
$sName = 'LARGER';
|
||||
} else if ('MAXSIZE' === $sName) {
|
||||
$sName = 'SMALLER';
|
||||
}
|
||||
$aResult[$sName] = $mMatch[2][$iIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aResult as $sName => $sValue) {
|
||||
if (isset($aCache[$sValue])) {
|
||||
$aResult[$sName] = \trim($aCache[$sValue], '"\' ');
|
||||
}
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
}
|
|
@ -27,26 +27,11 @@ class FolderCollection extends \MailSo\Base\Collection
|
|||
*/
|
||||
public $FoldersHash = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $IsMetadataSupported = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $IsThreadsSupported = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $IsSortSupported = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $IsListStatusSupported = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
@ -59,6 +44,7 @@ class FolderCollection extends \MailSo\Base\Collection
|
|||
|
||||
public $quotaUsage = 0;
|
||||
public $quotaLimit = 0;
|
||||
public $capabilities = array();
|
||||
|
||||
public function append($oFolder, bool $bToTop = false) : void
|
||||
{
|
||||
|
@ -148,15 +134,13 @@ class FolderCollection extends \MailSo\Base\Collection
|
|||
return \array_merge(parent::jsonSerialize(), array(
|
||||
'Namespace' => $this->GetNamespace(),
|
||||
'FoldersHash' => $this->FoldersHash ?: '',
|
||||
'IsMetadataSupported' => $this->IsMetadataSupported,
|
||||
'IsThreadsSupported' => $this->IsThreadsSupported,
|
||||
'IsSortSupported' => $this->IsSortSupported,
|
||||
'IsListStatusSupported' => $this->IsListStatusSupported,
|
||||
'quotaUsage' => $this->quotaUsage,
|
||||
'quotaLimit' => $this->quotaLimit,
|
||||
'Optimized' => $this->Optimized,
|
||||
'CountRec' => $this->CountRec(),
|
||||
'SystemFolders' => empty($this->SystemFolders) ? null : $this->SystemFolders
|
||||
'SystemFolders' => empty($this->SystemFolders) ? null : $this->SystemFolders,
|
||||
'Capabilities' => \array_values($this->capabilities)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -640,6 +640,8 @@ class MailClient
|
|||
{
|
||||
$aFlags = array();
|
||||
|
||||
list($iCount, $iUnseenCount, $iUidNext, $iHighestModSeq, $iAppendLimit, $sMailboxId) = $this->initFolderValues($sFolderName);
|
||||
|
||||
if (\count($aUids))
|
||||
{
|
||||
$this->oImapClient->FolderSelect($sFolderName);
|
||||
|
@ -661,16 +663,14 @@ class MailClient
|
|||
'IsFlagged' => \in_array('\\flagged', $aLowerFlags),
|
||||
'IsAnswered' => \in_array('\\answered', $aLowerFlags),
|
||||
'IsDeleted' => \in_array('\\deleted', $aLowerFlags),
|
||||
'IsForwarded' => \in_array(\strtolower('$Forwarded'), $aLowerFlags) || ($sForwardedFlag && \in_array(\strtolower($sForwardedFlag), $aLowerFlags)),
|
||||
'IsReadReceipt' => \in_array(\strtolower('$MDNSent'), $aLowerFlags) || ($sReadReceiptFlag && \in_array(\strtolower($sReadReceiptFlag), $aLowerFlags)),
|
||||
'IsForwarded' => \in_array(\strtolower('$Forwarded'), $aLowerFlags)/* || ($sForwardedFlag && \in_array(\strtolower($sForwardedFlag), $aLowerFlags))*/,
|
||||
'IsReadReceipt' => \in_array(\strtolower('$MDNSent'), $aLowerFlags)/* || ($sReadReceiptFlag && \in_array(\strtolower($sReadReceiptFlag), $aLowerFlags))*/,
|
||||
'IsJunk' => !\in_array(\strtolower('$NonJunk'), $aLowerFlags) && \in_array(\strtolower('$Junk'), $aLowerFlags),
|
||||
'IsPhishing' => \in_array(\strtolower('$Phishing'), $aLowerFlags)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
list($iCount, $iUnseenCount, $iUidNext, $iHighestModSeq, $iAppendLimit, $sMailboxId) = $this->initFolderValues($sFolderName);
|
||||
|
||||
return array(
|
||||
'Folder' => $sFolderName,
|
||||
'Hash' => $this->GenerateFolderHash($sFolderName, $iCount, $iUidNext, $iHighestModSeq),
|
||||
|
@ -714,470 +714,12 @@ class MailClient
|
|||
return 0 < $iResult ? $iResult : 0;
|
||||
}
|
||||
|
||||
private function escapeSearchString(string $sSearch) : string
|
||||
{
|
||||
return !\MailSo\Base\Utils::IsAscii($sSearch)
|
||||
? '{'.\strlen($sSearch).'}'."\r\n".$sSearch : $this->oImapClient->EscapeString($sSearch);
|
||||
}
|
||||
|
||||
private function parseSearchDate(string $sDate, int $iTimeZoneOffset) : int
|
||||
{
|
||||
$iResult = 0;
|
||||
if (\strlen($sDate))
|
||||
{
|
||||
$oDateTime = \DateTime::createFromFormat('Y.m.d', $sDate, \MailSo\Base\DateTimeHelper::GetUtcTimeZoneObject());
|
||||
return $oDateTime ? $oDateTime->getTimestamp() - $iTimeZoneOffset : 0;
|
||||
}
|
||||
|
||||
return $iResult;
|
||||
}
|
||||
|
||||
private function parseFriendlySize(string $sSize) : int
|
||||
{
|
||||
$sSize = preg_replace('/[^0-9bBkKmM]/', '', $sSize);
|
||||
|
||||
$iResult = 0;
|
||||
$aMatch = array();
|
||||
|
||||
if (\preg_match('/([\d]+)(B|KB|M|MB|G|GB)$/i', $sSize, $aMatch) && isset($aMatch[1], $aMatch[2]))
|
||||
{
|
||||
$iResult = (int) $aMatch[1];
|
||||
switch (\strtoupper($aMatch[2]))
|
||||
{
|
||||
case 'K':
|
||||
case 'KB':
|
||||
$iResult *= 1024;
|
||||
case 'M':
|
||||
case 'MB':
|
||||
$iResult *= 1024;
|
||||
case 'G':
|
||||
case 'GB':
|
||||
$iResult *= 1024;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iResult = (int) $sSize;
|
||||
}
|
||||
|
||||
return $iResult;
|
||||
}
|
||||
|
||||
private function parseSearchString(string $sSearch) : array
|
||||
{
|
||||
$aResult = array(
|
||||
'OTHER' => ''
|
||||
);
|
||||
|
||||
$aCache = array();
|
||||
|
||||
$sReg = 'e?mail|from|to|subject|has|is|date|text|body|size|larger|bigger|smaller|maxsize|minsize';
|
||||
|
||||
$sSearch = \MailSo\Base\Utils::StripSpaces($sSearch);
|
||||
$sSearch = \trim(\preg_replace('/('.$sReg.'): /i', '\\1:', $sSearch));
|
||||
|
||||
$mMatch = array();
|
||||
\preg_match_all('/".*?(?<!\\\)"/', $sSearch, $mMatch);
|
||||
if (\is_array($mMatch) && isset($mMatch[0]) && \is_array($mMatch[0]))
|
||||
{
|
||||
foreach ($mMatch[0] as $sItem)
|
||||
{
|
||||
do
|
||||
{
|
||||
$sKey = \MailSo\Base\Utils::Sha1Rand();
|
||||
}
|
||||
while (isset($aCache[$sKey]));
|
||||
|
||||
$aCache[$sKey] = \stripcslashes($sItem);
|
||||
$sSearch = \str_replace($sItem, $sKey, $sSearch);
|
||||
}
|
||||
}
|
||||
|
||||
\preg_match_all('/\'.*?(?<!\\\)\'/', $sSearch, $mMatch);
|
||||
if (\is_array($mMatch) && isset($mMatch[0]) && \is_array($mMatch[0]))
|
||||
{
|
||||
foreach ($mMatch[0] as $sItem)
|
||||
{
|
||||
do
|
||||
{
|
||||
$sKey = \MailSo\Base\Utils::Sha1Rand();
|
||||
}
|
||||
while (isset($aCache[$sKey]));
|
||||
|
||||
$aCache[$sKey] = \stripcslashes($sItem);
|
||||
$sSearch = \str_replace($sItem, $sKey, $sSearch);
|
||||
}
|
||||
}
|
||||
|
||||
$mMatch = array();
|
||||
\preg_match_all('/('.$sReg.'):([^\s]*)/i', $sSearch, $mMatch);
|
||||
if (\is_array($mMatch) && isset($mMatch[1]) && \is_array($mMatch[1]) && \count($mMatch[1]))
|
||||
{
|
||||
if (\is_array($mMatch[0]))
|
||||
{
|
||||
foreach ($mMatch[0] as $sToken)
|
||||
{
|
||||
$sSearch = \str_replace($sToken, '', $sSearch);
|
||||
}
|
||||
|
||||
$sSearch = \MailSo\Base\Utils::StripSpaces($sSearch);
|
||||
}
|
||||
|
||||
foreach ($mMatch[1] as $iIndex => $sName)
|
||||
{
|
||||
if (isset($mMatch[2][$iIndex]) && \strlen($mMatch[2][$iIndex]))
|
||||
{
|
||||
$sName = \strtoupper($sName);
|
||||
$sValue = $mMatch[2][$iIndex];
|
||||
switch ($sName)
|
||||
{
|
||||
case 'TEXT':
|
||||
case 'BODY':
|
||||
case 'EMAIL':
|
||||
case 'MAIL':
|
||||
case 'FROM':
|
||||
case 'TO':
|
||||
case 'SUBJECT':
|
||||
case 'IS':
|
||||
case 'HAS':
|
||||
case 'SIZE':
|
||||
case 'SMALLER':
|
||||
case 'LARGER':
|
||||
case 'BIGGER':
|
||||
case 'MAXSIZE':
|
||||
case 'MINSIZE':
|
||||
case 'DATE':
|
||||
if ('MAIL' === $sName)
|
||||
{
|
||||
$sName = 'EMAIL';
|
||||
}
|
||||
if ('BODY' === $sName)
|
||||
{
|
||||
$sName = 'TEXT';
|
||||
}
|
||||
if ('SIZE' === $sName || 'BIGGER' === $sName || 'MINSIZE' === $sName)
|
||||
{
|
||||
$sName = 'LARGER';
|
||||
}
|
||||
if ('MAXSIZE' === $sName)
|
||||
{
|
||||
$sName = 'SMALLER';
|
||||
}
|
||||
$aResult[$sName] = $sValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aResult['OTHER'] = $sSearch;
|
||||
foreach ($aResult as $sName => $sValue)
|
||||
{
|
||||
if (isset($aCache[$sValue]))
|
||||
{
|
||||
$aResult[$sName] = \trim($aCache[$sValue], '"\' ');
|
||||
}
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
private function getImapSearchCriterias(string $sSearch, string $sFilter, int $iTimeZoneOffset = 0, bool &$bUseCache = true) : string
|
||||
{
|
||||
$bUseCache = true;
|
||||
$iTimeFilter = 0;
|
||||
$aCriteriasResult = array();
|
||||
|
||||
if (0 < \MailSo\Config::$MessageListDateFilter)
|
||||
{
|
||||
$iD = \time() - 3600 * 24 * 30 * \MailSo\Config::$MessageListDateFilter;
|
||||
$iTimeFilter = \gmmktime(1, 1, 1, \gmdate('n', $iD), 1, \gmdate('Y', $iD));
|
||||
}
|
||||
|
||||
if (\strlen(\trim($sSearch)))
|
||||
{
|
||||
$sResultBodyTextSearch = '';
|
||||
|
||||
$aLines = $this->parseSearchString($sSearch);
|
||||
|
||||
if (1 === \count($aLines) && isset($aLines['OTHER']))
|
||||
{
|
||||
$sValue = $this->escapeSearchString($aLines['OTHER']);
|
||||
|
||||
if (\MailSo\Config::$MessageListFastSimpleSearch)
|
||||
{
|
||||
$aCriteriasResult[] = 'OR OR OR';
|
||||
$aCriteriasResult[] = 'FROM';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'TO';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'CC';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'SUBJECT';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCriteriasResult[] = 'TEXT';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($aLines['EMAIL']))
|
||||
{
|
||||
$sValue = $this->escapeSearchString($aLines['EMAIL']);
|
||||
|
||||
$aCriteriasResult[] = 'OR OR';
|
||||
$aCriteriasResult[] = 'FROM';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'TO';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'CC';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
|
||||
unset($aLines['EMAIL']);
|
||||
}
|
||||
|
||||
if (isset($aLines['TO']))
|
||||
{
|
||||
$sValue = $this->escapeSearchString($aLines['TO']);
|
||||
|
||||
$aCriteriasResult[] = 'OR';
|
||||
$aCriteriasResult[] = 'TO';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
$aCriteriasResult[] = 'CC';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
|
||||
unset($aLines['TO']);
|
||||
}
|
||||
|
||||
$sMainText = '';
|
||||
foreach ($aLines as $sName => $sRawValue)
|
||||
{
|
||||
if ('' === \trim($sRawValue))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$sValue = $this->escapeSearchString($sRawValue);
|
||||
switch ($sName)
|
||||
{
|
||||
case 'FROM':
|
||||
$aCriteriasResult[] = 'FROM';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
break;
|
||||
case 'SUBJECT':
|
||||
$aCriteriasResult[] = 'SUBJECT';
|
||||
$aCriteriasResult[] = $sValue;
|
||||
break;
|
||||
case 'OTHER':
|
||||
case 'TEXT':
|
||||
$sMainText .= ' '.$sRawValue;
|
||||
break;
|
||||
case 'HAS':
|
||||
$aValue = \explode(',', \strtolower($sRawValue));
|
||||
$aValue = \array_map('trim', $aValue);
|
||||
|
||||
$aCompareArray = array('file', 'files', 'attach', 'attachs', 'attachment', 'attachments');
|
||||
if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue)))
|
||||
{
|
||||
// Simple, is not detailed search (Sometimes doesn't work)
|
||||
$aCriteriasResult[] = 'OR OR OR';
|
||||
$aCriteriasResult[] = 'HEADER Content-Type application/';
|
||||
$aCriteriasResult[] = 'HEADER Content-Type multipart/m';
|
||||
$aCriteriasResult[] = 'HEADER Content-Type multipart/signed';
|
||||
$aCriteriasResult[] = 'HEADER Content-Type multipart/report';
|
||||
}
|
||||
|
||||
case 'IS':
|
||||
$aValue = \explode(',', \strtolower($sRawValue));
|
||||
$aValue = \array_map('trim', $aValue);
|
||||
|
||||
$aCompareArray = array('flag', 'flagged', 'star', 'starred', 'pinned');
|
||||
$aCompareArray2 = array('unflag', 'unflagged', 'unstar', 'unstarred', 'unpinned');
|
||||
if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue)))
|
||||
{
|
||||
$aCriteriasResult[] = 'FLAGGED';
|
||||
$bUseCache = false;
|
||||
}
|
||||
else if (\count($aCompareArray2) > \count(\array_diff($aCompareArray2, $aValue)))
|
||||
{
|
||||
$aCriteriasResult[] = 'UNFLAGGED';
|
||||
$bUseCache = false;
|
||||
}
|
||||
|
||||
$aCompareArray = array('unread', 'unseen');
|
||||
$aCompareArray2 = array('read', 'seen');
|
||||
if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue)))
|
||||
{
|
||||
$aCriteriasResult[] = 'UNSEEN';
|
||||
$bUseCache = false;
|
||||
}
|
||||
else if (\count($aCompareArray2) > \count(\array_diff($aCompareArray2, $aValue)))
|
||||
{
|
||||
$aCriteriasResult[] = 'SEEN';
|
||||
$bUseCache = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'LARGER':
|
||||
$aCriteriasResult[] = 'LARGER';
|
||||
$aCriteriasResult[] = $this->parseFriendlySize($sRawValue);
|
||||
break;
|
||||
case 'SMALLER':
|
||||
$aCriteriasResult[] = 'SMALLER';
|
||||
$aCriteriasResult[] = $this->parseFriendlySize($sRawValue);
|
||||
break;
|
||||
case 'DATE':
|
||||
$iDateStampFrom = $iDateStampTo = 0;
|
||||
|
||||
$sDate = $sRawValue;
|
||||
$aDate = \explode('/', $sDate);
|
||||
|
||||
if (2 === \count($aDate))
|
||||
{
|
||||
if (\strlen($aDate[0]))
|
||||
{
|
||||
$iDateStampFrom = $this->parseSearchDate($aDate[0], $iTimeZoneOffset);
|
||||
}
|
||||
|
||||
if (\strlen($aDate[1]))
|
||||
{
|
||||
$iDateStampTo = $this->parseSearchDate($aDate[1], $iTimeZoneOffset);
|
||||
$iDateStampTo += 60 * 60 * 24;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (\strlen($sDate))
|
||||
{
|
||||
$iDateStampFrom = $this->parseSearchDate($sDate, $iTimeZoneOffset);
|
||||
$iDateStampTo = $iDateStampFrom + 60 * 60 * 24;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < $iDateStampFrom)
|
||||
{
|
||||
$aCriteriasResult[] = 'SINCE';
|
||||
$aCriteriasResult[] = \gmdate('j-M-Y', $iTimeFilter > $iDateStampFrom ?
|
||||
$iTimeFilter : $iDateStampFrom);
|
||||
|
||||
$iTimeFilter = 0;
|
||||
}
|
||||
|
||||
if (0 < $iDateStampTo)
|
||||
{
|
||||
$aCriteriasResult[] = 'BEFORE';
|
||||
$aCriteriasResult[] = \gmdate('j-M-Y', $iDateStampTo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ('' !== \trim($sMainText))
|
||||
{
|
||||
$sMainText = \trim(\MailSo\Base\Utils::StripSpaces($sMainText), '"');
|
||||
$sResultBodyTextSearch .= ' '.$sMainText;
|
||||
}
|
||||
}
|
||||
|
||||
$sResultBodyTextSearch = \trim($sResultBodyTextSearch);
|
||||
if (\strlen($sResultBodyTextSearch))
|
||||
{
|
||||
$aCriteriasResult[] = 'BODY';
|
||||
$aCriteriasResult[] = $this->escapeSearchString($sResultBodyTextSearch);
|
||||
}
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim(\implode(' ', $aCriteriasResult));
|
||||
|
||||
if (0 < $iTimeFilter)
|
||||
{
|
||||
$sCriteriasResult .= ' SINCE '.\gmdate('j-M-Y', $iTimeFilter);
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
if (\MailSo\Config::$MessageListUndeletedOnly)
|
||||
{
|
||||
$sCriteriasResult = \trim($sCriteriasResult.' UNDELETED');
|
||||
}
|
||||
|
||||
$sFilter = \trim($sFilter);
|
||||
if ('' !== $sFilter)
|
||||
{
|
||||
$sCriteriasResult .= ' '.$sFilter;
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
if ('' !== \MailSo\Config::$MessageListPermanentFilter)
|
||||
{
|
||||
$sCriteriasResult = \trim($sCriteriasResult.' '.\MailSo\Config::$MessageListPermanentFilter);
|
||||
}
|
||||
|
||||
$sCriteriasResult = \trim($sCriteriasResult);
|
||||
if ('' === $sCriteriasResult)
|
||||
{
|
||||
$sCriteriasResult = 'ALL';
|
||||
}
|
||||
|
||||
return $sCriteriasResult;
|
||||
}
|
||||
|
||||
private function threadArrayMap(array $aThreads) : array
|
||||
{
|
||||
$aNew = array();
|
||||
foreach ($aThreads as $mItem)
|
||||
{
|
||||
if (!\is_array($mItem))
|
||||
{
|
||||
$aNew[] = $mItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
$mMap = $this->threadArrayMap($mItem);
|
||||
if (\count($mMap))
|
||||
{
|
||||
$aNew = \array_merge($aNew, $mMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aNew;
|
||||
}
|
||||
|
||||
private function compileThreadArray(array $aThreads) : array
|
||||
{
|
||||
$aResult = array();
|
||||
foreach ($aThreads as $mItem)
|
||||
{
|
||||
if (\is_array($mItem))
|
||||
{
|
||||
$aMap = $this->threadArrayMap($mItem);
|
||||
if (1 < \count($aMap))
|
||||
{
|
||||
$aResult[] = $aMap;
|
||||
}
|
||||
else if (\count($aMap))
|
||||
{
|
||||
$aResult[] = $aMap[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aResult[] = $mItem;
|
||||
}
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \MailSo\Base\Exceptions\InvalidArgumentException
|
||||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
public function MessageListThreadsMap(string $sFolderName, string $sFolderHash, array $aIndexOrUids, \MailSo\Cache\CacheClient $oCacher, bool $bCacheOnly = false) : array
|
||||
public function MessageListThreadsMap(string $sFolderName, string $sFolderHash, \MailSo\Cache\CacheClient $oCacher) : array
|
||||
{
|
||||
$iThreadLimit = \MailSo\Config::$LargeThreadLimit;
|
||||
|
||||
|
@ -1222,11 +764,6 @@ class MailClient
|
|||
}
|
||||
}
|
||||
|
||||
if ($bCacheOnly)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->oImapClient->FolderExamine($sFolderName);
|
||||
|
||||
$aThreadUids = array();
|
||||
|
@ -1237,117 +774,16 @@ class MailClient
|
|||
catch (\MailSo\Imap\Exceptions\RuntimeException $oException)
|
||||
{
|
||||
unset($oException);
|
||||
$aThreadUids = array();
|
||||
}
|
||||
|
||||
// Flatten to single levels
|
||||
$aResult = array();
|
||||
$aCompiledThreads = $this->compileThreadArray($aThreadUids);
|
||||
|
||||
foreach ($aCompiledThreads as $mData)
|
||||
{
|
||||
if (\is_array($mData))
|
||||
{
|
||||
foreach ($mData as $mSubData)
|
||||
{
|
||||
$aResult[(int) $mSubData] =
|
||||
\array_diff($mData, array((int) $mSubData));
|
||||
}
|
||||
}
|
||||
else if (\is_int($mData) || \is_string($mData))
|
||||
{
|
||||
$aResult[(int) $mData] = (int) $mData;
|
||||
}
|
||||
foreach ($aThreadUids as $mItem) {
|
||||
$aMap = [];
|
||||
\array_walk_recursive($mItem, function($a) use (&$aMap) { $aMap[] = $a; });
|
||||
$aResult[] = $aMap;
|
||||
}
|
||||
|
||||
$aParentsMap = array();
|
||||
foreach ($aIndexOrUids as $iUid)
|
||||
{
|
||||
if (isset($aResult[$iUid]) && \is_array($aResult[$iUid]))
|
||||
{
|
||||
foreach ($aResult[$iUid] as $iTempUid)
|
||||
{
|
||||
$aParentsMap[$iTempUid] = $iUid;
|
||||
if (isset($aResult[$iTempUid]))
|
||||
{
|
||||
unset($aResult[$iTempUid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aSortedThreads = array();
|
||||
foreach ($aIndexOrUids as $iUid)
|
||||
{
|
||||
if (isset($aResult[$iUid]))
|
||||
{
|
||||
$aSortedThreads[$iUid] = $iUid;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aIndexOrUids as $iUid)
|
||||
{
|
||||
if (!isset($aSortedThreads[$iUid]) &&
|
||||
isset($aParentsMap[$iUid]) &&
|
||||
isset($aSortedThreads[$aParentsMap[$iUid]]))
|
||||
{
|
||||
if (!\is_array($aSortedThreads[$aParentsMap[$iUid]]))
|
||||
{
|
||||
$aSortedThreads[$aParentsMap[$iUid]] = array();
|
||||
}
|
||||
|
||||
$aSortedThreads[$aParentsMap[$iUid]][] = $iUid;
|
||||
}
|
||||
}
|
||||
|
||||
$aResult = $aSortedThreads;
|
||||
unset($aParentsMap, $aSortedThreads);
|
||||
|
||||
$aTemp = array();
|
||||
foreach ($aResult as $iUid => $mValue)
|
||||
{
|
||||
if (0 < $iThreadLimit && \is_array($mValue) && $iThreadLimit < \count($mValue))
|
||||
{
|
||||
$aParts = \array_chunk($mValue, $iThreadLimit);
|
||||
if (0 < count($aParts))
|
||||
{
|
||||
foreach ($aParts as $iIndex => $aItem)
|
||||
{
|
||||
if (0 === $iIndex)
|
||||
{
|
||||
$aResult[$iUid] = $aItem;
|
||||
}
|
||||
else if (0 < $iIndex && \is_array($aItem))
|
||||
{
|
||||
$mFirst = \array_shift($aItem);
|
||||
if (!empty($mFirst))
|
||||
{
|
||||
$aTemp[$mFirst] = \count($aItem) ? $aItem : $mFirst;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aTemp as $iUid => $mValue)
|
||||
{
|
||||
$aResult[$iUid] = $mValue;
|
||||
}
|
||||
|
||||
unset($aTemp);
|
||||
|
||||
$aLastResult = array();
|
||||
foreach ($aIndexOrUids as $iUid)
|
||||
{
|
||||
if (isset($aResult[$iUid]))
|
||||
{
|
||||
$aLastResult[$iUid] = $aResult[$iUid];
|
||||
}
|
||||
}
|
||||
|
||||
$aResult = $aLastResult;
|
||||
unset($aLastResult);
|
||||
|
||||
if ($oCacher && $oCacher->IsInited() && !empty($sSerializedHashKey))
|
||||
{
|
||||
$oCacher->Set($sSerializedHashKey, \json_encode(array(
|
||||
|
@ -1423,7 +859,7 @@ class MailClient
|
|||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
public function GetUids(?\MailSo\Cache\CacheClient $oCacher, string $sSearch,
|
||||
string $sFilter, string $sFolderName, string $sFolderHash,
|
||||
string $sFolderName, string $sFolderHash,
|
||||
bool $bUseSortIfSupported = false, string $sSort = '') : array
|
||||
{
|
||||
/* TODO: Validate $sSort
|
||||
|
@ -1476,17 +912,16 @@ class MailClient
|
|||
|
||||
$bUseSortIfSupported = $bUseSortIfSupported && !\strlen($sSearch) && $this->oImapClient->IsSupported('SORT');
|
||||
|
||||
$sSearchCriterias = $this->getImapSearchCriterias($sSearch, $sFilter, 0, $bUseCacheAfterSearch);
|
||||
$sSearchCriterias = \MailSo\Imap\SearchCriterias::fromString($this->oImapClient, $sFolderName, $sSearch, 0, $bUseCacheAfterSearch);
|
||||
if ($bUseCacheAfterSearch && $oCacher && $oCacher->IsInited())
|
||||
{
|
||||
$sSerializedHash = 'GetUids/'.
|
||||
($bUseSortIfSupported ? 'S' . $sSort : 'N').'/'.
|
||||
$this->GenerateImapClientHash().'/'.
|
||||
$sFolderName.'/'.$sSearchCriterias;
|
||||
|
||||
$sSerializedLog = '"'.$sFolderName.'" / '.$sSearchCriterias.'';
|
||||
|
||||
$sSerialized = $oCacher->Get($sSerializedHash);
|
||||
// $sSerialized = $oCacher->Get($sSerializedHash);
|
||||
if (!empty($sSerialized))
|
||||
{
|
||||
$aSerialized = \json_decode($sSerialized, true);
|
||||
|
@ -1508,10 +943,15 @@ class MailClient
|
|||
|
||||
if (!\is_array($aResultUids))
|
||||
{
|
||||
$aResultUids = $bUseSortIfSupported ?
|
||||
$this->oImapClient->MessageSimpleSort(array($sSort ?: 'REVERSE DATE'), $sSearchCriterias, true) :
|
||||
$this->oImapClient->MessageSimpleSearch($sSearchCriterias, true, \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? '' : 'UTF-8')
|
||||
;
|
||||
if ($bUseSortIfSupported) {
|
||||
// $this->oImapClient->IsSupported('ESORT')
|
||||
// $aResultUids = $this->oImapClient->MessageSimpleESort(array($sSort ?: 'REVERSE DATE'), $sSearchCriterias)['ALL'];
|
||||
$aResultUids = $this->oImapClient->MessageSimpleSort(array($sSort ?: 'REVERSE DATE'), $sSearchCriterias);
|
||||
} else {
|
||||
// $this->oImapClient->IsSupported('ESEARCH')
|
||||
// $aResultUids = $this->oImapClient->MessageSimpleESearch($sSearchCriterias, null, true, \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? '' : 'UTF-8')
|
||||
$aResultUids = $this->oImapClient->MessageSimpleSearch($sSearchCriterias, true, \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? '' : 'UTF-8');
|
||||
}
|
||||
|
||||
if (!$bUidsFromCacher && $bUseCacheAfterSearch && \is_array($aResultUids) && $oCacher && $oCacher->IsInited() && \strlen($sSerializedHash))
|
||||
{
|
||||
|
@ -1531,6 +971,7 @@ class MailClient
|
|||
}
|
||||
|
||||
/**
|
||||
* Runs SORT/SEARCH when $sSearch is provided
|
||||
* @throws \MailSo\Base\Exceptions\InvalidArgumentException
|
||||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
|
@ -1538,17 +979,17 @@ class MailClient
|
|||
public function MessageList(string $sFolderName, int $iOffset = 0, int $iLimit = 10,
|
||||
string $sSearch = '', int $iPrevUidNext = 0, ?\MailSo\Cache\CacheClient $oCacher = null,
|
||||
bool $bUseSortIfSupported = false, bool $bUseThreadSortIfSupported = false,
|
||||
int $iThreadUid = 0, string $sFilter = '', string $sSort = '') : MessageCollection
|
||||
int $iThreadUid = 0, string $sSort = '') : MessageCollection
|
||||
{
|
||||
$sFilter = \trim($sFilter);
|
||||
$sSearch = \trim($sSearch);
|
||||
if (!\MailSo\Base\Validator::RangeInt($iOffset, 0) ||
|
||||
!\MailSo\Base\Validator::RangeInt($iLimit, 0, 999))
|
||||
{
|
||||
throw new \MailSo\Base\Exceptions\InvalidArgumentException;
|
||||
}
|
||||
|
||||
$bUseFilter = '' !== $sFilter;
|
||||
$sSearch = \trim($sSearch);
|
||||
|
||||
list($iMessageRealCount, $iMessageUnseenCount, $iUidNext, $iHighestModSeq) = $this->initFolderValues($sFolderName);
|
||||
|
||||
$this->oImapClient->FolderSelect($sFolderName);
|
||||
|
||||
|
@ -1561,7 +1002,6 @@ class MailClient
|
|||
$oMessageCollection->Filtered = '' !== \MailSo\Config::$MessageListPermanentFilter;
|
||||
|
||||
$aUids = array();
|
||||
$mAllSortedUids = null;
|
||||
$mAllThreads = null;
|
||||
|
||||
$bUseSortIfSupported = $bUseSortIfSupported && $this->oImapClient->IsSupported('SORT');
|
||||
|
@ -1579,13 +1019,6 @@ class MailClient
|
|||
$oCacher = null;
|
||||
}
|
||||
|
||||
list($iMessageRealCount, $iMessageUnseenCount, $iUidNext, $iHighestModSeq) = $this->initFolderValues($sFolderName);
|
||||
|
||||
if ($bUseFilter)
|
||||
{
|
||||
$iMessageUnseenCount = 0;
|
||||
}
|
||||
|
||||
$oMessageCollection->FolderHash = $this->GenerateFolderHash(
|
||||
$sFolderName, $iMessageRealCount, $iUidNext, $iHighestModSeq);
|
||||
|
||||
|
@ -1609,55 +1042,36 @@ class MailClient
|
|||
|
||||
if (0 < $iMessageRealCount && !$bMessageListOptimization)
|
||||
{
|
||||
$mAllSortedUids = $this->GetUids($oCacher, '', $sFilter,
|
||||
$aUids = $this->GetUids($oCacher, '',
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash, $bUseSortIfSupported, $sSort);
|
||||
|
||||
$mAllThreads = $bUseThreadSortIfSupported ? $this->MessageListThreadsMap(
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash, $mAllSortedUids, $oCacher) : null;
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash, $oCacher) : null;
|
||||
|
||||
if ($bUseThreadSortIfSupported && 0 < $iThreadUid && \is_array($mAllThreads))
|
||||
{
|
||||
$iResultRootUid = 0;
|
||||
|
||||
if (isset($mAllThreads[$iThreadUid]))
|
||||
if ($bUseThreadSortIfSupported && $mAllThreads) {
|
||||
if (0 < $iThreadUid)
|
||||
{
|
||||
$iResultRootUid = $iThreadUid;
|
||||
if (\is_array($mAllThreads[$iThreadUid]))
|
||||
{
|
||||
$aUids = $mAllThreads[$iThreadUid];
|
||||
// Only show the selected thread messages
|
||||
foreach ($mAllThreads as $aMap) {
|
||||
if (\in_array($iThreadUid, $aMap)) {
|
||||
$aUids = \array_intersect($aUids, $aMap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($mAllThreads as $iRootUid => $mSubUids)
|
||||
{
|
||||
if (\is_array($mSubUids) && \in_array($iThreadUid, $mSubUids))
|
||||
{
|
||||
$iResultRootUid = $iRootUid;
|
||||
$aUids = $mSubUids;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// $aUids = \array_diff($aUids, $mAllThreads);
|
||||
}
|
||||
|
||||
if (0 < $iResultRootUid && \in_array($iResultRootUid, $mAllSortedUids))
|
||||
{
|
||||
\array_unshift($aUids, $iResultRootUid);
|
||||
}
|
||||
}
|
||||
else if ($bUseThreadSortIfSupported && \is_array($mAllThreads))
|
||||
{
|
||||
$aUids = \array_keys($mAllThreads);
|
||||
}
|
||||
else
|
||||
{
|
||||
$bUseThreadSortIfSupported = false;
|
||||
$aUids = $mAllSortedUids;
|
||||
}
|
||||
|
||||
if (\strlen($sSearch) && \is_array($aUids))
|
||||
{
|
||||
$aSearchedUids = $this->GetUids($oCacher, $sSearch, $sFilter,
|
||||
$aSearchedUids = $this->GetUids($oCacher, $sSearch,
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash);
|
||||
|
||||
if (\count($aSearchedUids))
|
||||
|
@ -1719,9 +1133,9 @@ class MailClient
|
|||
$oMessageCollection->MessageCount = $iMessageRealCount;
|
||||
$oMessageCollection->MessageUnseenCount = $iMessageUnseenCount;
|
||||
|
||||
if (\strlen($sSearch) || $bUseFilter)
|
||||
if (\strlen($sSearch))
|
||||
{
|
||||
$aUids = $this->GetUids($oCacher, $sSearch, $sFilter,
|
||||
$aUids = $this->GetUids($oCacher, $sSearch,
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash);
|
||||
|
||||
if (\count($aUids))
|
||||
|
@ -1742,7 +1156,7 @@ class MailClient
|
|||
|
||||
if (1 < $iMessageRealCount)
|
||||
{
|
||||
$aRequestIndexes = \array_slice(array_reverse(range(1, $iMessageRealCount)), $iOffset, $iLimit);
|
||||
$aRequestIndexes = \array_slice(\array_reverse(\range(1, $iMessageRealCount)), $iOffset, $iLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1798,8 +1212,12 @@ class MailClient
|
|||
|
||||
public function Folders(string $sParent, string $sListPattern, bool $bUseListSubscribeStatus, int $iOptimizationLimit, bool $bUseListStatus) : ?FolderCollection
|
||||
{
|
||||
$aCapabilities = \array_filter($this->oImapClient->Capability(), function($item){
|
||||
return !\preg_match('/^(IMAP|AUTH|LOGIN|SASL)/', $item);
|
||||
});
|
||||
|
||||
$aImapSubscribedFoldersHelper = null;
|
||||
if ($this->oImapClient->IsSupported('LIST-EXTENDED')) {
|
||||
if (\in_array('LIST-EXTENDED', $aCapabilities)) {
|
||||
$bUseListSubscribeStatus = false;
|
||||
} else if ($bUseListSubscribeStatus) {
|
||||
//\error_log('RFC5258 not supported, using LSUB');
|
||||
|
@ -1818,7 +1236,13 @@ class MailClient
|
|||
}
|
||||
}
|
||||
|
||||
$bUseListStatus = $bUseListStatus && $this->oImapClient->IsSupported('LIST-STATUS');
|
||||
$bUseListStatus = $bUseListStatus && \in_array('LIST-STATUS', $aCapabilities);
|
||||
if (!$bUseListStatus) {
|
||||
$key = \array_search('LIST-STATUS', $aCapabilities);
|
||||
if (false !== $key) {
|
||||
unset($aCapabilities[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$aFolders = $bUseListStatus
|
||||
? $this->oImapClient->FolderStatusList($sParent, $sListPattern)
|
||||
|
@ -1828,11 +1252,9 @@ class MailClient
|
|||
}
|
||||
|
||||
$oFolderCollection = new FolderCollection;
|
||||
$oFolderCollection->IsMetadataSupported = $this->oImapClient->IsSupported('METADATA');
|
||||
$oFolderCollection->IsThreadsSupported = $this->IsThreadsSupported();
|
||||
$oFolderCollection->IsSortSupported = $this->oImapClient->IsSupported('SORT');
|
||||
$oFolderCollection->IsListStatusSupported = $bUseListStatus;
|
||||
$oFolderCollection->Optimized = 10 < $iOptimizationLimit && \count($aFolders) > $iOptimizationLimit;
|
||||
$oFolderCollection->capabilities = $aCapabilities;
|
||||
|
||||
$sINBOX = 'INBOX';
|
||||
$aSortedByLenImapFolders = array();
|
||||
|
|
|
@ -24,8 +24,8 @@ trait Folders
|
|||
try {
|
||||
$aQuota = $this->MailClient()->Quota();
|
||||
// $aQuota = $this->MailClient()->QuotaRoot();
|
||||
$oFolderCollection->quotaUsage = $aQuota[0] * 1024;
|
||||
$oFolderCollection->quotaLimit = $aQuota[1] * 1024;
|
||||
$oFolderCollection->quotaUsage = $aQuota ? $aQuota[0] * 1024 : null;
|
||||
$oFolderCollection->quotaLimit = $aQuota ? $aQuota[1] * 1024 : null;
|
||||
} catch (\Throwable $oException) {
|
||||
// ignore
|
||||
}
|
||||
|
@ -57,6 +57,12 @@ trait Folders
|
|||
$this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders);
|
||||
$oFolderCollection->SystemFolders = $aSystemFolders;
|
||||
|
||||
if (!$this->Config()->Get('labs', 'use_imap_sort', true)) {
|
||||
$oFolderCollection->capabilities = \array_filter($oFolderCollection->capabilities, function($item){
|
||||
return !\preg_match('/^E?SORT/', $item);
|
||||
});
|
||||
}
|
||||
|
||||
if ($this->Config()->Get('labs', 'autocreate_system_folders', true))
|
||||
{
|
||||
$bDoItAgain = false;
|
||||
|
|
|
@ -28,7 +28,6 @@ trait Messages
|
|||
|
||||
$sRawKey = $this->GetActionParam('RawKey', '');
|
||||
$aValues = $this->getDecodedClientRawKeyValue($sRawKey, 10);
|
||||
|
||||
if ($aValues && 7 < \count($aValues))
|
||||
{
|
||||
$sFolder = (string) $aValues[2];
|
||||
|
@ -63,7 +62,7 @@ trait Messages
|
|||
}
|
||||
}
|
||||
|
||||
if (0 === strlen($sFolder))
|
||||
if (!\strlen($sFolder))
|
||||
{
|
||||
throw new ClientException(Notifications::CantGetMessageList);
|
||||
}
|
||||
|
@ -83,7 +82,6 @@ trait Messages
|
|||
!!$this->Config()->Get('labs', 'use_imap_sort', true),
|
||||
$bUseThreads,
|
||||
$iThreadUid,
|
||||
'',
|
||||
$sSort
|
||||
);
|
||||
}
|
||||
|
@ -108,7 +106,7 @@ trait Messages
|
|||
$iMessageUid = $this->GetActionParam('MessageUid', 0);
|
||||
|
||||
$sDraftFolder = $this->GetActionParam('SaveFolder', '');
|
||||
if (0 === strlen($sDraftFolder))
|
||||
if (!\strlen($sDraftFolder))
|
||||
{
|
||||
throw new ClientException(Notifications::UnknownError);
|
||||
}
|
||||
|
@ -144,7 +142,7 @@ trait Messages
|
|||
|
||||
$mResult = true;
|
||||
|
||||
if (0 < strlen($sMessageFolder) && 0 < $iMessageUid)
|
||||
if (\strlen($sMessageFolder) && 0 < $iMessageUid)
|
||||
{
|
||||
$this->MailClient()->MessageDelete($sMessageFolder, array($iMessageUid), true, true);
|
||||
}
|
||||
|
@ -209,7 +207,7 @@ trait Messages
|
|||
break;
|
||||
case 'forward':
|
||||
$sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', '');
|
||||
if (0 < strlen($sForwardedFlag))
|
||||
if (\strlen($sForwardedFlag))
|
||||
{
|
||||
$this->MailClient()->MessageSetFlag($sDraftInfoFolder, array($sDraftInfoUid), true,
|
||||
$sForwardedFlag, true);
|
||||
|
@ -223,7 +221,7 @@ trait Messages
|
|||
}
|
||||
}
|
||||
|
||||
if (0 < \strlen($sSentFolder))
|
||||
if (\strlen($sSentFolder))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -277,7 +275,7 @@ trait Messages
|
|||
|
||||
$this->deleteMessageAttachmnets($oAccount);
|
||||
|
||||
if (0 < \strlen($sDraftFolder) && 0 < $iDraftUid)
|
||||
if (\strlen($sDraftFolder) && 0 < $iDraftUid)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -377,7 +375,7 @@ trait Messages
|
|||
|
||||
$this->Cacher($oAccount)->Set(\RainLoop\KeyPathHelper::ReadReceiptCache($oAccount->Email(), $sFolderFullName, $iUid), '1');
|
||||
|
||||
if (0 < \strlen($sFolderFullName) && 0 < $iUid)
|
||||
if (\strlen($sFolderFullName) && 0 < $iUid)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -1044,12 +1042,12 @@ trait Messages
|
|||
$oMessage->SetDraftInfo($aDraftInfo[0], $aDraftInfo[1], $aDraftInfo[2]);
|
||||
}
|
||||
|
||||
if (0 < \strlen($sInReplyTo))
|
||||
if (\strlen($sInReplyTo))
|
||||
{
|
||||
$oMessage->SetInReplyTo($sInReplyTo);
|
||||
}
|
||||
|
||||
if (0 < \strlen($sReferences))
|
||||
if (\strlen($sReferences))
|
||||
{
|
||||
$oMessage->SetReferences($sReferences);
|
||||
}
|
||||
|
@ -1064,7 +1062,7 @@ trait Messages
|
|||
$this->Plugins()->RunHook($bTextIsHtml ? 'filter.message-html' : 'filter.message-plain',
|
||||
array($oAccount, $oMessage, &$sTextToAdd));
|
||||
|
||||
if ($bTextIsHtml && 0 < \strlen($sTextToAdd))
|
||||
if ($bTextIsHtml && \strlen($sTextToAdd))
|
||||
{
|
||||
$sTextConverted = \MailSo\Base\HtmlUtils::ConvertHtmlToPlain($sTextToAdd);
|
||||
$this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sTextConverted));
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "منذ شهر",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "منذ 3 أشهر",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "منذ 6 أشهر",
|
||||
"LABEL_ADV_DATE_YEAR": "منذ سنة"
|
||||
"LABEL_ADV_DATE_YEAR": "منذ سنة",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "إعداد افتراضي لوضع ملء الشاشة",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "До 1 месец",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "До 3 месеца",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "До 6 месеца",
|
||||
"LABEL_ADV_DATE_YEAR": "До 1 година"
|
||||
"LABEL_ADV_DATE_YEAR": "До 1 година",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "На цял екран",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Ne starší než měsíc",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Ne starší než 3 měsíce",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Ne starší než 6 měsíců",
|
||||
"LABEL_ADV_DATE_YEAR": "Ne starší než 1 rok"
|
||||
"LABEL_ADV_DATE_YEAR": "Ne starší než 1 rok",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Celá obrazovka",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Nyere end 1 måned",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Nyere end 3 måneder",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Nyere end 6 måneder",
|
||||
"LABEL_ADV_DATE_YEAR": "Nyere end 1 år"
|
||||
"LABEL_ADV_DATE_YEAR": "Nyere end 1 år",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Fuld skærm",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Nicht älter als 1 Monat",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Nicht älter als 3 Monate",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Nicht älter als 6 Monate",
|
||||
"LABEL_ADV_DATE_YEAR": "Nicht älter als 1 Jahr"
|
||||
"LABEL_ADV_DATE_YEAR": "Nicht älter als 1 Jahr",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Vollbild umschalten",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Έως 1 μήνα πριν",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Έως 3 μήνες πριν",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Έως 6 μήνες πριν",
|
||||
"LABEL_ADV_DATE_YEAR": "Έως 1 χρόνο πριν"
|
||||
"LABEL_ADV_DATE_YEAR": "Έως 1 χρόνο πριν",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Ενναλαγή πλήρους οθόνης",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Up to 1 month old",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Up to 3 months old",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Up to 6 months old",
|
||||
"LABEL_ADV_DATE_YEAR": "Up to 1 year old"
|
||||
"LABEL_ADV_DATE_YEAR": "Up to 1 year old",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE" : "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE" : "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE" : "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Toggle fullscreen",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Hasta 1 mes de antigüedad",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Hasta 3 meses de antigüedad",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Hasta 6 meses de antigüedad",
|
||||
"LABEL_ADV_DATE_YEAR": "Hasta 1 año de antigüedad"
|
||||
"LABEL_ADV_DATE_YEAR": "Hasta 1 año de antigüedad",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Cambiar a pantalla completa",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Kuni 1 kuu vanune",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Kuni 3 kuu vanune ",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Kuni 6 kuu vanune",
|
||||
"LABEL_ADV_DATE_YEAR": "Kuni 1 aasta vanune"
|
||||
"LABEL_ADV_DATE_YEAR": "Kuni 1 aasta vanune",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Täisekraan sisse\/välja",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "تا 1 ماه گذشته",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "تا 3 ماه گذشته",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "تا 6 ماه گذشته",
|
||||
"LABEL_ADV_DATE_YEAR": "تا 1 سال گذشته"
|
||||
"LABEL_ADV_DATE_YEAR": "تا 1 سال گذشته",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "ضامن تمام صفحه",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "1 kk ajalta",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "3 kk ajalta",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "6 kk ajalta",
|
||||
"LABEL_ADV_DATE_YEAR": "1 vuoden ajalta"
|
||||
"LABEL_ADV_DATE_YEAR": "1 vuoden ajalta",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Koko ruutu",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Jusqu'à 1 mois",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Jusqu'à 3 mois",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Jusqu'à 6 mois",
|
||||
"LABEL_ADV_DATE_YEAR": "Jusqu'à 1 an"
|
||||
"LABEL_ADV_DATE_YEAR": "Jusqu'à 1 an",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Basculer en plein écran",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Legfeljebb 1 hónapos",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Legfeljebb 3 hónapos",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Legfeljebb 6 hónapos",
|
||||
"LABEL_ADV_DATE_YEAR": "Legfeljebb 1 éves"
|
||||
"LABEL_ADV_DATE_YEAR": "Legfeljebb 1 éves",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Teljes képernyő váltása",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Lebih dari 1 bulan lalu",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Lebih dari 3 bulan lalu",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Lebih dari 6 bulan lalu",
|
||||
"LABEL_ADV_DATE_YEAR": "Lebih dari 1 tahun lalu"
|
||||
"LABEL_ADV_DATE_YEAR": "Lebih dari 1 tahun lalu",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Beralih ke layar penuh",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Allt að mánaðar gömlu",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Allt að 3 mánaða gömlu",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Allt að 6 mánaða gömlu",
|
||||
"LABEL_ADV_DATE_YEAR": "Allt að 1 árs gömlu"
|
||||
"LABEL_ADV_DATE_YEAR": "Allt að 1 árs gömlu",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Víxla heilskjá af\/á",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Meno di un mese fa",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Meno di 3 mesi fa",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Meno di 6 mesi fa",
|
||||
"LABEL_ADV_DATE_YEAR": "Meno di un anno fa"
|
||||
"LABEL_ADV_DATE_YEAR": "Meno di un anno fa",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Passa a schermo intero",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "1ヶ月前まで",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "3ヶ月前まで",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "6ヶ月前まで",
|
||||
"LABEL_ADV_DATE_YEAR": "1年前まで"
|
||||
"LABEL_ADV_DATE_YEAR": "1年前まで",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "フルスクリーン",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "1개월 이내",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "3개월 이내",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "6개월 이내",
|
||||
"LABEL_ADV_DATE_YEAR": "1년 이내"
|
||||
"LABEL_ADV_DATE_YEAR": "1년 이내",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "전체화면으로 보기",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Nesenesni kaip 1 mėnesio",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Nesenesni kaip 3 mėnesių",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Nesenesni kaip 6 mėnesių",
|
||||
"LABEL_ADV_DATE_YEAR": "Nesenesni kaip 1 metų"
|
||||
"LABEL_ADV_DATE_YEAR": "Nesenesni kaip 1 metų",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Perjungti viso ekrano režimą",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Līdz 1 mēnesi vecs",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Līdz 3 mēnešus vecs",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Līdz 6 mēnešus vecs",
|
||||
"LABEL_ADV_DATE_YEAR": "Līdz 1 gadu vecs"
|
||||
"LABEL_ADV_DATE_YEAR": "Līdz 1 gadu vecs",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Toggle fullscreen",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Inntil 1 måned gamle",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Inntil 3 måneder gamle",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Inntil 6 måneder gamle",
|
||||
"LABEL_ADV_DATE_YEAR": "Inntil 1 år gamle"
|
||||
"LABEL_ADV_DATE_YEAR": "Inntil 1 år gamle",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Toggle fullscreen",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Tot 1 maand oud",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Tot 3 maanden oud",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Tot 6 maanden oud",
|
||||
"LABEL_ADV_DATE_YEAR": "Tot 1 jaar oud"
|
||||
"LABEL_ADV_DATE_YEAR": "Tot 1 jaar oud",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Schakel volledig scherm in\/uit",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "do miesiąca",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "do 3 miesięcy",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "do 6 miesięcy",
|
||||
"LABEL_ADV_DATE_YEAR": "do roku"
|
||||
"LABEL_ADV_DATE_YEAR": "do roku",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Pełny ekran",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Até 1 mês atrás",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Até 3 meses atrás",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Até 6 meses atrás",
|
||||
"LABEL_ADV_DATE_YEAR": "Até 1 ano atrás"
|
||||
"LABEL_ADV_DATE_YEAR": "Até 1 ano atrás",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Em tela cheia",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Até 1 mês atrás",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Até 3 meses atrás",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Até 6 meses atrás",
|
||||
"LABEL_ADV_DATE_YEAR": "Até 1 ano atrás"
|
||||
"LABEL_ADV_DATE_YEAR": "Até 1 ano atrás",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Alternar ecrã-inteiro",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "în această lună",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "3 luni",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "6 luni",
|
||||
"LABEL_ADV_DATE_YEAR": "în acest an"
|
||||
"LABEL_ADV_DATE_YEAR": "în acest an",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Toggle fullscreen",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "За месяц",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "За 3 месяца",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "За полгода",
|
||||
"LABEL_ADV_DATE_YEAR": "За год"
|
||||
"LABEL_ADV_DATE_YEAR": "За год",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Полный экран",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Nie staršie ako mesiac",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Nie staršie ako 3 mesiace",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Nie staršie ako 6 mesiacov",
|
||||
"LABEL_ADV_DATE_YEAR": "Nie staršie ako 1 rok"
|
||||
"LABEL_ADV_DATE_YEAR": "Nie staršie ako 1 rok",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Prepnúť na celú obrazovku",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Do 1 meseca nazaj",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Do 3 mesecev nazaj",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Do 6 mesecev nazaj",
|
||||
"LABEL_ADV_DATE_YEAR": "Do 1 leta nazaj"
|
||||
"LABEL_ADV_DATE_YEAR": "Do 1 leta nazaj",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Preklopi celozaslonski pogled",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "Upp till 1 månad gammalt",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "Upp till 3 månader gammalt",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "Upp till 6 månader gammalt",
|
||||
"LABEL_ADV_DATE_YEAR": "Upp till 1 år gammalt"
|
||||
"LABEL_ADV_DATE_YEAR": "Upp till 1 år gammalt",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Växla fullskärmsläge",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "1 ay önceki",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "3 ay önceki",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "6 ay önceki",
|
||||
"LABEL_ADV_DATE_YEAR": "1 yıl önceki"
|
||||
"LABEL_ADV_DATE_YEAR": "1 yıl önceki",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Toggle fullscreen",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "За місяць",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "За 3 місяці",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "За півроку",
|
||||
"LABEL_ADV_DATE_YEAR": "За рік"
|
||||
"LABEL_ADV_DATE_YEAR": "За рік",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Toggle fullscreen",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "1个月以内",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "3个月以内",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "半年以内",
|
||||
"LABEL_ADV_DATE_YEAR": "1年以内"
|
||||
"LABEL_ADV_DATE_YEAR": "1年以内",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "切换全屏",
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"LABEL_ADV_DATE_MONTH": "1月以內",
|
||||
"LABEL_ADV_DATE_3_MONTHS": "3月以內",
|
||||
"LABEL_ADV_DATE_6_MONTHS": "半年以內",
|
||||
"LABEL_ADV_DATE_YEAR": "1年以內"
|
||||
"LABEL_ADV_DATE_YEAR": "1年以內",
|
||||
"LABEL_ADV_SUBFOLDERS": "Subfolders",
|
||||
"LABEL_ADV_SUBFOLDERS_NONE": "None",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE": "All",
|
||||
"LABEL_ADV_SUBFOLDERS_SUBTREE_ONE": "One level"
|
||||
},
|
||||
"PREVIEW_POPUP": {
|
||||
"FULLSCREEN": "Toggle fullscreen",
|
||||
|
|
|
@ -50,6 +50,20 @@
|
|||
}
|
||||
}"></div>
|
||||
</div>
|
||||
<!-- ko if: showMultisearch -->
|
||||
<div class="control-group">
|
||||
<label class="control-label" data-i18n="SEARCH/LABEL_ADV_SUBFOLDERS"></label>
|
||||
<div class="controls" data-bind="component: {
|
||||
name: 'Select',
|
||||
params: {
|
||||
options: selectedTree,
|
||||
value: selectedTreeValue,
|
||||
optionsText: 'name',
|
||||
optionsValue: 'id'
|
||||
}
|
||||
}"></div>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
</label>
|
||||
|
|
Loading…
Reference in a new issue