Added: Sync contacts tags

Fixed: IMAP search
This commit is contained in:
RainLoop Team 2014-05-14 19:58:04 +04:00
parent e88c41c90a
commit 79e686f412
14 changed files with 28963 additions and 29048 deletions

View file

@ -9,7 +9,7 @@ ko.bindingHandlers.tooltip = {
sClass = $oEl.data('tooltip-class') || '',
sPlacement = $oEl.data('tooltip-placement') || 'top'
;
$oEl.tooltip({
'delay': {
'show': 500,
@ -826,4 +826,3 @@ ko.observable.fn.validateFunc = function (fFunc)
return this;
};

View file

@ -1742,4 +1742,4 @@ Utils.detectDropdownVisibility = _.debounce(function () {
Globals.dropdownVisibility(!!_.find(BootstrapDropdowns, function (oItem) {
return oItem.hasClass('open');
}));
}, 50);
}, 50);

View file

@ -735,8 +735,9 @@ PopupsContactsViewModel.prototype.onHide = function ()
this.currentContact(null);
this.emptySelection(true);
this.search('');
_.each(this.contacts(), function (oItem) {
oItem.checked(false);
});
this.contactsCount(0);
this.contacts([]);
// _.each(this.contacts(), function (oItem) {
// oItem.checked(false);
// });
};

View file

@ -1,104 +0,0 @@
<?php
namespace MailSo\Imap;
/**
* @category MailSo
* @package Imap
*/
class SearchBuilder
{
/**
* @var array
*/
private $aList;
/**
* @access private
*/
private function __construct()
{
$this->Clear();
}
/**
* @return \MailSo\Imap\SearchBuilder
*/
public static function NewInstance()
{
return new self();
}
/**
* @return \MailSo\Imap\SearchBuilder
*/
public function Clear()
{
$this->aList = array();
return $this;
}
/**
* @param string $sName
* @param string $sValue = ''
*
* @return \MailSo\Imap\SearchBuilder
*/
public function AddAnd($sName, $sValue = '')
{
return $this->addCri('AND', $sName, $sValue);
}
/**
* @param string $sName
* @param string $sValue = ''
*
* @return \MailSo\Imap\SearchBuilder
*/
public function AddOr($sName, $sValue = '')
{
return $this->addCri('OR', $sName, $sValue);
}
/**
* @return string
*/
public function Complete()
{
$sResult = '';
foreach ($this->aList as $iIndex => $aItem)
{
$sResult = trim((0 < $iIndex && 'OR' === $aItem[0] ? $aItem[0] : '').
(0 === strlen($sResult) ? '' : ' ('.$sResult.')').' '.$aItem[1].
(0 < strlen($aItem[2]) ? ' '.$aItem[2] : ''));
}
if (0 === strlen($sResult))
{
$sResult = 'ALL';
}
return $sResult;
}
/**
* @return string
*/
public function __toString()
{
return $this->Complete();
}
/**
* @param string $sType
* @param string $sName
* @param string $sValue = ''
*
* @return \MailSo\Imap\SearchBuilder
*/
private function addCri($sType, $sName, $sValue = '')
{
$this->aList[] = array($sType, $sName, $sValue);
return $this;
}
}

View file

@ -1058,11 +1058,11 @@ class MailClient
* @param string $sSearch
* @param int $iTimeZoneOffset = 0
*
* @return \MailSo\Imap\SearchBuilder
* @return string
*/
private function getSearchBuilder($sSearch, $iTimeZoneOffset = 0)
private function getImapSearchCriterias($sSearch, $iTimeZoneOffset = 0)
{
$oSearchBuilder = \MailSo\Imap\SearchBuilder::NewInstance();
$aCriteriasResult = array();
if (0 < \strlen(\trim($sSearch)))
{
$sGmailRawSearch = '';
@ -1077,10 +1077,15 @@ class MailClient
{
$sValue = $this->escapeSearchString($aLines['OTHER']);
$oSearchBuilder->AddOr('FROM', $sValue);
$oSearchBuilder->AddOr('TO', $sValue);
$oSearchBuilder->AddOr('CC', $sValue);
$oSearchBuilder->AddOr('SUBJECT', $sValue);
$aCriteriasResult[] = 'OR OR OR';
$aCriteriasResult[] = 'FROM';
$aCriteriasResult[] = $sValue;
$aCriteriasResult[] = 'TO';
$aCriteriasResult[] = $sValue;
$aCriteriasResult[] = 'CC';
$aCriteriasResult[] = $sValue;
$aCriteriasResult[] = 'SUBJECT';
$aCriteriasResult[] = $sValue;
}
else
{
@ -1101,9 +1106,14 @@ class MailClient
{
$sValue = $this->escapeSearchString($aLines['EMAIL']);
$oSearchBuilder->AddOr('FROM', $sValue);
$oSearchBuilder->AddOr('TO', $sValue);
$oSearchBuilder->AddOr('CC', $sValue);
$aCriteriasResult[] = 'OR OR';
$aCriteriasResult[] = 'FROM';
$aCriteriasResult[] = $sValue;
$aCriteriasResult[] = 'TO';
$aCriteriasResult[] = $sValue;
$aCriteriasResult[] = 'CC';
$aCriteriasResult[] = $sValue;
unset($aLines['EMAIL']);
}
@ -1111,8 +1121,12 @@ class MailClient
{
$sValue = $this->escapeSearchString($aLines['TO']);
$oSearchBuilder->AddAnd('TO', $this->escapeSearchString($aLines['TO']));
$oSearchBuilder->AddOr('CC', $this->escapeSearchString($aLines['TO']));
$aCriteriasResult[] = 'OR';
$aCriteriasResult[] = 'TO';
$aCriteriasResult[] = $sValue;
$aCriteriasResult[] = 'CC';
$aCriteriasResult[] = $sValue;
unset($aLines['TO']);
}
@ -1128,10 +1142,12 @@ class MailClient
switch ($sName)
{
case 'FROM':
$oSearchBuilder->AddAnd('FROM', $sValue);
$aCriteriasResult[] = 'FROM';
$aCriteriasResult[] = $sValue;
break;
case 'SUBJECT':
$oSearchBuilder->AddAnd('SUBJECT', $sValue);
$aCriteriasResult[] = 'SUBJECT';
$aCriteriasResult[] = $sValue;
break;
case 'OTHER':
case 'TEXT':
@ -1151,7 +1167,11 @@ class MailClient
else
{
// Simple, is not detailed search (Sometimes doesn't work)
$oSearchBuilder->AddAnd('HEADER CONTENT-TYPE', '"MULTIPART/MIXED"');
$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';
}
}
@ -1163,30 +1183,32 @@ class MailClient
$aCompareArray2 = array('unflag', 'unflagged', 'unstar', 'unstarred', 'unpinned');
if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue)))
{
$oSearchBuilder->AddAnd('FLAGGED');
$aCriteriasResult[] = 'FLAGGED';
}
else if (\count($aCompareArray2) > \count(\array_diff($aCompareArray2, $aValue)))
{
$oSearchBuilder->AddAnd('UNFLAGGED');
$aCriteriasResult[] = 'UNFLAGGED';
}
$aCompareArray = array('unread', 'unseen');
$aCompareArray2 = array('read', 'seen');
if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue)))
{
$oSearchBuilder->AddAnd('UNSEEN');
$aCriteriasResult[] = 'UNSEEN';
}
else if (\count($aCompareArray2) > \count(\array_diff($aCompareArray2, $aValue)))
{
$oSearchBuilder->AddAnd('SEEN');
$aCriteriasResult[] = 'SEEN';
}
break;
case 'LARGER':
$oSearchBuilder->AddAnd('LARGER', $this->parseFriendlySize($sRawValue));
$aCriteriasResult[] = 'LARGER';
$aCriteriasResult[] = $this->parseFriendlySize($sRawValue);
break;
case 'SMALLER':
$oSearchBuilder->AddAnd('SMALLER', $this->parseFriendlySize($sRawValue));
$aCriteriasResult[] = 'SMALLER';
$aCriteriasResult[] = $this->parseFriendlySize($sRawValue);
break;
case 'DATE':
$iDateStampFrom = $iDateStampTo = 0;
@ -1218,12 +1240,14 @@ class MailClient
if (0 < $iDateStampFrom)
{
$oSearchBuilder->AddAnd('SINCE', \gmdate('j-M-Y', $iDateStampFrom));
$aCriteriasResult[] = 'SINCE';
$aCriteriasResult[] = \gmdate('j-M-Y', $iDateStampFrom);
}
if (0 < $iDateStampTo)
{
$oSearchBuilder->AddAnd('BEFORE', \gmdate('j-M-Y', $iDateStampTo));
$aCriteriasResult[] = 'BEFORE';
$aCriteriasResult[] = \gmdate('j-M-Y', $iDateStampTo);
}
break;
}
@ -1246,17 +1270,25 @@ class MailClient
$sGmailRawSearch = \trim($sGmailRawSearch);
if ($bIsGmail && 0 < \strlen($sGmailRawSearch))
{
$oSearchBuilder->AddAnd('X-GM-RAW', $this->escapeSearchString($sGmailRawSearch, false));
$aCriteriasResult[] = 'X-GM-RAW';
$aCriteriasResult[] = $this->escapeSearchString($sGmailRawSearch, false);
}
$sResultBodyTextSearch = \trim($sResultBodyTextSearch);
if (0 < \strlen($sResultBodyTextSearch))
{
$oSearchBuilder->AddAnd('TEXT', $this->escapeSearchString($sResultBodyTextSearch));
$aCriteriasResult[] = 'TEXT';
$aCriteriasResult[] = $this->escapeSearchString($sResultBodyTextSearch);
}
}
return $oSearchBuilder;
$sCriteriasResult = \trim(\implode(' ', $aCriteriasResult));
if ('' === $sCriteriasResult)
{
$sCriteriasResult = 'ALL';
}
return $sCriteriasResult;
}
/**
@ -1594,7 +1626,7 @@ class MailClient
$bIndexAsUid = true;
$aIndexOrUids = null;
$sSearchCriterias = $this->getSearchBuilder($sSearch)->Complete();
$sSearchCriterias = $this->getImapSearchCriterias($sSearch);
if ($iMessageCacheCount < $iMessageRealCount && $oCacher && $oCacher->IsInited())
{
@ -1835,11 +1867,8 @@ class MailClient
$this->oImapClient->FolderExamine($sFolderName);
$sSearchCriterias = \MailSo\Imap\SearchBuilder::NewInstance()
->AddAnd('HEADER MESSAGE-ID', $sMessageId)
->Complete();
$aUids = $this->oImapClient->MessageSimpleSearch($sSearchCriterias, true);
$aUids = $this->oImapClient->MessageSimpleSearch(
'HEADER Message-ID '.$sMessageId, true);
return \is_array($aUids) && 1 === \count($aUids) && \is_numeric($aUids[0]) ? (int) $aUids[0] : null;
}

View file

@ -10,7 +10,7 @@ final class Version
/**
* @var string
*/
const APP_VERSION = '1.3.2';
const APP_VERSION = '1.3.3';
/**
* @var string

View file

@ -5011,25 +5011,6 @@ class Actions
));
}
/**
* @return array
*/
public function DoContactTags()
{
$oAccount = $this->getAccountFromToken();
$mResult = false;
if ($this->AddressBookProvider($oAccount)->IsActive())
{
$mResult = $this->AddressBookProvider($oAccount)->GetContactTags($oAccount->ParentEmailHelper());
}
return $this->DefaultResponse(__FUNCTION__, array(
'List' => $mResult
));
}
/**
* @return array
*/

View file

@ -125,12 +125,13 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
/**
* @param string $sEmail
* @param bool $bCache = true
*
* @return array
*/
public function GetContactTags($sEmail)
public function GetContactTags($sEmail, $bCache = true)
{
return $this->IsActive() ? $this->oDriver->GetContactTags($sEmail) : array();
return $this->IsActive() ? $this->oDriver->GetContactTags($sEmail, $bCache) : array();
}
/**

View file

@ -8,58 +8,4 @@ interface AddressBookInterface
* @return bool
*/
public function IsSupported();
/**
* @param string $sEmail
* @param \RainLoop\Providers\AddressBook\Classes\Contact $oContact
*
* @return bool
*/
public function ContactSave($sEmail, &$oContact);
/**
* @param string $sEmail
* @param array $aContactIds
*
* @return bool
*/
public function DeleteContacts($sEmail, $aContactIds);
/**
* @param string $sEmail
* @param array $aTagsIds
*
* @return bool
*/
public function DeleteTags($sEmail, $aTagsIds);
/**
* @param string $sEmail
* @param int $iOffset = 0
* @param int $iLimit = 20
* @param string $sSearch = ''
* @param int $iResultCount = 0
*
* @return array
*/
public function GetContacts($sEmail, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0);
/**
* @param string $sEmail
* @param string $sSearch
* @param int $iLimit = 20
*
* @return array
*
* @throws \InvalidArgumentException
*/
public function GetSuggestions($sEmail, $sSearch, $iLimit = 20);
/**
* @param string $sEmail
* @param array $aEmails
*
* @return bool
*/
public function IncFrec($sEmail, $aEmails);
}

View file

@ -149,6 +149,8 @@ class Contact
{
$this->Properties[] = new \RainLoop\Providers\AddressBook\Classes\Property(PropertyType::FULLNAME, $this->Display);
}
$this->Tags = \array_map('trim', $this->Tags);
}
/**
@ -209,7 +211,7 @@ class Contact
$oVCard->VERSION = '3.0';
$oVCard->PRODID = '-//RainLoop//'.APP_VERSION.'//EN';
unset($oVCard->FN, $oVCard->EMAIL, $oVCard->TEL, $oVCard->URL);
unset($oVCard->FN, $oVCard->EMAIL, $oVCard->TEL, $oVCard->URL, $oVCard->{'X-RL-TAGS'});
$sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = '';
foreach ($this->Properties as /* @var $oProperty \RainLoop\Providers\AddressBook\Classes\Property */ &$oProperty)
@ -270,6 +272,11 @@ class Contact
$oVCard->N = array($sLastName, $sFirstName, $sMiddleName, $sPrefix, $sSuffix);
$oVCard->REV = \gmdate('Ymd', $this->Changed).'T'.\gmdate('His', $this->Changed).'Z';
if (0 < \count($this->Tags))
{
$oVCard->{'X-RL-TAGS'} = \implode(';', $this->Tags);
}
return (string) $oVCard->serialize();
}
@ -582,6 +589,12 @@ class Contact
}
$this->Properties = $aProperties;
if (isset($oVCard->{'X-RL-TAGS'}) && 0 < \strlen($oVCard->{'X-RL-TAGS'}))
{
$this->Tags = \explode(';', $oVCard->{'X-RL-TAGS'});
$this->Tags = \array_map('trim', $this->Tags);
}
}
$this->UpdateDependentValues();

View file

@ -28,6 +28,11 @@ class PdoAddressBook
*/
private $sPassword;
/**
* @var array
*/
private $aTagsCache;
public function __construct($sDsn, $sUser = '', $sPassword = '', $sDsnType = 'mysql')
{
$this->sDsn = $sDsn;
@ -36,6 +41,7 @@ class PdoAddressBook
$this->sDsnType = $sDsnType;
$this->bExplain = false; // debug
$this->aTagsCache = array(); // debug
}
/**
@ -174,7 +180,7 @@ class PdoAddressBook
$oResponse = 'PUT' === $sCmd && null !== $mData ?
$oClient->request($sCmd, $sUrl, $mData) : $oClient->request($sCmd, $sUrl);
if ('GET' === $sCmd || false)
if ('GET' === $sCmd && false)
{
$this->oLogger->WriteDump($oResponse, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
}
@ -523,11 +529,14 @@ class PdoAddressBook
);
}
$sSql = 'INSERT INTO rainloop_ab_properties '.
'( id_contact, id_user, prop_type, prop_type_str, prop_value, prop_value_custom, prop_frec) VALUES '.
'(:id_contact, :id_user, :prop_type, :prop_type_str, :prop_value, :prop_value_custom, :prop_frec)';
if (0 < \count($aParams))
{
$sSql = 'INSERT INTO rainloop_ab_properties '.
'( id_contact, id_user, prop_type, prop_type_str, prop_value, prop_value_custom, prop_frec) VALUES '.
'(:id_contact, :id_user, :prop_type, :prop_type_str, :prop_value, :prop_value_custom, :prop_frec)';
$this->prepareAndExecute($sSql, $aParams, true);
$this->prepareAndExecute($sSql, $aParams, true);
}
$aTags = array();
$aContactTags = $this->GetContactTags($sEmail);
@ -564,8 +573,11 @@ class PdoAddressBook
}
}
$sSql = 'INSERT INTO rainloop_ab_tags_contacts (id_tag, id_contact) VALUES (:id_tag, :id_contact)';
$this->prepareAndExecute($sSql, $aTags, true);
if (0 < \count($aTags))
{
$sSql = 'INSERT INTO rainloop_ab_tags_contacts (id_tag, id_contact) VALUES (:id_tag, :id_contact)';
$this->prepareAndExecute($sSql, $aTags, true);
}
}
}
catch (\Exception $oException)
@ -602,6 +614,8 @@ class PdoAddressBook
$iUserID = $this->getUserId($sEmail);
$this->aTagsCache = array();
$mResult = false;
try
{
@ -707,6 +721,8 @@ class PdoAddressBook
return false;
}
$this->aTagsCache = array();
$sIDs = \implode(',', $aTagsIds);
$aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT));
@ -716,6 +732,99 @@ class PdoAddressBook
return true;
}
/**
* @param string $sEmail
* @param array|\RainLoop\Providers\AddressBook\Classes\Contact $mContactOrContacts
*/
private function populateContactsByTags($sEmail, &$mContactOrContacts)
{
$aContacts = array();
if ($mContactOrContacts)
{
$aContacts = is_array($mContactOrContacts) ? $mContactOrContacts : array(&$mContactOrContacts);
}
if (\is_array($aContacts) && 0 < \count($aContacts))
{
$aIdContacts = array();
$aIdTagsContacts = array();
$aTags = $this->GetContactTags($sEmail);
if (\is_array($aTags) && 0 < \count($aTags))
{
foreach ($aContacts as $oItem)
{
$aIdContacts[$oItem->IdContact] = true;
}
if (0 < \count($aIdContacts))
{
$sSql = 'SELECT id_tag, id_contact FROM rainloop_ab_tags_contacts WHERE id_contact IN ('.\implode(',', \array_keys($aIdContacts)).')';
$oStmt = $this->prepareAndExecute($sSql);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
foreach ($aFetch as $aItem)
{
if ($aItem && isset($aItem['id_tag'], $aItem['id_contact']))
{
$sID = (string) $aItem['id_contact'];
if (!isset($aIdTagsContacts[$sID]))
{
$aIdTagsContacts[$sID] = array();
}
$aIdTagsContacts[$sID][] = (string) $aItem['id_tag'];
}
}
}
unset($aFetch);
}
if (0 < \count($aIdTagsContacts))
{
$fFindFunc = function ($sID) use ($aTags) {
foreach ($aTags as $oItem)
{
if ($oItem && (string) $oItem->IdContactTag === $sID)
{
return\trim($oItem->Name);
}
}
return '';
};
foreach ($aContacts as &$oItem)
{
$sID = $oItem ? (string) $oItem->IdContact : '';
if (0 < \strlen($sID) && isset($aIdTagsContacts[$sID]) && \is_array($aIdTagsContacts[$sID]))
{
$aNames = array();
foreach ($aIdTagsContacts[$sID] as $sSubID)
{
$sName = $fFindFunc($sSubID);
if (0 < \strlen($sName))
{
$aNames[] = $sName;
}
}
$oItem->Tags = $aNames;
}
}
}
}
}
}
}
/**
* @param string $sEmail
* @param int $iOffset = 0
@ -908,95 +1017,23 @@ class PdoAddressBook
}
}
if (\is_array($aResult) && 0 < \count($aResult))
{
$aIdContacts = array();
$aIdTagsContacts = array();
$aTags = $this->GetContactTags($sEmail);
if (\is_array($aTags) && 0 < \count($aTags))
{
foreach ($aResult as $oItem)
{
$aIdContacts[$oItem->IdContact] = true;
}
if (0 < \count($aIdContacts))
{
$sSql = 'SELECT id_tag, id_contact FROM rainloop_ab_tags_contacts WHERE id_contact IN ('.\implode(',', \array_keys($aIdContacts)).')';
$oStmt = $this->prepareAndExecute($sSql);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
foreach ($aFetch as $aItem)
{
if ($aItem && isset($aItem['id_tag'], $aItem['id_contact']))
{
$sID = (string) $aItem['id_contact'];
if (!isset($aIdTagsContacts[$sID]))
{
$aIdTagsContacts[$sID] = array();
}
$aIdTagsContacts[$sID][] = (string) $aItem['id_tag'];
}
}
}
unset($aFetch);
}
if (0 < \count($aIdTagsContacts))
{
$fFindFunc = function ($sID) use ($aTags) {
foreach ($aTags as $oItem)
{
if ($oItem && (string) $oItem->IdContactTag === $sID)
{
return\trim($oItem->Name);
}
}
return '';
};
foreach ($aResult as &$oItem)
{
$sID = $oItem ? (string) $oItem->IdContact : '';
if (0 < \strlen($sID) && isset($aIdTagsContacts[$sID]) && \is_array($aIdTagsContacts[$sID]))
{
$aNames = array();
foreach ($aIdTagsContacts[$sID] as $sSubID)
{
$sName = $fFindFunc($sSubID);
if (0 < \strlen($sName))
{
$aNames[] = $sName;
}
}
$oItem->Tags = $aNames;
}
}
}
}
}
}
$this->populateContactsByTags($sEmail, $aResult);
return $aResult;
}
/**
* @param string $sEmail
* @param bool $bCache = true
*
* @return array
*/
public function GetContactTags($sEmail)
public function GetContactTags($sEmail, $bCache = true)
{
if ($bCache && isset($this->aTagsCache[$sEmail]))
{
return $this->aTagsCache[$sEmail];
}
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
@ -1031,6 +1068,11 @@ class PdoAddressBook
}
}
if ($bCache)
{
$this->aTagsCache[$sEmail] = $aResult;
}
return $aResult;
}
@ -1133,6 +1175,11 @@ class PdoAddressBook
}
}
}
if ($oContact)
{
$this->populateContactsByTags($sEmail, $oContact);
}
return $oContact;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long