From ad708be88a5986f2a5bfadda05024944a67a2fd7 Mon Sep 17 00:00:00 2001 From: RainLoop Team Date: Tue, 3 Dec 2013 04:08:42 +0400 Subject: [PATCH] MySQL pab driver implementation (part 3) --- dev/ViewModels/PopupsComposeViewModel.js | 2 +- .../0.0.0/app/libraries/RainLoop/Actions.php | 140 ++++++----- .../libraries/RainLoop/Common/PdoAbstract.php | 223 +++++++++++------- .../RainLoop/Providers/AbstractProvider.php | 41 ++-- .../Providers/PersonalAddressBook.php | 33 +++ .../PersonalAddressBook/Classes/Contact.php | 20 +- .../PersonalAddressBook/Classes/Property.php | 6 + .../MySqlPersonalAddressBook.php | 214 ++++++++++++++--- 8 files changed, 461 insertions(+), 218 deletions(-) diff --git a/dev/ViewModels/PopupsComposeViewModel.js b/dev/ViewModels/PopupsComposeViewModel.js index d67b019e5..3272d98ab 100644 --- a/dev/ViewModels/PopupsComposeViewModel.js +++ b/dev/ViewModels/PopupsComposeViewModel.js @@ -497,7 +497,7 @@ PopupsComposeViewModel.prototype.sendMessageResponse = function (sResult, oData) else { this.sendError(true); - window.alert(Utils.getNotification(oData.ErrorCode ? oData.ErrorCode : Enums.Notification.CantSendMessage)); + window.alert(Utils.getNotification(oData && oData.ErrorCode ? oData.ErrorCode : Enums.Notification.CantSendMessage)); } } }; diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php index e2309b03c..165227153 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -81,6 +81,11 @@ class Actions */ private $oContactsProvider; + /** + * @var \RainLoop\Providers\PersonalAddressBook + */ + private $oPersonalAddressBook; + /** * @var \RainLoop\Providers\Suggestions */ @@ -121,6 +126,8 @@ class Actions $this->oSettingsProvider = null; $this->oDomainProvider = null; $this->oLoginProvider = null; + $this->oContactsProvider = null; + $this->oPersonalAddressBook = null; $this->oSuggestionsProvider = null; $this->oChangePasswordProvider = null; @@ -196,6 +203,7 @@ class Actions { $oResult = null; $this->Plugins()->RunHook('main.fabrica', array($sName, &$oResult), false); + $this->Plugins()->RunHook('main.fabrica-account', array($sName, &$oResult, &$oAccount), false); if (null === $oResult) { @@ -226,6 +234,11 @@ class Actions // \RainLoop\Providers\Contacts\ContactsInterface $oResult = new \RainLoop\Providers\Contacts\DefaultContacts($this->Logger()); break; + case 'personal-address-book': +// \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface + $oResult = new \RainLoop\Providers\PersonalAddressBook\MySqlPersonalAddressBook(); + $oResult->SetLogger($this->Logger()); + break; case 'suggestions': // \RainLoop\Providers\Suggestions\SuggestionsInterface break; @@ -543,6 +556,38 @@ class Actions return $this->oContactsProvider; } + /** + * @return \RainLoop\Providers\Contacts + */ + public function PersonalAddressBookProvider($oAccount = null) + { + if (null === $this->oPersonalAddressBook) + { + $this->oPersonalAddressBook = new \RainLoop\Providers\PersonalAddressBook( + $this->Config()->Get('labs', 'allow_contacts', true) ? + $this->fabrica('personal-address-book', $oAccount) : null); + + $sVersion = (string) $this->StorageProvider()->Get(null, + \RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY, 'PersonalAddressBookVersion', ''); + + if ($sVersion !== APP_VERSION && $this->oPersonalAddressBook->IsActive()) + { + if ($this->oPersonalAddressBook->SynchronizeStorage()) + { + $this->StorageProvider()->Put(null, \RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY, + 'PersonalAddressBookVersion', APP_VERSION); + } + } + } + + if ($oAccount) + { + $this->oPersonalAddressBook->SetAccount($oAccount); + } + + return $this->oPersonalAddressBook; + } + /** * @return \RainLoop\Providers\Login */ @@ -3849,22 +3894,6 @@ class Actions $rMessageStream, $iMessageStreamSize, $sSentFolder, array( \MailSo\Imap\Enumerations\MessageFlag::SEEN )); - -// TODO -// $rAppendMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); -// -// $iAppendMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( -// $oMessage->ToStream(false), array($rAppendMessageStream), 8192, true, true, true); -// -// $this->MailClient()->MessageAppendStream( -// $rAppendMessageStream, $iAppendMessageStreamSize, $sSentFolder, array( -// \MailSo\Imap\Enumerations\MessageFlag::SEEN -// )); -// -// if (\is_resource($rAppendMessageStream)) -// { -// @\fclose($rAppendMessageStream); -// } } catch (\Exception $oException) { @@ -3912,26 +3941,24 @@ class Actions throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantSendMessage); } -// if ($oMessage && $this->ContactsProvider()->IsActive()) -// { -// $oToCollection = $oMessage->GetTo(); -// $oTo = $oToCollection->GetByIndex(0); -// if ($oTo) -// { -// $oContact = new \RainLoop\Providers\Contacts\Classes\Contact(); -// /* @var $oTo \MailSo\Mime\Email */ -// $oContact->Name = $oTo->GetDisplayName(); -// -// $i = 30; -// while ($i > 0) -// { -// $i--; -// $oContact->Emails = array('u'.$i.'-'.$oTo->GetEmail()); -// $this->ContactsProvider()->CreateContact($oAccount, $oContact); -// } -// } -// -// } + if ($oMessage && $this->PersonalAddressBookProvider($oAccount)->IsActive()) + { + $aArrayToFrec = array(); + $oToCollection = $oMessage->GetTo(); + if ($oToCollection) + { + $aTo =& $oToCollection->GetAsArray(); + foreach ($aTo as /* @var $oEmail \MailSo\Mime\Email */ $oEmail) + { + $aArrayToFrec[$oEmail->GetEmail()] = $oEmail->GetEmail(); + } + } + + if (0 < \count($aArrayToFrec)) + { + $this->PersonalAddressBookProvider($oAccount)->IncFrec($oAccount, \array_values($aArrayToFrec)); + } + } return $this->TrueResponse(__FUNCTION__); } @@ -4097,51 +4124,16 @@ class Actions $sQuery = \trim($this->GetActionParam('Query', '')); $aResult = array(); - if (0 < \strlen($sQuery) && $this->ContactsProvider()->IsActive()) + $oPab = $this->PersonalAddressBookProvider($oAccount); + if (0 < \strlen($sQuery) && $oPab->IsActive()) { - $mResult = $this->ContactsProvider()->GetContacts($oAccount, 0, RL_CONTACTS_PER_PAGE, $sQuery); - if (\is_array($mResult) && 0 < \count($mResult)) - { - $mResult = \array_slice($mResult, 0, RL_CONTACTS_PER_PAGE); - - foreach ($mResult as $oItem) - { - /* @var $oItem \RainLoop\Providers\Contacts\Classes\Contact */ - $aEmails = $oItem->Emails; - if (0 < \count($aEmails)) - { - foreach ($aEmails as $sEmail) - { - if (0 < \strlen($sEmail)) - { - $aResult[] = array($sEmail, $oItem->Name); - } - } - } - } - } + $aResult = $oPab->GetSuggestions($oAccount, $sQuery); } return $this->DefaultResponse(__FUNCTION__, array( 'More' => false, 'List' => $aResult )); - -// $oAccount = $this->getAccountFromToken(); -// -// $aResult = array(); -// $sQuery = \trim($this->GetActionParam('Query', '')); -// if (0 < \strlen($sQuery) && $oAccount) -// { -// $aResult = $this->SuggestionsProvider()->Process($oAccount, $sQuery); -// -// if (0 === count($aResult) && false !== \strpos(strtolower($oAccount->Email()), \strtolower($sQuery))) -// { -// $aResult[] = array($oAccount->Email(), $oAccount->Name()); -// } -// } -// -// return $this->DefaultResponse(__FUNCTION__, $aResult); } /** diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php index 6c3b81b04..a99bd252b 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php @@ -31,37 +31,11 @@ abstract class PdoAbstract } /** - * * @return array */ protected function getPdoAccessData() { - $aResult = array('mysql', '', '', ''); - return $aResult; - } - - /** - * @return array - */ - protected function getPdoSystemTables() - { - $aResult = array(); - if ('mysql' === $this->sDbType) - { - $aResult[] = 'CREATE TABLE IF NOT EXISTS `rainloop_system` ( - `sys_name` varchar(50) NOT NULL, - `value_int` int(11) UNSIGNED NOT NULL DEFAULT \'0\', - `value_str` varchar(255) NOT NULL DEFAULT \'\' - ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;'; - - $aResult[] = 'CREATE TABLE IF NOT EXISTS `rainloop_users` ( - `id_user` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, - `rl_email` varchar(255) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL, - UNIQUE `email_unique` (`rl_email`), - PRIMARY KEY(`id_user`) - ) /*!40000 ENGINE=INNODB */;'; - } - + $aResult = array('mysql', 'mysql:host=127.0.0.1;port=3306;dbname=rainloop', 'root', ''); return $aResult; } @@ -70,7 +44,7 @@ abstract class PdoAbstract * * @throws \Exception */ - protected function getPDO($oAccount = null) + protected function getPDO() { static $aPdoCache = null; if ($aPdoCache) @@ -86,7 +60,7 @@ abstract class PdoAbstract // TODO $sType = $sDsn = $sDbLogin = $sDbPassword = ''; list($sType, $sDsn, $sDbLogin, $sDbPassword) = $this->getPdoAccessData(); - $this->sType = $sType; + $this->sDbType = $sType; $oPdo = false; try @@ -95,9 +69,10 @@ abstract class PdoAbstract if ($oPdo) { $oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - if ('mysql' === $this->sType) + if ('mysql' === $oPdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) { - + $oPdo->exec('SET NAMES utf8 COLLATE utf8_general_ci'); +// $oPdo->exec('SET NAMES utf8'); } } } @@ -127,12 +102,9 @@ abstract class PdoAbstract */ protected function prepareAndExecute($sSql, $aParams = array()) { - if ($this->oLogger) - { - $this->oLogger->Write($sSql, \MailSo\Log\Enumerations\Type::INFO, 'SQL'); - } - $mResult = null; + + $this->writeLog($sSql); $oStmt = $this->getPDO()->prepare($sSql); if ($oStmt) { @@ -147,6 +119,17 @@ abstract class PdoAbstract return $mResult; } + /** + * @param string $sSql + */ + protected function writeLog($sSql) + { + if ($this->oLogger) + { + $this->oLogger->Write($sSql, \MailSo\Log\Enumerations\Type::INFO, 'SQL'); + } + } + /** * @param string $sEmail * @param bool $bSkipInsert = false @@ -183,16 +166,6 @@ abstract class PdoAbstract throw new \Exception('id_user = 0'); } - /** - * @param string $sSearch - * - * @return string - */ - protected function convertSearchValue($sSearch) - { - return '%'.$sSearch.'%'; - } - /** * @param string $sValue * @@ -200,7 +173,8 @@ abstract class PdoAbstract */ protected function quoteValue($sValue) { - return '\''.$sValue.'\''; + $oPdo = $this->getPDO(); + return $oPdo ? $oPdo->quote((string) $sValue, \PDO::PARAM_STR) : '\'\''; } /** @@ -214,18 +188,34 @@ abstract class PdoAbstract $oPdo = $this->getPDO(); if ($oPdo) { - $oStmt = $oPdo->prepare('SELECT * FROM rainloop_system WHERE sys_name = ?'); - $oStmt->execute(array($sName)); - $mRow = $oStmt->fetchAll(\PDO::FETCH_ASSOC); - if ($mRow && isset($mRow[0]['sys_name'], $mRow[0]['value_int'], $mRow[0]['value_str'])) + $sQuery = 'SELECT * FROM rainloop_system WHERE sys_name = ?'; + $this->writeLog($sQuery); + + $oStmt = $oPdo->prepare($sQuery); + if ($oStmt->execute(array($sName))) { - return $bReturnIntValue ? (int) $mRow[0]['value_int'] : (string) $mRow[0]['value_str']; + $mRow = $oStmt->fetchAll(\PDO::FETCH_ASSOC); + if ($mRow && isset($mRow[0]['sys_name'], $mRow[0]['value_int'], $mRow[0]['value_str'])) + { + return $bReturnIntValue ? (int) $mRow[0]['value_int'] : (string) $mRow[0]['value_str']; + } } } return false; } + /** + * @param string $sType + * @param bool $bReturnIntValue = true + * + * @return int|string|bool + */ + protected function getVersion($sName) + { + return $this->getSystemValue($sName.'_version', true); + } + /** * @param string $sType * @param int $iVersion @@ -239,15 +229,21 @@ abstract class PdoAbstract if ($oPdo) { $oPdo->beginTransaction(); + + $sQuery = 'DELETE FROM rainloop_system WHERE sys_name = ? AND value_int <= ?;'; + $this->writeLog($sQuery); - $oStmt = $oPdo->prepare('DELETE FROM rainloop_system WHERE sys_name = ? AND value_int <= ?'); - $bResult = !!$oStmt->execute(array($sName, $iVersion)); + $oStmt = $oPdo->prepare($sQuery); + $bResult = !!$oStmt->execute(array($sName.'_version', $iVersion)); if ($bResult) { - $oStmt = $oPdo->prepare('INSERT INTO `rainloop_system (sys_name, value_int) VALUES (?, ?)'); + $sQuery = 'INSERT INTO rainloop_system (sys_name, value_int) VALUES (?, ?);'; + $this->writeLog($sQuery); + + $oStmt = $oPdo->prepare($sQuery); if ($oStmt) { - $bResult = !!$oStmt->execute(array($sName, $iVersion)); + $bResult = !!$oStmt->execute(array($sName.'_version', $iVersion)); } } @@ -269,58 +265,121 @@ abstract class PdoAbstract */ protected function initSystemTables() { - $aQ = $this->getPdoSystemTables(); - $oPdo = $this->getPDO(); - if ($oPdo && 0 < count($aQ)) - { - $oPdo->beginTransaction(); + $bResult = true; - try + $oPdo = $this->getPDO(); + if ($oPdo) + { + $aQ = array(); + if ('mysql' === $this->sDbType) { - foreach ($aQ as $sQuery) + $aQ[] = 'CREATE TABLE IF NOT EXISTS `rainloop_system` ( + `sys_name` varchar(50) NOT NULL, + `value_int` int(11) UNSIGNED NOT NULL DEFAULT \'0\', + `value_str` varchar(255) NOT NULL DEFAULT \'\' + ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;'; + + $aQ[] = 'CREATE TABLE IF NOT EXISTS `rainloop_users` ( + `id_user` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `rl_email` varchar(255) NOT NULL, + UNIQUE `email_unique` (`rl_email`), + PRIMARY KEY(`id_user`) + ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ ;'; + } + + if (0 < \count($aQ)) + { + try { - $oPdo->exec($sQuery); + $oPdo->beginTransaction(); + + foreach ($aQ as $sQuery) + { + if ($bResult) + { + $this->writeLog($sQuery); + $bResult = false !== $oPdo->exec($sQuery); + } + } + + if ($bResult) + { + $oPdo->rollBack(); + } + else + { + $oPdo->commit(); + } + } + catch (\Exception $oException) + { + $oPdo->rollBack(); + + $this->oLogger->WriteException($oException); + throw $oException; } } - catch (\Exception $oException) - { - $oPdo->rollBack(); - throw $oException; - } - - $oPdo->commit(); } + + return $bResult; } /** - * @param string $sFromName - * @param int $iFromVersion + * @param string $sName * @param array $aData = array() * * @return bool */ - protected function smartDataBaseUpgrade($sFromName, $iFromVersion, $aData = array()) + protected function dataBaseUpgrade($sName, $aData = array()) { + $this->initSystemTables(); + + $iFromVersion = $this->getVersion($sName); + $bResult = false; $oPdo = $this->getPDO(); if ($oPdo) { $bResult = true; - if (0 === $iFromVersion) - { - $this->initSystemTables(); - } - foreach ($aData as $iVersion => $aQuery) { if ($iFromVersion < $iVersion) { - foreach ($aQuery as $sQuery) + try { - $oPdo->exec($sQuery); + $oPdo->beginTransaction(); + + foreach ($aQuery as $sQuery) + { + $this->writeLog($sQuery); + if (false === $oPdo->exec($sQuery)) + { + $bResult = false; + break; + } + } + + if ($bResult) + { + $oPdo->commit(); + } + else + { + $oPdo->rollBack(); + } + } + catch (\Exception $oException) + { + $oPdo->rollBack(); + throw $oException; } - $this->setVersion($sFromName, $iVersion); + if (!$bResult) + { + break; + } + + $this->setVersion($sName, $iVersion); } } } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AbstractProvider.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AbstractProvider.php index c8def51c7..7fa24bf31 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AbstractProvider.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AbstractProvider.php @@ -1,14 +1,27 @@ -oAccount = $oAccount; + } +} diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook.php index e2edaba86..b182e1e28 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook.php @@ -40,4 +40,37 @@ class PersonalAddressBook extends \RainLoop\Providers\AbstractProvider return $this->oDriver instanceof \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface && $this->oDriver->IsSupported(); } + + /** + * @return bool + */ + public function SynchronizeStorage() + { + return $this->IsSupported() && \method_exists($this->oDriver, 'SynchronizeStorage') && + $this->oDriver->SynchronizeStorage(); + } + + /** + * @param \RainLoop\Account $oAccount + * @param string $sSearch + * + * @return array + * + * @throws \InvalidArgumentException + */ + public function GetSuggestions($oAccount, $sSearch) + { + return $this->IsActive() ? $this->oDriver->GetSuggestions($oAccount, $sSearch) : array(); + } + + /** + * @param \RainLoop\Account $oAccount + * @param array $aEmail + * + * @return bool + */ + public function IncFrec($oAccount, $aEmail) + { + return $this->IsActive() ? $this->oDriver->IncFrec($oAccount, $aEmail) : false; + } } \ No newline at end of file diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Contact.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Contact.php index ada452b2c..82cafcbcb 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Contact.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Contact.php @@ -14,16 +14,6 @@ class Contact */ public $IdUser; - /** - * @var string - */ - public $DisplayName; - - /** - * @var string - */ - public $DisplayEmail; - /** * @var string */ @@ -42,12 +32,7 @@ class Contact /** * @var int */ - public $Created; - - /** - * @var int - */ - public $Updated; + public $Changed; /** * @var array @@ -68,11 +53,8 @@ class Contact { $this->IdContact = 0; $this->IdUser = 0; - $this->DisplayName = ''; - $this->DisplayEmail = ''; $this->DisplayInList = ''; $this->Type = \RainLoop\Providers\PersonalAddressBook\Enumerations\ContactType::DEFAULT_; - $this->IsShare = false; $this->CanBeChanged = false; $this->Changed = \time(); $this->TagsIds = array(); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Property.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Property.php index a41d1eeb2..679953462 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Property.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Property.php @@ -6,6 +6,11 @@ use RainLoop\Providers\PersonalAddressBook\Enumerations\PropertyType; class Property { + /** + * @var int + */ + public $IdProperty; + /** * @var int */ @@ -48,6 +53,7 @@ class Property public function Clear() { + $this->IdProperty = 0; $this->IdContact = 0; $this->IdUser = 0; diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/MySqlPersonalAddressBook.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/MySqlPersonalAddressBook.php index a4d129e44..d05ef6185 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/MySqlPersonalAddressBook.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/MySqlPersonalAddressBook.php @@ -61,7 +61,7 @@ class MySqlPersonalAddressBook */ public function DeleteContacts($oAccount, $aContactIds) { - $iUserID = $this->getUserId($oAccount); + $iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $aContactIds = \array_filter($aContactIds, function (&$mItem) { $mItem = (int) $mItem; @@ -73,8 +73,8 @@ class MySqlPersonalAddressBook return false; } - return !!$this->prepareAndExecute($oAccount, - 'DELETE FROM `rainloop_pab_contacts` WHERE id_user = :id_user AND `id_contact` IN ('.\implode(',', $aContactIds).')', + return !!$this->prepareAndExecute( + 'DELETE FROM `rainloop_pab_contacts` WHERE `id_user` = :id_user AND `id_contact` IN ('.\implode(',', $aContactIds).')', array(':id_user' => array($iUserID, \PDO::PARAM_INT))); } @@ -95,7 +95,7 @@ class MySqlPersonalAddressBook $iLimit = 0 < $iLimit ? (int) $iLimit : 20; $sSearch = \trim($sSearch); - $iUserID = $this->getUserId($oAccount); + $iUserID = $this->getUserId($oAccount->ParentEmailHelper()); if (!\in_array($iType, array(ContactType::SHARE, ContactType::AUTO))) { @@ -111,16 +111,93 @@ class MySqlPersonalAddressBook if (0 < \strlen($sSearch)) { $sSql .= ' AND `id_contact` IN ('. - 'SELECT DISTINCT `id_contact` FROM `rainloop_pab_prop` WHERE id_user = :id_user AND `value` LIKE :search'. + 'SELECT DISTINCT `id_contact` FROM `rainloop_pab_prop` WHERE id_user = :id_user AND `value` LIKE :search ESCAPE \'=\''. ')'; - $aParams[':search'] = array($this->convertSearchValue($sSearch), \PDO::PARAM_STR); + $aParams[':search'] = array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR); } $sSql .= ' ORDER BY `display_in_list` ASC LIMIT :limit OFFSET :offset'; $aParams[':limit'] = array($iLimit, \PDO::PARAM_INT); $aParams[':offset'] = array($iOffset, \PDO::PARAM_INT); + $oStmt = $this->prepareAndExecute($sSql, $aParams); + if ($oStmt) + { + $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); + + $aContacts = array(); + $aIdContacts = array(); + if (\is_array($aFetch) && 0 < \count($aFetch)) + { + foreach ($aFetch as $aItem) + { + $iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0; + if (0 < $iIdContact) + { + $aIdContacts[] = $iIdContact; + $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); + + $oContact->IdContact = $iIdContact; + $oContact->IdUser = $iUserID; + $oContact->DisplayInList = isset($aItem['display_in_list']) ? (string) $aItem['display_in_list'] : ''; + $oContact->Type = isset($aItem['type']) ? (int) $aItem['type'] : + \RainLoop\Providers\PersonalAddressBook\Enumerations\ContactType::DEFAULT_; + $oContact->Changed = isset($aItem['changed']) ? (int) $aItem['changed'] : 0; + $oContact->CanBeChanged = true; + + $aContacts[$iIdContact] = $oContact; + } + } + } + + unset($aFetch); + + if (0 < count($aIdContacts)) + { + $oStmt->closeCursor(); + + $sSql = 'SELECT * FROM `rainloop_pab_prop` WHERE id_user = :id_user AND `id_contact` IN ('.\implode(',', $aIdContacts).')'; + $oStmt = $this->prepareAndExecute($sSql, array( + ':id_user' => array($iUserID, \PDO::PARAM_INT) + )); + + if ($oStmt) + { + $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); + if (\is_array($aFetch) && 0 < \count($aFetch)) + { + foreach ($aFetch as $aItem) + { + if ($aItem && isset($aItem['id_prop'], $aItem['id_contact'], $aItem['type'], $aItem['value'])) + { + $iId = (int) $aItem['id_contact']; + if (0 < $iId && isset($aContacts[$iIdContact])) + { + $oProperty = new \RainLoop\Providers\PersonalAddressBook\Classes\Property(); + + $oProperty->IdProperty = (int) $aItem['id_prop']; + $oProperty->IdContact = $iIdContact; + $oProperty->IdUser = $iUserID; + $oProperty->Type = (int) $aItem['type']; + $oProperty->TypeCustom = isset($aItem['type_custom']) ? (string) $aItem['type_custom'] : ''; + $oProperty->Value = (string) $aItem['value']; + $oProperty->ValueClear = isset($aItem['value_clear']) ? (string) $aItem['value_clear'] : ''; + $oProperty->Frec = isset($aItem['frec']) ? (int) $aItem['frec'] : 0; + + $aContacts[$iIdContact]->Properties[] = $oProperty; + } + } + } + } + + unset($aFetch); + + return \array_values($aContacts); + } + } + } + return array(); } @@ -139,27 +216,27 @@ class MySqlPersonalAddressBook $sSearch = \trim($sSearch); if (0 === \strlen($sSearch)) { - throw new \InvalidArgumentException('$sSearch'); + throw new \InvalidArgumentException('Empty Search argument'); } - $iUserID = $this->getUserId($oAccount); + $iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $sTypes = implode(',', array( PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FULLNAME )); $sSql = 'SELECT DISTINCT `id_contact` FROM `rainloop_pab_prop` '. - 'WHERE id_user = :id_user AND `type` IN ('.$sTypes.') AND `value` LIKE :search'; + 'WHERE id_user = :id_user AND `type` IN ('.$sTypes.') AND `value` LIKE :search ESCAPE \'=\''; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT), ':limit' => array($iLimit, \PDO::PARAM_INT), - ':search' => array($this->convertSearchValue($sSearch), \PDO::PARAM_STR) + ':search' => array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR) ); $sSql .= ' ORDER BY `frec` ASC LIMIT :limit'; - $oStmt = $this->prepareAndExecute($oAccount, $sSql, $aParams); + $oStmt = $this->prepareAndExecute($sSql, $aParams); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); @@ -189,7 +266,7 @@ class MySqlPersonalAddressBook $sSql = 'SELECT `id_contact`, `type`, `value` FROM `rainloop_pab_prop` '. 'WHERE id_user = :id_user AND `type` IN ('.$sTypes.') AND `id_contact` IN ('.\implode(',', $aIdContacts).')'; - $oStmt = $this->prepareAndExecute($oAccount, $sSql, array( + $oStmt = $this->prepareAndExecute($sSql, array( ':id_user' => array($iUserID, \PDO::PARAM_INT) )); @@ -214,7 +291,7 @@ class MySqlPersonalAddressBook { $aResult[$iId][0] = $aItem['value']; } - else if ('' === $aResult[$iId][1] &&\in_array((int) $aItem['type'], array(PropertyType::FULLNAME))) + else if ('' === $aResult[$iId][1] && \in_array((int) $aItem['type'], array(PropertyType::FULLNAME))) { $aResult[$iId][1] = $aItem['value']; } @@ -238,16 +315,16 @@ class MySqlPersonalAddressBook /** * @param \RainLoop\Account $oAccount - * @param array $aEmail + * @param array $aEmails * * @return bool */ - public function IncFrec($oAccount, $aEmail) + public function IncFrec($oAccount, $aEmails) { - $iUserID = $this->getUserId($oAccount); + $iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $self = $this; - $aEmail = \array_filter($aEmail, function (&$mItem) use ($self) { + $aEmails = \array_filter($aEmails, function (&$mItem) use ($self) { $mItem = \strtolower(\trim($mItem)); if (0 < \strlen($mItem)) { @@ -257,28 +334,109 @@ class MySqlPersonalAddressBook return false; }); - if (0 === \count($aEmail)) + if (0 === \count($aEmails)) { - throw new \InvalidArgumentException('$aEmail'); + throw new \InvalidArgumentException('Empty Emails argument'); } - $sTypes = implode(',', array( - PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER - )); + $sSql = 'UPDATE `rainloop_pab_prop` SET `frec` = `frec` + 1 WHERE id_user = :id_user AND `type` IN ('. + \implode(',', array(PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER)); - $sSql = 'UPDATE `rainloop_pab_prop` SET `frec` = `frec` + 1 WHERE id_user = :id_user AND `type` IN ('.$sTypes; - - if (1 === \count($aEmail)) + if (1 === \count($aEmails)) { - $sSql .= ') AND `value` = '.$aEmail[0]; + $sSql .= ') AND `value` = '.$aEmails[0]; } else { - $sSql .= ') AND `value` IN ('.\implode(',', $aEmail).')'; + $sSql .= ') AND `value` IN ('.\implode(',', $aEmails).')'; } - return !!$this->prepareAndExecute($oAccount, $sSql, array( + return !!$this->prepareAndExecute($sSql, array( ':id_user' => array($iUserID, \PDO::PARAM_INT), )); } + + /** + * @return bool + */ + public function SynchronizeStorage() + { + return $this->dataBaseUpgrade('mysql-pab-version', array( + 1 => array( + +// -- rainloop_pab_contacts -- +'CREATE TABLE IF NOT EXISTS `rainloop_pab_contacts` ( + + `id_contact` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `id_user` int(11) UNSIGNED NOT NULL, + `display_in_list` varchar(255) NOT NULL DEFAULT \'\', + `type` int(11) UNSIGNED NOT NULL DEFAULT \'0\', + `changed` int(11) UNSIGNED NOT NULL DEFAULT \'0\', + + PRIMARY KEY(`id_contact`), + CONSTRAINT `id_user_fk_rainloop_pab_contacts` FOREIGN KEY (`id_user`) + REFERENCES `rainloop_users` (`id_user`) ON DELETE CASCADE ON UPDATE CASCADE + +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;', + +// -- rainloop_pab_prop -- +'CREATE TABLE IF NOT EXISTS `rainloop_pab_prop` ( + + `id_prop` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `id_contact` int(11) UNSIGNED NOT NULL, + `id_user` int(11) UNSIGNED NOT NULL, + `type` int(11) UNSIGNED NOT NULL, + `type_custom` varchar(50) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT \'\', + `value` varchar(255) NOT NULL DEFAULT \'\', + `value_custom` varchar(255) NOT NULL DEFAULT \'\', + `frec` int(11) UNSIGNED NOT NULL DEFAULT \'0\', + + PRIMARY KEY(`id_prop`), + INDEX `id_user_id_contact_index` (`id_user`, `id_contact`), + INDEX `id_user_value_index` (`id_user`, `value`), + CONSTRAINT `id_contact_fk_rainloop_pab_prop` FOREIGN KEY (`id_contact`) + REFERENCES `rainloop_pab_contacts` (`id_contact`) ON DELETE CASCADE ON UPDATE CASCADE + +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;', + +// -- rainloop_pab_tags -- +'CREATE TABLE IF NOT EXISTS `rainloop_pab_tags` ( + + `id_tag` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `id_contact` int(11) UNSIGNED NOT NULL, + `id_user` int(11) UNSIGNED NOT NULL, + `name` varchar(255) NOT NULL, + + PRIMARY KEY(`id_tag`), + UNIQUE `id_user_name_unique` (`id_user`, `name`), + CONSTRAINT `id_user_fk_rainloop_pab_tags` FOREIGN KEY (`id_user`) + REFERENCES `rainloop_users` (`id_user`) ON DELETE CASCADE ON UPDATE CASCADE + +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;', + +// -- rainloop_pab_tags_contacts -- +'CREATE TABLE IF NOT EXISTS `rainloop_pab_tags_contacts` ( + + `id_tag` int(11) UNSIGNED NOT NULL, + `id_contact` int(11) UNSIGNED NOT NULL, + + CONSTRAINT `id_contact_fk_rainloop_tags_contacts` FOREIGN KEY (`id_contact`) + REFERENCES `rainloop_pab_contacts` (`id_contact`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `id_tag_fk_rainloop_tags_contacts` FOREIGN KEY (`id_tag`) + REFERENCES `rainloop_pab_tags` (`id_tag`) ON DELETE CASCADE ON UPDATE CASCADE + +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;' + ))); + } + + /** + * @param string $sSearch + * + * @return string + */ + protected function specialConvertSearchValue($sSearch, $sEscapeSign = '=') + { + return '%'.\str_replace(array($sEscapeSign, '_', '%'), + array($sEscapeSign.$sEscapeSign, $sEscapeSign.'_', $sEscapeSign.'%'), $sSearch).'%'; + } } \ No newline at end of file