/home/ivoiecob/email.hirewise-va.com/modules/Contacts/Module.php
<?php
/**
* This code is licensed under AGPLv3 license or Afterlogic Software License
* if commercial version of the product was purchased.
* For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
*/
namespace Aurora\Modules\Contacts;
use Afterlogic\DAV\Backend;
use Afterlogic\DAV\Constants;
use Aurora\Api;
use Aurora\Modules\Contacts\Enums\Access;
use Aurora\Modules\Contacts\Enums\StorageType;
use Aurora\Modules\Contacts\Enums\SortField;
use Aurora\System\Enums\SortOrder;
use Aurora\Modules\Contacts\Classes\Contact;
use Aurora\Modules\Contacts\Classes\VCard\Helper;
use Aurora\Modules\Contacts\Models\ContactCard;
use Aurora\Modules\Contacts\Classes\Group;
use Aurora\Modules\Core\Module as CoreModule;
use Aurora\System\Exceptions\ApiException;
use Aurora\System\Notifications;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Capsule\Manager as Capsule;
use Sabre\DAV\UUIDUtil;
use Sabre\DAV\PropPatch;
/**
* @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
* @license https://afterlogic.com/products/common-licensing Afterlogic Software License
* @copyright Copyright (c) 2023, Afterlogic Corp.
*
* @property Settings $oModuleSettings
*
* @package Modules
*/
class Module extends \Aurora\System\Module\AbstractModule
{
protected $aImportExportFormats = ['csv', 'vcf'];
protected $userPublicIdToDelete = null;
/**
* @return Module
*/
public static function getInstance()
{
return parent::getInstance();
}
/**
* @return Module
*/
public static function Decorator()
{
return parent::Decorator();
}
/**
* @return Settings
*/
public function getModuleSettings()
{
return $this->oModuleSettings;
}
/**
* Initializes Contacts Module.
*
* @ignore
*/
public function init()
{
$this->subscribeEvent('Mail::AfterUseEmails', array($this, 'onAfterUseEmails'));
$this->subscribeEvent('Mail::GetBodyStructureParts', array($this, 'onGetBodyStructureParts'));
$this->subscribeEvent('Core::DeleteUser::before', array($this, 'onBeforeDeleteUser'));
$this->subscribeEvent('Core::DeleteUser::after', array($this, 'onAfterDeleteUser'));
$this->subscribeEvent('System::toResponseArray::after', array($this, 'onContactToResponseArray'));
$this->denyMethodsCallByWebApi([
'UpdateContactObject',
'CheckAccessToAddressBook',
'CheckAccessToObject'
]);
}
/***** public functions might be called with web API *****/
/**
* @apiDefine Contacts Contacts Module
* Main Contacts module. It provides PHP and Web APIs for managing contacts.
*/
/**
* @api {post} ?/Api/ GetSettings
* @apiName GetSettings
* @apiGroup Contacts
* @apiDescription Obtains list of module settings for authenticated user.
*
* @apiHeader {string} [Authorization] "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=GetSettings} Method Method name
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'GetSettings'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name.
* @apiSuccess {string} Result.Method Method name.
* @apiSuccess {mixed} Result.Result List of module settings in case of success, otherwise **false**.
* @apiSuccess {int} Result.Result.ContactsPerPage=20 Count of contacts that will be displayed on one page.
* @apiSuccess {string} Result.Result.ImportContactsLink="" Link for learning more about CSV format.
* @apiSuccess {array} Result.Result.Storages='[]' List of storages wich will be shown in the interface.
* @apiSuccess {array} Result.Result.ImportExportFormats='[]' List of formats that can be used for import and export contacts.
* @apiSuccess {array} Result.Result.\Aurora\Modules\Contacts\Enums\PrimaryEmail='[]' Enumeration with primary email values.
* @apiSuccess {array} Result.Result.\Aurora\Modules\Contacts\Enums\PrimaryPhone='[]' Enumeration with primary phone values.
* @apiSuccess {array} Result.Result.\Aurora\Modules\Contacts\Enums\PrimaryAddress='[]' Enumeration with primary address values.
* @apiSuccess {array} Result.Result.\Aurora\Modules\Contacts\Enums\SortField='[]' Enumeration with sort field values.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'GetSettings',
* Result: { ContactsPerPage: 20, ImportContactsLink: '', Storages: ['personal', 'team'],
* ImportExportFormats: ['csv', 'vcf'], \Aurora\Modules\Contacts\Enums\PrimaryEmail: {'Personal': 0, 'Business': 1, 'Other': 2},
* \Aurora\Modules\Contacts\Enums\PrimaryPhone: {'Mobile': 0, 'Personal': 1, 'Business': 2},
* \Aurora\Modules\Contacts\Enums\PrimaryAddress: {'Personal': 0, 'Business': 1},
* \Aurora\Modules\Contacts\Enums\SortField: {'Name': 1, 'Email': 2, 'Frequency': 3} }
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'GetSettings',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Obtains list of module settings for authenticated user.
* @return array
*/
public function GetSettings()
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$oUser = \Aurora\System\Api::getAuthenticatedUser();
$aResult = [
'AllowAddressBooksManagement' => $this->oModuleSettings->AllowAddressBooksManagement,
'ImportContactsLink' => $this->oModuleSettings->ImportContactsLink,
'PrimaryEmail' => (new Enums\PrimaryEmail())->getMap(),
'PrimaryPhone' => (new Enums\PrimaryPhone())->getMap(),
'PrimaryAddress' => (new Enums\PrimaryAddress())->getMap(),
'SortField' => (new SortField())->getMap(),
'ImportExportFormats' => $this->aImportExportFormats,
'SaveVcfServerModuleName' => \Aurora\System\Api::GetModuleManager()->ModuleExists('DavContacts') ? 'DavContacts' : '',
'ContactsPerPage' => $this->oModuleSettings->ContactsPerPage,
'ContactsSortBy' => $this->oModuleSettings->ContactsSortBy
];
if ($oUser && $oUser->isNormalOrTenant()) {
if (null !== $oUser->getExtendedProp(self::GetName() . '::ContactsPerPage')) {
$aResult['ContactsPerPage'] = $oUser->getExtendedProp(self::GetName() . '::ContactsPerPage');
}
$aResult['Storages'] = self::Decorator()->GetStorages();
}
return $aResult;
}
public function IsDisplayedStorage($Storage)
{
return true;
}
/**
* @deprecated since version 9.7.2
*/
public function GetContactStorages()
{
return $this->Decorator()->GetStorages();
}
public function GetStorageDisplayName($Storage)
{
$result = '';
switch($Storage) {
case Enums\StorageType::All:
$result = $this->i18N('LABEL_STORAGE_ALL');
break;
case Enums\StorageType::Personal:
$result = $this->i18N('LABEL_STORAGE_PERSONAL');
break;
case Enums\StorageType::Collected:
$result = $this->i18N('LABEL_STORAGE_COLLECTED');
break;
case Enums\StorageType::Team:
$result = $this->i18N('LABEL_STORAGE_TEAM');
break;
case Enums\StorageType::Shared:
$result = $this->i18N('LABEL_STORAGE_SHARED');
break;
}
return $result;
}
protected function GetStorageDisplayNameOverride($sStorageName, $sSotrageId)
{
$result = $sStorageName;
switch(true) {
case $sSotrageId === Enums\StorageType::Personal && $sStorageName === Constants::ADDRESSBOOK_DEFAULT_DISPLAY_NAME:
$result = $this->i18N('LABEL_STORAGE_PERSONAL');
break;
case $sSotrageId === Enums\StorageType::Collected && $sStorageName === Constants::ADDRESSBOOK_COLLECTED_DISPLAY_NAME:
$result = $this->i18N('LABEL_STORAGE_COLLECTED');
break;
case $sSotrageId === Enums\StorageType::Team && $sStorageName === Constants::ADDRESSBOOK_TEAM_DISPLAY_NAME:
$result = $this->i18N('LABEL_STORAGE_TEAM');
break;
case $sSotrageId === Enums\StorageType::Shared && $sStorageName === Constants::ADDRESSBOOK_SHARED_WITH_ALL_DISPLAY_NAME:
$result = $this->i18N('LABEL_STORAGE_SHARED');
break;
}
return $result;
}
public function GetStorages()
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$iUserId = \Aurora\System\Api::getAuthenticatedUserId();
$aAddressBooks = $this->Decorator()->GetAddressBooks($iUserId);
foreach ($aAddressBooks as &$oAddressBook) {
$oAddressBook['DisplayName'] = $this->GetStorageDisplayNameOverride($oAddressBook['DisplayName'], $oAddressBook['Id']);
}
$aStoragesOrder = [
StorageType::Personal,
StorageType::Collected,
StorageType::Shared,
StorageType::Team
];
return $this->sortAddressBooks($aAddressBooks, $aStoragesOrder);
}
protected function sortAddressBooks($aAddressBooks, $aOrder = [])
{
$priority_books = array();
$non_priority_books = array();
// Loop through the address books and check their ids
foreach ($aAddressBooks as $book) {
$id = $book['Id'];
if (in_array($id, $aOrder)) {
$priority_books[] = $book;
} else {
$non_priority_books[] = $book;
}
}
// Sort the priority books array by the order of the priority ids array
usort($priority_books, function ($a, $b) use ($aOrder) {
// Get the index of the ids in the priority ids array
$index_a = array_search($a['Id'], $aOrder);
$index_b = array_search($b['Id'], $aOrder);
// Compare the indexes
return $index_a - $index_b;
});
// Sort the non-priority books array by the DisplayName property in ascending order
usort($non_priority_books, function ($a, $b) {
// Compare the names
return strcmp($a['DisplayName'], $b['DisplayName']);
});
// Merge the two arrays and return the result
return array_merge($priority_books, $non_priority_books);
}
protected function getContactsCollection($iSortField = SortField::Name, $iSortOrder = SortOrder::ASC, $iOffset = 0, $iLimit = 20, $oFilters = null)
{
$sSortField = 'FullName';
$sSortFieldSecond = 'ViewEmail';
$sSortOrder = $iSortOrder === SortOrder::ASC ? 'asc' : 'desc';
switch ($iSortField) {
case SortField::Email:
$sSortField = 'ViewEmail';
$sSortFieldSecond = 'FullName';
break;
case SortField::Frequency:
$sSortField = 'AgeScore';
// $oFilters->select(Capsule::connection()->raw('*, (Frequency/CEIL(DATEDIFF(CURDATE() + INTERVAL 1 DAY, DateModified)/30)) as AgeScore'));
break;
case SortField::FirstName:
$sSortField = 'FirstName';
break;
case SortField::LastName:
$sSortField = 'LastName';
break;
case SortField::Name:
$sSortField = 'FullName';
break;
}
if ($iOffset > 0) {
$oFilters->offset($iOffset);
}
if ($iLimit > 0) {
$oFilters->limit($iLimit);
}
$oFilters
->orderBy(Capsule::connection()->raw("CASE WHEN `$sSortField` = '' THEN 1 ELSE 0 END"))
->orderBy($sSortField, $sSortOrder)
->orderBy($sSortFieldSecond, $sSortOrder)
;
$aArgs = [];
$mResult = $oFilters->get();
$this->broadcastEvent('getContactsCollection', $aArgs, $mResult);
return $mResult;
}
/**
* Resolve addressbooks numeric ids to text text ids
*
* @param mixed $oUser
* @param mixed $aContactsCollection
* @return void
*/
protected function resolveAddressbooksIdsForContacts($oUser, &$aContactsCollection)
{
$aAddressbooksMap = self::Decorator()->GetStoragesMapToAddressbooks();
$aAddressBooks = [];
$aPersonalAddressBooks = Backend::Carddav()->getAddressBooksForUser(Constants::PRINCIPALS_PREFIX . $oUser->PublicId);
foreach ($aPersonalAddressBooks as $oAddressBook) {
$aAddressBooks[$oAddressBook['id']] = $oAddressBook;
}
$aContactsCollection->each(function (&$contact) use ($aAddressBooks, $aAddressbooksMap) {
$contact->UUID = (string) $contact->UUID;
if (!isset($aAddressBooks[$contact->Storage])) {
$aAddressBooks[$contact->Storage] = Backend::Carddav()->getAddressBookById($contact->Storage);
}
$StorageTextId = false;
if ($aAddressBooks[$contact->Storage]) {
$StorageTextId = array_search($aAddressBooks[$contact->Storage]['uri'], $aAddressbooksMap);
}
$contact->AddressBookId = (int) $contact->Storage;
$contact->Storage = $StorageTextId ? $StorageTextId : (StorageType::AddressBook . '-' . $contact->Storage);
});
}
/**
* @api {post} ?/Api/ UpdateSettings
* @apiName UpdateSettings
* @apiGroup Contacts
* @apiDescription Updates module's settings - saves them to config.json file.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=UpdateSettings} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **ContactsPerPage** *int* Count of contacts per page.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'UpdateSettings',
* Parameters: '{ ContactsPerPage: 10 }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {bool} Result.Result Indicates if settings were updated successfully.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'UpdateSettings',
* Result: true
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'UpdateSettings',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Updates module's settings - saves them to config.json file or to user settings in db.
* @param int $ContactsPerPage Count of contacts per page.
* @return boolean
*/
public function UpdateSettings($ContactsPerPage)
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$bResult = false;
$oUser = \Aurora\System\Api::getAuthenticatedUser();
if ($oUser) {
if ($oUser->isNormalOrTenant()) {
$oUser->setExtendedProp(self::GetName() . '::ContactsPerPage', $ContactsPerPage);
return CoreModule::Decorator()->UpdateUserObject($oUser);
}
if ($oUser->isAdmin()) {
$this->setConfig('ContactsPerPage', $ContactsPerPage);
$bResult = $this->saveModuleConfig();
}
}
return $bResult;
}
/**
* @api {post} ?/Api/ Export
* @apiName Export
* @apiGroup Contacts
* @apiDescription Exports specified contacts to a file with specified format.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=Export} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **Format** *string* File format that should be used for export.<br>
*   **Filters** *array* Filters for obtaining specified contacts.<br>
*   **GroupUUID** *string* UUID of group that should contain contacts for export.<br>
*   **ContactUUIDs** *array* List of UUIDs of contacts that should be exported.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'Export',
* Parameters: '{ Format: "csv", Filters: [], GroupUUID: "", ContactUUIDs: [] }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result Contents of CSV or VCF file in case of success, otherwise **false**.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* contents of CSV or VCF file
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'Export',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Exports specified contacts to a file with specified format.
* @param string $Format File format that should be used for export.
* @param Builder $Filters Filters for obtaining specified contacts.
* @param string $GroupUUID UUID of group that should contain contacts for export.
* @param array $ContactUUIDs List of UUIDs of contacts that should be exported.
* @param bool $AddressBookId
*/
public function Export($UserId, $Storage, $Format, Builder $Filters = null, $GroupUUID = '', $ContactUUIDs = [], $AddressBookId = null)
{
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$sOutput = '';
if (!empty($GroupUUID)) {
$oGroup = self::Decorator()->GetGroup($UserId, $GroupUUID);
if ($oGroup) {
$ContactUUIDs = (is_array($ContactUUIDs) && count($ContactUUIDs) > 0) ? array_intersect(
$oGroup->Contacts,
$ContactUUIDs
) : $oGroup->Contacts;
}
}
if (is_array($ContactUUIDs)) {
$query = $this->getGetContactsQueryBuilder($UserId, $Storage, $AddressBookId, $Filters, false, true);
if ($Format === 'vcf') {
if (count($ContactUUIDs) > 0) {
$query = $query->whereIn('contacts_cards.CardId', $ContactUUIDs);
}
$rows = $query->select('carddata')->pluck('carddata')->toArray();
foreach ($rows as $row) {
$sOutput .= $row;
}
} elseif ($Format === 'csv') {
$oSync = new Classes\Csv\Sync();
if (count($ContactUUIDs) === 0) {
$ContactUUIDs = $query->select('CardId')->pluck('CardId')->toArray();
}
$aContacts = self::Decorator()->GetContactsByUids($UserId, $ContactUUIDs);
$sOutput = $oSync->Export($aContacts);
}
}
if (is_string($sOutput) && !empty($sOutput)) {
$fileName = 'export';
$aStorages = self::Decorator()->GetStorages();
foreach ($aStorages as $aStorage) {
if ($aStorage['Id'] === $Storage) {
$fileName = isset($aStorage['DisplayName']) ? $aStorage['DisplayName'] : $aStorage['Id'];
break;
}
}
header('Pragma: public');
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $fileName . '.' . $Format . '";');
header('Content-Transfer-Encoding: binary');
}
echo $sOutput;
}
public function GetContactAsVCF($UserId, $Contact)
{
Api::CheckAccess($UserId);
$oVCard = new \Sabre\VObject\Component\VCard();
Classes\VCard\Helper::UpdateVCardFromContact($Contact, $oVCard);
return $oVCard->serialize();
}
/**
* @api {post} ?/Api/ GetGroups
* @apiName GetGroups
* @apiGroup Contacts
* @apiDescription Returns all groups for authenticated user.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=GetGroups} Method Method name
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'GetGroups'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result List of groups in case of success, otherwise **false**.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'GetGroups',
* Result: [{ City: '', Company: '', Contacts: [], Country: '', Email: '', Fax: '', IdUser: 3,
* IsOrganization: false, Name: 'group_name', Phone: '', State: '', Street: '', UUID: 'uuid_value',
* Web: '', Zip: '' }]
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'GetGroups',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Returns all groups for authenticated user.
* @return array
*/
public function GetGroups($UserId = null, $UUIDs = [], $Search = '')
{
$result = [];
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$aArgs = [
'UserId' => $UserId,
'Storage' => StorageType::Personal,
'AddressBookId' => 0
];
if ($this->populateContactArguments($aArgs)) {
$query = Capsule::connection()->table('contacts_cards')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->select('adav_cards.id as card_id', 'carddata');
$query->where(function ($whereQuery) use ($UserId, $aArgs, $query) {
$this->prepareFiltersFromStorage($UserId, StorageType::Personal, $aArgs['AddressBookId'], $query, $whereQuery);
})->where('IsGroup', true);
if (is_array($UUIDs) && count($UUIDs) > 0) {
$query->whereIn('adav_cards.id', $UUIDs);
}
if (!empty($Search)) {
$query->where('FullName', 'LIKE', "%$Search%");
}
$groups = $query->get();
foreach ($groups as $group) {
$groupObj = new Group();
$groupObj->Id = (int) $group->card_id;
$groupObj->IdUser = $UserId;
$groupObj->populate(Helper::GetGroupDataFromVcard(
\Sabre\VObject\Reader::read(
$group->carddata,
\Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES
),
$group->card_id
));
$result[] = $groupObj;
}
}
return $result;
}
/**
* @api {post} ?/Api/ GetGroup
* @apiName GetGroup
* @apiGroup Contacts
* @apiDescription Returns group with specified UUID.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=GetGroup} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **$UUID** *string* UUID of group to return.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'GetGroup',
* Parameters: '{ UUID: "group_uuid" }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result Group object in case of success, otherwise **false**.
* @apiSuccess {string} Result.Result.City=""
* @apiSuccess {string} Result.Result.Company=""
* @apiSuccess {array} Result.Result.Contacts='[]'
* @apiSuccess {string} Result.Result.Country=""
* @apiSuccess {string} Result.Result.Email=""
* @apiSuccess {string} Result.Result.Fax=""
* @apiSuccess {int} Result.Result.IdUser=0
* @apiSuccess {bool} Result.Result.IsOrganization=false
* @apiSuccess {string} Result.Result.Name=""
* @apiSuccess {string} Result.Result.Phone=""
* @apiSuccess {string} Result.Result.Street=""
* @apiSuccess {string} Result.Result.UUID=""
* @apiSuccess {string} Result.Result.Web=""
* @apiSuccess {string} Result.Result.Zip=""
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'GetGroup',
* Result: { City: '', Company: 'group_company', Contacts: [], Country: '', Email: '', Fax: '',
* IdUser: 3, IsOrganization: true, Name: 'group_name', Phone:'', State:'', Street:'',
* UUID: 'group_uuid', Web:'', Zip: '' }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'GetGroup',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Returns group with specified UUID.
* @param string $UUID UUID of group to return.
* @return \Aurora\Modules\Contacts\Classes\Group
*/
public function GetGroup($UserId, $UUID)
{
$mResult = false;
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$oUser = Api::getUserById($UserId);
if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
$query = Capsule::connection()->table('contacts_cards')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->select('adav_cards.id as card_id', 'adav_cards.uri as card_uri', 'adav_addressbooks.id as addressbook_id', 'carddata');
$aArgs = [
'UUID' => $UUID,
'UserId' => $UserId
];
$query->where(function ($q) use ($aArgs, $query) {
$aArgs['Query'] = $query;
$this->broadcastEvent(self::GetName() . '::ContactQueryBuilder', $aArgs, $q);
});
$row = $query->where('contacts_cards.IsGroup', true)->first();
if ($row) {
if (!self::Decorator()->CheckAccessToAddressBook($oUser, $row->addressbook_id, Access::Read)) {
throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
}
$mResult = new Group();
$mResult->IdUser = $UserId;
$mResult->Id = $row->card_id;
$mResult->populate(
Helper::GetGroupDataFromVcard(
\Sabre\VObject\Reader::read(
$row->carddata,
\Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES
),
$row->card_uri
)
);
$mResult->UUID = $UUID;
}
}
return $mResult;
}
/**
* @api {post} ?/Api/ GetContacts
* @apiName GetContacts
* @apiGroup Contacts
* @apiDescription Returns list of contacts for specified parameters.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=GetContacts} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **Offset** *int* Offset of contacts list.<br>
*   **Limit** *int* Limit of result contacts list.<br>
*   **SortField** *int* Name of field order by.<br>
*   **SortOrder** *int* Sorting direction.<br>
*   **Storage** *string* Storage value.<br>
*   **Search** *string* Search string.<br>
*   **GroupUUID** *string* UUID of group that should contain all returned contacts.<br>
*   **Filters** *array* Other conditions for obtaining contacts list.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'GetContacts',
* Parameters: '{ Offset: 0, Limit: 20, SortField: 1, SortOrder: 0, Storage: "personal",
* Search: "", GroupUUID: "", Filters: [] }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result Object with contacts data in case of success, otherwise **false**.
* @apiSuccess {int} Result.Result.ContactCount Count of contacts that are obtained with specified conditions.
* @apiSuccess {array} Result.Result.List List of contacts objects.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'GetContacts',
* Result: '{ "ContactCount": 6, "List": [{ "UUID": "contact_uuid", "IdUser": 3, "Name": "",
* "Email": "contact@email.com", "Storage": "personal" }] }'
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'GetContacts',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Returns list of contacts for specified parameters.
* @param string $Storage Storage type of contacts.
* @param int $Offset Offset of contacts list.
* @param int $Limit Limit of result contacts list.
* @param int $SortField Name of field order by.
* @param int $SortOrder Sorting direction.
* @param string $Search Search string.
* @param string $GroupUUID UUID of group that should contain all returned contacts.
* @param Builder $Filters Other conditions for obtaining contacts list.
* @param bool $WithGroups Indicates whether contact groups should be included in the contact list
* @param bool $WithoutTeamContactsDuplicates Do not show a contact from the global address book if the contact with the same email address already exists in personal address book
* @param bool $Suggestions
* @param bool $AddressBookId
* @return array
*/
public function GetContacts($UserId, $Storage = '', $Offset = 0, $Limit = 20, $SortField = SortField::Name, $SortOrder = SortOrder::ASC, $Search = '', $GroupUUID = '', Builder $Filters = null, $WithGroups = false, $WithoutTeamContactsDuplicates = false, $Suggestions = false, $AddressBookId = null)
{
// $Storage is used by subscribers to prepare filters.
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$oUser = Api::getUserById($UserId);
$aContacts = [];
if (self::Decorator()->CheckAccessToAddressBook($oUser, $AddressBookId, Access::Read)) {
$query = $this->getGetContactsQueryBuilder($UserId, $Storage, $AddressBookId, $Filters, $Suggestions);
if (!empty($Search)) {
$query = $query->where(function ($query) use ($Search) {
$query->where('FullName', 'LIKE', "%$Search%")
->orWhere('PersonalEmail', 'LIKE', "%$Search%")
->orWhere('BusinessEmail', 'LIKE', "%$Search%")
->orWhere('OtherEmail', 'LIKE', "%$Search%")
->orWhere('BusinessCompany', 'LIKE', "%$Search%");
});
}
if (!empty($GroupUUID)) {
$oGroup = self::Decorator()->GetGroup($UserId, $GroupUUID);
if ($oGroup) {
$contacts = $oGroup->Contacts;
if (count($contacts) === 0) {
$contacts = [null];
}
$query->whereIn('adav_cards.id', $contacts);
}
}
$count = $query->count();
$aContactsCollection = $this->getContactsCollection($SortField, $SortOrder, $Offset, $Limit, $query);
if ($Storage === StorageType::All) {
$personalContacsCollection = $aContactsCollection->filter(function ($contact) {
return !$contact->IsTeam && !$contact->Shared;
});
if ($WithoutTeamContactsDuplicates) {
$aContactsCollection->each(function ($contact, $key) use (&$aContactsCollection, $personalContacsCollection) {
if ($contact->IsTeam && $personalContacsCollection->unique()->contains('ViewEmail', $contact->ViewEmail)) {
$aContactsCollection->forget($key);
} elseif ($contact->Auto) { // is collected contact
$aContactsCollection->each(function (&$subContact) use (&$aContactsCollection, $contact, $key) {
if ($subContact->IsTeam && $subContact->ViewEmail === $contact->ViewEmail) {
$subContact->AgeScore = $contact->AgeScore;
$aContactsCollection->forget($key);
}
if (!$contact->IsTeam && !$contact->Shared && !$contact->Auto && $subContact->ViewEmail === $contact->ViewEmail) {
$aContactsCollection->forget($key);
}
});
}
});
} else {
$aContactsCollection->each(function (&$contact, $key) use (&$aContactsCollection, $personalContacsCollection) {
if ($contact->IsTeam) {
$personalContact = $personalContacsCollection->unique()->filter(function ($subContact) use (&$contact) {
return strtolower($contact->ViewEmail) === strtolower($subContact->ViewEmail);
})->first(); // Find collected contact with same email
if ($personalContact) {
$contact->Frequency = $personalContact->Frequency;
if ($contact->Auto) { // is collected contact
$aContactsCollection = $aContactsCollection->filter(function ($subContact) use ($contact) {
return (strtolower($subContact->ViewEmail) === strtolower($contact->ViewEmail) && !$contact->Auto) ||
strtolower($subContact->ViewEmail) !== strtolower($contact->ViewEmail);
}); // remove all collected contacts
}
}
}
});
}
}
$this->resolveAddressbooksIdsForContacts($oUser, $aContactsCollection);
// TODO: workaround for mobile APP
$aContactsCollection->each(function ($contact) use ($UserId) {
if (!$contact->UserId) {
$contact->UserId = $UserId;
}
});
$aContacts = $aContactsCollection->toArray();
if ($WithGroups) {
$groups = self::Decorator()->GetGroups($UserId, [], $Search);
if (is_array($groups) && count($groups) > 0) {
$groupContactsUuids = [];
$contactsUuids = [];
array_map(function ($item) use (&$groupContactsUuids, &$contactsUuids) {
if (is_array($item->Contacts) && count($item->Contacts) > 0) {
$groupContactsUuids[$item->UUID] = $item->Contacts;
$contactsUuids = array_merge($contactsUuids, $item->Contacts);
}
}, $groups);
$groupContacts = [];
$contactsUuids = array_unique($contactsUuids);
if (count($contactsUuids) > 0) {
foreach (self::Decorator()->GetContactsByUids($UserId, $contactsUuids) as $groupContact) {
$groupContacts[$groupContact->Id] = $groupContact;
}
$aGroupUsersList = [];
foreach ($groups as $group) {
$aGroupContactsEmails = [];
if (is_array($group->Contacts)) {
foreach ($group->Contacts as $contactUuid) {
if (isset($groupContacts[$contactUuid])) {
$oContact = $groupContacts[$contactUuid];
$aGroupContactsEmails[] = $oContact->FullName ? "\"{$oContact->FullName}\" <{$oContact->ViewEmail}>" : $oContact->ViewEmail;
}
}
$aGroupUsersList[] = [
'Id' => $group->UUID,
'UUID' => (string)$group->UUID,
'IdUser' => $group->IdUser,
'Name' => $group->Name,
'Emails' => implode(', ', $aGroupContactsEmails),
'IsGroup' => true,
'Storage' => '',
];
}
}
$aContacts = array_merge($aContacts, $aGroupUsersList);
}
}
}
} else {
throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
}
return [
'ContactCount' => $count,
'List' => \Aurora\System\Managers\Response::GetResponseObject(array_values($aContacts))
];
}
public function GetContactSuggestions($UserId, $Storage, $Limit = 20, $SortField = SortField::Name, $SortOrder = SortOrder::ASC, $Search = '', $WithGroups = false, $WithoutTeamContactsDuplicates = false, $WithUserGroups = false)
{
$WithoutTeamContactsDuplicates = false;
// $Storage is used by subscribers to prepare filters.
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$aResult = array(
'ContactCount' => 0,
'List' => []
);
$aContacts = $this->Decorator()->GetContacts($UserId, $Storage, 0, $Limit, $SortField, $SortOrder, $Search, '', null, $WithGroups, $WithoutTeamContactsDuplicates, true);
$aResultList = $aContacts['List'];
$aResult['List'] = $aResultList;
$aResult['ContactCount'] = count($aResultList);
if ($WithUserGroups) {
$oUser = \Aurora\Api::getUserById($UserId);
if ($oUser) {
$aGroups = CoreModule::Decorator()->GetGroups($oUser->IdTenant, $Search);
foreach ($aGroups['Items'] as $aGroup) {
$aGroup['IsGroup'] = true;
$aResult['List'][] = $aGroup;
$aResult['ContactCount']++;
}
}
}
return $aResult;
}
/**
* This method used as trigger for subscibers. Check these modules: PersonalContacts, SharedContacts, TeamContacts
*/
public function CheckAccessToObject($User, $Contact, $Access = null)
{
return true;
}
public function CheckAccessToAddressBook($User, $AddressBookId, $Access = null)
{
return true;
}
/**
* @api {post} ?/Api/ GetContact
* @apiName GetContact
* @apiGroup Contacts
* @apiDescription Returns contact with specified UUID.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=GetContact} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **UUID** *string* UUID of contact to return.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'GetContact',
* Parameters: '{ UUID: "contact_uuid" }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result Object with contact data in case of success, otherwise **false**.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'GetContact',
* Result: '{ "IdUser": 3, "UUID": "group_uuid", "Storage": "personal", "FullName": "", "PrimaryEmail": 0,
* "PrimaryPhone": 1, "PrimaryAddress": 0, "FirstName": "", "LastName": "", "NickName": "", "Skype": "",
* "Facebook": "", "PersonalEmail": "contact@email.com", "PersonalAddress": "", "PersonalCity": "",
* "PersonalState": "", "PersonalZip": "", "PersonalCountry": "", "PersonalWeb": "", "PersonalFax": "",
* "PersonalPhone": "", "PersonalMobile": "123-234-234", "BusinessEmail": "", "BusinessCompany": "",
* "BusinessAddress": "", "BusinessCity": "", "BusinessState": "", "BusinessZip": "", "BusinessCountry": "",
* "BusinessJobTitle": "", "BusinessDepartment": "", "BusinessOffice": "", "BusinessPhone": "",
* "BusinessFax": "", "BusinessWeb": "", "OtherEmail": "", "Notes": "", "BirthDay": 0, "BirthMonth": 0,
* "BirthYear": 0, "ETag": "", "GroupUUIDs": ["group1_uuid", "group2_uuid"] }'
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'GetContact',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Returns contact with specified UUID.
* @param string $UUID UUID of contact to return.
* @return \Aurora\Modules\Contacts\Classes\Contact
*/
public function GetContact($UUID, $UserId = null)
{
$mResult = false;
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$aContacts = self::Decorator()->GetContactsByUids($UserId, [$UUID]);
if (count($aContacts) > 0) {
$mResult = $aContacts[0];
}
return $mResult;
}
/**
* @api {post} ?/Api/ GetContactsByEmails
* @apiName GetContactsByEmails
* @apiGroup Contacts
* @apiDescription Returns list of contacts with specified emails.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=GetContactsByEmails} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **Emails** *array* List of emails of contacts to return.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'GetContactsByEmails',
* Parameters: '{ Emails: ["contact@email.com"] }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result List of contacts in case of success, otherwise **false**.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'GetContactsByEmails',
* Result: [{ "IdUser": 3, "UUID": "group_uuid", "Storage": "personal", "FullName": "", "PrimaryEmail": 0,
* "PrimaryPhone": 1, "PrimaryAddress": 0, "FirstName": "", "LastName": "", "NickName": "", "Skype": "",
* "Facebook": "", "PersonalEmail": "contact@email.com", "PersonalAddress": "", "PersonalCity": "",
* "PersonalState": "", "PersonalZip": "", "PersonalCountry": "", "PersonalWeb": "", "PersonalFax": "",
* "PersonalPhone": "", "PersonalMobile": "123-234-234", "BusinessEmail": "", "BusinessCompany": "",
* "BusinessAddress": "", "BusinessCity": "", "BusinessState": "", "BusinessZip": "", "BusinessCountry": "",
* "BusinessJobTitle": "", "BusinessDepartment": "", "BusinessOffice": "", "BusinessPhone": "",
* "BusinessFax": "", "BusinessWeb": "", "OtherEmail": "", "Notes": "", "BirthDay": 0, "BirthMonth": 0,
* "BirthYear": 0, "ETag": "", "GroupUUIDs": ["group1_uuid", "group2_uuid"] }]
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'GetContactsByEmails',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Returns list of contacts with specified emails.
* @param string $Storage storage of contacts.
* @param array $Emails List of emails of contacts to return.
* @param int|null $AddressBookId
* @return \Illuminate\Database\Eloquent\Collection|null
*/
public function GetContactsByEmails($UserId, $Storage, $Emails, $AddressBookId = null)
{
$result = [];
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$oUser = Api::getUserById($UserId);
if (self::Decorator()->CheckAccessToAddressBook($oUser, $AddressBookId, Access::Read)) {
$filter = ContactCard::whereIn('ViewEmail', $Emails);
$query = $this->getGetContactsQueryBuilder($UserId, $Storage, $AddressBookId, $filter);
$result = $query->get();
$this->resolveAddressbooksIdsForContacts($oUser, $result);
} else {
throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
}
return $result;
}
/**
* Returns list of contacts with specified uids.
* @param int $UserId
* @param array $Uids List of uids of contacts to return.
* @return array
*/
public function GetContactsByUids($UserId, $Uids)
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$oUser = Api::getUserById($UserId);
$mResult = [];
if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
$query = Capsule::connection()->table('contacts_cards')
->select('adav_cards.id as CardId', 'adav_cards.uri as card_uri', 'adav_addressbooks.id as addressbook_id', 'contacts_cards.Properties', 'carddata', 'etag', 'core_users.Id as UserId')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->leftJoin('core_users', 'adav_addressbooks.principaluri', '=', Capsule::connection()->raw("CONCAT('principals/', " . Capsule::connection()->getTablePrefix() . "core_users.PublicId)"));
$aArgs = [
'UUID' => $Uids,
'UserId' => $UserId
];
$query->where(function ($q) use ($aArgs, $query) {
$aArgs['Query'] = $query;
$this->broadcastEvent(self::GetName() . '::ContactQueryBuilder', $aArgs, $q);
});
$rows = $query->get();
foreach($rows as $row) {
if (!self::Decorator()->CheckAccessToAddressBook($oUser, $row->addressbook_id, Access::Read)) {
continue;
}
$oContact = new Contact();
$oContact->Id = $row->CardId;
$oContact->InitFromVCardStr($row->UserId, $row->carddata);
$oContact->ETag = \trim($row->etag, '"');
$storagesMapToAddressbooks = self::Decorator()->GetStoragesMapToAddressbooks();
$addressbook = Backend::Carddav()->getAddressBookById($row->addressbook_id);
$key = false;
if ($addressbook) {
$key = array_search($addressbook['uri'], $storagesMapToAddressbooks);
}
$oContact->Storage = $key !== false ? $key : StorageType::AddressBook;
$oContact->AddressBookId = (int) $row->addressbook_id;
if ($row->Properties) {
$oContact->Properties = \json_decode($row->Properties, true);
}
$groups = self::Decorator()->GetGroups($UserId);
foreach ($groups as $group) {
if (in_array($row->CardId, $group->Contacts)) {
$oContact->GroupUUIDs[] = $group->UUID;
}
}
$mResult[] = $oContact;
}
}
return $mResult;
}
/**
* Returns list of contacts with specified emails.
* @param string $Storage storage of contacts.
* @param int|null $UserId
* @param Builder $Filters
* @return array
*/
public function GetContactsInfo($Storage, $UserId = null, Builder $Filters = null)
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$aResult = [
'CTag' => 0,
'Info' => []
];
$aArgs = [
'UserId' => $UserId,
'Storage' => $Storage,
'AddressBookId' => 0
];
if ($this->populateContactArguments($aArgs)) {
if ((int) $aArgs['AddressBookId'] > 0) {
$addressbook = Backend::Carddav()->getAddressBookById($aArgs['AddressBookId']);
if ($addressbook) {
$aResult['CTag'] = (int) $addressbook['{http://sabredav.org/ns}sync-token'];
}
}
$query = $this->getGetContactsQueryBuilder($UserId, $Storage, $aArgs['AddressBookId'], $Filters);
$aContacts = $query->get(['UUID', 'ETag', 'Auto', 'Storage']);
$storagesMapToAddressbooks = self::Decorator()->GetStoragesMapToAddressbooks();
foreach ($aContacts as $oContact) {
$StorageTextId = false;
if (!empty($addressbook)) {
$StorageTextId = array_search($addressbook['uri'], $storagesMapToAddressbooks);
}
/**
* @var \Aurora\Modules\Contacts\Models\ContactCard $oContact
*/
$aResult['Info'][] = [
'UUID' => (string) $oContact->UUID,
'ETag' => $oContact->ETag,
'Storage' => $StorageTextId ? $StorageTextId : (string) $oContact->Storage,
'IsTeam' => $oContact->IsTeam,
'Shared' => $oContact->Shared,
];
}
}
return $aResult;
}
/**
* @api {post} ?/Api/ CreateContact
* @apiName CreateContact
* @apiGroup Contacts
* @apiDescription Creates contact with specified parameters.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=CreateContact} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **Contact** *object* Parameters of contact to create.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'CreateContact',
* Parameters: '{ "Contact": { "UUID": "", "PrimaryEmail": 0, "PrimaryPhone": 0, "PrimaryAddress": 0,
* "FullName": "second", "FirstName": "", "LastName": "", "NickName": "", "Storage": "personal",
* "Skype": "", "Facebook": "", "PersonalEmail": "contact2@email.com", "PersonalAddress": "",
* "PersonalCity": "", "PersonalState": "", "PersonalZip": "", "PersonalCountry": "", "PersonalWeb": "",
* "PersonalFax": "", "PersonalPhone": "", "PersonalMobile": "", "BusinessEmail": "", "BusinessCompany": "",
* "BusinessJobTitle": "", "BusinessDepartment": "", "BusinessOffice": "", "BusinessAddress": "",
* "BusinessCity": "", "BusinessState": "", "BusinessZip": "", "BusinessCountry": "", "BusinessFax": "",
* "BusinessPhone": "", "BusinessWeb": "", "OtherEmail": "", "Notes": "", "ETag": "", "BirthDay": 0,
* "BirthMonth": 0, "BirthYear": 0, "GroupUUIDs": [] } }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result New contact UUID in case of success, otherwise **false**.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'CreateContact',
* Result: 'new_contact_uuid'
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'CreateContact',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Creates contact with specified parameters.
* @param array $Contact Parameters of contact to create.
* @param int $UserId Identifier of user that should own a new contact.
* @return bool|string
* @throws \Aurora\System\Exceptions\ApiException
*/
public function CreateContact($Contact, $UserId = null)
{
Api::CheckAccess($UserId);
$oUser = \Aurora\Api::getUserById($UserId);
$mResult = false;
if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
$oContact = new Classes\Contact();
$oContact->IdUser = $oUser->Id;
$oContact->IdTenant = $oUser->IdTenant;
$oContact->populate($Contact);
$oContact->Frequency = $this->getAutocreatedContactFrequencyAndDeleteIt($oUser->Id, $oContact->ViewEmail);
$oVCard = new \Sabre\VObject\Component\VCard();
Helper::UpdateVCardFromContact($oContact, $oVCard);
if (self::Decorator()->CheckAccessToAddressBook($oUser, $oContact->AddressBookId, Access::Write)) {
$cardUri = $oContact->UUID . '.vcf';
$cardETag = Backend::Carddav()->createCard($oContact->AddressBookId, $cardUri, $oVCard->serialize());
if ($cardETag) {
$newCard = Backend::Carddav()->getCard($oContact->AddressBookId, $cardUri);
if ($newCard) {
ContactCard::where('CardId', $newCard['id'])->update(['Frequency' => $oContact->Frequency]);
if (is_array($oContact->GroupUUIDs) && count($oContact->GroupUUIDs) > 0) {
$oGroups = self::Decorator()->GetGroups($UserId, $oContact->GroupUUIDs);
if ($oGroups) {
foreach ($oGroups as $oGroup) {
$oGroup->Contacts = array_merge($oGroup->Contacts, [(string) $newCard['id']]);
$this->UpdateGroupObject($UserId, $oGroup);
}
}
}
$mResult = [
'UUID' => (string) $newCard['id'],
'ETag' => \trim($newCard['etag'], '"')
];
}
}
} else {
throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
}
}
return $mResult;
}
/**
* Obtains autocreated contact frequency if user have already created it.
* Removes autocreated contact.
* @param int $UserId User identifier.
* @param string $sViewEmail View email of contact to create
*/
private function getAutocreatedContactFrequencyAndDeleteIt($UserId, $sViewEmail)
{
Api::CheckAccess($UserId);
$iFrequency = 0;
$aArgs = [
'UserId' => $UserId,
'Storage' => StorageType::Collected,
'AddressBookId' => 0
];
if ($this->populateContactArguments($aArgs)) {
$oQuery = ContactCard::where([
['AddressBookId', '=', $aArgs['AddressBookId']],
['ViewEmail', '=', $sViewEmail]
]);
$oAutocreatedContacts = $this->getContactsCollection(
SortField::Name,
SortOrder::ASC,
0,
1,
$oQuery
);
$oContact = $oAutocreatedContacts->first();
if ($oContact instanceof ContactCard) {
$card_uri = Capsule::connection()->table('adav_cards')
->where('id', $oContact->CardId)
->pluck('uri')->first();
Backend::Carddav()->deleteCard($oContact->AddressBookId, $card_uri);
$iFrequency = $oContact->Frequency;
}
}
return $iFrequency;
}
/**
* @api {post} ?/Api/ UpdateContact
* @apiName UpdateContact
* @apiGroup Contacts
* @apiDescription Updates contact with specified parameters.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=UpdateContact} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **Contact** *array* Parameters of contact to update.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'UpdateContact',
* Parameters: '{ "Contact": { "UUID": "contact2_uuid", "PrimaryEmail": 0, "PrimaryPhone": 0,
* "PrimaryAddress": 0, "FullName": "contact2", "FirstName": "", "LastName": "", "NickName": "",
* "Storage": "personal", "Skype": "", "Facebook": "", "PersonalEmail": "contact2@email.com",
* "PersonalAddress": "", "PersonalCity": "", "PersonalState": "", "PersonalZip": "", "PersonalCountry": "",
* "PersonalWeb": "", "PersonalFax": "", "PersonalPhone": "", "PersonalMobile": "", "BusinessEmail": "",
* "BusinessCompany": "", "BusinessJobTitle": "", "BusinessDepartment": "", "BusinessOffice": "",
* "BusinessAddress": "", "BusinessCity": "", "BusinessState": "", "BusinessZip": "", "BusinessCountry": "",
* "BusinessFax": "", "BusinessPhone": "", "BusinessWeb": "", "OtherEmail": "", "Notes": "", "ETag": "",
* "BirthDay": 0, "BirthMonth": 0, "BirthYear": 0, "GroupUUIDs": [] } }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {bool} Result.Result Indicates if contact was updated successfully.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'UpdateContact',
* Result: true
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'UpdateContact',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Updates contact with specified parameters.
* @param array $Contact Parameters of contact to update.
* @return array|bool
*/
public function UpdateContact($UserId, $Contact)
{
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$oContact = self::Decorator()->GetContact($Contact['UUID'], $UserId);
$oUser = Api::getUserById($UserId);
if ($oContact && self::Decorator()->CheckAccessToObject($oUser, $oContact, Access::Write)) {
$oContact->populate($Contact);
$result = self::Decorator()->UpdateContactObject($oContact);
if ($result) {
if (is_array($oContact->GroupUUIDs)) {
$groups = self::Decorator()->GetGroups($UserId);
foreach ($groups as $group) {
if ($group) {
if (!in_array($group->UUID, $oContact->GroupUUIDs)) {
$group->Contacts = array_diff($group->Contacts, [$oContact->UUID]);
} else {
$group->Contacts = array_merge($group->Contacts, [$oContact->UUID]);
}
$this->UpdateGroupObject($UserId, $group);
}
}
}
return [
'UUID' => (string) $oContact->UUID,
'ETag' => $result
];
} else {
return false;
}
} else {
throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
}
return false;
}
public function MoveContactsToStorage($UserId, $FromStorage, $ToStorage, $UUIDs)
{
$result = false;
if ($ToStorage === StorageType::Team) { // skip moving to team storage
return false;
}
$query = Capsule::connection()
->table('contacts_cards')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->select('adav_cards.uri as card_uri', 'adav_cards.id as card_id', 'adav_addressbooks.id as addressbook_id');
$aArgs = [
'UserId' => $UserId,
'UUID' => $UUIDs
];
// build a query to obtain the card_uri and card_id with checking access to the contact
$cardsUris = $query->where(function ($q) use ($aArgs, $query) {
$aArgs['Query'] = $query;
$this->broadcastEvent(self::GetName() . '::ContactQueryBuilder', $aArgs, $q);
})->pluck('card_uri', 'card_id')->toArray();
$aArgsTo = [
'UserId' => $UserId,
'Storage' => $ToStorage,
'AddressBookId' => 0
];
$resultFrom = true;
$resultTo = $this->populateContactArguments($aArgsTo);
$ToAddressBookId = (int) $aArgsTo['AddressBookId']; // getting ToAddressBookId from ToStorage
foreach ($cardsUris as $cardId => $cardUri) {
$FromAddressBookId = 0;
if ($FromStorage === StorageType::All) { // getting $FromAddressBookId from the contact
$oContact = self::Decorator()->GetContact($cardId, $UserId);
if ($oContact instanceof Contact) {
if ($oContact->Storage === StorageType::Team) { // skip the team contact
continue;
}
$FromAddressBookId = (int) $oContact->AddressBookId;
}
} else {
$aArgsFrom = [
'UserId' => $UserId,
'Storage' => $FromStorage,
'AddressBookId' => 0
];
$resultFrom = $this->populateContactArguments($aArgsFrom);
$FromAddressBookId = (int) $aArgsFrom['AddressBookId'];
}
if ($FromAddressBookId != $ToAddressBookId && $resultFrom && $resultTo) { // do not allow contact to be moved to its own storage
$result = Backend::Carddav()->updateCardAddressBook($FromAddressBookId, $ToAddressBookId, $cardUri);
}
}
return $result;
}
/**
* !Not public
* This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
* @param Contact $Contact
* @return string|bool
*/
public function UpdateContactObject($Contact)
{
$mResult = false;
$oUser = \Aurora\System\Api::getAuthenticatedUser();
$aStorageParts = \explode('-', $Contact->Storage);
if (isset($aStorageParts[0], $aStorageParts[1]) && $aStorageParts[0] === StorageType::AddressBook) {
$Contact->AddressBookId = (int) $aStorageParts[1];
$Contact->Storage = StorageType::AddressBook;
}
$query = Capsule::connection()->table('contacts_cards')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->select('adav_cards.uri as card_uri', 'adav_addressbooks.id as addressbook_id', 'carddata');
$aArgs = [
'UserId' => $oUser->Id,
'UUID' => $Contact->Id
];
// build a query to obtain the addressbook_id and card_uri with checking access to the contact
$query->where(function ($q) use ($aArgs, $query) {
$aArgs['Query'] = $query;
$this->broadcastEvent(self::GetName() . '::ContactQueryBuilder', $aArgs, $q);
});
$row = $query->first();
if ($row) {
$oVCard = \Sabre\VObject\Reader::read($row->carddata);
$uidVal = $oVCard->UID->getValue();
if (empty($uidVal) || is_numeric($uidVal)) {
$uriInfo = pathinfo($row->card_uri);
if (isset($uriInfo['filename'])) {
$oVCard->UID = $uriInfo['filename'];
}
}
Helper::UpdateVCardFromContact($Contact, $oVCard);
$mResult = Backend::Carddav()->updateCard($row->addressbook_id, $row->card_uri, $oVCard->serialize());
$mResult = str_replace('"', '', $mResult);
}
return $mResult;
}
/**
* @api {post} ?/Api/ DeleteContacts
* @apiName DeleteContacts
* @apiGroup Contacts
* @apiDescription Deletes contacts with specified UUIDs.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=DeleteContacts} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **UUIDs** *array* Array of strings - UUIDs of contacts to delete.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'DeleteContacts',
* Parameters: '{ UUIDs: ["uuid1", "uuid"] }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {bool} Result.Result Indicates if contacts were deleted successfully.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'DeleteContacts',
* Result: true
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'DeleteContacts',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Deletes contacts with specified UUIDs.
* @param int $UserId
* @param string $Storage
* @param array $UUIDs Array of strings - UUIDs of contacts to delete.
* @return bool
*/
public function DeleteContacts($UserId, $Storage, $UUIDs)
{
$mResult = false;
Api::CheckAccess($UserId);
$oUser = Api::getUserById($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$AddressBookId = $Storage; // It's trick for API compatibility. Method should accept numeric AddressBookId, but clients sends storage name as ID
if (self::Decorator()->CheckAccessToAddressBook($oUser, $AddressBookId, Enums\Access::Write)) {
$query = Capsule::connection()->table('contacts_cards')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->select('adav_cards.id as card_id', 'adav_cards.uri as card_uri', 'adav_addressbooks.id as addressbook_id')
->where('adav_cards.addressbookid', '=', $AddressBookId);
$aArgs = [
'UUID' => $UUIDs,
'UserId' => $UserId
];
$query->where(function ($q) use ($aArgs, $query) {
$aArgs['Query'] = $query;
$this->broadcastEvent(self::GetName() . '::ContactQueryBuilder', $aArgs, $q);
});
$rows = $query->distinct()->get()->all();
$groups = self::Decorator()->GetGroups($UserId);
$groupsToUpdate = [];
foreach ($rows as $row) {
Backend::Carddav()->deleteCard($row->addressbook_id, $row->card_uri);
foreach ($groups as $group) {
if (($key = array_search($row->card_id, $group->Contacts)) !== false) {
unset($group->Contacts[$key]);
if (!in_array($group->UUID, $groupsToUpdate)) {
$groupsToUpdate[] = $group->UUID;
}
}
}
}
foreach ($groups as $group) {
if (in_array($group->UUID, $groupsToUpdate)) {
$this->UpdateGroupObject($UserId, $group);
}
}
$mResult = true;
} else {
throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
}
return $mResult;
}
/**
* @api {post} ?/Api/ CreateGroup
* @apiName CreateGroup
* @apiGroup Contacts
* @apiDescription Creates group with specified parameters.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=CreateGroup} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **Group** *object* Parameters of group to create.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'CreateGroup',
* Parameters: '{ "Group": { "UUID": "", "Name": "new_group_name", "IsOrganization": "0", "Email": "",
* "Country": "", "City": "", "Company": "", "Fax": "", "Phone": "", "State": "", "Street": "",
* "Web": "", "Zip": "", "Contacts": [] } }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result New group UUID in case of success, otherwise **false**.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'CreateGroup',
* Result: 'new_group_uuid'
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'CreateGroup',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Creates group with specified parameters.
* @param array $Group Parameters of group to create.
* @return string|bool
*/
public function CreateGroup($Group, $UserId = null)
{
$mResult = false;
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
if (is_array($Group)) {
\Aurora\System\Validator::validate($Group, [
'Name' => 'required'
], [
'required' => 'The :attribute field is required.'
]);
$oGroup = new Classes\Group();
$oGroup->IdUser = (int) $UserId;
$oGroup->populate($Group);
if (isset($Group['Contacts']) && is_array($Group['Contacts'])) {
$oGroup->Contacts = $this->getContactsUUIDsFromIds($UserId, $Group['Contacts']);
}
$oVCard = new \Sabre\VObject\Component\VCard();
Helper::UpdateVCardFromGroup($oGroup, $oVCard);
$userPublicId = Api::getUserPublicIdById($UserId);
$addressBook = Backend::Carddav()->getAddressBookForUser(Constants::PRINCIPALS_PREFIX . $userPublicId, Constants::ADDRESSBOOK_DEFAULT_NAME);
$cardUri = $oGroup->UUID . '.vcf';
if ($addressBook) {
$cardETag = Backend::Carddav()->createCard($addressBook['id'], $cardUri, $oVCard->serialize());
if ($cardETag) {
$newCard = Backend::Carddav()->getCard($addressBook['id'], $cardUri);
if ($newCard) {
$mResult = [
'UUID' => (string) $newCard['id'],
'ETag' => \trim($newCard['etag'], '"')
];
}
}
}
}
return $mResult;
}
protected function getContactsUUIDsFromIds($UserId, $Ids)
{
if (is_array($Ids) && count($Ids) > 0) {
$query = Capsule::connection()->table('contacts_cards')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->select('adav_cards.uri as card_uri');
$aArgs = [
'UserId' => $UserId,
'UUID' => $Ids
];
// build a query to obtain the addressbook_id and card_uri with checking access to the contact
$query->where(function ($q) use ($aArgs, $query) {
$aArgs['Query'] = $query;
$this->broadcastEvent(self::GetName() . '::ContactQueryBuilder', $aArgs, $q);
});
$contactsIds = $query->pluck('card_uri')->all();
return array_map(function ($item) {
$pathInfo = pathinfo($item);
return $pathInfo['filename'];
}, $contactsIds);
} else {
return [];
}
}
protected function getContactsIdsFromUUIDs($UserId, $UUIDs)
{
$Uris = array_map(function ($item) {
return $item . '.vcf';
}, $UUIDs);
$contactsIds = Capsule::connection()->table('adav_cards')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->select('adav_cards.id as card_id')
->where('principaluri', Constants::PRINCIPALS_PREFIX . Api::getUserPublicIdById($UserId))
->whereIn('adav_cards.uri', $Uris)->get()->all();
return array_map(function ($item) {
return $item->card_id;
}, $contactsIds);
}
/**
* @api {post} ?/Api/ UpdateGroup
* @apiName UpdateGroup
* @apiGroup Contacts
* @apiDescription Updates group with specified parameters.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=UpdateGroup} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **Group** *object* Parameters of group to update.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'UpdateGroup',
* Parameters: '{ "Group": { "UUID": "group_uuid", "Name": "group_name", "IsOrganization": "0",
* "Email": "", "Country": "", "City": "", "Company": "", "Fax": "", "Phone": "", "State": "",
* "Street": "", "Web": "", "Zip": "", "Contacts": [] } }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {bool} Result.Result Indicates if group was updated successfully.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'UpdateGroup',
* Result: true
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'UpdateGroup',
* Result: false,
* ErrorCode: 102
* }
*/
protected function UpdateGroupObject($UserId, $oGroup)
{
$mResult = false;
if (is_array($oGroup->Contacts) && count($oGroup->Contacts)) {
$oGroup->Contacts = $this->getContactsUUIDsFromIds($UserId, $oGroup->Contacts);
}
$query = Capsule::connection()->table('contacts_cards')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->select('adav_cards.uri as card_uri', 'adav_addressbooks.id as addressbook_id', 'carddata');
$aArgs = [
'UserId' => $UserId,
'UUID' => $oGroup->UUID
];
// build a query to obtain the addressbook_id and card_uri with checking access to the contact
$query->where(function ($q) use ($aArgs, $query) {
$aArgs['Query'] = $query;
$this->broadcastEvent(self::GetName() . '::ContactQueryBuilder', $aArgs, $q);
});
$row = $query->first();
if ($row) {
$oVCard = \Sabre\VObject\Reader::read($row->carddata);
$uidVal = $oVCard->UID->getValue();
if (empty($uidVal) || is_numeric($uidVal)) {
$uriInfo = pathinfo($row->card_uri);
if (isset($uriInfo['filename'])) {
$oVCard->UID = $uriInfo['filename'];
}
}
Helper::UpdateVCardFromGroup($oGroup, $oVCard);
$mResult = !!Backend::Carddav()->updateCard($row->addressbook_id, $row->card_uri, $oVCard->serialize());
}
return $mResult;
}
/**
* Updates group with specified parameters.
* @param array $Group Parameters of group to update.
* @return boolean
*/
public function UpdateGroup($UserId, $Group)
{
$mResult = false;
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$oGroup = self::Decorator()->GetGroup($UserId, $Group['UUID']);
if ($oGroup) {
$oGroup->populate($Group);
$mResult = $this->UpdateGroupObject($UserId, $oGroup);
}
return $mResult;
}
/**
* @api {post} ?/Api/ DeleteGroup
* @apiName DeleteGroup
* @apiGroup Contacts
* @apiDescription Deletes group with specified UUID.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=DeleteGroup} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **UUID** *string* UUID of group to delete.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'DeleteGroup',
* Parameters: '{ UUID: "group_uuid" }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {bool} Result.Result Indicates if group was deleted successfully.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'DeleteGroup',
* Result: true
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'DeleteGroup',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Deletes group with specified UUID.
* @param string $UUID UUID of group to delete.
* @return bool
*/
public function DeleteGroup($UserId, $UUID)
{
Api::CheckAccess($UserId);
return self::Decorator()->DeleteContacts($UserId, StorageType::Personal, [$UUID]);
}
/**
* @api {post} ?/Api/ AddContactsToGroup
* @apiName AddContactsToGroup
* @apiGroup Contacts
* @apiDescription Adds specified contacts to specified group.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=AddContactsToGroup} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **GroupUUID** *string* Id of the group.<br>
*   **ContactUUIDs** *array* Array of strings - IDs of contacts to add to group.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'AddContactsToGroup',
* Parameters: '{ GroupUUID: "group_id", ContactUUIDs: ["contact1_id", "contact2_id"] }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {bool} Result.Result Indicates if contacts were successfully added to group.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'AddContactsToGroup',
* Result: true
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'AddContactsToGroup',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Adds specified contacts to specified group.
*
* @param string $GroupUUID ID of group.
* @param array $ContactUUIDs Array of strings - IDs of contacts to add to group.
*
* @return boolean
*/
public function AddContactsToGroup($UserId, $GroupUUID, $ContactUUIDs)
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$mResult = false;
Api::CheckAccess($UserId);
// currently method accepts Ids (not UUIDs), argument names are kept for compatibility
$GroupId = $GroupUUID;
$ContactIds = $ContactUUIDs;
if (is_array($ContactIds) && !empty($ContactIds)) {
$oGroup = self::Decorator()->GetGroup($UserId, $GroupId);
if ($oGroup) {
//getting contacts by ids is needed here just for making sure that they exist
$aContacts = self::Decorator()->GetContactsByUids($UserId, $ContactIds);
$newContactIds = array_map(function ($item) {
return $item->Id;
}, $aContacts);
$oGroup->Contacts = array_merge($oGroup->Contacts, $newContactIds);
$mResult = $this->UpdateGroupObject($UserId, $oGroup);
}
}
return $mResult;
}
/**
* @api {post} ?/Api/ RemoveContactsFromGroup
* @apiName RemoveContactsFromGroup
* @apiGroup Contacts
* @apiDescription Removes specified contacts from specified group.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=RemoveContactsFromGroup} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **GroupUUID** *string* ID of group.<br>
*   **ContactUUIDs** *array* Array of strings - IDs of contacts to remove from group.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'RemoveContactsFromGroup',
* Parameters: '{ GroupUUID: "group_id", ContactUUIDs: ["contact1_id", "contact2_id"] }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {bool} Result.Result Indicates if contacts were successfully removed from group.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'RemoveContactsFromGroup',
* Result: true
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'RemoveContactsFromGroup',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Removes specified contacts from specified group.
* @param string $GroupUUID ID of group.
* @param array $ContactUUIDs Array of strings - IDs of contacts to remove from group.
* @return boolean
*/
public function RemoveContactsFromGroup($UserId, $GroupUUID, $ContactUUIDs)
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$mResult = false;
// currently method accepts Ids (not UUIDs), argument names are kept for compatibility
$GroupId = $GroupUUID;
$ContactIds = $ContactUUIDs;
if (is_array($ContactIds) && !empty($ContactIds)) {
$oGroup = self::Decorator()->GetGroup($UserId, $GroupId);
if ($oGroup) {
$ContactIds = array_map(function ($id) {
return (int) $id;
}, $ContactIds);
$oGroup->Contacts = array_diff($oGroup->Contacts, $ContactIds);
$mResult = $this->UpdateGroupObject($UserId, $oGroup);
}
}
return $mResult;
}
/**
* @api {post} ?/Api/ Import
* @apiName Import
* @apiGroup Contacts
* @apiDescription Imports contacts from file with specified format.
*
* @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
* @apiHeaderExample {json} Header-Example:
* {
* "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
* }
*
* @apiParam {string=Contacts} Module Module name
* @apiParam {string=Import} Method Method name
* @apiParam {string} Parameters JSON.stringified object <br>
* {<br>
*   **UploadData** *array* Array of uploaded file data.<br>
*   **Storage** *string* Storage name.<br>
*   **GroupUUID** *array* Group UUID.<br>
* }
*
* @apiParamExample {json} Request-Example:
* {
* Module: 'Contacts',
* Method: 'Import',
* Parameters: '{ "UploadData": { "tmp_name": "tmp_name_value", "name": "name_value" },
* "Storage": "personal", "GroupUUID": "" }'
* }
*
* @apiSuccess {object[]} Result Array of response objects.
* @apiSuccess {string} Result.Module Module name
* @apiSuccess {string} Result.Method Method name
* @apiSuccess {mixed} Result.Result Object with counts of imported and parsed contacts in case of success, otherwise **false**.
* @apiSuccess {int} [Result.ErrorCode] Error code
*
* @apiSuccessExample {json} Success response example:
* {
* Module: 'Contacts',
* Method: 'Import',
* Result: { "ImportedCount" : 2, "ParsedCount": 3}
* }
*
* @apiSuccessExample {json} Error response example:
* {
* Module: 'Contacts',
* Method: 'Import',
* Result: false,
* ErrorCode: 102
* }
*/
/**
* Imports contacts from file with specified format.
* @param array $UploadData Array of uploaded file data.
* @param array $GroupUUID Group UUID.
* @return array
* @throws \Aurora\System\Exceptions\ApiException
*/
public function Import($UserId, $UploadData, $GroupUUID, $Storage = null)
{
Api::CheckAccess($UserId);
$oUser = \Aurora\Api::getUserById($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$aResponse = array(
'ImportedCount' => 0,
'ParsedCount' => 0
);
if (is_array($UploadData)) {
$oApiFileCacheManager = new \Aurora\System\Managers\Filecache();
$sTempFileName = 'import-post-' . md5($UploadData['name'] . $UploadData['tmp_name']);
if ($oApiFileCacheManager->moveUploadedFile($oUser->UUID, $sTempFileName, $UploadData['tmp_name'], '', self::GetName())) {
$sTempFilePath = $oApiFileCacheManager->generateFullFilePath($oUser->UUID, $sTempFileName, '', self::GetName());
$aImportResult = array();
$sFileExtension = strtolower(\Aurora\System\Utils::GetFileExtension($UploadData['name']));
switch ($sFileExtension) {
case 'csv':
$oSync = new Classes\Csv\Sync();
$aImportResult = $oSync->Import($oUser->Id, $sTempFilePath, $GroupUUID, $Storage);
break;
case 'vcf':
$aImportResult = $this->importVcf($oUser->Id, $sTempFilePath, $Storage);
break;
}
if (is_array($aImportResult) && isset($aImportResult['ImportedCount']) && isset($aImportResult['ParsedCount'])) {
$aResponse['ImportedCount'] = $aImportResult['ImportedCount'];
$aResponse['ParsedCount'] = $aImportResult['ParsedCount'];
} else {
throw new ApiException(Notifications::IncorrectFileExtension);
}
$oApiFileCacheManager->clear($oUser->UUID, $sTempFileName, '', self::GetName());
} else {
throw new ApiException(Notifications::UnknownError);
}
} else {
throw new ApiException(Notifications::UnknownError);
}
return $aResponse;
}
public function UpdateSharedContacts($UserId, $UUIDs)
{
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
return true;
}
public function AddContactsFromFile($UserId, $File)
{
Api::CheckAccess($UserId);
$oUser = \Aurora\Api::getUserById($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
if (empty($File)) {
throw new ApiException(Notifications::InvalidInputParameter);
}
$oApiFileCache = new \Aurora\System\Managers\Filecache();
$sTempFilePath = $oApiFileCache->generateFullFilePath($oUser->UUID, $File); // Temp files with access from another module should be stored in System folder
$aImportResult = $this->importVcf($oUser->Id, $sTempFilePath);
return $aImportResult;
}
/**
*
* @param int $UserId
* @param string $UUID
* @param string $FileName
*/
public function SaveContactAsTempFile($UserId, $UUID, $FileName)
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
Api::CheckAccess($UserId);
$mResult = false;
$oContact = self::Decorator()->GetContact($UUID, $UserId);
if ($oContact) {
$oVCard = new \Sabre\VObject\Component\VCard();
Helper::UpdateVCardFromContact($oContact, $oVCard);
$sVCardData = $oVCard->serialize();
if ($sVCardData) {
$sUUID = \Aurora\System\Api::getUserUUIDById($UserId);
$sTempName = md5($sUUID . $UUID);
$oApiFileCache = new \Aurora\System\Managers\Filecache();
$oApiFileCache->put($sUUID, $sTempName, $sVCardData);
if ($oApiFileCache->isFileExists($sUUID, $sTempName)) {
$mResult = \Aurora\System\Utils::GetClientFileResponse(
null,
$UserId,
$FileName,
$sTempName,
$oApiFileCache->fileSize($sUUID, $sTempName)
);
}
}
}
return $mResult;
}
/***** public functions might be called with web API *****/
/***** private functions *****/
private function importVcf($iUserId, $sTempFilePath, $sStorage = null)
{
$aImportResult = array(
'ParsedCount' => 0,
'ImportedCount' => 0,
'ImportedUids' => []
);
// You can either pass a readable stream, or a string.
$oHandler = fopen($sTempFilePath, 'r');
$oSplitter = new \Sabre\VObject\Splitter\VCard($oHandler, \Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES);
$oContactsDecorator = Module::Decorator();
$aGroupsData = [];
$aContactsData = [];
while ($oVCard = $oSplitter->getNext()) {
set_time_limit(30);
$Uid = (string) $oVCard->UID;
if (empty($Uid)) {
$Uid = UUIDUtil::getUUID();
}
if ((isset($oVCard->KIND) && (string) $oVCard->KIND === 'GROUP') ||
(isset($oVCard->{'X-ADDRESSBOOKSERVER-KIND'}) && (string) $oVCard->{'X-ADDRESSBOOKSERVER-KIND'} === 'GROUP')) {
$aGroupsData[] = Classes\VCard\Helper::GetGroupDataFromVcard($oVCard, $Uid);
} else {
$aContactData = Classes\VCard\Helper::GetContactDataFromVcard($oVCard, $Uid);
$oContact = self::Decorator()->GetContact($Uid, $iUserId);
$aImportResult['ParsedCount']++;
if (!$oContact) {
if (isset($sStorage)) {
$aContactData['Storage'] = $sStorage;
}
$aContactsData[$Uid] = $aContactData;
}
}
}
foreach ($aContactsData as $key => $aContactData) {
$CreatedContactData = $oContactsDecorator->CreateContact($aContactData, $iUserId);
if ($CreatedContactData) {
$aImportResult['ImportedCount']++;
$aImportResult['ImportedUids'][] = $CreatedContactData['UUID'];
$aContactsData[$key]['NewUUID'] = $CreatedContactData['UUID'];
}
}
foreach ($aGroupsData as $aGroupData) {
if (isset($aGroupData['Contacts'])) {
$aUuids = $aGroupData['Contacts'];
$aGroupData['Contacts'] = [];
foreach ($aUuids as $value) {
if (isset($aContactsData[$value])) {
$aGroupData['Contacts'][] = $aContactsData[$value]['NewUUID'];
}
}
}
$oContactsDecorator->CreateGroup($aGroupData, $iUserId);
}
return $aImportResult;
}
protected function populateContactArguments(&$aArgs)
{
$mResult = false;
$this->broadcastEvent('PopulateContactArguments', $aArgs, $mResult);
return $mResult;
}
private function prepareFiltersFromStorage($UserId, $Storage = '', $AddressBookId = 0, &$Query = null, &$WhereQuery = null, $Suggestions = false)
{
$aArgs = [
'UserId' => $UserId,
'Storage' => $Storage,
'AddressBookId' => $AddressBookId,
'IsValid' => false,
'Query' => $Query,
'Suggestions' => $Suggestions
];
$this->broadcastEvent('PrepareFiltersFromStorage', $aArgs, $WhereQuery);
if (!$aArgs['IsValid']) {
throw new ApiException(Notifications::InvalidInputParameter, null, 'Invalid Storage parameter value');
}
return $WhereQuery;
}
public function onAfterUseEmails($Args, &$Result)
{
$aAddresses = $Args['Emails'];
$iUserId = $Args['IdUser'];
foreach ($aAddresses as $sEmail => $sName) {
try {
$contactsColl = self::Decorator()->GetContactsByEmails($iUserId, StorageType::Personal, [$sEmail]);
$oContact = $contactsColl->first();
if (!$oContact) {
$contactsColl = self::Decorator()->GetContactsByEmails($iUserId, StorageType::Collected, [$sEmail]);
$oContact = $contactsColl->first();
}
if ($oContact) {
ContactCard::where('CardId', $oContact->Id)->update(['Frequency' => $oContact->Frequency + 1]);
} else {
self::Decorator()->CreateContact([
'FullName' => $sName,
'PersonalEmail' => $sEmail,
'Auto' => true,
'Storage' => StorageType::Collected,
], $iUserId);
}
} catch (\Exception $ex) {
}
}
}
public function onGetBodyStructureParts($aParts, &$aResultParts)
{
foreach ($aParts as $oPart) {
if ($oPart instanceof \MailSo\Imap\BodyStructure &&
($oPart->ContentType() === 'text/vcard' || $oPart->ContentType() === 'text/x-vcard')) {
$aResultParts[] = $oPart;
break;
}
}
}
public function onBeforeDeleteUser(&$aArgs, &$mResult)
{
if (isset($aArgs['UserId'])) {
$this->userPublicIdToDelete = Api::getUserPublicIdById($aArgs['UserId']);
}
}
public function onAfterDeleteUser(&$aArgs, &$mResult)
{
if ($mResult && $this->userPublicIdToDelete) {
$abooks = Backend::Carddav()->getAddressBooksForUser(Constants::PRINCIPALS_PREFIX . $this->userPublicIdToDelete);
if ($abooks) {
foreach ($abooks as $book) {
Backend::Carddav()->deleteAddressBook($book['id']);
}
}
}
}
public function onContactToResponseArray($aArgs, &$mResult)
{
if (isset($aArgs[0]) && $aArgs[0] instanceof Contact && is_array($mResult)) {
$mResult['UUID'] = (string) $mResult['Id']; // The UUID property type must be a string.
}
}
/***** private functions *****/
public function GetAddressBook($UserId, $UUID)
{
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$principalUri = Constants::PRINCIPALS_PREFIX . \Aurora\System\Api::getUserPublicIdById($UserId);
return Backend::Carddav()->getAddressBookForUser($principalUri, $UUID);
}
public function GetAddressBooks($UserId = null)
{
$aResult = [];
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
return $aResult;
}
public function CreateAddressBook($AddressBookName, $UserId = null, $UUID = null)
{
$mResult = false;
Api::CheckAccess($UserId);
if (isset($UUID)) {
$sAddressBookUUID = $UUID;
} else {
$sAddressBookUUID = UUIDUtil::getUUID();
}
$userPublicId = Api::getUserPublicIdById($UserId);
$iAddressBookId = Backend::Carddav()->createAddressBook(Constants::PRINCIPALS_PREFIX . $userPublicId, $sAddressBookUUID, ['{DAV:}displayname' => $AddressBookName]);
if (is_numeric($iAddressBookId)) {
$oAddressBook = Backend::Carddav()->getAddressBookById($iAddressBookId);
if ($oAddressBook) {
return [
'Id' => StorageType::AddressBook . '-' . $oAddressBook['id'],
'EntityId' => (int) $oAddressBook['id'],
'CTag' => (int) $oAddressBook['{http://sabredav.org/ns}sync-token'],
'Display' => true,
'Owner' => basename($oAddressBook['principaluri']),
'Order' => 1,
'DisplayName' => $oAddressBook['{DAV:}displayname'],
'Uri' => $oAddressBook['uri']
];
}
}
return $mResult;
}
public function UpdateAddressBook($EntityId, $AddressBookName, $UserId = null)
{
$mResult = false;
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
if ($this->CheckAccessToAddressBook($UserId, $EntityId, Access::Write)) {
$propParch = new PropPatch([
'{DAV:}displayname' => $AddressBookName
]);
Backend::Carddav()->updateAddressBook($EntityId, $propParch);
$mResult = $propParch->commit();
} else {
throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
}
return $mResult;
}
public function DeleteAddressBook($EntityId, $UserId = null)
{
$mResult = false;
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$userPublicId = Api::getUserPublicIdById($UserId);
$abook = Capsule::connection()->table('adav_addressbooks')
->where('id', $EntityId)
->where('principaluri', Constants::PRINCIPALS_PREFIX . $userPublicId)
->first();
if ($abook) {
Backend::Carddav()->deleteAddressBook($EntityId);
$mResult = true;
}
return $mResult;
}
public function DeleteUsersAddressBooks($UserId = null)
{
$mResult = false;
Api::CheckAccess($UserId);
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$userPublicId = Api::getUserPublicIdById($UserId);
$abooks = Capsule::connection()->table('adav_addressbooks')
->where('principaluri', Constants::PRINCIPALS_PREFIX . $userPublicId)
->get();
foreach ($abooks as $abook) {
Backend::Carddav()->deleteAddressBook($abook->id);
$mResult = true;
}
return $mResult;
}
public function GetStoragesMapToAddressbooks()
{
return [];
}
protected function getGetContactsQueryBuilder($UserId, $Storage = '', $AddressBookId = null, Builder $Filters = null, $Suggestions = false, $withGroups = false)
{
if ($Filters instanceof Builder) {
$query = & $Filters;
} else {
$query = ContactCard::query();
}
$con = Capsule::connection();
$query->select(
'adav_cards.id as Id',
'adav_cards.id as UUID',
'adav_cards.uri as Uri',
'adav_cards.addressbookid as Storage',
'etag as ETag',
$con->raw('FROM_UNIXTIME(lastmodified) as DateModified'),
'contacts_cards.PrimaryEmail',
'contacts_cards.PersonalEmail',
'contacts_cards.BusinessEmail',
'contacts_cards.OtherEmail',
'contacts_cards.BusinessCompany',
'contacts_cards.FullName',
'contacts_cards.FirstName',
'contacts_cards.LastName',
'contacts_cards.Frequency',
'contacts_cards.Properties',
$con->raw('(Frequency/CEIL(DATEDIFF(CURDATE() + INTERVAL 1 DAY, FROM_UNIXTIME(lastmodified))/30)) as AgeScore'),
'core_users.Id as UserId'
)
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->where(function ($wherQuery) use ($UserId, $Storage, $AddressBookId, $query, $Suggestions) {
$this->prepareFiltersFromStorage($UserId, $Storage, $AddressBookId, $query, $wherQuery, $Suggestions);
});
if (!$withGroups) {
$query->where('IsGroup', false);
}
if ($Suggestions) {
$query->where('Frequency', '>=', 0);
}
$query->leftJoin('core_users', 'adav_addressbooks.principaluri', '=', Capsule::connection()->raw("CONCAT('principals/', " . Capsule::connection()->getTablePrefix() . "core_users.PublicId)"));
return $query;
}
}