Initial commit
This commit is contained in:
169
application/Espo/Repositories/ArrayValue.php
Normal file
169
application/Espo/Repositories/ArrayValue.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
use Espo\Entities\ArrayValue as ArrayValueEntity;
|
||||
use Espo\ORM\Defs\Params\AttributeParam;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use RuntimeException;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* @extends Database<ArrayValueEntity>
|
||||
*/
|
||||
class ArrayValue extends Database
|
||||
{
|
||||
private const ITEM_MAX_LENGTH = 100;
|
||||
|
||||
public function storeEntityAttribute(CoreEntity $entity, string $attribute, bool $populateMode = false): void
|
||||
{
|
||||
if ($entity->getAttributeType($attribute) !== Entity::JSON_ARRAY) {
|
||||
throw new LogicException("ArrayValue: Can't store non array attribute.");
|
||||
}
|
||||
|
||||
if ($entity->getAttributeParam($attribute, AttributeParam::NOT_STORABLE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entity->getAttributeParam($attribute, 'storeArrayValues')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entity->has($attribute)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$valueList = $entity->get($attribute);
|
||||
|
||||
if (is_null($valueList)) {
|
||||
$valueList = [];
|
||||
}
|
||||
|
||||
if (!is_array($valueList)) {
|
||||
throw new RuntimeException("ArrayValue: Bad value passed to JSON_ARRAY attribute {$attribute}.");
|
||||
}
|
||||
|
||||
$valueList = array_unique($valueList);
|
||||
$toSkipValueList = [];
|
||||
|
||||
$isTransaction = false;
|
||||
|
||||
if (!$entity->isNew() && !$populateMode) {
|
||||
$this->entityManager->getTransactionManager()->start();
|
||||
|
||||
$isTransaction = true;
|
||||
|
||||
$existingList = $this
|
||||
->select([Attribute::ID, 'value'])
|
||||
->where([
|
||||
'entityType' => $entity->getEntityType(),
|
||||
'entityId' => $entity->getId(),
|
||||
'attribute' => $attribute,
|
||||
])
|
||||
->forUpdate()
|
||||
->find();
|
||||
|
||||
foreach ($existingList as $existing) {
|
||||
if (!in_array($existing->get('value'), $valueList)) {
|
||||
$this->deleteFromDb($existing->getId());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$toSkipValueList[] = $existing->get('value');
|
||||
}
|
||||
}
|
||||
|
||||
$itemMaxLength = $this->entityManager
|
||||
->getDefs()
|
||||
->getEntity(ArrayValueEntity::ENTITY_TYPE)
|
||||
->getField('value')
|
||||
->getParam(FieldParam::MAX_LENGTH) ?? self::ITEM_MAX_LENGTH;
|
||||
|
||||
foreach ($valueList as $value) {
|
||||
if (in_array($value, $toSkipValueList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strlen($value) > $itemMaxLength) {
|
||||
$value = substr($value, 0, $itemMaxLength);
|
||||
}
|
||||
|
||||
$arrayValue = $this->getNew();
|
||||
|
||||
$arrayValue->set([
|
||||
'entityType' => $entity->getEntityType(),
|
||||
'entityId' => $entity->getId(),
|
||||
'attribute' => $attribute,
|
||||
'value' => $value,
|
||||
]);
|
||||
|
||||
$this->save($arrayValue);
|
||||
}
|
||||
|
||||
if ($isTransaction) {
|
||||
$this->entityManager->getTransactionManager()->commit();
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteEntityAttribute(CoreEntity $entity, string $attribute): void
|
||||
{
|
||||
if (!$entity->hasId()) {
|
||||
throw new LogicException("ArrayValue: Can't delete {$attribute} w/o id given.");
|
||||
}
|
||||
|
||||
$this->entityManager->getTransactionManager()->start();
|
||||
|
||||
$list = $this
|
||||
->select([Attribute::ID])
|
||||
->where([
|
||||
'entityType' => $entity->getEntityType(),
|
||||
'entityId' => $entity->getId(),
|
||||
'attribute' => $attribute,
|
||||
])
|
||||
->forUpdate()
|
||||
->find();
|
||||
|
||||
foreach ($list as $arrayValue) {
|
||||
$this->deleteFromDb($arrayValue->getId());
|
||||
}
|
||||
|
||||
$this->entityManager->getTransactionManager()->commit();
|
||||
}
|
||||
}
|
||||
132
application/Espo/Repositories/Attachment.php
Normal file
132
application/Espo/Repositories/Attachment.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\Attachment as AttachmentEntity;
|
||||
use Espo\Core\Repositories\Database;
|
||||
use Espo\Core\FileStorage\Storages\EspoUploadDir;
|
||||
use Espo\Core\Di;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* @extends Database<AttachmentEntity>
|
||||
*/
|
||||
class Attachment extends Database implements
|
||||
Di\FileStorageManagerAware,
|
||||
Di\ConfigAware
|
||||
{
|
||||
use Di\FileStorageManagerSetter;
|
||||
use Di\ConfigSetter;
|
||||
|
||||
/**
|
||||
* @param AttachmentEntity $entity
|
||||
*/
|
||||
protected function beforeSave(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::beforeSave($entity, $options);
|
||||
|
||||
if ($entity->isNew()) {
|
||||
$this->processBeforeSaveNew($entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processBeforeSaveNew(AttachmentEntity $entity): void
|
||||
{
|
||||
if ($entity->isBeingUploaded()) {
|
||||
$entity->setStorage(EspoUploadDir::NAME);
|
||||
}
|
||||
|
||||
if (!$entity->getStorage()) {
|
||||
$defaultStorage = $this->config->get('defaultFileStorage');
|
||||
|
||||
$entity->setStorage($defaultStorage);
|
||||
}
|
||||
|
||||
$contents = $entity->get('contents');
|
||||
|
||||
if (is_null($contents)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entity->isBeingUploaded()) {
|
||||
$entity->setSize(strlen($contents));
|
||||
}
|
||||
|
||||
$this->fileStorageManager->putContents($entity, $contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an attachment record (to reuse the same file w/o copying it in the storage).
|
||||
*/
|
||||
public function getCopiedAttachment(AttachmentEntity $entity, ?string $role = null): AttachmentEntity
|
||||
{
|
||||
$new = $this->getNew();
|
||||
|
||||
$new
|
||||
->setSourceId($entity->getSourceId())
|
||||
->setName($entity->getName())
|
||||
->setType($entity->getType())
|
||||
->setSize($entity->getSize())
|
||||
->setRole($entity->getRole());
|
||||
|
||||
if ($role) {
|
||||
$new->setRole($role);
|
||||
}
|
||||
|
||||
$this->save($new);
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function getContents(AttachmentEntity $entity): string
|
||||
{
|
||||
return $this->fileStorageManager->getContents($entity);
|
||||
}
|
||||
|
||||
public function getStream(AttachmentEntity $entity): StreamInterface
|
||||
{
|
||||
return $this->fileStorageManager->getStream($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* A size in bytes.
|
||||
*/
|
||||
public function getSize(AttachmentEntity $entity): int
|
||||
{
|
||||
return $this->fileStorageManager->getSize($entity);
|
||||
}
|
||||
|
||||
public function getFilePath(AttachmentEntity $entity): string
|
||||
{
|
||||
return $this->fileStorageManager->getLocalFilePath($entity);
|
||||
}
|
||||
}
|
||||
602
application/Espo/Repositories/Email.php
Normal file
602
application/Espo/Repositories/Email.php
Normal file
@@ -0,0 +1,602 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\Core\ORM\Repository\Option\SaveOption;
|
||||
use Espo\Entities\EmailFilter;
|
||||
use Espo\Entities\InboundEmail;
|
||||
use Espo\Entities\User as UserEntity;
|
||||
use Espo\Modules\Crm\Entities\Account;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
use Espo\Core\Repositories\Database;
|
||||
use Espo\Entities\Email as EmailEntity;
|
||||
use Espo\ORM\EntityCollection;
|
||||
use Espo\Repositories\EmailAddress as EmailAddressRepository;
|
||||
use Espo\Entities\EmailAddress;
|
||||
use Espo\Core\Di;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @extends Database<EmailEntity>
|
||||
* @internal
|
||||
*/
|
||||
class Email extends Database implements
|
||||
|
||||
Di\EmailFilterManagerAware
|
||||
{
|
||||
use Di\EmailFilterManagerSetter;
|
||||
|
||||
private const ADDRESS_FROM = EmailEntity::ADDRESS_FROM;
|
||||
private const ADDRESS_TO = EmailEntity::ADDRESS_TO;
|
||||
private const ADDRESS_CC = EmailEntity::ADDRESS_CC;
|
||||
private const ADDRESS_BCC = EmailEntity::ADDRESS_BCC;
|
||||
private const ADDRESS_REPLY_TO = EmailEntity::ADDRESS_REPLY_TO;
|
||||
|
||||
private const ATTR_FROM_EMAIL_ADDRESS_ID = 'fromEmailAddressId';
|
||||
private const ATTR_FROM_EMAIL_ADDRESS_NAME = 'fromEmailAddressName';
|
||||
|
||||
/**
|
||||
* @private string[]
|
||||
*/
|
||||
private const ADDRESS_TYPE_LIST = [
|
||||
self::ADDRESS_FROM,
|
||||
self::ADDRESS_TO,
|
||||
self::ADDRESS_CC,
|
||||
self::ADDRESS_BCC,
|
||||
self::ADDRESS_REPLY_TO,
|
||||
];
|
||||
|
||||
private function prepareAddresses(
|
||||
EmailEntity $entity,
|
||||
string $type,
|
||||
bool $addAssignedUser = false,
|
||||
bool $skipUsers = false,
|
||||
): void {
|
||||
|
||||
if (!$entity->has($type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$link = $type . 'EmailAddresses';
|
||||
|
||||
$addressValue = $entity->get($type);
|
||||
|
||||
if (!$addressValue) {
|
||||
$entity->setLinkMultipleIdList($link, []);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$previousIds = [];
|
||||
|
||||
if (!$entity->isNew()) {
|
||||
$previousIds = $entity->getFetchedLinkMultipleIdList($link);
|
||||
}
|
||||
|
||||
$addressList = $this->explodeAndPrepareAddressList($addressValue);
|
||||
|
||||
$ids = $this->getEmailAddressRepository()->getIdListFormAddressList($addressList);
|
||||
|
||||
$entity->setLinkMultipleIdList($link, $ids);
|
||||
|
||||
if (
|
||||
$skipUsers ||
|
||||
array_diff($previousIds, $ids) === array_diff($ids, $previousIds)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($ids as $id) {
|
||||
$this->addUserByEmailAddressId($entity, $id, $addAssignedUser);
|
||||
}
|
||||
}
|
||||
|
||||
private function addUserByEmailAddressId(
|
||||
EmailEntity $entity,
|
||||
string $emailAddressId,
|
||||
bool $addAssignedUser = false
|
||||
): void {
|
||||
|
||||
/** @var UserEntity[] $users */
|
||||
$users = $this->getEmailAddressRepository()
|
||||
->getEntityListByAddressId($emailAddressId, null, UserEntity::ENTITY_TYPE, true);
|
||||
|
||||
foreach ($users as $user) {
|
||||
$entity->addUserId($user->getId());
|
||||
|
||||
if ($addAssignedUser && !$user->isPortal()) {
|
||||
$entity->addAssignedUserId($user->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function loadFromField(EmailEntity $entity): void
|
||||
{
|
||||
$fromEmailAddressName = $entity->get(self::ATTR_FROM_EMAIL_ADDRESS_NAME);
|
||||
|
||||
if ($fromEmailAddressName && !$entity->isAttributeChanged(self::ATTR_FROM_EMAIL_ADDRESS_NAME)) {
|
||||
$entity->set(self::ADDRESS_FROM, $fromEmailAddressName);
|
||||
$entity->setFetched(self::ADDRESS_FROM, $fromEmailAddressName);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$fromEmailAddressId = $entity->get(self::ATTR_FROM_EMAIL_ADDRESS_ID);
|
||||
|
||||
if ($fromEmailAddressId) {
|
||||
$emailAddress = $this->getEmailAddressRepository()->getById($fromEmailAddressId);
|
||||
|
||||
if ($emailAddress) {
|
||||
$entity->setFromAddress($emailAddress->getAddress());
|
||||
$entity->setFetched(self::ADDRESS_FROM, $emailAddress->getAddress());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$entity->has(self::ATTR_FROM_EMAIL_ADDRESS_ID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity->setFromAddress(null);
|
||||
$entity->setFetched(self::ADDRESS_FROM, null);
|
||||
}
|
||||
|
||||
private function loadAddressMultiField(EmailEntity $entity, string $type): void
|
||||
{
|
||||
$entity->loadLinkMultipleField($type . 'EmailAddresses');
|
||||
|
||||
/** @var ?stdClass $names */
|
||||
$names = $entity->get($type . 'EmailAddressesNames');
|
||||
|
||||
if ($names === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$setFetched = !$entity->isAttributeChanged($type . 'EmailAddressesNames');
|
||||
|
||||
$addresses = [];
|
||||
|
||||
foreach (get_object_vars($names) as $address) {
|
||||
$addresses[] = $address;
|
||||
}
|
||||
|
||||
$value = implode(';', $addresses);
|
||||
|
||||
$entity->set($type, $value);
|
||||
|
||||
if ($setFetched) {
|
||||
$entity->setFetched($type, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function loadToField(EmailEntity $entity): void
|
||||
{
|
||||
$this->loadAddressMultiField($entity, self::ADDRESS_TO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function loadCcField(EmailEntity $entity): void
|
||||
{
|
||||
$this->loadAddressMultiField($entity, self::ADDRESS_CC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function loadBccField(EmailEntity $entity): void
|
||||
{
|
||||
$this->loadAddressMultiField($entity, self::ADDRESS_BCC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function loadReplyToField(EmailEntity $entity): void
|
||||
{
|
||||
$this->loadAddressMultiField($entity, self::ADDRESS_REPLY_TO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param string[] $fieldList
|
||||
*/
|
||||
public function loadNameHash(EmailEntity $entity, array $fieldList = self::ADDRESS_TYPE_LIST): void
|
||||
{
|
||||
$addressList = [];
|
||||
|
||||
if (in_array(self::ADDRESS_FROM, $fieldList) && $entity->get(self::ADDRESS_FROM)) {
|
||||
$addressList[] = $entity->get(self::ADDRESS_FROM);
|
||||
}
|
||||
|
||||
if (in_array(self::ADDRESS_TO, $fieldList)) {
|
||||
$this->addAddresses($entity, self::ADDRESS_TO, $addressList);
|
||||
}
|
||||
|
||||
if (in_array(self::ADDRESS_CC, $fieldList)) {
|
||||
$this->addAddresses($entity, self::ADDRESS_CC, $addressList);
|
||||
}
|
||||
|
||||
if (in_array(self::ADDRESS_BCC, $fieldList)) {
|
||||
$this->addAddresses($entity, self::ADDRESS_BCC, $addressList);
|
||||
}
|
||||
|
||||
if (in_array(self::ADDRESS_REPLY_TO, $fieldList)) {
|
||||
$this->addAddresses($entity, self::ADDRESS_REPLY_TO, $addressList);
|
||||
}
|
||||
|
||||
$nameHash = (object) [];
|
||||
$typeHash = (object) [];
|
||||
$idHash = (object) [];
|
||||
|
||||
foreach ($addressList as $address) {
|
||||
$related = $this->getEmailAddressRepository()->getEntityByAddress($address);
|
||||
|
||||
if (!$related) {
|
||||
$related = $this->entityManager
|
||||
->getRDBRepositoryByClass(InboundEmail::class)
|
||||
->where(['emailAddress' => $address])
|
||||
->findOne();
|
||||
}
|
||||
|
||||
if ($related) {
|
||||
$nameHash->$address = $related->get(Field::NAME);
|
||||
$typeHash->$address = $related->getEntityType();
|
||||
$idHash->$address = $related->getId();
|
||||
}
|
||||
}
|
||||
|
||||
$addressNameMap = $entity->get('addressNameMap');
|
||||
|
||||
if (is_object($addressNameMap)) {
|
||||
foreach (get_object_vars($addressNameMap) as $key => $value) {
|
||||
if (!isset($nameHash->$key)) {
|
||||
$nameHash->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$entity->set('nameHash', $nameHash);
|
||||
$entity->set('typeHash', $typeHash);
|
||||
$entity->set('idHash', $idHash);
|
||||
|
||||
$entity->setFetched('nameHash', $nameHash);
|
||||
$entity->setFetched('typeHash', $typeHash);
|
||||
$entity->setFetched('idHash', $idHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EmailEntity $entity
|
||||
*/
|
||||
protected function beforeSave(Entity $entity, array $options = [])
|
||||
{
|
||||
if ($entity->isNew() && !$entity->getMessageId()) {
|
||||
$entity->setDummyMessageId();
|
||||
}
|
||||
|
||||
if ($entity->has('attachmentsIds')) {
|
||||
/** @var string[] $attachmentsIds */
|
||||
$attachmentsIds = $entity->get('attachmentsIds') ?? [];
|
||||
|
||||
if ($attachmentsIds !== []) {
|
||||
$entity->set('hasAttachment', true);
|
||||
}
|
||||
}
|
||||
|
||||
$this->processBeforeSaveAddresses($entity);
|
||||
|
||||
if ($entity->getAssignedUser()) {
|
||||
$entity->addUserId($entity->getAssignedUser()->getId());
|
||||
}
|
||||
|
||||
parent::beforeSave($entity, $options);
|
||||
|
||||
if ($entity->getStatus() === EmailEntity::STATUS_SENDING && $entity->getCreatedBy()) {
|
||||
$entity->addUserId($entity->getCreatedBy()->getId());
|
||||
$entity->setUserColumnIsRead($entity->getCreatedBy()->getId(), true);
|
||||
}
|
||||
|
||||
if ($entity->isNew() || $entity->isAttributeChanged('parentId')) {
|
||||
$this->fillAccount($entity);
|
||||
}
|
||||
|
||||
if (
|
||||
!empty($options[EmailEntity::SAVE_OPTION_IS_BEING_IMPORTED]) ||
|
||||
!empty($options[EmailEntity::SAVE_OPTION_IS_JUST_SENT])
|
||||
) {
|
||||
if (!$entity->has(self::ADDRESS_FROM)) {
|
||||
$this->loadFromField($entity);
|
||||
}
|
||||
|
||||
if (!$entity->has(self::ADDRESS_TO)) {
|
||||
$this->loadToField($entity);
|
||||
}
|
||||
|
||||
$this->applyUsersFilters($entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function fillAccount(EmailEntity $entity): void
|
||||
{
|
||||
if (!$entity->isNew()) {
|
||||
$entity->setAccount(null);
|
||||
}
|
||||
|
||||
$parent = $entity->getParent();
|
||||
|
||||
if (!$parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
$accountId = null;
|
||||
|
||||
if ($parent->getEntityType() == Account::ENTITY_TYPE) {
|
||||
$accountId = $parent->getId();
|
||||
}
|
||||
|
||||
if (
|
||||
!$accountId &&
|
||||
$parent->get('accountId') &&
|
||||
$parent instanceof CoreEntity &&
|
||||
$parent->getRelationParam('account', RelationParam::ENTITY) === Account::ENTITY_TYPE
|
||||
) {
|
||||
$accountId = $parent->get('accountId');
|
||||
}
|
||||
|
||||
if ($accountId) {
|
||||
$account = $this->entityManager->getRDBRepositoryByClass(Account::class)->getById($accountId);
|
||||
|
||||
if ($account) {
|
||||
$entity->setAccount($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function applyUsersFilters(EmailEntity $entity): void
|
||||
{
|
||||
foreach ($entity->getUsers()->getIdList() as $userId) {
|
||||
if (
|
||||
$entity->getStatus() === EmailEntity::STATUS_SENT &&
|
||||
$entity->getSentBy()?->getId() === $userId
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filter = $this->emailFilterManager->getMatchingFilter($entity, $userId);
|
||||
|
||||
if (!$filter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($filter->getAction() === EmailFilter::ACTION_SKIP) {
|
||||
$entity->setUserColumnInTrash($userId, true);
|
||||
} else if ($filter->getAction() === EmailFilter::ACTION_MOVE_TO_FOLDER) {
|
||||
if ($filter->getEmailFolderId()) {
|
||||
$entity->setUserColumnFolderId($userId, $filter->getEmailFolderId());
|
||||
}
|
||||
}
|
||||
|
||||
if ($filter->markAsRead()) {
|
||||
$entity->setUserColumnIsRead($userId, true);
|
||||
}
|
||||
|
||||
if ($filter->skipNotification()) {
|
||||
$entity->setUserSkipNotification($userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EmailEntity $entity
|
||||
*/
|
||||
protected function afterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::afterSave($entity, $options);
|
||||
|
||||
if (
|
||||
!$entity->isNew() &&
|
||||
$entity->getParentType() &&
|
||||
$entity->getParentId() &&
|
||||
$entity->isAttributeChanged('parentId')
|
||||
) {
|
||||
/** @var EntityCollection<EmailEntity> $replyList */
|
||||
$replyList = $this->getRelation($entity, 'replies')
|
||||
->find();
|
||||
|
||||
foreach ($replyList as $reply) {
|
||||
if ($reply->getId() === $entity->getId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($reply->getParentId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reply->setMultiple([
|
||||
'parentId' => $entity->getParentId(),
|
||||
'parentType' => $entity->getParentType(),
|
||||
]);
|
||||
|
||||
$this->entityManager->saveEntity($reply);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(
|
||||
$entity->getStatus() === EmailEntity::STATUS_ARCHIVED ||
|
||||
$entity->getStatus() === EmailEntity::STATUS_SENT
|
||||
) &&
|
||||
(
|
||||
$entity->isAttributeChanged('status') ||
|
||||
$entity->isNew()
|
||||
)
|
||||
) {
|
||||
$replied = $entity->getReplied();
|
||||
|
||||
if (
|
||||
$replied &&
|
||||
$replied->getId() !== $entity->getId() &&
|
||||
!$replied->isReplied()
|
||||
) {
|
||||
$replied->setIsReplied();
|
||||
|
||||
$this->entityManager->saveEntity($replied, [SaveOption::SILENT => true]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->get('isBeingImported')) {
|
||||
$entity->set('isBeingImported', false);
|
||||
}
|
||||
}
|
||||
|
||||
private function getEmailAddressRepository(): EmailAddressRepository
|
||||
{
|
||||
/** @var EmailAddressRepository */
|
||||
return $this->entityManager->getRepository(EmailAddress::ENTITY_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $addressList
|
||||
*/
|
||||
private function addAddresses(EmailEntity $entity, string $type, array &$addressList): void
|
||||
{
|
||||
$value = $entity->get($type) ?? '';
|
||||
|
||||
$splitList = explode(';', $value);
|
||||
|
||||
foreach ($splitList as $address) {
|
||||
if (!in_array($address, $addressList)) {
|
||||
$addressList[] = $address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processBeforeSaveAddresses(EmailEntity $entity): void
|
||||
{
|
||||
$hasOne =
|
||||
$entity->has(self::ADDRESS_FROM) ||
|
||||
$entity->has(self::ADDRESS_TO) ||
|
||||
$entity->has(self::ADDRESS_CC) ||
|
||||
$entity->has(self::ADDRESS_BCC) ||
|
||||
$entity->has(self::ADDRESS_REPLY_TO);
|
||||
|
||||
if (!$hasOne) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entity->has('usersIds')) {
|
||||
$entity->loadLinkMultipleField('users');
|
||||
}
|
||||
|
||||
if ($entity->has(self::ADDRESS_FROM)) {
|
||||
$this->processBeforeSaveFrom($entity);
|
||||
}
|
||||
|
||||
if ($entity->has(self::ADDRESS_TO)) {
|
||||
$this->prepareAddresses($entity, self::ADDRESS_TO, true);
|
||||
}
|
||||
|
||||
if ($entity->has(self::ADDRESS_CC)) {
|
||||
$this->prepareAddresses($entity, self::ADDRESS_CC);
|
||||
}
|
||||
|
||||
if ($entity->has(self::ADDRESS_BCC)) {
|
||||
$this->prepareAddresses($entity, self::ADDRESS_BCC);
|
||||
}
|
||||
|
||||
if ($entity->has(self::ADDRESS_REPLY_TO)) {
|
||||
$this->prepareAddresses($entity, self::ADDRESS_REPLY_TO, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
private function processBeforeSaveFrom(EmailEntity $entity): void
|
||||
{
|
||||
$from = trim($entity->getFromAddress() ?? '');
|
||||
|
||||
if (!$from) {
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$entity->set(self::ATTR_FROM_EMAIL_ADDRESS_ID, null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$ids = $this->getEmailAddressRepository()->getIdListFormAddressList([$from]);
|
||||
|
||||
if ($ids === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity->set(self::ATTR_FROM_EMAIL_ADDRESS_ID, $ids[0]);
|
||||
$entity->set(self::ATTR_FROM_EMAIL_ADDRESS_NAME, $from);
|
||||
|
||||
$this->addUserByEmailAddressId($entity, $ids[0], true);
|
||||
|
||||
if ($entity->getSentBy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->getEmailAddressRepository()->getEntityByAddressId($ids[0], UserEntity::ENTITY_TYPE, true);
|
||||
|
||||
if (
|
||||
$user instanceof UserEntity &&
|
||||
$entity->getStatus() !== EmailEntity::STATUS_DRAFT &&
|
||||
$user->getType() !== UserEntity::TYPE_PORTAL
|
||||
) {
|
||||
$entity->setSentBy($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function explodeAndPrepareAddressList(string $addressValue): array
|
||||
{
|
||||
$addressList = array_map(fn ($item) => trim($item), explode(';', $addressValue));
|
||||
|
||||
return array_filter($addressList, fn ($item) => filter_var($item, FILTER_VALIDATE_EMAIL) !== false);
|
||||
}
|
||||
}
|
||||
400
application/Espo/Repositories/EmailAddress.php
Normal file
400
application/Espo/Repositories/EmailAddress.php
Normal file
@@ -0,0 +1,400 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\Core\Repositories\Database;
|
||||
use Espo\Entities\User as UserEntity;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\EmailAddress as EmailAddressEntity;
|
||||
use Espo\Core\Di;
|
||||
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Not to be used directly. Use utilities from `Espo\Tools\EmailAddress` instead.
|
||||
* @internal
|
||||
* @extends Database<EmailAddressEntity>
|
||||
*/
|
||||
class EmailAddress extends Database implements
|
||||
Di\ApplicationStateAware,
|
||||
Di\AclManagerAware,
|
||||
Di\ConfigAware
|
||||
{
|
||||
use Di\ApplicationStateSetter;
|
||||
use Di\AclManagerSetter;
|
||||
use Di\ConfigSetter;
|
||||
|
||||
private const LOOKUP_SMALL_MAX_SIZE = 20;
|
||||
private const LOOKUP_MAX_SIZE = 50;
|
||||
|
||||
/**
|
||||
* @param string[] $addressList
|
||||
* @return string[]
|
||||
*/
|
||||
public function getIdListFormAddressList(array $addressList = []): array
|
||||
{
|
||||
return $this->getIds($addressList);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `getIdListFormAddressList`.
|
||||
* @param string[] $addressList
|
||||
* @return string[]
|
||||
*/
|
||||
public function getIds(array $addressList = []): array
|
||||
{
|
||||
if (empty($addressList)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
$lowerAddressList = [];
|
||||
|
||||
foreach ($addressList as $address) {
|
||||
$lowerAddressList[] = trim(strtolower($address));
|
||||
}
|
||||
|
||||
$eaCollection = $this
|
||||
->where(['lower' => $lowerAddressList])
|
||||
->find();
|
||||
|
||||
$exist = [];
|
||||
|
||||
foreach ($eaCollection as $ea) {
|
||||
$ids[] = $ea->getId();
|
||||
$exist[] = $ea->get('lower');
|
||||
}
|
||||
|
||||
foreach ($addressList as $address) {
|
||||
$address = trim($address);
|
||||
|
||||
if (empty($address) || !filter_var($address, FILTER_VALIDATE_EMAIL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array(strtolower($address), $exist)) {
|
||||
$ea = $this->getNew();
|
||||
|
||||
$ea->set(Field::NAME, $address);
|
||||
|
||||
$this->save($ea);
|
||||
|
||||
$ids[] = $ea->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return stdClass[]
|
||||
*/
|
||||
public function getEmailAddressData(Entity $entity): array
|
||||
{
|
||||
if (!$entity->hasId()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dataList = [];
|
||||
|
||||
$emailAddressList = $this
|
||||
->select([Field::NAME, 'lower', 'invalid', 'optOut', ['ee.primary', 'primary']])
|
||||
->join(
|
||||
EmailAddressEntity::RELATION_ENTITY_EMAIL_ADDRESS,
|
||||
'ee',
|
||||
[
|
||||
'ee.emailAddressId:' => 'id',
|
||||
]
|
||||
)
|
||||
->where([
|
||||
'ee.entityId' => $entity->getId(),
|
||||
'ee.entityType' => $entity->getEntityType(),
|
||||
'ee.deleted' => false,
|
||||
])
|
||||
->order('ee.primary', true)
|
||||
->find();
|
||||
|
||||
foreach ($emailAddressList as $emailAddress) {
|
||||
$item = (object) [
|
||||
'emailAddress' => $emailAddress->get(Field::NAME),
|
||||
'lower' => $emailAddress->get('lower'),
|
||||
'primary' => $emailAddress->get('primary'),
|
||||
'optOut' => $emailAddress->get('optOut'),
|
||||
'invalid' => $emailAddress->get('invalid'),
|
||||
];
|
||||
|
||||
$dataList[] = $item;
|
||||
}
|
||||
|
||||
return $dataList;
|
||||
}
|
||||
|
||||
public function getByAddress(string $address): ?EmailAddressEntity
|
||||
{
|
||||
/** @var ?EmailAddressEntity */
|
||||
return $this->where(['lower' => strtolower($address)])->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Entity[]
|
||||
*/
|
||||
public function getEntityListByAddressId(
|
||||
string $emailAddressId,
|
||||
?Entity $exceptionEntity = null,
|
||||
?string $entityType = null,
|
||||
bool $onlyName = false
|
||||
): array {
|
||||
|
||||
$entityList = [];
|
||||
|
||||
$where = [
|
||||
'emailAddressId' => $emailAddressId,
|
||||
];
|
||||
|
||||
if ($exceptionEntity) {
|
||||
$where[] = [
|
||||
'OR' => [
|
||||
'entityType!=' => $exceptionEntity->getEntityType(),
|
||||
'entityId!=' => $exceptionEntity->getId(),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
if ($entityType) {
|
||||
$where[] = [
|
||||
'entityType' => $entityType,
|
||||
];
|
||||
}
|
||||
|
||||
$itemList = $this->entityManager
|
||||
->getRDBRepository(EmailAddressEntity::RELATION_ENTITY_EMAIL_ADDRESS)
|
||||
->sth()
|
||||
->select(['entityType', 'entityId'])
|
||||
->where($where)
|
||||
->limit(0, self::LOOKUP_MAX_SIZE)
|
||||
->find();
|
||||
|
||||
foreach ($itemList as $item) {
|
||||
$itemEntityType = $item->get('entityType');
|
||||
$itemEntityId = $item->get('entityId');
|
||||
|
||||
if (!$itemEntityType || !$itemEntityId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->entityManager->hasRepository($itemEntityType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($onlyName) {
|
||||
$select = [Attribute::ID, 'name'];
|
||||
|
||||
if ($itemEntityType === UserEntity::ENTITY_TYPE) {
|
||||
$select[] = 'isActive';
|
||||
}
|
||||
|
||||
$entity = $this->entityManager
|
||||
->getRDBRepository($itemEntityType)
|
||||
->select($select)
|
||||
->where([Attribute::ID => $itemEntityId])
|
||||
->findOne();
|
||||
} else {
|
||||
$entity = $this->entityManager->getEntityById($itemEntityType, $itemEntityId);
|
||||
}
|
||||
|
||||
if (!$entity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($entity instanceof UserEntity && !$entity->isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entityList[] = $entity;
|
||||
}
|
||||
|
||||
return $entityList;
|
||||
}
|
||||
|
||||
public function getEntityByAddressId(
|
||||
string $emailAddressId,
|
||||
?string $entityType = null,
|
||||
bool $onlyName = false
|
||||
): ?Entity {
|
||||
|
||||
$where = [
|
||||
'emailAddressId' => $emailAddressId,
|
||||
];
|
||||
|
||||
if ($entityType) {
|
||||
$where[] = ['entityType' => $entityType];
|
||||
}
|
||||
|
||||
$itemList = $this->entityManager
|
||||
->getRDBRepository(EmailAddressEntity::RELATION_ENTITY_EMAIL_ADDRESS)
|
||||
->sth()
|
||||
->select(['entityType', 'entityId'])
|
||||
->where($where)
|
||||
->limit(0, self::LOOKUP_SMALL_MAX_SIZE)
|
||||
->order([
|
||||
['primary', 'DESC'],
|
||||
['LIST:entityType:User,Contact,Lead,Account'],
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($itemList as $item) {
|
||||
$itemEntityType = $item->get('entityType');
|
||||
$itemEntityId = $item->get('entityId');
|
||||
|
||||
if (!$itemEntityType || !$itemEntityId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->entityManager->hasRepository($itemEntityType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($onlyName) {
|
||||
$select = ['id', 'name'];
|
||||
|
||||
if ($itemEntityType === UserEntity::ENTITY_TYPE) {
|
||||
$select[] = 'isActive';
|
||||
}
|
||||
|
||||
$entity = $this->entityManager
|
||||
->getRDBRepository($itemEntityType)
|
||||
->select($select)
|
||||
->where([Attribute::ID => $itemEntityId])
|
||||
->findOne();
|
||||
} else {
|
||||
$entity = $this->entityManager->getEntityById($itemEntityType, $itemEntityId);
|
||||
}
|
||||
|
||||
if ($entity) {
|
||||
if ($entity instanceof UserEntity) {
|
||||
if (!$entity->isActive()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $order
|
||||
*/
|
||||
public function getEntityByAddress(string $address, ?string $entityType = null, ?array $order = null): ?Entity
|
||||
{
|
||||
$order ??= $this->config->get('emailAddressEntityLookupDefaultOrder') ?? [];
|
||||
|
||||
$selectBuilder = $this->entityManager
|
||||
->getRDBRepository(EmailAddressEntity::RELATION_ENTITY_EMAIL_ADDRESS)
|
||||
->select();
|
||||
|
||||
$selectBuilder
|
||||
->select(['entityType', 'entityId'])
|
||||
->sth()
|
||||
->join(
|
||||
EmailAddressEntity::ENTITY_TYPE,
|
||||
'ea',
|
||||
['ea.id:' => 'emailAddressId', 'ea.deleted' => false]
|
||||
)
|
||||
->where('ea.lower=', strtolower($address))
|
||||
->order([
|
||||
['LIST:entityType:' . implode(',', $order)],
|
||||
['primary', 'DESC'],
|
||||
])
|
||||
->limit(0, self::LOOKUP_MAX_SIZE);
|
||||
|
||||
if ($entityType) {
|
||||
$selectBuilder->where('entityType=', $entityType);
|
||||
}
|
||||
|
||||
foreach ($selectBuilder->find() as $item) {
|
||||
$itemEntityType = $item->get('entityType');
|
||||
$itemEntityId = $item->get('entityId');
|
||||
|
||||
if (!$itemEntityType || !$itemEntityId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->entityManager->hasRepository($itemEntityType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entity = $this->entityManager->getEntityById($itemEntityType, $itemEntityId);
|
||||
|
||||
if ($entity) {
|
||||
if ($entity instanceof UserEntity) {
|
||||
if (!$entity->isActive()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function markAddressOptedOut(string $address, bool $isOptedOut = true): void
|
||||
{
|
||||
$emailAddress = $this->getByAddress($address);
|
||||
|
||||
if (!$emailAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
$emailAddress->set('optOut', $isOptedOut);
|
||||
|
||||
$this->save($emailAddress);
|
||||
}
|
||||
|
||||
public function markAddressInvalid(string $address, bool $isInvalid = true): void
|
||||
{
|
||||
$emailAddress = $this->getByAddress($address);
|
||||
|
||||
if (!$emailAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
$emailAddress->set('invalid', $isInvalid);
|
||||
|
||||
$this->save($emailAddress);
|
||||
}
|
||||
}
|
||||
57
application/Espo/Repositories/EmailFolder.php
Normal file
57
application/Espo/Repositories/EmailFolder.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @extends \Espo\Core\Repositories\Database<\Espo\Entities\EmailFolder>
|
||||
*/
|
||||
class EmailFolder extends \Espo\Core\Repositories\Database
|
||||
{
|
||||
protected function beforeSave(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::beforeSave($entity, $options);
|
||||
|
||||
$order = $entity->get('order');
|
||||
|
||||
if (is_null($order)) {
|
||||
$order = $this->max('order');
|
||||
|
||||
if (!$order) {
|
||||
$order = 0;
|
||||
}
|
||||
|
||||
$order++;
|
||||
|
||||
$entity->set('order', $order);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
application/Espo/Repositories/ExternalAccount.php
Normal file
56
application/Espo/Repositories/ExternalAccount.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
use Espo\Entities\ExternalAccount as ExternalAccountEntity;
|
||||
|
||||
/**
|
||||
* @extends Database<ExternalAccountEntity>
|
||||
*/
|
||||
class ExternalAccount extends Database
|
||||
{
|
||||
public function getById(string $id): ?Entity
|
||||
{
|
||||
$entity = parent::getById($id);
|
||||
|
||||
if (!$entity) {
|
||||
/** @var ExternalAccountEntity $entity */
|
||||
$entity = $this->getNew();
|
||||
|
||||
$entity->set('id', $id);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
174
application/Espo/Repositories/Import.php
Normal file
174
application/Espo/Repositories/Import.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Entities\Import as ImportEntity;
|
||||
use Espo\Entities\ImportEntity as ImportEntityEntity;
|
||||
use Espo\ORM\Collection;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\Query\Select as Query;
|
||||
use Espo\ORM\Query\SelectBuilder;
|
||||
use Espo\Entities\Attachment as AttachmentEntity;
|
||||
use Espo\Core\Repositories\Database;
|
||||
use Espo\Entities\ImportError;
|
||||
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* @extends Database<ImportEntity>
|
||||
*/
|
||||
class Import extends Database
|
||||
{
|
||||
/**
|
||||
* @return Collection<Entity>
|
||||
*/
|
||||
public function findResultRecords(ImportEntity $entity, string $relationName, Query $query): Collection
|
||||
{
|
||||
$entityType = $entity->getTargetEntityType();
|
||||
|
||||
if (!$entityType) {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
$modifiedQuery = $this->addImportEntityJoin($entity, $relationName, $query);
|
||||
|
||||
return $this->entityManager
|
||||
->getRDBRepository($entityType)
|
||||
->clone($modifiedQuery)
|
||||
->find();
|
||||
}
|
||||
|
||||
protected function addImportEntityJoin(ImportEntity $entity, string $link, Query $query): Query
|
||||
{
|
||||
$entityType = $entity->getTargetEntityType();
|
||||
|
||||
if (!$entityType) {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
switch ($link) {
|
||||
case 'imported':
|
||||
$param = 'isImported';
|
||||
|
||||
break;
|
||||
|
||||
case 'duplicates':
|
||||
$param = 'isDuplicate';
|
||||
|
||||
break;
|
||||
|
||||
case 'updated':
|
||||
$param = 'isUpdated';
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return $query;
|
||||
}
|
||||
|
||||
$builder = SelectBuilder::create()->clone($query);
|
||||
|
||||
$builder->join(
|
||||
'ImportEntity',
|
||||
'importEntity',
|
||||
[
|
||||
'importEntity.importId' => $entity->getId(),
|
||||
'importEntity.entityType' => $entityType,
|
||||
'importEntity.entityId:' => 'id',
|
||||
'importEntity.' . $param => true,
|
||||
]
|
||||
);
|
||||
|
||||
return $builder->build();
|
||||
}
|
||||
|
||||
public function countResultRecords(ImportEntity $entity, string $relationName, ?Query $query = null): int
|
||||
{
|
||||
$entityType = $entity->getTargetEntityType();
|
||||
|
||||
if (!$entityType) {
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
$query = $query ??
|
||||
$this->entityManager
|
||||
->getQueryBuilder()
|
||||
->select()
|
||||
->from($entityType)
|
||||
->build();
|
||||
|
||||
$modifiedQuery = $this->addImportEntityJoin($entity, $relationName, $query);
|
||||
|
||||
return $this->entityManager
|
||||
->getRDBRepository($entityType)
|
||||
->clone($modifiedQuery)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportEntity $entity
|
||||
*/
|
||||
protected function afterRemove(Entity $entity, array $options = [])
|
||||
{
|
||||
$fileId = $entity->getFileId();
|
||||
|
||||
if ($fileId) {
|
||||
$attachment = $this->entityManager->getEntityById(AttachmentEntity::ENTITY_TYPE, $fileId);
|
||||
|
||||
if ($attachment) {
|
||||
$this->entityManager->removeEntity($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
$delete1 = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from(ImportEntityEntity::ENTITY_TYPE)
|
||||
->where([
|
||||
'importId' => $entity->getId(),
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($delete1);
|
||||
|
||||
$delete2 = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from(ImportError::ENTITY_TYPE)
|
||||
->where([
|
||||
'importId' => $entity->getId(),
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($delete2);
|
||||
|
||||
parent::afterRemove($entity, $options);
|
||||
}
|
||||
}
|
||||
56
application/Espo/Repositories/Integration.php
Normal file
56
application/Espo/Repositories/Integration.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
use Espo\Entities\Integration as IntegrationEntity;
|
||||
|
||||
/**
|
||||
* @extends Database<IntegrationEntity>
|
||||
*/
|
||||
class Integration extends Database
|
||||
{
|
||||
public function getById(string $id): ?Entity
|
||||
{
|
||||
$entity = parent::getById($id);
|
||||
|
||||
if (!$entity) {
|
||||
/** @var IntegrationEntity $entity */
|
||||
$entity = $this->getNew();
|
||||
|
||||
$entity->set('id', $id);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
62
application/Espo/Repositories/Job.php
Normal file
62
application/Espo/Repositories/Job.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\Repositories\Database;
|
||||
use Espo\Core\Utils\DateTime;
|
||||
use Espo\Entities\Job as JobEntity;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\Di;
|
||||
|
||||
/**
|
||||
* @extends Database<JobEntity>
|
||||
*/
|
||||
class Job extends Database implements
|
||||
Di\ConfigAware
|
||||
{
|
||||
use Di\ConfigSetter;
|
||||
|
||||
/**
|
||||
* @param JobEntity $entity
|
||||
*/
|
||||
public function beforeSave(Entity $entity, array $options = [])
|
||||
{
|
||||
if ($entity->get('executeTime') === null && $entity->isNew()) {
|
||||
$entity->set('executeTime', DateTime::getSystemNowString());
|
||||
}
|
||||
|
||||
if ($entity->get('attempts') === null && $entity->isNew()) {
|
||||
$attempts = $this->config->get('jobRerunAttemptNumber', 0);
|
||||
|
||||
$entity->set('attempts', $attempts);
|
||||
}
|
||||
}
|
||||
}
|
||||
83
application/Espo/Repositories/LayoutSet.php
Normal file
83
application/Espo/Repositories/LayoutSet.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\Repositories\Database;
|
||||
use Espo\Entities\LayoutRecord;
|
||||
use Espo\Entities\LayoutSet as LayoutSetEntity;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
/**
|
||||
* @extends Database<LayoutSetEntity>
|
||||
*/
|
||||
class LayoutSet extends Database
|
||||
{
|
||||
protected function afterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::afterSave($entity);
|
||||
|
||||
if (!$entity->isNew() && $entity->has('layoutList')) {
|
||||
$listBefore = $entity->getFetched('layoutList') ?? [];
|
||||
$listNow = $entity->get('layoutList') ?? [];
|
||||
|
||||
foreach ($listBefore as $name) {
|
||||
if (!in_array($name, $listNow)) {
|
||||
$layout = $this->entityManager
|
||||
->getRDBRepository(LayoutRecord::ENTITY_TYPE)
|
||||
->where([
|
||||
'layoutSetId' => $entity->getId(),
|
||||
'name' => $name,
|
||||
])
|
||||
->findOne();
|
||||
|
||||
if ($layout) {
|
||||
$this->entityManager->removeEntity($layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function afterRemove(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::afterRemove($entity);
|
||||
|
||||
$layoutList = $this->entityManager
|
||||
->getRDBRepository(LayoutRecord::ENTITY_TYPE)
|
||||
->where([
|
||||
'layoutSetId' => $entity->getId(),
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($layoutList as $layout) {
|
||||
$this->entityManager->removeEntity($layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
305
application/Espo/Repositories/PhoneNumber.php
Normal file
305
application/Espo/Repositories/PhoneNumber.php
Normal file
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\Entities\User as UserEntity;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Entities\PhoneNumber as PhoneNumberEntity;
|
||||
use Espo\Core\Repositories\Database;
|
||||
use Espo\Core\Di;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Not to be used directly. Use utilities from `Espo\Tools\PhoneNumber` instead.
|
||||
* @internal
|
||||
* @extends Database<PhoneNumberEntity>
|
||||
*/
|
||||
class PhoneNumber extends Database implements
|
||||
|
||||
Di\ApplicationStateAware,
|
||||
Di\AclManagerAware,
|
||||
Di\ConfigAware
|
||||
{
|
||||
use Di\ApplicationStateSetter;
|
||||
use Di\AclManagerSetter;
|
||||
use Di\ConfigSetter;
|
||||
|
||||
private const ERASED_PREFIX = 'ERASED:';
|
||||
|
||||
private const LOOKUP_SMALL_MAX_SIZE = 20;
|
||||
private const LOOKUP_MAX_SIZE = 50;
|
||||
|
||||
/**
|
||||
* @param string[] $numberList
|
||||
* @return string[]
|
||||
*/
|
||||
public function getIds($numberList = []): array
|
||||
{
|
||||
if (empty($numberList)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
$phoneNumbers = $this
|
||||
->where([
|
||||
'name' => $numberList,
|
||||
])
|
||||
->find();
|
||||
|
||||
$exist = [];
|
||||
|
||||
foreach ($phoneNumbers as $phoneNumber) {
|
||||
$ids[] = $phoneNumber->getId();
|
||||
$exist[] = $phoneNumber->get(Field::NAME);
|
||||
}
|
||||
|
||||
foreach ($numberList as $number) {
|
||||
$number = trim($number);
|
||||
|
||||
if (empty($number)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array($number, $exist)) {
|
||||
$phoneNumber = $this->getNew();
|
||||
$phoneNumber->set(Field::NAME, $number);
|
||||
$this->save($phoneNumber);
|
||||
|
||||
$ids[] = $phoneNumber->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, stdClass>
|
||||
*/
|
||||
public function getPhoneNumberData(Entity $entity): array
|
||||
{
|
||||
if (!$entity->hasId()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dataList = [];
|
||||
|
||||
$numberList = $this
|
||||
->select([Field::NAME, 'type', 'invalid', 'optOut', ['en.primary', 'primary']])
|
||||
->join(
|
||||
PhoneNumberEntity::RELATION_ENTITY_PHONE_NUMBER,
|
||||
'en',
|
||||
[
|
||||
'en.phoneNumberId:' => 'id',
|
||||
]
|
||||
)
|
||||
->where([
|
||||
'en.entityId' => $entity->getId(),
|
||||
'en.entityType' => $entity->getEntityType(),
|
||||
'en.deleted' => false,
|
||||
])
|
||||
->order('en.primary', true)
|
||||
->find();
|
||||
|
||||
foreach ($numberList as $number) {
|
||||
$item = (object) [
|
||||
'phoneNumber' => $number->get(Field::NAME),
|
||||
'type' => $number->get('type'),
|
||||
'primary' => $number->get('primary'),
|
||||
'optOut' => $number->get('optOut'),
|
||||
'invalid' => $number->get('invalid'),
|
||||
];
|
||||
|
||||
$dataList[] = $item;
|
||||
}
|
||||
|
||||
return $dataList;
|
||||
}
|
||||
|
||||
public function getByNumber(string $number): ?PhoneNumberEntity
|
||||
{
|
||||
/** @var ?PhoneNumberEntity */
|
||||
return $this->where(['name' => $number])->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Entity[]
|
||||
*/
|
||||
public function getEntityListByPhoneNumberId(string $phoneNumberId, ?Entity $exceptionEntity = null): array
|
||||
{
|
||||
$entityList = [];
|
||||
|
||||
$where = [
|
||||
'phoneNumberId' => $phoneNumberId,
|
||||
];
|
||||
|
||||
if ($exceptionEntity) {
|
||||
$where[] = [
|
||||
'OR' => [
|
||||
'entityType!=' => $exceptionEntity->getEntityType(),
|
||||
'entityId!=' => $exceptionEntity->getId(),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$itemList = $this->entityManager
|
||||
->getRDBRepository(PhoneNumberEntity::RELATION_ENTITY_PHONE_NUMBER)
|
||||
->sth()
|
||||
->select(['entityType', 'entityId'])
|
||||
->where($where)
|
||||
->limit(0, self::LOOKUP_MAX_SIZE)
|
||||
->find();
|
||||
|
||||
foreach ($itemList as $item) {
|
||||
$itemEntityType = $item->get('entityType');
|
||||
$itemEntityId = $item->get('entityId');
|
||||
|
||||
if (!$itemEntityType || !$itemEntityId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->entityManager->hasRepository($itemEntityType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entity = $this->entityManager->getEntityById($itemEntityType, $itemEntityId);
|
||||
|
||||
if (!$entity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entityList[] = $entity;
|
||||
}
|
||||
|
||||
return $entityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $order
|
||||
*/
|
||||
public function getEntityByPhoneNumberId(
|
||||
string $phoneNumberId,
|
||||
?string $entityType = null,
|
||||
?array $order = null
|
||||
): ?Entity {
|
||||
|
||||
$order ??= $this->config->get('phoneNumberEntityLookupDefaultOrder') ?? [];
|
||||
|
||||
$where = ['phoneNumberId' => $phoneNumberId];
|
||||
|
||||
if ($entityType) {
|
||||
$where[] = ['entityType' => $entityType];
|
||||
}
|
||||
|
||||
$collection = $this->entityManager
|
||||
->getRDBRepository(PhoneNumberEntity::RELATION_ENTITY_PHONE_NUMBER)
|
||||
->sth()
|
||||
->select(['entityType', 'entityId'])
|
||||
->where($where)
|
||||
->limit(0, self::LOOKUP_SMALL_MAX_SIZE)
|
||||
->order([
|
||||
['LIST:entityType:' . implode(',', $order)],
|
||||
['primary', 'DESC'],
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$itemEntityType = $item->get('entityType');
|
||||
$itemEntityId = $item->get('entityId');
|
||||
|
||||
if (!$itemEntityType || !$itemEntityId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->entityManager->hasRepository($itemEntityType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entity = $this->entityManager->getEntityById($itemEntityType, $itemEntityId);
|
||||
|
||||
if ($entity) {
|
||||
if ($entity instanceof UserEntity) {
|
||||
if (!$entity->isActive()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function beforeSave(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::beforeSave($entity, $options);
|
||||
|
||||
if ($entity->has(Field::NAME)) {
|
||||
$number = $entity->get(Field::NAME);
|
||||
|
||||
if (is_string($number) && !str_starts_with($number, self::ERASED_PREFIX)) {
|
||||
$numeric = preg_replace('/[^0-9]/', '', $number);
|
||||
} else {
|
||||
$numeric = null;
|
||||
}
|
||||
|
||||
$entity->set('numeric', $numeric);
|
||||
}
|
||||
}
|
||||
|
||||
public function markNumberOptedOut(string $number, bool $isOptedOut = true): void
|
||||
{
|
||||
$phoneNumber = $this->getByNumber($number);
|
||||
|
||||
if (!$phoneNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phoneNumber->set('optOut', $isOptedOut);
|
||||
|
||||
$this->save($phoneNumber);
|
||||
}
|
||||
|
||||
public function markNumberInvalid(string $number, bool $isInvalid = true): void
|
||||
{
|
||||
$phoneNumber = $this->getByNumber($number);
|
||||
|
||||
if (!$phoneNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phoneNumber->set('invalid', $isInvalid);
|
||||
|
||||
$this->save($phoneNumber);
|
||||
}
|
||||
}
|
||||
75
application/Espo/Repositories/Portal.php
Normal file
75
application/Espo/Repositories/Portal.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Entities\Portal as PortalEntity;
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
use Espo\Core\Di;
|
||||
|
||||
/**
|
||||
* @extends Database<PortalEntity>
|
||||
*/
|
||||
class Portal extends Database implements
|
||||
|
||||
Di\ConfigAware
|
||||
{
|
||||
use Di\ConfigSetter;
|
||||
|
||||
public function loadUrlField(PortalEntity $entity): void
|
||||
{
|
||||
if ($entity->get('customUrl')) {
|
||||
$entity->set('url', $entity->get('customUrl'));
|
||||
}
|
||||
|
||||
$siteUrl = $this->config->get('siteUrl');
|
||||
$siteUrl = rtrim($siteUrl , '/') . '/';
|
||||
|
||||
$url = $siteUrl . 'portal/';
|
||||
|
||||
if ($entity->getId() === $this->config->get('defaultPortalId')) {
|
||||
$entity->set('isDefault', true);
|
||||
$entity->setFetched('isDefault', true);
|
||||
} else {
|
||||
if ($entity->get('customId')) {
|
||||
$url .= $entity->get('customId') . '/';
|
||||
} else {
|
||||
$url .= $entity->getId() . '/';
|
||||
}
|
||||
|
||||
$entity->set('isDefault', false);
|
||||
$entity->setFetched('isDefault', false);
|
||||
}
|
||||
|
||||
if (!$entity->get('customUrl')) {
|
||||
$entity->set('url', $url);
|
||||
}
|
||||
}
|
||||
}
|
||||
331
application/Espo/Repositories/Preferences.php
Normal file
331
application/Espo/Repositories/Preferences.php
Normal file
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Entities\Autofollow;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\EntityFactory;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Repository\Repository;
|
||||
use Espo\Core\Utils\Json;
|
||||
|
||||
use Espo\Entities\Preferences as PreferencesEntity;
|
||||
use Espo\Entities\User;
|
||||
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
|
||||
use Espo\Core\Di;
|
||||
|
||||
/**
|
||||
* @implements Repository<PreferencesEntity>
|
||||
*/
|
||||
class Preferences implements Repository,
|
||||
|
||||
Di\MetadataAware,
|
||||
Di\ConfigAware,
|
||||
Di\EntityManagerAware
|
||||
{
|
||||
use Di\MetadataSetter;
|
||||
use Di\ConfigSetter;
|
||||
use Di\EntityManagerSetter;
|
||||
|
||||
/**
|
||||
* @var EntityFactory
|
||||
*/
|
||||
protected $entityFactory;
|
||||
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
EntityFactory $entityFactory
|
||||
) {
|
||||
$this->entityFactory = $entityFactory;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $defaultAttributeListFromSettings = [
|
||||
'decimalMark',
|
||||
'thousandSeparator',
|
||||
'exportDelimiter',
|
||||
'followCreatedEntities',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
public function getNew(): Entity
|
||||
{
|
||||
/** @var PreferencesEntity */
|
||||
return $this->entityFactory->create(PreferencesEntity::ENTITY_TYPE);
|
||||
}
|
||||
|
||||
public function getById(string $id): ?Entity
|
||||
{
|
||||
/** @var PreferencesEntity $entity */
|
||||
$entity = $this->entityFactory->create(PreferencesEntity::ENTITY_TYPE);
|
||||
|
||||
$entity->set('id', $id);
|
||||
|
||||
if (!isset($this->data[$id])) {
|
||||
$this->loadData($id);
|
||||
}
|
||||
|
||||
$entity->set($this->data[$id]);
|
||||
|
||||
$this->fetchAutoFollowEntityTypeList($entity);
|
||||
|
||||
$entity->setAsFetched();
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `getById`.
|
||||
* @todo Remove in v10.0.
|
||||
*/
|
||||
public function get(?string $id = null): ?Entity
|
||||
{
|
||||
if ($id === null) {
|
||||
return $this->getNew();
|
||||
}
|
||||
|
||||
return $this->getById($id);
|
||||
}
|
||||
|
||||
protected function loadData(string $id): void
|
||||
{
|
||||
$data = null;
|
||||
|
||||
$select = $this->entityManager->getQueryBuilder()
|
||||
->select()
|
||||
->from(PreferencesEntity::ENTITY_TYPE)
|
||||
->select([Attribute::ID, 'data'])
|
||||
->where([
|
||||
Attribute::ID => $id,
|
||||
])
|
||||
->limit(0, 1)
|
||||
->build();
|
||||
|
||||
$sth = $this->entityManager->getQueryExecutor()->execute($select);
|
||||
|
||||
while ($row = $sth->fetch()) {
|
||||
$data = Json::decode($row['data']);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($data) {
|
||||
$this->data[$id] = get_object_vars($data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var array<string, array<string, mixed>> $fields */
|
||||
$fields = $this->metadata->get('entityDefs.Preferences.fields');
|
||||
|
||||
$defaults = [];
|
||||
|
||||
$dashboardLayout = $this->config->get('dashboardLayout');
|
||||
$dashletsOptions = null;
|
||||
|
||||
if (!$dashboardLayout) {
|
||||
$dashboardLayout = $this->metadata->get('app.defaultDashboardLayouts.Standard');
|
||||
$dashletsOptions = $this->metadata->get('app.defaultDashboardOptions.Standard');
|
||||
}
|
||||
|
||||
if ($dashletsOptions === null) {
|
||||
$dashletsOptions = $this->config->get('dashletsOptions', (object) []);
|
||||
}
|
||||
|
||||
$defaults['dashboardLayout'] = $dashboardLayout;
|
||||
$defaults['dashletsOptions'] = $dashletsOptions;
|
||||
|
||||
foreach ($fields as $field => $d) {
|
||||
if (array_key_exists('default', $d)) {
|
||||
$defaults[$field] = $d['default'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->defaultAttributeListFromSettings as $attr) {
|
||||
$defaults[$attr] = $this->config->get($attr);
|
||||
}
|
||||
|
||||
$this->data[$id] = $defaults;
|
||||
}
|
||||
|
||||
protected function fetchAutoFollowEntityTypeList(PreferencesEntity $entity): void
|
||||
{
|
||||
$id = $entity->getId();
|
||||
|
||||
$autoFollowEntityTypeList = [];
|
||||
|
||||
$autofollowList = $this->entityManager
|
||||
->getRDBRepository(Autofollow::ENTITY_TYPE)
|
||||
->select(['entityType'])
|
||||
->where([
|
||||
'userId' => $id,
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($autofollowList as $autofollow) {
|
||||
$autoFollowEntityTypeList[] = $autofollow->get('entityType');
|
||||
}
|
||||
|
||||
$this->data[$id]['autoFollowEntityTypeList'] = $autoFollowEntityTypeList;
|
||||
|
||||
$entity->set('autoFollowEntityTypeList', $autoFollowEntityTypeList);
|
||||
}
|
||||
|
||||
protected function storeAutoFollowEntityTypeList(Entity $entity): void
|
||||
{
|
||||
$id = $entity->getId();
|
||||
|
||||
if (!$entity->isAttributeChanged('autoFollowEntityTypeList')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entityTypeList = $entity->get('autoFollowEntityTypeList') ?? [];
|
||||
|
||||
$delete = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from(Autofollow::ENTITY_TYPE)
|
||||
->where([
|
||||
'userId' => $id,
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($delete);
|
||||
|
||||
$entityTypeList = array_filter($entityTypeList, function ($item) {
|
||||
return (bool) $this->metadata->get(['scopes', $item, 'stream']);
|
||||
});
|
||||
|
||||
foreach ($entityTypeList as $entityType) {
|
||||
$this->entityManager->createEntity(Autofollow::ENTITY_TYPE, [
|
||||
'userId' => $id,
|
||||
'entityType' => $entityType,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function save(Entity $entity, array $options = []): void
|
||||
{
|
||||
if (!$entity->hasId()) {
|
||||
throw new RuntimeException("ID is not set.");
|
||||
}
|
||||
|
||||
$this->data[$entity->getId()] = get_object_vars($entity->getValueMap());
|
||||
|
||||
$fields = $this->metadata->get('entityDefs.Preferences.fields');
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ($this->data[$entity->getId()] as $field => $value) {
|
||||
if (empty($fields[$field][FieldParam::NOT_STORABLE])) {
|
||||
$data[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$dataString = Json::encode($data, \JSON_PRETTY_PRINT);
|
||||
|
||||
$insert = $this->entityManager->getQueryBuilder()
|
||||
->insert()
|
||||
->into(PreferencesEntity::ENTITY_TYPE)
|
||||
->columns([Attribute::ID, 'data'])
|
||||
->values([
|
||||
Attribute::ID => $entity->getId(),
|
||||
'data' => $dataString,
|
||||
])
|
||||
->updateSet([
|
||||
'data' => $dataString,
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($insert);
|
||||
|
||||
/** @var User|null $user */
|
||||
$user = $this->entityManager->getEntityById(User::ENTITY_TYPE, $entity->getId());
|
||||
|
||||
if ($user && !$user->isPortal()) {
|
||||
$this->storeAutoFollowEntityTypeList($entity);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteFromDb(string $id): void
|
||||
{
|
||||
$delete = $this->entityManager->getQueryBuilder()
|
||||
->delete()
|
||||
->from(PreferencesEntity::ENTITY_TYPE)
|
||||
->where([
|
||||
'id' => $id,
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($delete);
|
||||
}
|
||||
|
||||
public function remove(Entity $entity, array $options = []): void
|
||||
{
|
||||
if (!$entity->hasId()) {
|
||||
throw new RuntimeException("ID is not set.");
|
||||
}
|
||||
|
||||
$this->deleteFromDb($entity->getId());
|
||||
|
||||
if (isset($this->data[$entity->getId()])) {
|
||||
unset($this->data[$entity->getId()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function resetToDefaults(string $userId): ?stdClass
|
||||
{
|
||||
$this->deleteFromDb($userId);
|
||||
|
||||
if (isset($this->data[$userId])) {
|
||||
unset($this->data[$userId]);
|
||||
}
|
||||
|
||||
$entity = $this->getById($userId);
|
||||
|
||||
if ($entity) {
|
||||
return $entity->getValueMap();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
60
application/Espo/Repositories/ScheduledJob.php
Normal file
60
application/Espo/Repositories/ScheduledJob.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Entities\Job as JobEntity;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Job\Job\Status;
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
/**
|
||||
* @extends Database<\Espo\Entities\ScheduledJob>
|
||||
*/
|
||||
class ScheduledJob extends Database
|
||||
{
|
||||
protected function afterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::afterSave($entity, $options);
|
||||
|
||||
if ($entity->isAttributeChanged('scheduling')) {
|
||||
$jobList = $this->entityManager
|
||||
->getRDBRepository(JobEntity::ENTITY_TYPE)
|
||||
->where([
|
||||
'scheduledJobId' => $entity->getId(),
|
||||
'status' => Status::PENDING,
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($jobList as $job) {
|
||||
$this->entityManager->removeEntity($job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
application/Espo/Repositories/Sms.php
Normal file
88
application/Espo/Repositories/Sms.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\Entities\Sms as SmsEntity;
|
||||
use Espo\Entities\PhoneNumber;
|
||||
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
/**
|
||||
* @extends Database<\Espo\Entities\Sms>
|
||||
*/
|
||||
class Sms extends Database
|
||||
{
|
||||
public function loadFromField(SmsEntity $entity): void
|
||||
{
|
||||
if ($entity->get('fromPhoneNumberName')) {
|
||||
$entity->set('from', $entity->get('fromPhoneNumberName'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$numberId = $entity->get('fromPhoneNumberId');
|
||||
|
||||
if ($numberId) {
|
||||
$phoneNumber = $this->entityManager
|
||||
->getRepository(PhoneNumber::ENTITY_TYPE)
|
||||
->getById($numberId);
|
||||
|
||||
if ($phoneNumber) {
|
||||
$entity->set('from', $phoneNumber->get(Field::NAME));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$entity->set('from', null);
|
||||
}
|
||||
|
||||
public function loadToField(SmsEntity $entity): void
|
||||
{
|
||||
$entity->loadLinkMultipleField('toPhoneNumbers');
|
||||
|
||||
$names = $entity->get('toPhoneNumbersNames');
|
||||
|
||||
if (empty($names)) {
|
||||
$entity->set('to', null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ($names as $address) {
|
||||
$list[] = $address;
|
||||
}
|
||||
|
||||
$entity->set('to', implode(';', $list));
|
||||
}
|
||||
}
|
||||
51
application/Espo/Repositories/UniqueId.php
Normal file
51
application/Espo/Repositories/UniqueId.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
/**
|
||||
* @extends Database<\Espo\Entities\UniqueId>
|
||||
*/
|
||||
class UniqueId extends Database
|
||||
{
|
||||
public function getNew(): Entity
|
||||
{
|
||||
$entity = parent::getNew();
|
||||
|
||||
$entity->set(Field::NAME, Util::generateMoreEntropyId());
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
143
application/Espo/Repositories/User.php
Normal file
143
application/Espo/Repositories/User.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Entities\Team;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Repositories\Database;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\Repositories\UserData as UserDataRepository;
|
||||
use Espo\Entities\UserData;
|
||||
use Espo\Entities\User as UserEntity;
|
||||
|
||||
/**
|
||||
* @extends Database<UserEntity>
|
||||
*/
|
||||
class User extends Database
|
||||
{
|
||||
private const AUTHENTICATION_METHOD_HMAC = 'Hmac';
|
||||
|
||||
/**
|
||||
* @param UserEntity $entity
|
||||
* @param array<string, mixed> $options
|
||||
* @return void
|
||||
*/
|
||||
protected function beforeSave(Entity $entity, array $options = [])
|
||||
{
|
||||
if ($entity->has('type') && !$entity->getType()) {
|
||||
$entity->set('type', UserEntity::TYPE_REGULAR);
|
||||
}
|
||||
|
||||
if ($entity->isApi()) {
|
||||
if ($entity->isAttributeChanged('userName')) {
|
||||
$entity->set('lastName', $entity->getUserName());
|
||||
}
|
||||
|
||||
if ($entity->has('authMethod') && $entity->getAuthMethod() !== self::AUTHENTICATION_METHOD_HMAC) {
|
||||
$entity->clear('secretKey');
|
||||
}
|
||||
} else {
|
||||
if ($entity->isAttributeChanged('type')) {
|
||||
$entity->set('authMethod', null);
|
||||
}
|
||||
}
|
||||
|
||||
parent::beforeSave($entity, $options);
|
||||
|
||||
if ($entity->has('type') && !$entity->isPortal()) {
|
||||
$entity->set('portalRolesIds', []);
|
||||
$entity->set('portalRolesNames', (object) []);
|
||||
$entity->set('portalsIds', []);
|
||||
$entity->set('portalsNames', (object) []);
|
||||
}
|
||||
|
||||
if ($entity->has('type') && $entity->isPortal()) {
|
||||
$entity->set('rolesIds', []);
|
||||
$entity->set('rolesNames', (object) []);
|
||||
$entity->set('teamsIds', []);
|
||||
$entity->set('teamsNames', (object) []);
|
||||
$entity->set('defaultTeamId', null);
|
||||
$entity->set('defaultTeamName', null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
* @return void
|
||||
*/
|
||||
protected function afterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
if ($this->entityManager->getLocker()->isLocked()) {
|
||||
$this->entityManager->getLocker()->commit();
|
||||
}
|
||||
|
||||
parent::afterSave($entity, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
* @return void
|
||||
*/
|
||||
protected function afterRemove(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::afterRemove($entity, $options);
|
||||
|
||||
$userData = $this->getUserDataRepository()->getByUserId($entity->getId());
|
||||
|
||||
if ($userData) {
|
||||
$this->entityManager->removeEntity($userData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $teamIds
|
||||
*/
|
||||
public function checkBelongsToAnyOfTeams(string $userId, array $teamIds): bool
|
||||
{
|
||||
if ($teamIds === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $this->entityManager
|
||||
->getRDBRepository(Team::RELATIONSHIP_TEAM_USER)
|
||||
->where([
|
||||
Attribute::DELETED => false,
|
||||
'userId' => $userId,
|
||||
'teamId' => $teamIds,
|
||||
])
|
||||
->findOne();
|
||||
}
|
||||
|
||||
private function getUserDataRepository(): UserDataRepository
|
||||
{
|
||||
/** @var UserDataRepository */
|
||||
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
73
application/Espo/Repositories/UserData.php
Normal file
73
application/Espo/Repositories/UserData.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Core\ORM\Repository\Option\SaveOption;
|
||||
use Espo\Entities\User as UserEntity;
|
||||
use Espo\Entities\UserData as UserDataEntity;
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
/**
|
||||
* @internal Use Espo\Tools\User\UserDataProvider.
|
||||
* @extends Database<UserDataEntity>
|
||||
*/
|
||||
class UserData extends Database
|
||||
{
|
||||
public function getByUserId(string $userId): ?UserDataEntity
|
||||
{
|
||||
/** @var ?UserDataEntity $userData */
|
||||
$userData = $this
|
||||
->where(['userId' => $userId])
|
||||
->findOne();
|
||||
|
||||
if ($userData) {
|
||||
return $userData;
|
||||
}
|
||||
|
||||
$user = $this->entityManager
|
||||
->getRepository(UserEntity::ENTITY_TYPE)
|
||||
->getById($userId);
|
||||
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$userData = $this->getNew();
|
||||
|
||||
$userData->set('userId', $userId);
|
||||
|
||||
$this->save($userData, [
|
||||
SaveOption::SILENT => true,
|
||||
SaveOption::SKIP_HOOKS => true,
|
||||
]);
|
||||
|
||||
return $userData;
|
||||
}
|
||||
}
|
||||
95
application/Espo/Repositories/Webhook.php
Normal file
95
application/Espo/Repositories/Webhook.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
/**
|
||||
* @extends Database<\Espo\Entities\Webhook>
|
||||
*/
|
||||
class Webhook extends Database
|
||||
{
|
||||
protected function beforeSave(Entity $entity, array $options = [])
|
||||
{
|
||||
if ($entity->isNew()) {
|
||||
$this->fillSecretKey($entity);
|
||||
}
|
||||
|
||||
parent::beforeSave($entity);
|
||||
|
||||
$this->processSettingAdditionalFields($entity);
|
||||
}
|
||||
|
||||
protected function fillSecretKey(Entity $entity): void
|
||||
{
|
||||
$secretKey = Util::generateSecretKey();
|
||||
|
||||
$entity->set('secretKey', $secretKey);
|
||||
}
|
||||
|
||||
protected function processSettingAdditionalFields(Entity $entity): void
|
||||
{
|
||||
$event = $entity->get('event');
|
||||
|
||||
if (!$event) {
|
||||
return;
|
||||
}
|
||||
|
||||
$arr = explode('.', $event);
|
||||
|
||||
if (count($arr) !== 2 && count($arr) !== 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entityType = $arr[0];
|
||||
$type = $arr[1];
|
||||
|
||||
$entity->set('entityType', $entityType);
|
||||
$entity->set('type', $type);
|
||||
|
||||
$field = null;
|
||||
|
||||
if (!$entityType) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($type === 'fieldUpdate') {
|
||||
if (count($arr) == 3) {
|
||||
$field = $arr[2];
|
||||
}
|
||||
|
||||
$entity->set('field', $field);
|
||||
} else {
|
||||
$entity->set('field', null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user