Initial commit

This commit is contained in:
root
2026-01-19 17:44:46 +01:00
commit 823af8b11d
8721 changed files with 1130846 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
<?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\Modules\Crm;
use Espo\Core\Binding\Binder;
use Espo\Core\Binding\BindingProcessor;
class Binding implements BindingProcessor
{
public function process(Binder $binder): void
{
$binder->bindImplementation(
'Espo\\Modules\\Crm\\Tools\\MassEmail\\MessageHeadersPreparator',
'Espo\\Modules\\Crm\\Tools\\MassEmail\\DefaultMessageHeadersPreparator'
);
$binder->bindImplementation(
'Espo\\Modules\\Crm\\Tools\\EmailAddress\\FreeDomainChecker',
'Espo\\Modules\\Crm\\Tools\\EmailAddress\\DefaultFreeDomainChecker'
);
}
}

View File

@@ -0,0 +1,121 @@
<?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\Modules\Crm\Business\Distribution\Lead;
use Espo\Modules\Crm\Entities\Lead;
use Espo\ORM\EntityManager;
use Espo\Entities\User;
use Espo\Entities\Team;
class LeastBusy
{
/**
* @var EntityManager
*/
protected $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* @param Team $team
* @param ?string $targetUserPosition
* @return ?User
*/
public function getUser($team, $targetUserPosition = null)
{
$where = [
'isActive' => true,
];
if (!empty($targetUserPosition)) {
$where['@relation.role'] = $targetUserPosition;
}
/**
* @var \Espo\ORM\Collection<User> $userList
*/
$userList = $this->entityManager
->getRDBRepository(Team::ENTITY_TYPE)
->getRelation($team, 'users')
->where($where)
->order('id')
->find();
if (is_countable($userList) && count($userList) == 0) {
return null;
}
$countHash = [];
foreach ($userList as $user) {
$where = [
'assignedUserId' => $user->getId(),
'status<>' => [
Lead::STATUS_CONVERTED,
Lead::STATUS_RECYCLED,
Lead::STATUS_DEAD,
],
];
$count = $this->entityManager
->getRDBRepository(Lead::ENTITY_TYPE)
->where($where)
->count();
$countHash[$user->getId()] = $count;
}
$foundUserId = false;
$min = false;
foreach ($countHash as $userId => $count) {
if ($min === false) {
$min = $count;
$foundUserId = $userId;
} else {
if ($count < $min) {
$min = $count;
$foundUserId = $userId;
}
}
}
if ($foundUserId !== false) {
return $this->entityManager->getEntityById(User::ENTITY_TYPE, $foundUserId);
}
return null;
}
}

View File

@@ -0,0 +1,108 @@
<?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\Modules\Crm\Business\Distribution\Lead;
use Espo\Core\Name\Field;
use Espo\Modules\Crm\Entities\Lead;
use Espo\ORM\EntityManager;
use Espo\Entities\User;
use Espo\Entities\Team;
class RoundRobin
{
/**
* @var EntityManager
*/
protected $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* @param Team $team
* @param ?string $targetUserPosition
* @return User|null
*/
public function getUser($team, $targetUserPosition = null)
{
$where = [
'isActive' => true,
];
if (!empty($targetUserPosition)) {
$where['@relation.role'] = $targetUserPosition;
}
/**
* @var \Espo\ORM\Collection<User> $userList
*/
$userList = $this->entityManager
->getRDBRepository(Team::ENTITY_TYPE)
->getRelation($team, 'users')
->where($where)
->order('id')
->find();
if (is_countable($userList) && count($userList) == 0) {
return null;
}
$userIdList = [];
foreach ($userList as $user) {
$userIdList[] = $user->getId();
}
$lead = $this->entityManager
->getRDBRepository(Lead::ENTITY_TYPE)
->where([
'assignedUserId' => $userIdList
])
->order(Field::CREATED_AT, 'DESC')
->findOne();
if (empty($lead)) {
$num = 0;
} else {
$num = array_search($lead->get('assignedUserId'), $userIdList);
if ($num === false || $num == count($userIdList) - 1) {
$num = 0;
} else {
$num++;
}
}
return $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userIdList[$num]);
}
}

View File

@@ -0,0 +1,176 @@
<?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\Modules\Crm\Business\Event;
use RuntimeException;
class Ics
{
public const STATUS_CONFIRMED = 'CONFIRMED';
public const STATUS_TENTATIVE = 'TENTATIVE';
public const STATUS_CANCELLED = 'CANCELLED';
public const METHOD_REQUEST = 'REQUEST';
public const METHOD_CANCEL = 'CANCEL';
/** @var self::METHOD_* string */
private string $method;
private ?string $output = null;
private string $prodid;
private ?int $startDate = null;
private ?int $endDate = null;
private ?string $summary = null;
private ?string $address = null;
private ?string $description = null;
private ?string $uid = null;
/** @var self::STATUS_* string */
private string $status;
private ?int $stamp = null;
/** @var array{string, ?string}|null */
private ?array $organizer = null;
/** @var array{string, ?string}[] */
private array $attendees = [];
/**
* @param array{
* organizer?: array{string, ?string}|null,
* attendees?: array{string, ?string}[],
* startDate?: ?int,
* endDate?: ?int,
* summary?: ?string,
* address?: ?string,
* description?: ?string,
* uid?: ?string,
* status?: self::STATUS_CONFIRMED|self::STATUS_TENTATIVE|self::STATUS_CANCELLED,
* method?: self::METHOD_REQUEST|self::METHOD_CANCEL,
* stamp?: ?int,
* } $attributes
*/
public function __construct(string $prodid, array $attributes = [])
{
if ($prodid === '') {
throw new RuntimeException('PRODID is required');
}
$this->status = self::STATUS_CONFIRMED;
$this->method = self::METHOD_REQUEST;
$this->prodid = $prodid;
foreach ($attributes as $key => $value) {
if (!property_exists($this, $key)) {
throw new RuntimeException("Bad attribute '$key'.");
}
$this->$key = $value;
}
}
public function get(): string
{
if ($this->output === null) {
$this->generate();
}
/** @var string */
return $this->output;
}
/** @noinspection SpellCheckingInspection */
private function generate(): void
{
$start =
"BEGIN:VCALENDAR\r\n" .
"VERSION:2.0\r\n" .
"PRODID:-$this->prodid\r\n" .
"METHOD:$this->method\r\n" .
"BEGIN:VEVENT\r\n";
$organizerPart = '';
if ($this->organizer) {
$organizerPart = "ORGANIZER;{$this->preparePerson($this->organizer[0], $this->organizer[1])}";
}
$body =
"DTSTART:{$this->formatTimestamp($this->startDate)}\r\n" .
"DTEND:{$this->formatTimestamp($this->endDate)}\r\n" .
"SUMMARY:{$this->escapeString($this->summary)}\r\n" .
"LOCATION:{$this->escapeString($this->address)}\r\n" .
$organizerPart .
"DESCRIPTION:{$this->escapeString($this->formatMultiline($this->description))}\r\n" .
"UID:$this->uid\r\n" .
"SEQUENCE:0\r\n" .
"DTSTAMP:{$this->formatTimestamp($this->stamp ?? time())}\r\n" .
"STATUS:$this->status\r\n";
foreach ($this->attendees as $attendee) {
$body .= "ATTENDEE;{$this->preparePerson($attendee[0], $attendee[1])}";
}
$end =
"END:VEVENT\r\n".
"END:VCALENDAR";
$this->output = $start . $body . $end;
}
private function preparePerson(string $address, ?string $name): string
{
return "CN={$this->escapeString($name)}:MAILTO:{$this->escapeString($address)}\r\n";
}
private function formatTimestamp(?int $timestamp): string
{
if (!$timestamp) {
$timestamp = time();
}
return date('Ymd\THis\Z', $timestamp);
}
private function escapeString(?string $string): string
{
if (!$string) {
return '';
}
/** @var string */
return preg_replace('/([,;])/', '\\\$1', $string);
}
private function formatMultiline(?string $string): string
{
if (!$string) {
return '';
}
return str_replace(["\r\n", "\n"], "\\n", $string);
}
}

View File

@@ -0,0 +1,396 @@
<?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\Modules\Crm\Business\Event;
use Espo\Core\Field\DateTime as DateTimeField;
use Espo\Core\Field\LinkParent;
use Espo\Core\Mail\Exceptions\SendingError;
use Espo\Core\Name\Field;
use Espo\Core\Utils\Config\ApplicationConfig;
use Espo\Entities\Attachment;
use Espo\Entities\Email;
use Espo\Entities\Preferences;
use Espo\Entities\UniqueId;
use Espo\Modules\Crm\Entities\Call;
use Espo\Modules\Crm\Entities\Contact;
use Espo\Modules\Crm\Entities\Lead;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\ORM\Entity;
use Espo\Entities\User;
use Espo\Core\Utils\Util;
use Espo\Core\Htmlizer\HtmlizerFactory as HtmlizerFactory;
use Espo\Core\Mail\EmailSender;
use Espo\Core\Mail\SmtpParams;
use Espo\Core\ORM\EntityManager;
use Espo\Core\Utils\DateTime as DateTimeUtil;
use Espo\Core\Utils\Language;
use Espo\Core\Utils\TemplateFileManager;
use DateTime;
/**
* Do not use. Use `Espo\Modules\Crm\Tools\Meeting\Invitation\Sender`.
* @internal
*/
class Invitations
{
private const TYPE_INVITATION = 'invitation';
private const TYPE_CANCELLATION = 'cancellation';
/**
* Some dependencies are unused to keep backward compatibility.
* @todo Revise.
*/
public function __construct(
private EntityManager $entityManager,
private ?SmtpParams $smtpParams,
private EmailSender $emailSender,
private Language $language,
private TemplateFileManager $templateFileManager,
private HtmlizerFactory $htmlizerFactory,
private ApplicationConfig $applicationConfig,
private DateTimeUtil $dateTime,
) {}
/**
* @throws SendingError
*/
public function sendInvitation(Entity $entity, Entity $invitee, string $link, ?string $emailAddress = null): void
{
$this->sendInternal($entity, $invitee, $link, self::TYPE_INVITATION, $emailAddress);
}
/**
* @throws SendingError
*/
public function sendCancellation(Entity $entity, Entity $invitee, string $link, ?string $emailAddress = null): void
{
$this->sendInternal($entity, $invitee, $link, self::TYPE_CANCELLATION, $emailAddress);
}
/**
* @throws SendingError
*/
private function sendInternal(
Entity $entity,
Entity $invitee,
string $link,
string $type,
?string $emailAddress,
): void {
$uid = $type === self::TYPE_INVITATION ? $this->createUniqueId($entity, $invitee, $link) : null;
/** @var ?string $emailAddress */
$emailAddress ??= $invitee->get(Field::EMAIL_ADDRESS);
if (!$emailAddress) {
return;
}
$htmlizer = $invitee instanceof User ?
$this->htmlizerFactory->createForUser($invitee) :
$this->htmlizerFactory->createNoAcl();
$data = $this->prepareData($entity, $uid, $invitee);
$subjectTpl = $this->templateFileManager->getTemplate($type, 'subject', $entity->getEntityType(), 'Crm');
$subjectTpl = str_replace(["\n", "\r"], '', $subjectTpl);
$bodyTpl = $this->templateFileManager->getTemplate($type, 'body', $entity->getEntityType(), 'Crm');
$subject = $htmlizer->render(
$entity,
$subjectTpl,
"$type-email-subject-{$entity->getEntityType()}",
$data,
true,
true
);
$body = $htmlizer->render(
$entity,
$bodyTpl,
"$type-email-body-{$entity->getEntityType()}",
$data,
false,
true
);
$email = $this->entityManager->getRDBRepositoryByClass(Email::class)->getNew();
$email
->addToAddress($emailAddress)
->setSubject($subject)
->setBody($body)
->setIsHtml()
->setParent(LinkParent::createFromEntity($entity));
$attachmentName = ucwords($this->language->translateLabel($entity->getEntityType(), 'scopeNames')) . '.ics';
$attachment = $this->entityManager->getRDBRepositoryByClass(Attachment::class)->getNew();
$attachment
->setName($attachmentName)
->setType('text/calendar')
->setContents($this->getIcsContents($entity, $type));
$sender = $this->emailSender->create();
if ($this->smtpParams) {
$sender->withSmtpParams($this->smtpParams);
}
$sender
->withAttachments([$attachment])
->send($email);
}
private function createUniqueId(Entity $entity, Entity $invitee, string $link): UniqueId
{
$uid = $this->entityManager->getRDBRepositoryByClass(UniqueId::class)->getNew();
$uid->setData([
'eventType' => $entity->getEntityType(),
'eventId' => $entity->getId(),
'inviteeId' => $invitee->getId(),
'inviteeType' => $invitee->getEntityType(),
'link' => $link,
'dateStart' => $entity->get('dateStart'),
]);
if ($entity->get('dateEnd')) {
$terminateAt = $entity->get('dateEnd');
} else {
$dt = new DateTime();
$dt->modify('+1 month');
$terminateAt = $dt->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
}
$uid->setTarget(LinkParent::createFromEntity($entity));
$uid->setTerminateAt(DateTimeField::fromString($terminateAt));
$this->entityManager->saveEntity($uid);
return $uid;
}
protected function getIcsContents(Entity $entity, string $type): string
{
/** @var ?User $user */
$user = $this->entityManager
->getRelation($entity, Field::ASSIGNED_USER)
->findOne();
$addressList = [];
$organizerName = null;
$organizerAddress = null;
if ($user) {
$organizerName = $user->getName();
$organizerAddress = $user->getEmailAddress();
if ($organizerAddress) {
$addressList[] = $organizerAddress;
}
}
$status = $type === self::TYPE_CANCELLATION ?
Ics::STATUS_CANCELLED :
Ics::STATUS_CONFIRMED;
$method = $type === self::TYPE_CANCELLATION ?
Ics::METHOD_CANCEL :
Ics::METHOD_REQUEST;
$attendees = [];
$uid = $entity->getId();
if ($entity instanceof Meeting || $entity instanceof Call) {
$attendees = $this->getAttendees($entity, $addressList);
$uid = $entity->getUid() ?? $uid;
}
$ics = new Ics('//EspoCRM//EspoCRM Calendar//EN', [
'method' => $method,
'status' => $status,
'startDate' => strtotime($entity->get('dateStart')),
'endDate' => strtotime($entity->get('dateEnd')),
'uid' => $uid,
'summary' => $entity->get(Field::NAME),
'organizer' => $organizerAddress ? [$organizerAddress, $organizerName] : null,
'attendees' => $attendees,
'description' => $entity->get('description'),
]);
return $ics->get();
}
/**
* @return array<string, mixed>
*/
private function prepareData(Entity $entity, ?UniqueId $uid, Entity $invitee): array
{
$data = [];
$siteUrl = $this->applicationConfig->getSiteUrl();
$data['recordUrl'] = "$siteUrl/#{$entity->getEntityType()}/view/{$entity->getId()}";
if ($uid) {
$part = "$siteUrl?entryPoint=eventConfirmation&action=";
$data['acceptLink'] = $part . 'accept&uid=' . $uid->getIdValue();
$data['declineLink'] = $part . 'decline&uid=' . $uid->getIdValue();
$data['tentativeLink'] = $part . 'tentative&uid=' . $uid->getIdValue();
}
if ($invitee instanceof User) {
$data['isUser'] = true;
}
$data['inviteeName'] = $invitee->get(Field::NAME);
$data['entityType'] = $this->language->translateLabel($entity->getEntityType(), 'scopeNames');
$data['entityTypeLowerFirst'] = Util::mbLowerCaseFirst($data['entityType']);
[$timeZone, $language] = $this->getTimeZoneAndLanguage($invitee);
$data['timeZone'] = $timeZone;
$data['dateStartFull'] = $this->prepareDateStartFull($entity, $timeZone, $language);
return $data;
}
/**
* @param string[] $addressList
* @return array{string, ?string}[]
*/
private function getAttendees(Meeting|Call $entity, array $addressList): array
{
$attendees = [];
/** @var iterable<User> $users */
$users = $this->entityManager
->getRelation($entity, Meeting::LINK_USERS)
->find();
foreach ($users as $it) {
$address = $it->getEmailAddress();
if ($address && !in_array($address, $addressList)) {
$addressList[] = $address;
$attendees[] = [$address, $it->getName()];
}
}
/** @var iterable<Contact> $contacts */
$contacts = $this->entityManager
->getRelation($entity, Meeting::LINK_CONTACTS)
->find();
foreach ($contacts as $it) {
$address = $it->getEmailAddress();
if ($address && !in_array($address, $addressList)) {
$addressList[] = $address;
$attendees[] = [$address, $it->getName()];
}
}
/** @var iterable<Lead> $leads */
$leads = $this->entityManager
->getRelation($entity, Meeting::LINK_LEADS)
->find();
foreach ($leads as $it) {
$address = $it->getEmailAddress();
if ($address && !in_array($address, $addressList)) {
$addressList[] = $address;
$attendees[] = [$address, $it->getName()];
}
}
return $attendees;
}
/**
* @return array{string, string}
*/
private function getTimeZoneAndLanguage(Entity $invitee): array
{
$timeZone = $this->applicationConfig->getTimeZone();
$language = $this->applicationConfig->getLanguage();
if ($invitee instanceof User) {
$preferences = $this->entityManager
->getRepositoryByClass(Preferences::class)
->getById($invitee->getId());
if ($preferences && $preferences->getTimeZone()) {
$timeZone = $preferences->getTimeZone();
}
if ($preferences && $preferences->getLanguage()) {
$language = $preferences->getLanguage();
}
}
return [$timeZone, $language];
}
/**
* @todo Take into account the invitees time format if a user.
*/
private function prepareDateStartFull(Entity $entity, string $timeZone, string $language): ?string
{
$format = "dddd, MMMM Do, YYYY";
if ($entity->get('dateStartDate')) {
$value = $entity->get('dateStartDate');
return $this->dateTime->convertSystemDate($value, $format, $language);
}
$value = $entity->get('dateStart');
if (!$value) {
return null;
}
$format = $this->applicationConfig->getTimeFormat() . ", " . $format;
return $this->dateTime->convertSystemDateTime($value, $timeZone, $format, $language);
}
}

View File

@@ -0,0 +1,87 @@
<?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\Modules\Crm\Classes\Acl\Call;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Call;
use Espo\ORM\Entity;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\Core\Acl\AccessEntityCREDSChecker;
use Espo\Core\Acl\DefaultAccessChecker;
use Espo\Core\Acl\ScopeData;
use Espo\Core\Acl\Table;
use Espo\Core\Acl\Traits\DefaultAccessCheckerDependency;
/**
* @implements AccessEntityCREDSChecker<Call>
*/
class AccessChecker implements AccessEntityCREDSChecker
{
use DefaultAccessCheckerDependency;
public function __construct(DefaultAccessChecker $defaultAccessChecker)
{
$this->defaultAccessChecker = $defaultAccessChecker;
}
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
{
if ($this->defaultAccessChecker->checkEntityRead($user, $entity, $data)) {
return true;
}
assert($entity instanceof CoreEntity);
if ($data->getRead() === Table::LEVEL_OWN || $data->getRead() === Table::LEVEL_TEAM) {
if ($entity->hasLinkMultipleId('users', $user->getId())) {
return true;
}
}
return false;
}
public function checkEntityStream(User $user, Entity $entity, ScopeData $data): bool
{
if ($this->defaultAccessChecker->checkEntityStream($user, $entity, $data)) {
return true;
}
assert($entity instanceof CoreEntity);
if ($data->getStream() === Table::LEVEL_OWN || $data->getRead() === Table::LEVEL_TEAM) {
if ($entity->hasLinkMultipleId('users', $user->getId())) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,82 @@
<?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\Modules\Crm\Classes\Acl\CampaignLogRecord;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\CampaignLogRecord;
use Espo\ORM\Entity;
use Espo\Core\Acl\OwnershipOwnChecker;
use Espo\Core\Acl\OwnershipTeamChecker;
use Espo\Core\AclManager;
use Espo\Core\ORM\EntityManager;
/**
* @implements OwnershipOwnChecker<CampaignLogRecord>
* @implements OwnershipTeamChecker<CampaignLogRecord>
*/
class OwnershipChecker implements OwnershipOwnChecker, OwnershipTeamChecker
{
public function __construct(private AclManager $aclManager, private EntityManager $entityManager)
{}
public function checkOwn(User $user, Entity $entity): bool
{
$campaignId = $entity->get('campaignId');
if (!$campaignId) {
return false;
}
$campaign = $this->entityManager->getEntityById('Campaign', $campaignId);
if ($campaign && $this->aclManager->checkOwnershipOwn($user, $campaign)) {
return true;
}
return false;
}
public function checkTeam(User $user, Entity $entity): bool
{
$campaignId = $entity->get('campaignId');
if (!$campaignId) {
return false;
}
$campaign = $this->entityManager->getEntityById('Campaign', $campaignId);
if ($campaign && $this->aclManager->checkOwnershipTeam($user, $campaign)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,82 @@
<?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\Modules\Crm\Classes\Acl\CampaignTrackingUrl;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\CampaignTrackingUrl;
use Espo\ORM\Entity;
use Espo\Core\Acl\OwnershipOwnChecker;
use Espo\Core\Acl\OwnershipTeamChecker;
use Espo\Core\AclManager;
use Espo\Core\ORM\EntityManager;
/**
* @implements OwnershipOwnChecker<CampaignTrackingUrl>
* @implements OwnershipTeamChecker<CampaignTrackingUrl>
*/
class OwnershipChecker implements OwnershipOwnChecker, OwnershipTeamChecker
{
public function __construct(private AclManager $aclManager, private EntityManager $entityManager)
{}
public function checkOwn(User $user, Entity $entity): bool
{
$campaignId = $entity->get('campaignId');
if (!$campaignId) {
return false;
}
$campaign = $this->entityManager->getEntityById('Campaign', $campaignId);
if ($campaign && $this->aclManager->checkOwnershipOwn($user, $campaign)) {
return true;
}
return false;
}
public function checkTeam(User $user, Entity $entity): bool
{
$campaignId = $entity->get('campaignId');
if (!$campaignId) {
return false;
}
$campaign = $this->entityManager->getEntityById('Campaign', $campaignId);
if ($campaign && $this->aclManager->checkOwnershipTeam($user, $campaign)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,89 @@
<?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\Modules\Crm\Classes\Acl\Case\LinkCheckers;
use Espo\Core\Acl\LinkChecker;
use Espo\Core\AclManager;
use Espo\Entities\Email;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Account;
use Espo\Modules\Crm\Entities\CaseObj;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements LinkChecker<CaseObj, Account>
* @noinspection PhpUnused
*/
class AccountLinkChecker implements LinkChecker
{
public function __construct(
private AclManager $aclManager,
private EntityManager $entityManager
) {}
public function check(User $user, Entity $entity, Entity $foreignEntity): bool
{
if ($this->aclManager->checkEntityRead($user, $foreignEntity)) {
return true;
}
if (!$entity->isNew()) {
return false;
}
$emailId = $entity->get('originalEmailId');
if (!$emailId) {
return false;
}
$email = $this->entityManager->getRepositoryByClass(Email::class)->getById($emailId);
if (!$email) {
return false;
}
$parent = $email->getParent();
if (!$parent) {
return false;
}
if (
$parent->getEntityType() !== Account::ENTITY_TYPE ||
$parent->getId() !== $foreignEntity->getId()
) {
return false;
}
return $this->aclManager->checkEntityRead($user, $email);
}
}

View File

@@ -0,0 +1,89 @@
<?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\Modules\Crm\Classes\Acl\Case\LinkCheckers;
use Espo\Core\Acl\LinkChecker;
use Espo\Core\AclManager;
use Espo\Entities\Email;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\CaseObj;
use Espo\Modules\Crm\Entities\Contact;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements LinkChecker<CaseObj, Contact>
* @noinspection PhpUnused
*/
class ContactLinkChecker implements LinkChecker
{
public function __construct(
private AclManager $aclManager,
private EntityManager $entityManager
) {}
public function check(User $user, Entity $entity, Entity $foreignEntity): bool
{
if ($this->aclManager->checkEntityRead($user, $foreignEntity)) {
return true;
}
if (!$entity->isNew()) {
return false;
}
$emailId = $entity->get('originalEmailId');
if (!$emailId) {
return false;
}
$email = $this->entityManager->getRepositoryByClass(Email::class)->getById($emailId);
if (!$email) {
return false;
}
$parent = $email->getParent();
if (!$parent) {
return false;
}
if (
$parent->getEntityType() !== Contact::ENTITY_TYPE ||
$parent->getId() !== $foreignEntity->getId()
) {
return false;
}
return $this->aclManager->checkEntityRead($user, $email);
}
}

View File

@@ -0,0 +1,90 @@
<?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\Modules\Crm\Classes\Acl\Case\LinkCheckers;
use Espo\Core\Acl\LinkChecker;
use Espo\Core\AclManager;
use Espo\Entities\Email;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\CaseObj;
use Espo\Modules\Crm\Entities\Lead;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements LinkChecker<CaseObj, Lead>
* @noinspection PhpUnused
*/
class LeadLinkChecker implements LinkChecker
{
public function __construct(
private AclManager $aclManager,
private EntityManager $entityManager
) {}
public function check(User $user, Entity $entity, Entity $foreignEntity): bool
{
if ($this->aclManager->checkEntityRead($user, $foreignEntity)) {
return true;
}
if (!$entity->isNew()) {
return false;
}
$emailId = $entity->get('originalEmailId');
if (!$emailId) {
return false;
}
$email = $this->entityManager->getRepositoryByClass(Email::class)->getById($emailId);
if (!$email) {
return false;
}
$parent = $email->getParent();
if (!$parent) {
return false;
}
if (
$parent->getEntityType() !== Lead::ENTITY_TYPE ||
$parent->getId() !== $foreignEntity->getId()
) {
return false;
}
return $this->aclManager->checkEntityRead($user, $email);
}
}

View File

@@ -0,0 +1,94 @@
<?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\Modules\Crm\Classes\Acl\MassEmail;
use Espo\Core\Acl\AccessEntityCREDChecker;
use Espo\Core\Acl\DefaultAccessChecker;
use Espo\Core\Acl\ScopeData;
use Espo\Core\Acl\Traits\DefaultAccessCheckerDependency;
use Espo\Core\AclManager;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\MassEmail;
use Espo\ORM\Entity;
/**
* @implements AccessEntityCREDChecker<MassEmail>
*/
class AccessChecker implements AccessEntityCREDChecker
{
use DefaultAccessCheckerDependency;
public function __construct(
DefaultAccessChecker $defaultAccessChecker,
private AclManager $aclManager,
) {
$this->defaultAccessChecker = $defaultAccessChecker;
}
public function checkCreate(User $user, ScopeData $data): bool
{
return $this->checkEdit($user, $data);
}
public function checkDelete(User $user, ScopeData $data): bool
{
return $this->checkEdit($user, $data) || $this->defaultAccessChecker->checkDelete($user, $data);
}
public function checkEntityCreate(User $user, Entity $entity, ScopeData $data): bool
{
if ($this->defaultAccessChecker->checkEntityCreate($user, $entity, $data)) {
return true;
}
$campaign = $entity->getCampaign();
if ($campaign && $this->aclManager->checkEntityEdit($user, $campaign)) {
return true;
}
return false;
}
public function checkEntityDelete(User $user, Entity $entity, ScopeData $data): bool
{
if ($this->defaultAccessChecker->checkEntityDelete($user, $entity, $data)) {
return true;
}
$campaign = $entity->getCampaign();
if ($campaign && $this->aclManager->checkEntityEdit($user, $campaign)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,48 @@
<?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\Modules\Crm\Classes\Acl\MassEmail\LinkCheckers;
use Espo\Core\Acl\LinkChecker;
use Espo\Entities\InboundEmail;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\MassEmail;
use Espo\ORM\Entity;
/**
* @implements LinkChecker<MassEmail, InboundEmail>
* @noinspection PhpUnused
*/
class InboundEmailLinkChecker implements LinkChecker
{
public function check(User $user, Entity $entity, Entity $foreignEntity): bool
{
return $foreignEntity->smtpIsForMassEmail();
}
}

View File

@@ -0,0 +1,70 @@
<?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\Modules\Crm\Classes\Acl\MassEmail;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\MassEmail;
use Espo\ORM\Entity;
use Espo\Core\Acl\OwnershipOwnChecker;
use Espo\Core\Acl\OwnershipTeamChecker;
use Espo\Core\AclManager;
/**
* @implements OwnershipOwnChecker<MassEmail>
* @implements OwnershipTeamChecker<MassEmail>
*/
class OwnershipChecker implements OwnershipOwnChecker, OwnershipTeamChecker
{
public function __construct(
private AclManager $aclManager,
) {}
public function checkOwn(User $user, Entity $entity): bool
{
$campaign = $entity->getCampaign();
if ($campaign && $this->aclManager->checkOwnershipOwn($user, $campaign)) {
return true;
}
return false;
}
public function checkTeam(User $user, Entity $entity): bool
{
$campaign = $entity->getCampaign();
if ($campaign && $this->aclManager->checkOwnershipTeam($user, $campaign)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,87 @@
<?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\Modules\Crm\Classes\Acl\Meeting;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\ORM\Entity;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\Core\Acl\AccessEntityCREDSChecker;
use Espo\Core\Acl\DefaultAccessChecker;
use Espo\Core\Acl\ScopeData;
use Espo\Core\Acl\Table;
use Espo\Core\Acl\Traits\DefaultAccessCheckerDependency;
/**
* @implements AccessEntityCREDSChecker<Meeting>
*/
class AccessChecker implements AccessEntityCREDSChecker
{
use DefaultAccessCheckerDependency;
public function __construct(DefaultAccessChecker $defaultAccessChecker)
{
$this->defaultAccessChecker = $defaultAccessChecker;
}
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
{
if ($this->defaultAccessChecker->checkEntityRead($user, $entity, $data)) {
return true;
}
assert($entity instanceof CoreEntity);
if ($data->getRead() === Table::LEVEL_OWN || $data->getRead() === Table::LEVEL_TEAM) {
if ($entity->hasLinkMultipleId('users', $user->getId())) {
return true;
}
}
return false;
}
public function checkEntityStream(User $user, Entity $entity, ScopeData $data): bool
{
if ($this->defaultAccessChecker->checkEntityStream($user, $entity, $data)) {
return true;
}
assert($entity instanceof CoreEntity);
if ($data->getStream() === Table::LEVEL_OWN || $data->getRead() === Table::LEVEL_TEAM) {
if ($entity->hasLinkMultipleId('users', $user->getId())) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,102 @@
<?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\Modules\Crm\Classes\Acl\Meeting;
use Espo\Core\Acl;
use Espo\Core\Acl\AssignmentChecker as AssignmentCheckerInterface;
use Espo\Core\Acl\DefaultAssignmentChecker;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Call;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use Espo\ORM\Name\Attribute;
/**
* @implements AssignmentCheckerInterface<Meeting|Call>
*/
class AssignmentChecker implements AssignmentCheckerInterface
{
public function __construct(
private DefaultAssignmentChecker $defaultAssignmentChecker,
private EntityManager $entityManager,
private Acl $acl
) {}
public function check(User $user, Entity $entity): bool
{
if (!$this->defaultAssignmentChecker->check($user, $entity)) {
return false;
}
$userIds = $this->getUserIds($entity);
foreach ($userIds as $userId) {
if (!$this->acl->checkAssignmentPermission($userId)) {
return false;
}
}
return true;
}
/**
* @return string[]
*/
private function getUserIds(Meeting|Call $entity): array
{
$userIdList = $entity->getUsers()->getIdList();
if ($entity->isNew()) {
return $userIdList;
}
$newIdList = [];
$existingIdList = [];
$usersCollection = $this->entityManager
->getRDBRepository($entity->getEntityType())
->getRelation($entity, 'users')
->select(Attribute::ID)
->find();
foreach ($usersCollection as $user) {
$existingIdList[] = $user->getId();
}
foreach ($userIdList as $id) {
if (!in_array($id, $existingIdList)) {
$newIdList[] = $id;
}
}
return $newIdList;
}
}

View File

@@ -0,0 +1,100 @@
<?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\Modules\Crm\Classes\Acl\Task\LinkCheckers;
use Espo\Core\Acl\LinkChecker;
use Espo\Core\AclManager;
use Espo\Entities\Email;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Account;
use Espo\Modules\Crm\Entities\Task;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements LinkChecker<Task, Account>
* @noinspection PhpUnused
*/
class AccountLinkChecker implements LinkChecker
{
public function __construct(
private AclManager $aclManager,
private EntityManager $entityManager
) {}
public function check(User $user, Entity $entity, Entity $foreignEntity): bool
{
if ($this->aclManager->checkEntityRead($user, $foreignEntity)) {
return true;
}
if (!$entity->isNew()) {
return false;
}
/** @var ?string $emailId */
$emailId = $entity->get('originalEmailId');
if (!$emailId) {
return false;
}
$email = $this->entityManager
->getRepositoryByClass(Email::class)
->getById($emailId);
if (!$email) {
return false;
}
if (
$email->getAccount() &&
$foreignEntity->getId() === $email->getAccount()->getId() &&
$this->aclManager->checkEntityRead($user, $email)
) {
return true;
}
$parent = $email->getParent();
if (!$parent) {
return false;
}
if (
$parent->getEntityType() !== Account::ENTITY_TYPE ||
$parent->getId() !== $foreignEntity->getId()
) {
return false;
}
return $this->aclManager->checkEntityRead($user, $email);
}
}

View File

@@ -0,0 +1,91 @@
<?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\Modules\Crm\Classes\Acl\Task\LinkCheckers;
use Espo\Core\Acl\LinkChecker;
use Espo\Core\AclManager;
use Espo\Entities\Email;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Task;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements LinkChecker<Task, Entity>
* @noinspection PhpUnused
*/
class ParentLinkChecker implements LinkChecker
{
public function __construct(
private AclManager $aclManager,
private EntityManager $entityManager
) {}
public function check(User $user, Entity $entity, Entity $foreignEntity): bool
{
if ($this->aclManager->checkEntityRead($user, $foreignEntity)) {
return true;
}
if (!$entity->isNew()) {
return false;
}
/** @var ?string $emailId */
$emailId = $entity->get('originalEmailId');
if (!$emailId) {
return false;
}
$email = $this->entityManager
->getRepositoryByClass(Email::class)
->getById($emailId);
if (!$email) {
return false;
}
$parent = $email->getParent();
if (!$parent) {
return false;
}
if (
$parent->getEntityType() !== $foreignEntity->getEntityType() ||
$parent->getId() !== $foreignEntity->getId()
) {
return false;
}
return $this->aclManager->checkEntityRead($user, $email);
}
}

View File

@@ -0,0 +1,54 @@
<?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\Modules\Crm\Classes\AclPortal\Account;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Account;
use Espo\ORM\Entity;
use Espo\Core\Portal\Acl\OwnershipAccountChecker;
/**
* @implements OwnershipAccountChecker<Account>
*/
class OwnershipChecker implements OwnershipAccountChecker
{
public function checkAccount(User $user, Entity $entity): bool
{
$accountIdList = $user->getLinkMultipleIdList('accounts');
if (in_array($entity->getId(), $accountIdList)) {
return true;
}
return false;
}
}

View 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\Modules\Crm\Classes\AclPortal\Case;
use Espo\Core\Acl\OwnershipOwnChecker;
use Espo\Core\Portal\Acl\DefaultOwnershipChecker;
use Espo\Core\Portal\Acl\OwnershipAccountChecker;
use Espo\Core\Portal\Acl\OwnershipContactChecker;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\CaseObj;
use Espo\ORM\Entity;
/**
* @implements OwnershipAccountChecker<CaseObj>
* @implements OwnershipOwnChecker<CaseObj>
* @implements OwnershipContactChecker<CaseObj>
*/
class OwnershipChecker implements OwnershipOwnChecker, OwnershipAccountChecker, OwnershipContactChecker
{
public function __construct(private DefaultOwnershipChecker $defaultOwnershipChecker) {}
public function checkAccount(User $user, Entity $entity): bool
{
if ($entity->isInternal()) {
return false;
}
return $this->defaultOwnershipChecker->checkAccount($user, $entity);
}
public function checkContact(User $user, Entity $entity): bool
{
if ($entity->isInternal()) {
return false;
}
return $this->defaultOwnershipChecker->checkContact($user, $entity);
}
public function checkOwn(User $user, Entity $entity): bool
{
if ($entity->isInternal()) {
return false;
}
return $this->defaultOwnershipChecker->checkOwn($user, $entity);
}
}

View File

@@ -0,0 +1,64 @@
<?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\Modules\Crm\Classes\AclPortal\Contact;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Contact;
use Espo\ORM\Entity;
use Espo\Core\Portal\Acl\DefaultOwnershipChecker;
use Espo\Core\Portal\Acl\OwnershipAccountChecker;
use Espo\Core\Portal\Acl\OwnershipContactChecker;
/**
* @implements OwnershipAccountChecker<Contact>
* @implements OwnershipContactChecker<Contact>
*/
class OwnershipChecker implements OwnershipAccountChecker, OwnershipContactChecker
{
public function __construct(private DefaultOwnershipChecker $defaultOwnershipChecker) {}
public function checkContact(User $user, Entity $entity): bool
{
$contactId = $user->get('contactId');
if ($contactId) {
if ($entity->getId() === $contactId) {
return true;
}
}
return false;
}
public function checkAccount(User $user, Entity $entity): bool
{
return $this->defaultOwnershipChecker->checkAccount($user, $entity);
}
}

View File

@@ -0,0 +1,81 @@
<?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\Modules\Crm\Classes\AclPortal\KnowledgeBaseArticle;
use Espo\Core\Utils\Metadata;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\KnowledgeBaseArticle;
use Espo\ORM\Entity;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\Core\Acl\AccessEntityCREDChecker;
use Espo\Core\Acl\ScopeData;
use Espo\Core\Portal\Acl\DefaultAccessChecker;
use Espo\Core\Portal\Acl\Traits\DefaultAccessCheckerDependency;
/**
* @implements AccessEntityCREDChecker<KnowledgeBaseArticle>
*/
class AccessChecker implements AccessEntityCREDChecker
{
use DefaultAccessCheckerDependency;
public function __construct(
DefaultAccessChecker $defaultAccessChecker,
private Metadata $metadata
) {
$this->defaultAccessChecker = $defaultAccessChecker;
}
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
{
if (!$this->defaultAccessChecker->checkEntityRead($user, $entity, $data)) {
return false;
}
$statusList = $this->metadata->get("entityDefs.KnowledgeBaseArticle.fields.status.activeOptions") ??
[KnowledgeBaseArticle::STATUS_PUBLISHED];
if (!in_array($entity->getStatus(), $statusList)) {
return false;
}
assert($entity instanceof CoreEntity);
$portalIdList = $entity->getLinkMultipleIdList('portals');
$portalId = $user->get('portalId');
if (!$portalId) {
return false;
}
return in_array($portalId, $portalIdList);
}
}

View File

@@ -0,0 +1,135 @@
<?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\Modules\Crm\Classes\AssignmentNotificators;
use Espo\Core\Field\LinkParent;
use Espo\Core\Notification\AssignmentNotificator;
use Espo\Core\Notification\AssignmentNotificator\Params;
use Espo\Core\Notification\DefaultAssignmentNotificator;
use Espo\Core\Notification\UserEnabledChecker;
use Espo\Core\Utils\Metadata;
use Espo\Entities\Notification;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Call;
use Espo\Modules\Crm\Entities\Meeting as MeetingEntity;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements AssignmentNotificator<MeetingEntity|Call>
*/
class Meeting implements AssignmentNotificator
{
private const ATTR_USERS_IDS = 'usersIds';
private const NOTIFICATION_TYPE_EVENT_ATTENDEE = 'EventAttendee';
public function __construct(
private DefaultAssignmentNotificator $defaultAssignmentNotificator,
private UserEnabledChecker $userEnabledChecker,
private EntityManager $entityManager,
private User $user,
private Metadata $metadata,
) {}
public function process(Entity $entity, Params $params): void
{
// Default assignment notifications not needed if stream is enabled.
if (!$this->hasStream($entity->getEntityType())) {
$this->defaultAssignmentNotificator->process($entity, $params);
}
if ($entity->getStatus() !== MeetingEntity::STATUS_PLANNED) {
return;
}
if (!$entity->isAttributeChanged(self::ATTR_USERS_IDS)) {
return;
}
/** @var string[] $prevIds */
$prevIds = $entity->getFetched(self::ATTR_USERS_IDS) ?? [];
$ids = $entity->getUsers()->getIdList();
$newIds = array_filter($ids, fn ($id) => !in_array($id, $prevIds));
$assignedUser = $entity->getAssignedUser();
if ($assignedUser) {
$newIds = array_filter($newIds, fn($id) => $id !== $assignedUser->getId());
}
$newIds = array_values($newIds);
foreach ($newIds as $id) {
$this->processForUser($entity, $id, $params);
}
}
private function processForUser(MeetingEntity|Call $entity, string $userId, Params $params): void
{
if (!$this->userEnabledChecker->checkAssignment($entity->getEntityType(), $userId)) {
return;
}
$createdBy = $entity->getCreatedBy();
$modifiedBy = $entity->getModifiedBy();
$isSelfAssignment = $entity->isNew() ?
$createdBy && $userId === $createdBy->getId() :
$modifiedBy && $userId === $modifiedBy->getId();
if ($isSelfAssignment) {
return;
}
$notification = $this->entityManager->getRDBRepositoryByClass(Notification::class)->getNew();
$notification
->setType(self::NOTIFICATION_TYPE_EVENT_ATTENDEE)
->setUserId($userId)
->setRelated(LinkParent::createFromEntity($entity))
->setData([
'entityType' => $entity->getEntityType(),
'entityId' => $entity->getId(),
'entityName' => $entity->getName(),
'isNew' => $entity->isNew(),
'userId' => $this->user->getId(),
'userName' => $this->user->getName(),
])
->setActionId($params->getActionId());
$this->entityManager->saveEntity($notification);
}
private function hasStream(string $entityType): bool
{
return (bool) $this->metadata->get(['scopes', $entityType, 'stream']);
}
}

View File

@@ -0,0 +1,86 @@
<?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\Modules\Crm\Classes\EmailNotificationHandlers;
use Espo\Core\Notification\EmailNotificationHandler;
use Espo\Core\Mail\SenderParams;
use Espo\Entities\InboundEmail;
use Espo\ORM\Entity;
use Espo\Entities\User;
use Espo\Entities\Email;
use Espo\ORM\EntityManager;
class CaseObj implements EmailNotificationHandler
{
/**
* @var array<string,\Espo\Entities\InboundEmail|null>
*/
private $inboundEmailEntityHash = [];
private EntityManager $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function prepareEmail(Email $email, Entity $entity, User $user): void {}
public function getSenderParams(Entity $entity, User $user): ?SenderParams
{
/** @var ?string $inboundEmailId */
$inboundEmailId = $entity->get('inboundEmailId');
if (!$inboundEmailId) {
return null;
}
if (!array_key_exists($inboundEmailId, $this->inboundEmailEntityHash)) {
$this->inboundEmailEntityHash[$inboundEmailId] =
$this->entityManager->getEntityById(InboundEmail::ENTITY_TYPE, $inboundEmailId);
}
$inboundEmail = $this->inboundEmailEntityHash[$inboundEmailId];
if (!$inboundEmail) {
return null;
}
$emailAddress = $inboundEmail->get('emailAddress');
if (!$emailAddress) {
return null;
}
return SenderParams::create()->withReplyToAddress($emailAddress);
}
}

View File

@@ -0,0 +1,42 @@
<?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\Modules\Crm\Classes\FieldDuplicators\Meeting;
use Espo\Core\Record\Duplicator\FieldDuplicator;
use Espo\ORM\Entity;
use stdClass;
class Attendees implements FieldDuplicator
{
public function duplicate(Entity $entity, string $field): stdClass
{
return (object) [$field . 'Columns' => null];
}
}

View File

@@ -0,0 +1,105 @@
<?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\Modules\Crm\Classes\FieldProcessing\Call;
use Espo\Modules\Crm\Entities\Call;
use Espo\Modules\Crm\Entities\Contact;
use Espo\Modules\Crm\Entities\Lead;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\ORM\Entity;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\Core\FieldProcessing\Loader;
use Espo\Core\FieldProcessing\Loader\Params;
use Espo\Core\ORM\EntityManager;
use Espo\ORM\Name\Attribute;
use stdClass;
/**
* @implements Loader<Call>
*/
class PhoneNumberMapLoader implements Loader
{
private const ERASED_PART = 'ERASED:';
public function __construct(private EntityManager $entityManager)
{}
public function process(Entity $entity, Params $params): void
{
$map = (object) [];
assert($entity instanceof CoreEntity);
$contactIdList = $entity->getLinkMultipleIdList(Meeting::LINK_CONTACTS);
if (count($contactIdList)) {
$this->populate($map, Contact::ENTITY_TYPE, $contactIdList);
}
$leadIdList = $entity->getLinkMultipleIdList(Meeting::LINK_LEADS);
if (count($leadIdList)) {
$this->populate($map, Lead::ENTITY_TYPE, $leadIdList);
}
$entity->set('phoneNumbersMap', $map);
}
/**
* @param string[] $idList
*/
private function populate(stdClass $map, string $entityType, array $idList): void
{
$entityList = $this->entityManager
->getRDBRepository($entityType)
->where([
Attribute::ID => $idList,
])
->select([Attribute::ID, 'phoneNumber'])
->find();
foreach ($entityList as $entity) {
$phoneNumber = $entity->get('phoneNumber');
if (!$phoneNumber) {
continue;
}
if (str_starts_with($phoneNumber, self::ERASED_PART)) {
continue;
}
$key = $entity->getEntityType() . '_' . $entity->getId();
$map->$key = $phoneNumber;
}
}
}

View File

@@ -0,0 +1,264 @@
<?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\Modules\Crm\Classes\FieldProcessing\Campaign;
use Espo\Modules\Crm\Entities\Campaign;
use Espo\Modules\Crm\Entities\CampaignLogRecord;
use Espo\Modules\Crm\Entities\Lead;
use Espo\Modules\Crm\Entities\Opportunity;
use Espo\ORM\Entity;
use Espo\Core\Acl;
use Espo\Core\Currency\ConfigDataProvider as CurrencyConfigDataProvider;
use Espo\Core\FieldProcessing\Loader;
use Espo\Core\FieldProcessing\Loader\Params;
use Espo\Core\ORM\EntityManager;
use Espo\ORM\Query\Part\Condition;
use Espo\ORM\Query\Part\Expression;
use Espo\ORM\Query\Part\Join;
use Espo\ORM\Query\SelectBuilder;
use PDO;
use const PHP_ROUND_HALF_EVEN;
/**
* @implements Loader<Campaign>
*/
class StatsLoader implements Loader
{
private EntityManager $entityManager;
private Acl $acl;
private CurrencyConfigDataProvider $currencyDataProvider;
public function __construct(
EntityManager $entityManager,
Acl $acl,
CurrencyConfigDataProvider $currencyDataProvider
) {
$this->entityManager = $entityManager;
$this->acl = $acl;
$this->currencyDataProvider = $currencyDataProvider;
}
public function process(Entity $entity, Params $params): void
{
$sentCount = $this->entityManager
->getRDBRepository(CampaignLogRecord::ENTITY_TYPE)
->where([
'campaignId' => $entity->getId(),
'action' => CampaignLogRecord::ACTION_SENT,
'isTest' => false,
])
->count();
if (!$sentCount) {
$sentCount = null;
}
$entity->set('sentCount', $sentCount);
$openedCount = $this->entityManager
->getRDBRepository(CampaignLogRecord::ENTITY_TYPE)
->where([
'campaignId' => $entity->getId(),
'action' => CampaignLogRecord::ACTION_OPENED,
'isTest' => false,
])
->count();
$entity->set('openedCount', $openedCount);
$openedPercentage = null;
if ($sentCount > 0) {
$openedPercentage = round($openedCount / $sentCount * 100, 2, PHP_ROUND_HALF_EVEN);
}
$entity->set('openedPercentage', $openedPercentage);
$clickedCount = $this->entityManager
->getRDBRepository(CampaignLogRecord::ENTITY_TYPE)
->clone(
SelectBuilder::create()
->from(CampaignLogRecord::ENTITY_TYPE)
->leftJoin(
Join::createWithTableTarget(CampaignLogRecord::ENTITY_TYPE, 'b')
->withConditions(
Condition::and(
Condition::equal(
Expression::column('queueItemId'),
Expression::column('b.queueItemId'),
),
Condition::greater(
Expression::column('b.id'),
Expression::column('id'),
),
Condition::equal(
Expression::create('b.action'),
CampaignLogRecord::ACTION_CLICKED
),
Condition::equal(
Expression::create('b.isTest'),
false
)
)
)
)
->where([
'campaignId' => $entity->getId(),
'action' => CampaignLogRecord::ACTION_CLICKED,
'isTest' => false,
'b.id' => null,
])
->build()
)
->count();
$entity->set('clickedCount', $clickedCount);
$clickedPercentage = null;
if ($sentCount > 0) {
$clickedPercentage = round(
$clickedCount / $sentCount * 100, 2,
PHP_ROUND_HALF_EVEN
);
}
$entity->set('clickedPercentage', $clickedPercentage);
$optedInCount = $this->entityManager
->getRDBRepository(CampaignLogRecord::ENTITY_TYPE)
->where([
'campaignId' => $entity->getId(),
'action' => CampaignLogRecord::ACTION_OPTED_IN,
'isTest' => false,
])
->count();
if (!$optedInCount) {
$optedInCount = null;
}
$entity->set('optedInCount', $optedInCount);
$optedOutCount = $this->entityManager
->getRDBRepository(CampaignLogRecord::ENTITY_TYPE)
->where([
'campaignId' => $entity->getId(),
'action' => CampaignLogRecord::ACTION_OPTED_OUT,
'isTest' => false,
])
->count();
$entity->set('optedOutCount', $optedOutCount);
$optedOutPercentage = null;
if ($sentCount > 0) {
$optedOutPercentage = round(
$optedOutCount / $sentCount * 100, 2,
PHP_ROUND_HALF_EVEN
);
}
$entity->set('optedOutPercentage', $optedOutPercentage);
$bouncedCount = $this->entityManager
->getRDBRepository(CampaignLogRecord::ENTITY_TYPE)
->where([
'campaignId' => $entity->getId(),
'action' => CampaignLogRecord::ACTION_BOUNCED,
'isTest' => false,
])
->count();
$entity->set('bouncedCount', $bouncedCount);
$bouncedPercentage = null;
if ($sentCount && $sentCount > 0) {
$bouncedPercentage = round(
$bouncedCount / $sentCount * 100, 2,
PHP_ROUND_HALF_EVEN
);
}
$entity->set('bouncedPercentage', $bouncedPercentage);
if ($this->acl->check(Lead::ENTITY_TYPE)) {
$leadCreatedCount = $this->entityManager
->getRDBRepository(Lead::ENTITY_TYPE)
->where([
'campaignId' => $entity->getId(),
])
->count();
if (!$leadCreatedCount) {
$leadCreatedCount = null;
}
$entity->set('leadCreatedCount', $leadCreatedCount);
}
if ($this->acl->check(Opportunity::ENTITY_TYPE)) {
$entity->set('revenueCurrency', $this->currencyDataProvider->getDefaultCurrency());
$query = $this->entityManager
->getQueryBuilder()
->select()
->from(Opportunity::ENTITY_TYPE)
->select(['SUM:amountConverted'])
->where([
'stage' => Opportunity::STAGE_CLOSED_WON,
'campaignId' => $entity->getId(),
])
->group('opportunity.campaignId')
->build();
$sth = $this->entityManager->getQueryExecutor()->execute($query);
$revenue = null;
$row = $sth->fetch(PDO::FETCH_ASSOC);
if ($row) {
$revenue = floatval($row['SUM:amountConverted']);
}
if (!$revenue) {
$revenue = null;
}
$entity->set('revenue', $revenue);
}
}
}

View File

@@ -0,0 +1,84 @@
<?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\Modules\Crm\Classes\FieldProcessing\Meeting;
use Espo\Entities\User;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use Espo\Core\FieldProcessing\Loader;
use Espo\Core\FieldProcessing\Loader\Params;
/**
* @implements Loader<\Espo\Core\ORM\Entity>
*/
class AcceptanceStatusLoader implements Loader
{
private EntityManager $entityManager;
private User $user;
private const ATTR_ACCEPTANCE_STATUS = 'acceptanceStatus';
public function __construct(EntityManager $entityManager, User $user)
{
$this->entityManager = $entityManager;
$this->user = $user;
}
public function process(Entity $entity, Params $params): void
{
if (!$params->hasInSelect(self::ATTR_ACCEPTANCE_STATUS)) {
return;
}
if ($entity->has(self::ATTR_ACCEPTANCE_STATUS)) {
return;
}
$attribute = self::ATTR_ACCEPTANCE_STATUS;
$user = $this->entityManager
->getRDBRepository($entity->getEntityType())
->getRelation($entity, 'users')
->where([
'id' => $this->user->getId(),
])
->select([$attribute])
->findOne();
$value = null;
if ($user) {
$value = $user->get($attribute);
}
$entity->set(self::ATTR_ACCEPTANCE_STATUS, $value);
}
}

View File

@@ -0,0 +1,94 @@
<?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\Modules\Crm\Classes\FieldProcessing\Meeting;
use Espo\Entities\Email;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use Espo\Core\FieldProcessing\Saver;
use Espo\Core\FieldProcessing\Saver\Params;
use Espo\Core\Mail\Event\EventFactory;
use ICal\ICal;
/**
* @implements Saver<Meeting>
*/
class SourceEmailSaver implements Saver
{
public function __construct(private EntityManager $entityManager)
{}
/**
* @param Meeting $entity
*/
public function process(Entity $entity, Params $params): void
{
if (!$entity->isNew()) {
return;
}
$email = $this->getEmail($entity);
if (!$email) {
return;
}
$icsContents = $email->getIcsContents();
if ($icsContents === null) {
return;
}
$ical = new ICal();
$ical->initString($icsContents);
$espoEvent = EventFactory::createFromU01jmg3Ical($ical);
$email->set('createdEventId', $entity->getId());
$email->set('createdEventType', $entity->getEntityType());
$email->set('icsEventUid', $espoEvent->getUid());
$this->entityManager->saveEntity($email);
}
private function getEmail(Meeting $entity): ?Email
{
$emailId = $entity->get('sourceEmailId');
if (!$emailId) {
return null;
}
return $this->entityManager->getRDBRepositoryByClass(Email::class)->getById($emailId);
}
}

View File

@@ -0,0 +1,80 @@
<?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\Modules\Crm\Classes\FieldProcessing\TargetList;
use Espo\Modules\Crm\Entities\TargetList;
use Espo\ORM\Entity;
use Espo\Core\Utils\Metadata;
use Espo\Core\FieldProcessing\Loader;
use Espo\Core\FieldProcessing\Loader\Params;
use Espo\Core\ORM\EntityManager;
/**
* @implements Loader<TargetList>
*/
class EntryCountLoader implements Loader
{
/** @var string[] */
private array $targetLinkList;
private EntityManager $entityManager;
private Metadata $metadata;
public function __construct(EntityManager $entityManager, Metadata $metadata)
{
$this->entityManager = $entityManager;
$this->metadata = $metadata;
$this->targetLinkList = $this->metadata->get(['scopes', 'TargetList', 'targetLinkList']) ?? [];
}
public function process(Entity $entity, Params $params): void
{
if (
$params->hasSelect() &&
!in_array('entryCount', $params->getSelect() ?? [])
) {
return;
}
$count = 0;
foreach ($this->targetLinkList as $link) {
$count += $this->entityManager
->getRDBRepository(TargetList::ENTITY_TYPE)
->getRelation($entity, $link)
->count();
}
$entity->set('entryCount', $count);
}
}

View File

@@ -0,0 +1,82 @@
<?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\Modules\Crm\Classes\FieldProcessing\TargetList;
use Espo\ORM\Defs\Params\RelationParam;
use Espo\ORM\Entity;
use Espo\Modules\Crm\Entities\TargetList;
use Espo\Core\Utils\Metadata;
use Espo\Core\FieldProcessing\Loader;
use Espo\Core\FieldProcessing\Loader\Params;
use Espo\Core\ORM\EntityManager;
/**
* @implements Loader<TargetList>
*/
class OptedOutCountLoader implements Loader
{
/** @var string[] */
private array $targetLinkList;
public function __construct(private EntityManager $entityManager, private Metadata $metadata)
{
$this->targetLinkList = $this->metadata->get(['scopes', 'TargetList', 'targetLinkList']) ?? [];
}
public function process(Entity $entity, Params $params): void
{
if (
$params->hasSelect() &&
!in_array('optedOutCount', $params->getSelect() ?? [])
) {
return;
}
assert($entity instanceof TargetList);
$count = 0;
foreach ($this->targetLinkList as $link) {
$foreignEntityType = $entity->getRelationParam($link, RelationParam::ENTITY);
$count += $this->entityManager
->getRDBRepository($foreignEntityType)
->join('targetLists')
->where([
'targetListsMiddle.targetListId' => $entity->getId(),
'targetListsMiddle.optedOut' => true,
])
->count();
}
$entity->set('optedOutCount', $count);
}
}

View File

@@ -0,0 +1,63 @@
<?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\Modules\Crm\Classes\FieldValidators\Campaign\EndDate;
use Espo\Core\FieldValidation\Validator;
use Espo\Core\FieldValidation\Validator\Data;
use Espo\Core\FieldValidation\Validator\Failure;
use Espo\Modules\Crm\Entities\Campaign;
use Espo\ORM\Entity;
/**
* @implements Validator<Campaign>
*/
class AfterStartDate implements Validator
{
/**
* @param Campaign $entity
*/
public function validate(Entity $entity, string $field, Data $data): ?Failure
{
$startDate = $entity->getStartDate();
$endDate = $entity->getEndDate();
if (!$startDate || !$endDate) {
return null;
}
if ($endDate->isLessThan($startDate)) {
return Failure::create();
}
return null;
}
}

View File

@@ -0,0 +1,63 @@
<?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\Modules\Crm\Classes\FieldValidators\Campaign\StartDate;
use Espo\Core\FieldValidation\Validator;
use Espo\Core\FieldValidation\Validator\Data;
use Espo\Core\FieldValidation\Validator\Failure;
use Espo\Modules\Crm\Entities\Campaign;
use Espo\ORM\Entity;
/**
* @implements Validator<Campaign>
*/
class BeforeEndDate implements Validator
{
/**
* @param Campaign $entity
*/
public function validate(Entity $entity, string $field, Data $data): ?Failure
{
$startDate = $entity->getStartDate();
$endDate = $entity->getEndDate();
if (!$startDate || !$endDate) {
return null;
}
if ($endDate->isLessThan($startDate)) {
return Failure::create();
}
return null;
}
}

View File

@@ -0,0 +1,64 @@
<?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\Modules\Crm\Classes\FieldValidators\Event\Reminders;
use Espo\Core\FieldValidation\Validator;
use Espo\Core\FieldValidation\Validator\Data;
use Espo\Core\FieldValidation\Validator\Failure;
use Espo\Core\Utils\Config;
use Espo\ORM\Entity;
/**
* @implements Validator<Entity>
*/
class MaxCount implements Validator
{
private const MAX_COUNT = 10;
public function __construct(private Config $config)
{}
public function validate(Entity $entity, string $field, Data $data): ?Failure
{
$maxCount = $this->config->get('reminderMaxCount') ?? self::MAX_COUNT;
$value = $entity->get($field);
if (!is_array($value)) {
return null;
}
if (count($value) <= $maxCount) {
return null;
}
return Failure::create();
}
}

View File

@@ -0,0 +1,86 @@
<?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\Modules\Crm\Classes\FieldValidators\Event\Reminders;
use Espo\Core\FieldValidation\Validator;
use Espo\Core\FieldValidation\Validator\Data;
use Espo\Core\FieldValidation\Validator\Failure;
use Espo\Modules\Crm\Entities\Reminder;
use Espo\ORM\Defs;
use Espo\ORM\Entity;
use stdClass;
/**
* @implements Validator<Entity>
*/
class Valid implements Validator
{
public function __construct(
private Defs $ormDefs
) {}
public function validate(Entity $entity, string $field, Data $data): ?Failure
{
/** @var ?mixed[] $list */
$list = $entity->get($field);
if ($list === null) {
return null;
}
$typeList = $this->ormDefs
->getEntity(Reminder::ENTITY_TYPE)
->getField('type')
->getParam('options') ?? [];
foreach ($list as $item) {
if (!$item instanceof stdClass) {
return Failure::create();
}
$seconds = $item->seconds ?? null;
$type = $item->type ?? null;
if (!is_int($seconds)) {
return Failure::create();
}
if ($seconds < 0) {
return Failure::create();
}
if (!in_array($type, $typeList)) {
return Failure::create();
}
}
return null;
}
}

View File

@@ -0,0 +1,142 @@
<?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\Modules\Crm\Classes\FormulaFunctions\ExtGroup\AccountGroup;
use Espo\Core\Utils\Json;
use Espo\Modules\Crm\Entities\Account;
use Espo\Modules\Crm\Entities\Contact;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Di;
class FindByEmailAddressType extends BaseFunction implements
Di\EntityManagerAware,
Di\FileManagerAware
{
use Di\EntityManagerSetter;
use Di\FileManagerSetter;
/** @var string[] */
private array $domainFileList = [
'application/Espo/Modules/Crm/Resources/data/freeEmailProviderDomains.json',
'custom/Espo/Custom/Resources/data/freeEmailProviderDomains.json',
];
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments(1);
}
$emailAddress = $args[0];
if (!$emailAddress) {
return null;
}
if (!is_string($emailAddress)) {
$this->log("Formula: ext\\account\\findByEmailAddress: Bad argument type.");
return null;
}
$domain = $emailAddress;
if (str_contains($emailAddress, '@')) {
[, $domain] = explode('@', $emailAddress);
}
$domain = strtolower($domain);
$em = $this->entityManager;
$account = $em->getRDBRepository(Account::ENTITY_TYPE)
->where(['emailAddress' => $emailAddress])
->findOne();
if ($account) {
return $account->getId();
}
$ignoreList = [];
foreach ($this->domainFileList as $file) {
if (!$this->fileManager->isFile($file)) {
continue;
}
$ignoreList = array_merge(
$ignoreList,
Json::decode($this->fileManager->getContents($file))
);
}
$contact = $em->getRDBRepository(Contact::ENTITY_TYPE)
->where(['emailAddress' => $emailAddress])
->findOne();
if ($contact) {
if (!in_array($domain, $ignoreList)) {
$account = $em->getRDBRepository(Account::ENTITY_TYPE)
->join('contacts')
->where([
'emailAddress*' => '%@' . $domain,
'contacts.id' => $contact->getId(),
])
->findOne();
if ($account) {
return $account->getId();
}
} else {
if ($contact->get('accountId')) {
return $contact->get('accountId');
}
}
}
if (in_array($domain, $ignoreList)) {
return null;
}
$account = $em->getRDBRepository(Account::ENTITY_TYPE)
->where(['emailAddress*' => '%@' . $domain])
->findOne();
if (!$account) {
return null;
}
return $account->getId();
}
}

View File

@@ -0,0 +1,113 @@
<?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\Modules\Crm\Classes\FormulaFunctions\ExtGroup\CalendarGroup;
use Espo\Core\Field\DateTime;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Espo\Entities\User;
use Espo\Modules\Crm\Tools\Calendar\FreeBusy\FetchParams;
use Espo\Modules\Crm\Tools\Calendar\FreeBusy\Service;
use Espo\Modules\Crm\Tools\Calendar\Items\Event;
use Espo\ORM\EntityManager;
use Exception;
use RuntimeException;
/**
* @noinspection PhpUnused
*/
class UserIsBusyType implements Func
{
public function __construct(
private Service $service,
private EntityManager $entityManager,
) {}
public function process(EvaluatedArgumentList $arguments): bool
{
if (count($arguments) < 3) {
throw TooFewArguments::create(3);
}
$userId = $arguments[0];
$from = $arguments[1];
$to = $arguments[2];
$entityType = $arguments[3] ?? null;
$id = $arguments[4] ?? null;
if (!is_string($userId)) {
throw BadArgumentType::create(1, 'string');
}
if (!is_string($from)) {
throw BadArgumentType::create(2, 'string');
}
if (!is_string($to)) {
throw BadArgumentType::create(3, 'string');
}
if ($entityType !== null && !is_string($entityType)) {
throw BadArgumentType::create(4, 'string');
}
if ($id !== null && !is_string($id)) {
throw BadArgumentType::create(5, 'string');
}
$ignoreList = [];
if ($entityType && $id) {
$ignoreList[] = (new Event(null, null, $entityType, []))->withId($id);
}
$user = $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
if (!$user) {
throw new RuntimeException("User $userId not found.");
}
$busyParams = new FetchParams(
from: DateTime::fromString($from),
to: DateTime::fromString($to),
ignoreEventList: $ignoreList,
);
try {
$ranges = $this->service->fetchRanges($user, $busyParams);
} catch (Exception $e) {
throw new RuntimeException($e->getMessage());
}
return $ranges !== [];
}
}

View File

@@ -0,0 +1,66 @@
<?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\Modules\Crm\Classes\MassAction\Opportunity;
use Espo\Core\MassAction\Actions\MassUpdate as MassUpdateOriginal;
use Espo\Core\MassAction\Params;
use Espo\Core\MassAction\Result;
use Espo\Core\MassAction\Data;
use Espo\Core\MassAction\MassAction;
use Espo\Tools\MassUpdate\Data as MassUpdateData;
use Espo\Core\Utils\Metadata;
class MassUpdate implements MassAction
{
public function __construct(
private MassUpdateOriginal $massUpdateOriginal,
private Metadata $metadata
) {}
public function process(Params $params, Data $data): Result
{
$massUpdateData = MassUpdateData::fromMassActionData($data);
$probability = null;
$stage = $massUpdateData->getValue('stage');
if ($stage && !$massUpdateData->has('probability')) {
$probability = $this->metadata->get("entityDefs.Opportunity.fields.stage.probabilityMap.$stage");
}
if ($probability !== null) {
$massUpdateData = $massUpdateData->with('probability', $probability);
}
return $this->massUpdateOriginal->process($params, $massUpdateData->toMassActionData());
}
}

View File

@@ -0,0 +1,48 @@
<?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\Modules\Crm\Classes\Pdf\Account;
use Espo\Tools\Pdf\Data\DataLoader;
use Espo\Tools\Pdf\Params;
use Espo\ORM\Entity;
use stdClass;
class ExampleDataLoader implements DataLoader
{
public function load(Entity $entity, Params $params): stdClass
{
// Here you can load additional data for PDF;
return (object) [
];
}
}

View 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\Modules\Crm\Classes\RecordHooks\Call;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Core\Record\ServiceContainer;
use Espo\Modules\Crm\Entities\Call;
use Espo\ORM\Entity;
/**
* @implements SaveHook<Call>
*/
class AfterUpdate implements SaveHook
{
public function __construct(
private ServiceContainer $serviceContainer
) {}
public function process(Entity $entity): void
{
if (
!$entity->isAttributeChanged('contactsIds') &&
!$entity->isAttributeChanged('leadsIds')
) {
return;
}
$this->serviceContainer->getByClass(Call::class)->loadAdditionalFields($entity);
}
}

View File

@@ -0,0 +1,77 @@
<?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\Modules\Crm\Classes\RecordHooks\Campaign;
use Espo\Core\Exceptions\Error\Body;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Modules\Crm\Entities\Campaign;
use Espo\Modules\Crm\Entities\CampaignTrackingUrl;
use Espo\Modules\Crm\Entities\MassEmail;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements SaveHook<Campaign>
*/
class BeforeUpdate implements SaveHook
{
public function __construct(
private EntityManager $entityManager,
) {}
public function process(Entity $entity): void
{
$this->checkType($entity);
}
/**
* @throws Forbidden
*/
private function checkType(Campaign $entity): void
{
if (!$entity->isAttributeChanged('type')) {
return;
}
$massEmail = $this->entityManager
->getRDBRepositoryByClass(MassEmail::class)
->where(['campaignId' => $entity->getId()])
->findOne();
if ($massEmail) {
throw Forbidden::createWithBody(
'Cannot change type.',
Body::create()->withMessageTranslation('cannotChangeType', Campaign::ENTITY_TYPE)
);
}
}
}

View 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\Modules\Crm\Classes\RecordHooks\CampaignTrackingUrl;
use Espo\Core\Acl;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Modules\Crm\Entities\Campaign;
use Espo\Modules\Crm\Entities\CampaignTrackingUrl;
use Espo\ORM\Entity;
/**
* @implements SaveHook<CampaignTrackingUrl>
*/
class BeforeCreate implements SaveHook
{
public function __construct(
private Acl $acl
) {}
public function process(Entity $entity): void
{
if (!$this->acl->check($entity, Acl\Table::ACTION_EDIT)) {
throw new Forbidden("No 'edit' access.");
}
$this->checkCampaign($entity);
}
/**
* @throws Forbidden
*/
private function checkCampaign(CampaignTrackingUrl $entity): void
{
if (
!$entity->getCampaign() || in_array($entity->getCampaign()->getType(), [
Campaign::TYPE_EMAIL,
Campaign::TYPE_NEWSLETTER,
])
) {
return;
}
throw new Forbidden("Cannot create tacking URL for non-email campaign.");
}
}

View File

@@ -0,0 +1,118 @@
<?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\Modules\Crm\Classes\RecordHooks\Case;
use Espo\Core\Acl;
use Espo\Core\Name\Field;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Entities\Email;
use Espo\Modules\Crm\Entities\Account;
use Espo\Modules\Crm\Entities\CaseObj;
use Espo\Modules\Crm\Entities\Contact;
use Espo\Modules\Crm\Entities\Lead;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use RuntimeException;
/**
* @implements SaveHook<CaseObj>
* @noinspection PhpUnused
*/
class AfterCreate implements SaveHook
{
private const EMAIL_REPLY_LEVEL = 3;
private const EMAIL_REPLY_LIMIT = 2;
private const EMAIL_REPLY_LIMIT_SECOND = 1;
public function __construct(
private EntityManager $entityManager,
private Acl $acl,
) {}
public function process(Entity $entity): void
{
/** @var ?string $emailId */
$emailId = $entity->get('originalEmailId');
if (!$emailId) {
return;
}
$email = $this->entityManager->getRDBRepositoryByClass(Email::class)->getById($emailId);
if (!$email) {
return;
}
$this->changeEmailParent($email, $entity);
}
private function changeEmailParent(Email $email, CaseObj $entity, int $level = 0): void
{
if (!$this->acl->checkEntityRead($email)) {
return;
}
if (
$email->getParentId() &&
!in_array($email->getParentType(), [
Account::ENTITY_TYPE,
Contact::ENTITY_TYPE,
Lead::ENTITY_TYPE,
])
) {
return;
}
$email->setParent($entity);
$this->entityManager->saveEntity($email);
if ($level === self::EMAIL_REPLY_LEVEL) {
return;
}
$limit = $level === 0 ? self::EMAIL_REPLY_LIMIT : self::EMAIL_REPLY_LIMIT_SECOND;
$replies = $this->entityManager
->getRelation($email, Email::LINK_REPLIES)
->limit(0, $limit)
->order(Field::CREATED_AT)
->find();
foreach ($replies as $reply) {
if (!$reply instanceof Email) {
throw new RuntimeException();
}
$this->changeEmailParent($reply, $entity, $level + 1);
}
}
}

View File

@@ -0,0 +1,67 @@
<?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\Modules\Crm\Classes\RecordHooks\Case;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\CaseObj;
use Espo\ORM\Entity;
/**
* @implements SaveHook<CaseObj>
* @noinspection PhpUnused
*/
class BeforeCreate implements SaveHook
{
public function __construct(
private User $user
) {}
public function process(Entity $entity): void
{
if (!$this->user->isPortal()) {
return;
}
$userContact = $this->user->getContact();
if (!$userContact) {
return;
}
if (!$entity->getAccount() && $userContact->getAccount()) {
$entity->setAccount($userContact->getAccount());
}
if (!$entity->getContact()) {
$entity->setContact($userContact);
}
}
}

View File

@@ -0,0 +1,81 @@
<?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\Modules\Crm\Classes\RecordHooks\Contact;
use Espo\Core\Acl;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Core\Utils\Config;
use Espo\Entities\Email;
use Espo\Modules\Crm\Entities\Account;
use Espo\Modules\Crm\Entities\Contact;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements SaveHook<Contact>
*/
class AfterCreate implements SaveHook
{
public function __construct(
private EntityManager $entityManager,
private Config $config,
private Acl $acl
) {}
public function process(Entity $entity): void
{
$emailId = $entity->get('originalEmailId');
if (!$emailId) {
return;
}
/** @var ?Email $email */
$email = $this->entityManager->getEntityById(Email::ENTITY_TYPE, $emailId);
if (!$email || $email->getParentId() || !$this->acl->check($email)) {
return;
}
if ($this->config->get('b2cMode') || !$entity->getAccount()) {
$email->set([
'parentType' => Contact::ENTITY_TYPE,
'parentId' => $entity->getId(),
]);
} else if ($entity->getAccount()) {
$email->set([
'parentType' => Account::ENTITY_TYPE,
'parentId' => $entity->getAccount()->getId(),
]);
}
$this->entityManager->saveEntity($email);
}
}

View File

@@ -0,0 +1,101 @@
<?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\Modules\Crm\Classes\RecordHooks\Lead;
use Espo\Core\Acl;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Core\Utils\DateTime;
use Espo\Entities\Email;
use Espo\Modules\Crm\Entities\Campaign as Campaign;
use Espo\Modules\Crm\Entities\CampaignLogRecord as CampaignLogRecord;
use Espo\Modules\Crm\Entities\Lead;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements SaveHook<Lead>
*/
class AfterCreate implements SaveHook
{
public function __construct(
private EntityManager $entityManager,
private Acl $acl
) {}
public function process(Entity $entity): void
{
$this->processOriginalEmail($entity);
$this->processCampaignLog($entity);
}
private function processOriginalEmail(Lead $entity): void
{
$emailId = $entity->get('originalEmailId');
if (!$emailId) {
return;
}
/** @var ?Email $email */
$email = $this->entityManager->getEntityById(Email::ENTITY_TYPE, $emailId);
if (!$email || $email->getParentId() || !$this->acl->check($email)) {
return;
}
$email->set([
'parentType' => Lead::ENTITY_TYPE,
'parentId' => $entity->getId(),
]);
$this->entityManager->saveEntity($email);
}
private function processCampaignLog(Lead $entity): void
{
$campaign = $entity->getCampaign();
if (!$campaign) {
return;
}
$log = $this->entityManager->getNewEntity(CampaignLogRecord::ENTITY_TYPE);
$log->set([
'action' => CampaignLogRecord::ACTION_LEAD_CREATED,
'actionDate' => DateTime::getSystemNowString(),
'parentType' => Lead::ENTITY_TYPE,
'parentId' => $entity->getId(),
'campaignId' => $campaign->getId(),
]);
$this->entityManager->saveEntity($log);
}
}

View 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\Modules\Crm\Classes\RecordHooks\MassEmail;
use Espo\Core\Acl;
use Espo\Core\Acl\Table;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Modules\Crm\Entities\Campaign;
use Espo\Modules\Crm\Entities\MassEmail;
use Espo\ORM\Entity;
/**
* @implements SaveHook<MassEmail>
*/
class BeforeCreate implements SaveHook
{
public function __construct(
private Acl $acl
) {}
public function process(Entity $entity): void
{
if (!$this->acl->check($entity, Table::ACTION_EDIT)) {
throw new Forbidden("No 'edit' access.");
}
$this->checkCampaign($entity);
}
/**
* @throws Forbidden
*/
private function checkCampaign(MassEmail $entity): void
{
if (
!$entity->getCampaign() || in_array($entity->getCampaign()->getType(), [
Campaign::TYPE_EMAIL,
Campaign::TYPE_NEWSLETTER,
Campaign::TYPE_INFORMATIONAL_EMAIL,
])
) {
return;
}
throw new Forbidden("Cannot create mass email for non-email campaign.");
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Espo\Modules\Crm\Classes\RecordHooks\Meeting;
use Espo\Core\Acl;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Record\CreateParams;
use Espo\Core\Record\Hook\CreateHook;
use Espo\Entities\Email;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements CreateHook<Meeting>
*/
class BeforeCreateSourceEmailCheck implements CreateHook
{
public function __construct(
private EntityManager $entityManager,
private Acl $acl,
) {}
public function process(Entity $entity, CreateParams $params): void
{
$emailId = $entity->get('sourceEmailId');
if (!$emailId) {
return;
}
$email = $this->entityManager->getRDBRepositoryByClass(Email::class)->getById($emailId);
if (!$email) {
return;
}
if (!$this->acl->checkEntityRead($email)) {
throw new Forbidden("No access to source email.");
}
}
}

View File

@@ -0,0 +1,67 @@
<?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\Modules\Crm\Classes\RecordHooks\Opportunity;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Core\Utils\Metadata;
use Espo\Modules\Crm\Entities\Opportunity;
use Espo\ORM\Entity;
/**
* @implements SaveHook<Opportunity>
*/
class BeforeUpdate implements SaveHook
{
public function __construct(
private Metadata $metadata
) {}
public function process(Entity $entity): void
{
$this->setProbability($entity);
}
private function setProbability(Opportunity $entity): void
{
if ($entity->isAttributeWritten('probability') && $entity->getProbability() !== null) {
return;
}
$stage = $entity->getStage();
$probability = $this->metadata->get("entityDefs.Opportunity.fields.stage.probabilityMap.$stage");
if ($probability === null) {
return;
}
$entity->setProbability($probability);
}
}

View File

@@ -0,0 +1,184 @@
<?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\Modules\Crm\Classes\RecordHooks\TargetList;
use Espo\Core\Acl;
use Espo\Core\Acl\Table;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Modules\Crm\Entities\Campaign;
use Espo\Modules\Crm\Entities\CampaignLogRecord;
use Espo\Modules\Crm\Entities\TargetList;
use Espo\Modules\Crm\Tools\TargetList\MetadataProvider;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use Espo\ORM\Name\Attribute;
/**
* @implements SaveHook<TargetList>
*/
class AfterCreate implements SaveHook
{
public function __construct(
private EntityManager $entityManager,
private Acl $acl,
private MetadataProvider $metadataProvider
) {}
/**
* @throws Forbidden
* @throws NotFound
*/
public function process(Entity $entity): void
{
if (
!$entity->get('sourceCampaignId') ||
!$entity->get('includingActionList')
) {
return;
}
/** @var string $campaignId */
$campaignId = $entity->get('sourceCampaignId');
/** @var string[] $includingActionList */
$includingActionList = $entity->get('includingActionList');
/** @var string[] $excludingActionList */
$excludingActionList = $entity->get('excludingActionList') ?? [];
$this->populateFromCampaignLog(
$entity,
$campaignId,
$includingActionList,
$excludingActionList
);
}
/**
* @param string[] $includingActionList
* @param string[] $excludingActionList
* @throws NotFound
* @throws Forbidden
*/
protected function populateFromCampaignLog(
TargetList $entity,
string $sourceCampaignId,
array $includingActionList,
array $excludingActionList
): void {
$campaign = $this->entityManager->getEntityById(Campaign::ENTITY_TYPE, $sourceCampaignId);
if (!$campaign) {
throw new NotFound("Campaign not found.");
}
if (!$this->acl->check($campaign, Table::ACTION_READ)) {
throw new Forbidden("No access to campaign.");
}
$queryBuilder = $this->entityManager
->getQueryBuilder()
->select()
->from(CampaignLogRecord::ENTITY_TYPE)
->select([Attribute::ID, 'parentId', 'parentType'])
->where([
'isTest' => false,
'campaignId' => $sourceCampaignId,
]);
$notQueryBuilder = clone $queryBuilder;
$queryBuilder->where([
'action=' => $includingActionList,
]);
$queryBuilder->group([
'parentId',
'parentType',
Attribute::ID,
]);
$notQueryBuilder->where(['action=' => $excludingActionList]);
$notQueryBuilder->select([Attribute::ID]);
/** @var iterable<CampaignLogRecord> $logRecords */
$logRecords = $this->entityManager
->getRDBRepository(CampaignLogRecord::ENTITY_TYPE)
->clone($queryBuilder->build())
->find();
$entityTypeLinkMap = $this->metadataProvider->getEntityTypeLinkMap();
foreach ($logRecords as $logRecord) {
if (!$logRecord->getParent()) {
continue;
}
$parentType = $logRecord->getParent()->getEntityType();
$parentId = $logRecord->getParent()->getId();
if (!$parentType) {
continue;
}
if (empty($entityTypeLinkMap[$parentType])) {
continue;
}
$existing = null;
if (!empty($excludingActionList)) {
$cloneQueryBuilder = clone $notQueryBuilder;
$cloneQueryBuilder->where([
'parentType' => $parentType,
'parentId' => $parentId,
]);
$existing = $this->entityManager
->getRDBRepository(CampaignLogRecord::ENTITY_TYPE)
->clone($cloneQueryBuilder->build())
->findOne();
}
if ($existing) {
continue;
}
$relation = $entityTypeLinkMap[$parentType];
$this->entityManager
->getRDBRepositoryByClass(TargetList::class)
->getRelation($entity, $relation)
->relateById($parentId);
}
}
}

View File

@@ -0,0 +1,89 @@
<?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\Modules\Crm\Classes\RecordHooks\TargetList;
use Espo\Core\Acl;
use Espo\Core\Record\CreateParams;
use Espo\Core\Record\Hook\CreateHook;
use Espo\Modules\Crm\Entities\TargetList;
use Espo\Modules\Crm\Tools\TargetList\MetadataProvider;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements CreateHook<TargetList>
*/
class AfterCreateDuplicate implements CreateHook
{
public function __construct(
private Acl $acl,
private EntityManager $entityManager,
private MetadataProvider $metadataProvider
) {}
public function process(Entity $entity, CreateParams $params): void
{
$id = $params->getDuplicateSourceId();
if (!$id) {
return;
}
$sourceEntity = $this->entityManager->getRDBRepositoryByClass(TargetList::class)->getById($id);
if (!$sourceEntity) {
return;
}
if (!$this->acl->check($sourceEntity, Acl\Table::ACTION_READ)) {
return;
}
$this->duplicateLinks($entity, $sourceEntity);
}
private function duplicateLinks(TargetList $entity, TargetList $sourceEntity): void
{
$repository = $this->entityManager->getRDBRepositoryByClass(TargetList::class);
foreach ($this->metadataProvider->getTargetLinkList() as $link) {
$collection = $repository
->getRelation($sourceEntity, $link)
->where(['@relation.optedOut' => false])
->find();
foreach ($collection as $relatedEntity) {
$repository
->getRelation($entity, $link)
->relate($relatedEntity, ['optedOut' => false]);
}
}
}
}

View File

@@ -0,0 +1,78 @@
<?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\Modules\Crm\Classes\RecordHooks\Task;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Entities\Email;
use Espo\Entities\User;
use Espo\Modules\Crm\Entities\Task;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements SaveHook<Task>
*/
class AfterSave implements SaveHook
{
public function __construct(
private EntityManager $entityManager,
private User $user,
) {}
public function process(Entity $entity): void
{
/** @var ?string $emailId */
$emailId = $entity->get('emailId');
if (!$emailId || !$entity->getAssignedUser()) {
return;
}
if (!$entity->isNew() && !$entity->isAttributeChanged('assignedUserId')) {
return;
}
$email = $this->entityManager->getRDBRepositoryByClass(Email::class)->getById($emailId);
if (!$email) {
return;
}
$relation = $this->entityManager->getRelation($email, 'users');
if ($relation->isRelatedById($entity->getAssignedUser()->getId())) {
return;
}
$isRead = $entity->getAssignedUser()->getId() === $this->user->getId();
$relation->relateById($entity->getAssignedUser()->getId(), [Email::USERS_COLUMN_IS_READ => $isRead]);
}
}

View File

@@ -0,0 +1,72 @@
<?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\Modules\Crm\Classes\RecordHooks\Task;
use Espo\Core\Acl;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Entities\Email;
use Espo\Modules\Crm\Entities\Task;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
/**
* @implements SaveHook<Task>
*/
class BeforeCreate implements SaveHook
{
public function __construct(
private Acl $acl,
private EntityManager $entityManager,
) {}
public function process(Entity $entity): void
{
/** @var ?string $emailId */
$emailId = $entity->get('originalEmailId');
if ($emailId === null) {
return;
}
$email = $this->entityManager->getRDBRepositoryByClass(Email::class)->getById($emailId);
if (!$email) {
throw new BadRequest("Email record not found.");
}
if (!$this->acl->checkEntityRead($entity)) {
throw new Forbidden("No access to email.");
}
$entity->set('emailId', $emailId);
}
}

View File

@@ -0,0 +1,55 @@
<?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\Modules\Crm\Classes\Select\Account\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Name\Attribute;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class PortalOnlyAccount implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$accountIdList = $this->user->getLinkMultipleIdList(User::LINK_ACCOUNTS);
if (!count($accountIdList)) {
$queryBuilder->where([Attribute::ID => null]);
return;
}
$queryBuilder->where([Attribute::ID => $accountIdList]);
}
}

View File

@@ -0,0 +1,42 @@
<?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\Modules\Crm\Classes\Select\Account\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\Account;
use Espo\ORM\Query\SelectBuilder;
class Customers implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where(['type' => Account::TYPE_CUSTOMER]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\Account\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class Partners implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'type' => 'Partner',
]);
}
}

View File

@@ -0,0 +1,50 @@
<?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\Modules\Crm\Classes\Select\Account\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Core\Utils\DateTime as DateTimeUtil;
use DateTime;
class RecentlyCreated implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$from = (new DateTime())
->modify('-7 days')
->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
$queryBuilder->where([
'createdAt>=' => $from,
]);
}
}

View File

@@ -0,0 +1,42 @@
<?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\Modules\Crm\Classes\Select\Account\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\Account;
use Espo\ORM\Query\SelectBuilder;
class Resellers implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where(['type' => Account::TYPE_RESELLER]);
}
}

View 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\Modules\Crm\Classes\Select\Call\PrimaryFilters;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
use Espo\Entities\User;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Select\Helpers\UserTimeZoneProvider;
use Espo\Core\Select\Where\ConverterFactory;
use Espo\Core\Select\Where\Item;
use Espo\ORM\Query\SelectBuilder;
use Espo\Modules\Crm\Entities\Call;
use LogicException;
class Todays implements Filter
{
public function __construct(
private User $user,
private UserTimeZoneProvider $userTimeZoneProvider,
private ConverterFactory $converterFactory
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$item = Item::fromRaw([
'type' => Item\Type::TODAY,
'attribute' => 'dateStart',
'timeZone' => $this->userTimeZoneProvider->get(),
'dateTime' => true,
]);
try {
$whereItem = $this->converterFactory
->create(Call::ENTITY_TYPE, $this->user)
->convert($queryBuilder, $item);
} catch (BadRequest $e) {
throw new LogicException($e->getMessage());
}
$queryBuilder->where($whereItem);
}
}

View File

@@ -0,0 +1,42 @@
<?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\Modules\Crm\Classes\Select\Campaign\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\Campaign;
use Espo\ORM\Query\SelectBuilder;
class Active implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where(['status' => Campaign::STATUS_ACTIVE]);
}
}

View File

@@ -0,0 +1,50 @@
<?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\Modules\Crm\Classes\Select\CampaignLogRecord\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class OnlyOwn implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder
->leftJoin('campaign', 'campaignAccess')
->where([
'campaignAccess.assignedUserId' => $this->user->getId(),
]);
}
}

View 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\Modules\Crm\Classes\Select\CampaignLogRecord\AccessControlFilters;
use Espo\Core\Name\Field;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class OnlyTeam implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->leftJoin('campaign', 'campaignAccess');
$teamIdList = $this->user->getLinkMultipleIdList(Field::TEAMS);
if (count($teamIdList) === 0) {
$queryBuilder->where([
'campaignAccess.assignedUserId' => $this->user->getId(),
]);
return;
}
$queryBuilder
->leftJoin(
'EntityTeam',
'entityTeamAccess',
[
'entityTeamAccess.entityType' => 'Campaign',
'entityTeamAccess.entityId:' => 'campaignAccess.id',
'entityTeamAccess.deleted' => false,
]
)
->where([
'OR' => [
'entityTeamAccess.teamId' => $teamIdList,
'campaignAccess.assignedUserId' => $this->user->getId(),
],
'campaignId!=' => null,
]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\CampaignLogRecord\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class Bounced implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'action' => 'Bounced',
]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\CampaignLogRecord\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class Clicked implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'action' => 'Clicked',
]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\CampaignLogRecord\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class LeadCreated implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'action' => 'Lead Created',
]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\CampaignLogRecord\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class Opened implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'action' => 'Opened',
]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\CampaignLogRecord\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class OptedIn implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'action' => 'Opted In',
]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\CampaignLogRecord\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class OptedOut implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'action' => 'Opted Out',
]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\CampaignLogRecord\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class Sent implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'action' => 'Sent',
]);
}
}

View File

@@ -0,0 +1,50 @@
<?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\Modules\Crm\Classes\Select\CampaignTrackingUrl\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class OnlyOwn implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder
->leftJoin('campaign', 'campaignAccess')
->where([
'campaignAccess.assignedUserId' => $this->user->getId(),
]);
}
}

View 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\Modules\Crm\Classes\Select\CampaignTrackingUrl\AccessControlFilters;
use Espo\Core\Name\Field;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class OnlyTeam implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->leftJoin('campaign', 'campaignAccess');
$teamIdList = $this->user->getLinkMultipleIdList(Field::TEAMS);
if (count($teamIdList) === 0) {
$queryBuilder->where([
'campaignAccess.assignedUserId' => $this->user->getId(),
]);
return;
}
$queryBuilder
->leftJoin(
'EntityTeam',
'entityTeamAccess',
[
'entityTeamAccess.entityType' => 'Campaign',
'entityTeamAccess.entityId:' => 'campaignAccess.id',
'entityTeamAccess.deleted' => false,
]
)
->where([
'OR' => [
'entityTeamAccess.teamId' => $teamIdList,
'campaignAccess.assignedUserId' => $this->user->getId(),
],
'campaignId!=' => null,
]);
}
}

View File

@@ -0,0 +1,50 @@
<?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\Modules\Crm\Classes\Select\Case\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\Entities\User;
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
class Mandatory implements Filter
{
public function __construct(
private User $user,
) {}
public function apply(QueryBuilder $queryBuilder): void
{
if (!$this->user->isPortal()) {
return;
}
$queryBuilder->where(['isInternal' => false]);
}
}

View 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\Modules\Crm\Classes\Select\CaseObj\BoolFilters;
use Espo\Core\Select\Bool\Filter;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Where\OrGroupBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
class Open implements Filter
{
public function __construct(private Metadata $metadata)
{}
public function apply(SelectBuilder $queryBuilder, OrGroupBuilder $orGroupBuilder): void
{
$notActualStatusList = $this->metadata
->get(['entityDefs', 'Case', 'fields', 'status', 'notActualOptions']) ?? [];
$orGroupBuilder->add(
Cond::notIn(
Cond::column('status'),
$notActualStatusList
)
);
}
}

View File

@@ -0,0 +1,42 @@
<?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\Modules\Crm\Classes\Select\CaseObj\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\CaseObj;
use Espo\ORM\Query\SelectBuilder;
class Closed implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where(['status' => CaseObj::STATUS_CLOSED]);
}
}

View File

@@ -0,0 +1,54 @@
<?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\Modules\Crm\Classes\Select\CaseObj\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\Part\Condition as Cond;
class Open implements Filter
{
public function __construct(private Metadata $metadata)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$notActualStatusList = $this->metadata
->get(['entityDefs', 'Case', 'fields', 'status', 'notActualOptions']) ?? [];
$queryBuilder->where(
Cond::notIn(
Cond::column('status'),
$notActualStatusList
)
);
}
}

View File

@@ -0,0 +1,54 @@
<?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\Modules\Crm\Classes\Select\Contact\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Name\Attribute;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class PortalOnlyContact implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$contactId = $this->user->getContactId();
if ($contactId === null) {
$queryBuilder->where([Attribute::ID => null]);
return;
}
$queryBuilder->where([Attribute::ID => $contactId]);
}
}

View File

@@ -0,0 +1,41 @@
<?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\Modules\Crm\Classes\Select\Contact\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class AccountActive implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where(['@relation.isInactive' => false]);
}
}

View File

@@ -0,0 +1,43 @@
<?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\Modules\Crm\Classes\Select\Contact\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class NotPortalUsers implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder
->leftJoin('portalUser', 'portalUserFilter')
->where(['portalUserFilter.id' => null]);
}
}

View File

@@ -0,0 +1,41 @@
<?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\Modules\Crm\Classes\Select\Contact\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
class PortalUsers implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->join('portalUser', 'portalUserFilter');
}
}

View File

@@ -0,0 +1,48 @@
<?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\Modules\Crm\Classes\Select\Document\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Utils\Metadata;
use Espo\Modules\Crm\Entities\Document;
use Espo\ORM\Query\SelectBuilder;
class Active implements Filter
{
public function __construct(private Metadata $metadata) {}
public function apply(SelectBuilder $queryBuilder): void
{
$statusList = $this->metadata->get("entityDefs.Document.fields.status.activeOptions") ??
[Document::STATUS_ACTIVE];
$queryBuilder->where(['status' => $statusList]);
}
}

View File

@@ -0,0 +1,44 @@
<?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\Modules\Crm\Classes\Select\Document\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\Document;
use Espo\ORM\Query\SelectBuilder;
class Draft implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => Document::STATUS_DRAFT,
]);
}
}

View File

@@ -0,0 +1,44 @@
<?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\Modules\Crm\Classes\Select\EmailQueueItem\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\EmailQueueItem;
use Espo\ORM\Query\SelectBuilder;
class Failed implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => EmailQueueItem::STATUS_FAILED,
]);
}
}

View File

@@ -0,0 +1,44 @@
<?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\Modules\Crm\Classes\Select\EmailQueueItem\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\EmailQueueItem;
use Espo\ORM\Query\SelectBuilder;
class Pending implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => EmailQueueItem::STATUS_PENDING,
]);
}
}

View File

@@ -0,0 +1,44 @@
<?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\Modules\Crm\Classes\Select\EmailQueueItem\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\EmailQueueItem;
use Espo\ORM\Query\SelectBuilder;
class Sent implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => EmailQueueItem::STATUS_SENT,
]);
}
}

View File

@@ -0,0 +1,70 @@
<?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\Modules\Crm\Classes\Select\KnowledgeBaseArticle\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\Core\Utils\Metadata;
use Espo\Entities\Portal;
use Espo\Modules\Crm\Entities\KnowledgeBaseArticle;
use Espo\ORM\Query\Part\Condition;
use Espo\ORM\Query\Part\Expression;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class Mandatory implements Filter
{
public function __construct(
private User $user,
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
if (!$this->user->isPortal()) {
return;
}
$statusList = $this->metadata->get("entityDefs.KnowledgeBaseArticle.fields.status.activeOptions") ??
[KnowledgeBaseArticle::STATUS_PUBLISHED];
$queryBuilder
->where(['status' => $statusList])
->where(
Condition::in(
Expression::column('id'),
SelectBuilder::create()
->select('knowledgeBaseArticleId')
->from(KnowledgeBaseArticle::ENTITY_TYPE . Portal::ENTITY_TYPE)
->where(['portalId' => $this->user->getPortalId()])
->build()
)
);
}
}

View File

@@ -0,0 +1,50 @@
<?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\Modules\Crm\Classes\Select\KnowledgeBaseArticle\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Utils\Metadata;
use Espo\Modules\Crm\Entities\KnowledgeBaseArticle;
use Espo\ORM\Query\SelectBuilder;
class Published implements Filter
{
public function __construct(
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$statusList = $this->metadata->get("entityDefs.KnowledgeBaseArticle.fields.status.activeOptions") ??
[KnowledgeBaseArticle::STATUS_PUBLISHED];
$queryBuilder->where(['status' => $statusList]);
}
}

View File

@@ -0,0 +1,54 @@
<?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\Modules\Crm\Classes\Select\Lead\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\Part\Condition as Cond;
class Actual implements Filter
{
public function __construct(private Metadata $metadata)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$notActualStatusList = $this->metadata
->get(['entityDefs', 'Lead', 'fields', 'status', 'notActualOptions']) ?? [];
$queryBuilder->where(
Cond::notIn(
Cond::column('status'),
$notActualStatusList
)
);
}
}

View File

@@ -0,0 +1,47 @@
<?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\Modules\Crm\Classes\Select\Lead\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
class Converted implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where(
Cond::equal(
Cond::column('status'),
'Converted'
)
);
}
}

View File

@@ -0,0 +1,50 @@
<?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\Modules\Crm\Classes\Select\MassEmail\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class OnlyOwn implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder
->leftJoin('campaign', 'campaignAccess')
->where([
'campaignAccess.assignedUserId' => $this->user->getId(),
]);
}
}

View 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\Modules\Crm\Classes\Select\MassEmail\AccessControlFilters;
use Espo\Core\Name\Field;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Entities\User;
class OnlyTeam implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->leftJoin('campaign', 'campaignAccess');
$teamIdList = $this->user->getLinkMultipleIdList(Field::TEAMS);
if (count($teamIdList) === 0) {
$queryBuilder->where([
'campaignAccess.assignedUserId' => $this->user->getId(),
]);
return;
}
$queryBuilder
->leftJoin(
'EntityTeam',
'entityTeamAccess',
[
'entityTeamAccess.entityType' => 'Campaign',
'entityTeamAccess.entityId:' => 'campaignAccess.id',
'entityTeamAccess.deleted' => false,
]
)
->where([
'OR' => [
'entityTeamAccess.teamId' => $teamIdList,
'campaignAccess.assignedUserId' => $this->user->getId(),
],
'campaignId!=' => null,
]);
}
}

View File

@@ -0,0 +1,47 @@
<?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\Modules\Crm\Classes\Select\MassEmail\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\MassEmail;
use Espo\ORM\Query\SelectBuilder;
class Actual implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => [
MassEmail::STATUS_PENDING,
MassEmail::STATUS_DRAFT,
],
]);
}
}

View File

@@ -0,0 +1,44 @@
<?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\Modules\Crm\Classes\Select\MassEmail\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\MassEmail;
use Espo\ORM\Query\SelectBuilder;
class Complete implements Filter
{
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => MassEmail::STATUS_COMPLETE,
]);
}
}

View 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\Modules\Crm\Classes\Select\Meeting\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Defs;
use Espo\ORM\Name\Attribute;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
use Espo\Entities\User;
class OnlyOwn implements Filter
{
public function __construct(
private User $user,
private string $entityType,
private Defs $defs
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$relationDefs = $this->defs
->getEntity($this->entityType)
->getRelation('users');
$middleEntityType = ucfirst($relationDefs->getRelationshipName());
$key1 = $relationDefs->getMidKey();
$queryBuilder->where(
Cond::in(
Cond::column(Attribute::ID),
SelectBuilder::create()
->select(Attribute::ID)
->from($this->entityType)
->leftJoin($middleEntityType, 'usersMiddle', [
"usersMiddle.{$key1}:" => 'id',
'usersMiddle.deleted' => false,
])
->where(
Cond::or(
Cond::equal(
Cond::column('usersMiddle.userId'),
$this->user->getId()
),
Cond::equal(
Cond::column('assignedUserId'),
$this->user->getId()
)
)
)
->build()
)
);
}
}

View File

@@ -0,0 +1,92 @@
<?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\Modules\Crm\Classes\Select\Meeting\AccessControlFilters;
use Espo\Core\Select\AccessControl\Filter;
use Espo\ORM\Defs;
use Espo\ORM\Name\Attribute;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
use Espo\Entities\User;
class OnlyTeam implements Filter
{
public function __construct(
private User $user,
private string $entityType,
private Defs $defs
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$relationDefs = $this->defs
->getEntity($this->entityType)
->getRelation('users');
$middleEntityType = ucfirst($relationDefs->getRelationshipName());
$key1 = $relationDefs->getMidKey();
$queryBuilder->where(
Cond::in(
Cond::column(Attribute::ID),
SelectBuilder::create()
->select(Attribute::ID)
->from($this->entityType)
->leftJoin('EntityTeam', 'entityTeam', [
'entityTeam.entityId:' => 'id',
'entityTeam.entityType' => $this->entityType,
'entityTeam.deleted' => false,
])
->leftJoin($middleEntityType, 'usersMiddle', [
"usersMiddle.{$key1}:" => 'id',
'usersMiddle.deleted' => false,
])
->where(
Cond::or(
Cond::in(
Cond::column('entityTeam.teamId'),
$this->user->getTeamIdList(),
),
Cond::equal(
Cond::column('usersMiddle.userId'),
$this->user->getId()
),
Cond::equal(
Cond::column('assignedUserId'),
$this->user->getId()
)
)
)
->build()
)
);
}
}

View File

@@ -0,0 +1,70 @@
<?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\Modules\Crm\Classes\Select\Meeting\BoolFilters;
use Espo\Core\Select\Bool\Filter;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Where\OrGroupBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
use Espo\Entities\User;
class OnlyMy implements Filter
{
public function __construct(private User $user)
{}
public function apply(SelectBuilder $queryBuilder, OrGroupBuilder $orGroupBuilder): void
{
$queryBuilder
->leftJoin('users', 'usersFilterOnlyMy');
$orGroupBuilder
->add(
Cond::and(
Cond::equal(
Cond::column('usersFilterOnlyMyMiddle.userId'),
$this->user->getId()
),
Cond::or(
Cond::notEqual(
Cond::column('usersFilterOnlyMyMiddle.status'),
Meeting::ATTENDEE_STATUS_DECLINED
),
Cond::equal(
Cond::column('usersFilterOnlyMyMiddle.status'),
null
)
)
)
);
}
}

View File

@@ -0,0 +1,49 @@
<?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\Modules\Crm\Classes\Select\Meeting\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\SelectBuilder;
class Held implements Filter
{
public function __construct(
private string $entityType,
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$statusList = $this->metadata->get(['scopes', $this->entityType, 'completedStatusList']) ?? [];
$queryBuilder->where(['status' => $statusList]);
}
}

View 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\Modules\Crm\Classes\Select\Meeting\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\SelectBuilder;
class Planned implements Filter
{
public const NAME = 'planned';
public function __construct(
private string $entityType,
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$statusList = $this->metadata->get(['scopes', $this->entityType, 'activityStatusList']) ?? [];
$queryBuilder->where(['status' => $statusList]);
}
}

View File

@@ -0,0 +1,72 @@
<?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\Modules\Crm\Classes\Select\Meeting\PrimaryFilters;
use Espo\Core\Exceptions\BadRequest;
use Espo\Entities\User;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\Core\Select\Helpers\UserTimeZoneProvider;
use Espo\Core\Select\Where\ConverterFactory;
use Espo\Core\Select\Where\Item;
use Espo\Modules\Crm\Entities\Meeting;
use LogicException;
/**
* @noinspection PhpUnused
*/
class Todays implements Filter
{
public function __construct(
private User $user,
private UserTimeZoneProvider $userTimeZoneProvider,
private ConverterFactory $converterFactory
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$item = Item::fromRaw([
'type' => Item\Type::TODAY,
'attribute' => 'dateStart',
'timeZone' => $this->userTimeZoneProvider->get(),
'dateTime' => true,
]);
try {
$whereItem = $this->converterFactory
->create(Meeting::ENTITY_TYPE, $this->user)
->convert($queryBuilder, $item);
} catch (BadRequest $e) {
throw new LogicException($e->getMessage());
}
$queryBuilder->where($whereItem);
}
}

View File

@@ -0,0 +1,112 @@
<?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\Modules\Crm\Classes\Select\Meeting\Where;
use Espo\Core\Select\Where\DateTimeItemTransformer as DateTimeItemTransformerInterface;
use Espo\Core\Select\Where\DefaultDateTimeItemTransformer;
use Espo\Core\Select\Where\Item;
/**
* Extends to take into account DateStartDate and DateEndDate fields.
*
* @noinspection PhpUnused
*/
class DateTimeItemTransformer implements DateTimeItemTransformerInterface
{
public function __construct(
private DefaultDateTimeItemTransformer $defaultDateTimeItemTransformer
) {}
public function transform(Item $item): Item
{
$type = $item->getType();
$value = $item->getValue();
$attribute = $item->getAttribute();
$transformedItem = $this->defaultDateTimeItemTransformer->transform($item);
if (
!in_array($attribute, ['dateStart', 'dateEnd']) ||
in_array($type, [
Item\Type::IS_NULL,
Item\Type::EVER,
Item\Type::IS_NOT_NULL,
])
) {
return $transformedItem;
}
$attributeDate = $attribute . 'Date';
if (is_string($value)) {
if (strlen($value) > 11) {
return $transformedItem;
}
} else if (is_array($value)) {
foreach ($value as $valueItem) {
if (is_string($valueItem) && strlen($valueItem) > 11) {
return $transformedItem;
}
}
}
$datePartRaw = [
'attribute' => $attributeDate,
'type' => $type,
'value' => $value,
];
$data = $item->getData();
if ($data instanceof Item\Data\DateTime) {
$datePartRaw['timeZone'] = $data->getTimeZone();
}
$raw = [
'type' => Item::TYPE_OR,
'value' => [
$datePartRaw,
[
'type' => Item::TYPE_AND,
'value' => [
$transformedItem->getRaw(),
[
'type' => Item\Type::IS_NULL,
'attribute' => $attributeDate,
]
]
]
]
];
return Item::fromRaw($raw);
}
}

View File

@@ -0,0 +1,58 @@
<?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\Modules\Crm\Classes\Select\Opportunity\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
use Espo\Modules\Crm\Classes\Select\Opportunity\Utils\StageListProvider;
class Lost implements Filter
{
private $stageListPoriver;
public function __construct(StageListProvider $stageListPoriver)
{
$this->stageListPoriver = $stageListPoriver;
}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where(
Cond::in(
Cond::column('stage'),
array_merge(
$this->stageListPoriver->getLost(),
)
)
);
}
}

Some files were not shown because too many files have changed in this diff Show More