/home/ivoiecob/email.hirewise-va.com/modules/TeamContacts/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\TeamContacts;
use Afterlogic\DAV\Backend;
use Afterlogic\DAV\Constants;
use Aurora\Api;
use Aurora\Modules\Contacts\Enums\StorageType;
use Aurora\Modules\Contacts\Models\ContactCard;
use Aurora\Modules\Contacts\Module as ContactsModule;
use Aurora\System\Enums\UserRole;
use Sabre\VObject\UUIDUtil;
use Illuminate\Database\Capsule\Manager as Capsule;
use Aurora\System\Exceptions\ApiException;
use Aurora\Modules\Contacts\Enums\Access;
/**
* @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 static $iStorageOrder = 20;
protected $userPublicIdToDelete = null;
protected $teamAddressBook = null;
protected $storagesMapToAddressbooks = [
StorageType::Team => Constants::ADDRESSBOOK_TEAM_NAME
];
public function init()
{
$this->subscribeEvent('Contacts::GetAddressBooks::after', array($this, 'onAfterGetAddressBooks'));
$this->subscribeEvent('Core::CreateUser::after', array($this, 'onAfterCreateUser'));
$this->subscribeEvent('Contacts::PrepareFiltersFromStorage', array($this, 'onPrepareFiltersFromStorage'));
$this->subscribeEvent('Contacts::GetContacts::after', array($this, 'onAfterGetContacts'));
$this->subscribeEvent('Contacts::GetContact::after', array($this, 'onAfterGetContact'));
$this->subscribeEvent('Core::DoServerInitializations::after', array($this, 'onAfterDoServerInitializations'));
$this->subscribeEvent('Contacts::CheckAccessToObject::after', array($this, 'onAfterCheckAccessToObject'));
$this->subscribeEvent('Contacts::GetContactSuggestions', array($this, 'onGetContactSuggestions'));
$this->subscribeEvent('Contacts::CheckAccessToAddressBook::after', array($this, 'onAfterCheckAccessToAddressBook'));
$this->subscribeEvent('Contacts::UpdateAddressBook::before', array($this, 'onBeforeUpdateAddressBook'));
$this->subscribeEvent('Contacts::PopulateContactArguments', array($this, 'populateContactArguments'));
$this->subscribeEvent('Contacts::CreateContact::before', array($this, 'populateContactArguments'));
$this->subscribeEvent('Contacts::ContactQueryBuilder', array($this, 'onContactQueryBuilder'));
$this->subscribeEvent('Core::DeleteUser::before', array($this, 'onBeforeDeleteUser'));
$this->subscribeEvent('Core::DeleteUser::after', array($this, 'onAfterDeleteUser'));
$this->subscribeEvent('Contacts::UpdateContactObject::before', array($this, 'onBeforeUpdateContactObject'));
$this->subscribeEvent('Contacts::GetStoragesMapToAddressbooks::after', array($this, 'onAfterGetStoragesMapToAddressbooks'));
$this->subscribeEvent('Core::GetGroupContactsEmails', array($this, 'onGetGroupContactsEmails'));
}
/**
* @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;
}
public function onAfterGetAddressBooks(&$aArgs, &$mResult)
{
if (!is_array($mResult)) {
$mResult = [];
}
$addressbook = $this->GetTeamAddressbook($aArgs['UserId']);
if ($addressbook) {
/**
* @var array $addressbook
*/
$mResult[] = [
'Id' => 'team',
'EntityId' => (int) $addressbook['id'],
'CTag' => (int) $addressbook['{http://sabredav.org/ns}sync-token'],
'Display' => true,
'Order' => 1,
'DisplayName' => $addressbook['{DAV:}displayname'],
'Uri' => $addressbook['uri'],
'Url' => $addressbook['uri'],
];
}
}
public function GetTeamAddressbook($UserId)
{
Api::CheckAccess($UserId);
$addressbook = false;
$oUser = Api::getUserById($UserId);
if ($oUser) {
$sPrincipalUri = Constants::PRINCIPALS_PREFIX . $oUser->IdTenant . '_' . Constants::DAV_TENANT_PRINCIPAL;
$addressbook = Backend::Carddav()->getAddressBookForUser($sPrincipalUri, 'gab');
if (!$addressbook) {
if (Backend::Carddav()->createAddressBook($sPrincipalUri, 'gab', ['{DAV:}displayname' => Constants::ADDRESSBOOK_TEAM_DISPLAY_NAME])) {
$addressbook = Backend::Carddav()->getAddressBookForUser($sPrincipalUri, 'gab');
}
}
if ($addressbook) {
$addressbook['id_tenant'] = $oUser->IdTenant;
}
}
return $addressbook;
}
private function createContactForUser($iUserId, $sEmail)
{
$mResult = false;
if (0 < $iUserId) {
$addressbook = $this->GetTeamAddressbook($iUserId);
if ($addressbook) {
$uid = UUIDUtil::getUUID();
$vcard = new \Sabre\VObject\Component\VCard(['UID' => $uid]);
$vcard->add(
'EMAIL',
$sEmail,
[
'type' => ['work'],
'pref' => 1,
]
);
$mResult = !!Backend::Carddav()->createCard($addressbook['id'], $uid . '.vcf', $vcard->serialize());
}
}
return $mResult;
}
public function onAfterCreateUser($aArgs, &$mResult)
{
$iUserId = isset($mResult) && (int) $mResult > 0 ? $mResult : 0;
if ((int) $iUserId > 0) {
$this->createContactForUser($iUserId, $aArgs['PublicId']);
}
}
public function onPrepareFiltersFromStorage(&$aArgs, &$mResult)
{
if (isset($aArgs['Storage']) && ($aArgs['Storage'] === StorageType::Team || $aArgs['Storage'] === StorageType::All)) {
$aArgs['IsValid'] = true;
$oUser = \Aurora\System\Api::getAuthenticatedUser();
$addressbook = $this->GetTeamAddressbook($oUser->Id);
if ($addressbook) {
if (isset($aArgs['Query'])) {
$aArgs['Query']->addSelect(Capsule::connection()->raw(
'CASE
WHEN ' . Capsule::connection()->getTablePrefix() . 'adav_cards.addressbookid = ' . $addressbook['id'] . ' THEN true
ELSE false
END as IsTeam'
));
}
$mResult = $mResult->orWhere('adav_cards.addressbookid', $addressbook['id']);
}
}
}
public function onAfterGetContacts($aArgs, &$mResult)
{
if (\is_array($mResult) && \is_array($mResult['List'])) {
$user = Api::getUserById($aArgs['UserId']);
$teamAddressbook = $this->GetTeamAddressbook($aArgs['UserId']);
if ($user && $teamAddressbook) {
$authenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
foreach ($mResult['List'] as $iIndex => $aContact) {
$allowEditTeamContactsByTenantAdmins = ContactsModule::getInstance()->oModuleSettings->AllowEditTeamContactsByTenantAdmins;
$isUserTenantAdmin = $authenticatedUser->Role === UserRole::TenantAdmin && $user->IdTenant === $authenticatedUser->IdTenant;
if (isset($aContact['AddressBookId']) && $aContact['AddressBookId'] == $teamAddressbook['id']) {
$aContact['ReadOnly'] = false;
if ($aContact['ViewEmail'] === $user->PublicId) {
$aContact['ItsMe'] = true;
} elseif (!(($allowEditTeamContactsByTenantAdmins && $isUserTenantAdmin) || $authenticatedUser->isAdmin())) {
$aContact['ReadOnly'] = true;
}
$mResult['List'][$iIndex] = $aContact;
}
}
}
}
}
public function onAfterGetContact($aArgs, &$mResult)
{
$authenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
$teamAddressbook = $this->GetTeamAddressbook($authenticatedUser->Id);
if ($teamAddressbook) {
if ($mResult && $authenticatedUser && $mResult->AddressBookId == $teamAddressbook['id']) {
$mResult->IdTenant = $teamAddressbook['id_tenant'] ?? 0;
$allowEditTeamContactsByTenantAdmins = ContactsModule::getInstance()->oModuleSettings->AllowEditTeamContactsByTenantAdmins;
$isUserTenantAdmin = $authenticatedUser->Role === UserRole::TenantAdmin;
$isContactInTenant = $mResult->IdTenant === $authenticatedUser->IdTenant;
if ($mResult->BusinessEmail === $authenticatedUser->PublicId) {
$mResult->ExtendedInformation['ItsMe'] = true;
} elseif (!(($allowEditTeamContactsByTenantAdmins && $isUserTenantAdmin && $isContactInTenant) || $authenticatedUser->isAdmin())) {
$mResult->ExtendedInformation['ReadOnly'] = true;
}
}
}
}
/**
* Creates team contacts if they are missing within current tenant.
*/
public function onAfterDoServerInitializations($aArgs, &$mResult)
{
$oUser = \Aurora\System\Api::getAuthenticatedUser();
if ($oUser && $oUser->Role === UserRole::NormalUser) {
$teamAddressBook = $this->GetTeamAddressbook($oUser->Id);
if ($teamAddressBook) {
$contact = Capsule::connection()->table('contacts_cards')
->where('AddressBookId', $teamAddressBook['id'])
->where('ViewEmail', $oUser->PublicId)
->first();
if (!$contact) {
$this->createContactForUser($oUser->Id, $oUser->PublicId);
}
}
}
}
public function onGetContactSuggestions(&$aArgs, &$mResult)
{
if ($aArgs['Storage'] === 'all' || $aArgs['Storage'] === StorageType::Team) {
$mResult[StorageType::Team] = \Aurora\Modules\Contacts\Module::Decorator()->GetContacts(
$aArgs['UserId'],
StorageType::Team,
0,
$aArgs['Limit'],
$aArgs['SortField'],
$aArgs['SortOrder'],
$aArgs['Search']
);
}
}
/**
*
*/
public function populateContactArguments(&$aArgs, &$mResult)
{
if (isset($aArgs['Storage'], $aArgs['UserId'])) {
$aStorageParts = \explode('-', $aArgs['Storage']);
if (isset($aStorageParts[0]) && $aStorageParts[0] === StorageType::Team) {
$addressbook = $this->GetTeamAddressbook($aArgs['UserId']);
if ($addressbook) {
$aArgs['Storage'] = StorageType::Team;
$aArgs['AddressBookId'] = $addressbook['id'];
$mResult = true;
}
}
}
}
public function onContactQueryBuilder(&$aArgs, &$query)
{
$addressbook = $this->GetTeamAddressbook($aArgs['UserId']);
$query->orWhere(function ($q) use ($addressbook, $aArgs) {
$q->where('adav_addressbooks.id', $addressbook['id']);
if (is_array($aArgs['UUID'])) {
$ids = $aArgs['UUID'];
if (count($aArgs['UUID']) === 0) {
$ids = [null];
}
$q->whereIn('adav_cards.id', $ids);
} else {
$q->where('adav_cards.id', $aArgs['UUID']);
}
});
}
public function onBeforeDeleteUser(&$aArgs, &$mResult)
{
if (isset($aArgs['UserId'])) {
$this->userPublicIdToDelete = Api::getUserPublicIdById($aArgs['UserId']);
$this->teamAddressBook = $this->GetTeamAddressbook($aArgs['UserId']);
}
}
public function onAfterDeleteUser($aArgs, &$mResult)
{
if ($mResult && $this->userPublicIdToDelete && $this->teamAddressBook) {
$card = Capsule::connection()->table('contacts_cards')
->join('adav_cards', 'contacts_cards.CardId', '=', 'adav_cards.id')
->join('adav_addressbooks', 'adav_cards.addressbookid', '=', 'adav_addressbooks.id')
->where('adav_addressbooks.id', $this->teamAddressBook['id'])
->where('ViewEmail', $this->userPublicIdToDelete)
->select('adav_cards.uri as card_uri', 'adav_addressbooks.id as addressbook_id')
->first();
if ($card) {
Backend::Carddav()->deleteCard($card->addressbook_id, $card->card_uri);
}
}
}
public function onAfterCheckAccessToAddressBook(&$aArgs, &$mResult)
{
if (isset($aArgs['User'], $aArgs['AddressBookId'])) {
$oUser = $aArgs['User'];
$addressbook = $this->GetTeamAddressbook($oUser->Id);
if ($addressbook && $addressbook['id'] == $aArgs['AddressBookId']) {
if ($aArgs['Access'] === Access::Write && $oUser->UserRole !== UserRole::SuperAdmin) {
if ($oUser->UserRole === UserRole::TenantAdmin && ContactsModule::getInstance()->oModuleSettings->AllowEditTeamContactsByTenantAdmins) {
$mResult = true;
} else {
$mResult = false;
}
} else {
$mResult = true;
}
return true;
}
}
}
public function onAfterCheckAccessToObject(&$aArgs, &$mResult)
{
$oUser = $aArgs['User'] ?? null;
$oContact = $aArgs['Contact'] ?? null;
if ($oUser) {
$teamAddressBook = $this->GetTeamAddressbook($oUser->Id);
if ($oContact instanceof \Aurora\Modules\Contacts\Classes\Contact && (int) $oContact->AddressBookId === (int) $teamAddressBook['id']) {
if ($aArgs['Access'] === Access::Write && $aArgs['User']->UserRole !== UserRole::SuperAdmin) {
if ((isset($oContact->ExtendedInformation['ItsMe']) && $oContact->ExtendedInformation['ItsMe']) || // ItsMe
($oUser->Role === UserRole::TenantAdmin && ContactsModule::getInstance()->oModuleSettings->AllowEditTeamContactsByTenantAdmins)) {
$mResult = true;
} else {
$mResult = false;
}
} else { // is SuperAdmin
$mResult = true;
}
return true;
}
}
}
public function onBeforeUpdateContactObject(&$aArgs, &$mResult)
{
$user = Api::getAuthenticatedUser();
$oContact = $aArgs['Contact'] ?? null;
if ($user && $oContact) {
$addressbook = Backend::Carddav()->getAddressBookById($oContact->AddressBookId);
if ($addressbook['uri'] === 'gab') {
// no one can edit the BusinessEmail property because Contact has a relationship with User on this property,
// so if the property was changed, then we return the previous value
$oOldContact = ContactCard::firstWhere('CardId', $oContact->Id);
if ($oOldContact && $oContact->BusinessEmail != $oOldContact->BusinessEmail) {
$oContact->BusinessEmail = $oOldContact->BusinessEmail;
}
$teamAddressbook = $this->GetTeamAddressbook($user->Id);
$isSuperAdmin = $user->Role === UserRole::SuperAdmin;
$isTenant = $user->Role === UserRole::TenantAdmin;
$isCorrectTeamAddressbook = $teamAddressbook['id'] == $oContact->AddressBookId;
$isItsMe = isset($oContact->ExtendedInformation['ItsMe']) && $oContact->ExtendedInformation['ItsMe'];
$isReadOnly = isset($oContact->ExtendedInformation['ReadOnly']) && $oContact->ExtendedInformation['ReadOnly'];
if (!($isSuperAdmin || ($isTenant && !$isReadOnly && $isCorrectTeamAddressbook) || $isItsMe)) {
throw new ApiException(\Aurora\System\Notifications::AccessDenied, null, 'AccessDenied');
}
}
}
}
public function onBeforeUpdateAddressBook(&$aArgs, &$mResult)
{
$addressbook = Backend::Carddav()->getAddressBookById($aArgs['EntityId']);
if ($addressbook && $addressbook['uri'] === 'gab') {
throw new ApiException(\Aurora\System\Notifications::AccessDenied, null, 'AccessDenied');
}
}
public function onAfterGetStoragesMapToAddressbooks(&$aArgs, &$mResult)
{
$mResult = array_merge($mResult, $this->storagesMapToAddressbooks);
}
public function onGetGroupContactsEmails(&$aArgs, &$mResult)
{
$oUser = $aArgs['User'];
$oGroup = $aArgs['Group'];
if ($oUser && $oGroup) {
$abook = $this->GetTeamAddressbook($oUser->Id);
if ($abook) {
if ($oGroup->IsAll) {
$mResult = ContactCard::where('AddressBookId', $abook['id'])->get()->map(
function (ContactCard $oContact) {
if (!empty($oContact->FullName)) {
return '"' . $oContact->FullName . '"' . '<' . $oContact->ViewEmail . '>';
} else {
return $oContact->ViewEmail;
}
}
)->toArray();
} else {
$mResult = $oGroup->Users->map(function ($oUser) use ($abook) {
$oContact = ContactCard::where('IdUser', $oUser->Id)->where('AddressBookId', $abook['id'])->first();
if ($oContact && !empty($oContact->FullName)) {
return '"' . $oContact->FullName . '"' . '<' . $oUser->PublicId . '>';
} else {
return $oUser->PublicId;
}
})->toArray();
}
}
}
}
}