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,107 @@
<?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\Core\Formula;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Parser\Ast\Attribute;
use Espo\Core\Formula\Parser\Ast\Node;
use Espo\Core\Formula\Parser\Ast\Value;
use Espo\Core\Formula\Parser\Ast\Variable;
/**
* A function argument.
*/
class Argument implements Evaluatable
{
public function __construct(private mixed $data)
{}
/**
* Get an argument type (function name).
*
* @throws Error
*/
public function getType(): string
{
if ($this->data instanceof Node) {
return $this->data->getType();
}
if ($this->data instanceof Value) {
return 'value';
}
if ($this->data instanceof Variable) {
return 'variable';
}
if ($this->data instanceof Attribute) {
return 'attribute';
}
throw new Error("Can't get type from scalar.");
}
/**
* Get a nested argument list.
*
* @throws Error
*/
public function getArgumentList(): ArgumentList
{
if ($this->data instanceof Node) {
return new ArgumentList($this->data->getChildNodes());
}
if ($this->data instanceof Value) {
return new ArgumentList([$this->data->getValue()]);
}
if ($this->data instanceof Variable) {
$value = new Value($this->data->getName());
return new ArgumentList([$value]);
}
if ($this->data instanceof Attribute) {
return new ArgumentList([$this->data->getName()]);
}
throw new Error("Can't get argument list from a non-node item.");
}
/**
* Get data.
*/
public function getData(): mixed
{
return $this->data;
}
}

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\Core\Formula;
use BadMethodCallException;
use OutOfBoundsException;
use Iterator;
use Countable;
use ArrayAccess;
use SeekableIterator;
/**
* A list of function arguments.
*
* @implements ArrayAccess<int, Argument>
* @implements Iterator<Argument>
* @implements SeekableIterator<int, Argument>
*/
class ArgumentList implements Evaluatable, Iterator, Countable, ArrayAccess, SeekableIterator
{
private int $position = 0;
/**
* @param array<int, mixed> $dataList
*/
public function __construct(private array $dataList)
{}
private function getLastValidKey(): int
{
$keys = array_keys($this->dataList);
$i = end($keys);
if ($i === false) {
return -1;
}
while ($i > 0) {
if (array_key_exists($i, $this->dataList)) {
break;
}
$i--;
}
return $i;
}
public function rewind(): void
{
$this->position = 0;
while (!$this->valid() && $this->position <= $this->getLastValidKey()) {
$this->position ++;
}
}
private function getArgumentByIndex(int $index): Argument
{
return new Argument($this->dataList[$index]);
}
/**
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->getArgumentByIndex($this->position);
}
/**
* @return mixed
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
}
public function next(): void
{
do {
$this->position ++;
$next = false;
if (
!$this->valid() &&
$this->position <= $this->getLastValidKey()
) {
$next = true;
}
} while ($next);
}
public function valid(): bool
{
return array_key_exists($this->position, $this->dataList);
}
/**
* @param mixed $offset
*/
public function offsetExists($offset): bool
{
return array_key_exists($offset, $this->dataList);
}
/**
* @param mixed $offset
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
if (!$this->offsetExists($offset)) {
return null;
}
return $this->getArgumentByIndex($offset);
}
/**
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value): void
{
throw new BadMethodCallException('Setting is not allowed.');
}
/**
* @param mixed $offset
*/
public function offsetUnset($offset): void
{
throw new BadMethodCallException('Unsetting is not allowed.');
}
public function count(): int
{
return count($this->dataList);
}
/**
* @param int $offset
*/
public function seek($offset): void
{
$this->position = $offset;
if (!$this->valid()) {
throw new OutOfBoundsException("Invalid seek offset ($offset).");
}
}
}

View File

@@ -0,0 +1,187 @@
<?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\Core\Formula;
use Espo\Core\FieldProcessing\SpecificFieldLoader;
use Espo\Core\ORM\Defs\AttributeParam;
use Espo\Core\Utils\FieldUtil;
use Espo\Entities\EmailAddress;
use Espo\Entities\PhoneNumber;
use Espo\ORM\Defs\Params\AttributeParam as OrmAttributeParam;
use Espo\ORM\Entity;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\ORM\EntityManager;
use Espo\Repositories\EmailAddress as EmailAddressRepository;
use Espo\Repositories\PhoneNumber as PhoneNumberRepository;
/**
* Fetches attributes from an entity.
*/
class AttributeFetcher
{
/** @var array<string, mixed> */
private $relatedEntitiesCacheMap = [];
public function __construct(
private EntityManager $entityManager,
private FieldUtil $fieldUtil,
private SpecificFieldLoader $specificFieldLoader,
) {}
public function fetch(Entity $entity, string $attribute, bool $getFetchedAttribute = false): mixed
{
if (str_contains($attribute, '.')) {
$arr = explode('.', $attribute);
$relationName = $arr[0];
$key = $this->buildKey($entity, $relationName);
if (
!array_key_exists($key, $this->relatedEntitiesCacheMap) &&
$entity->hasRelation($relationName) &&
!in_array(
$entity->getRelationType($relationName),
[Entity::MANY_MANY, Entity::HAS_MANY, Entity::HAS_CHILDREN]
)
) {
$this->relatedEntitiesCacheMap[$key] = $this->entityManager
->getRDBRepository($entity->getEntityType())
->getRelation($entity, $relationName)
->findOne();
}
$relatedEntity = $this->relatedEntitiesCacheMap[$key] ?? null;
if (
$relatedEntity instanceof Entity &&
count($arr) > 1
) {
return $this->fetch($relatedEntity, $arr[1]);
}
return null;
}
if ($getFetchedAttribute) {
return $entity->getFetched($attribute);
}
if (
$entity instanceof CoreEntity &&
!$entity->has($attribute)
) {
$this->load($entity, $attribute);
}
return $entity->get($attribute);
}
private function load(CoreEntity $entity, string $attribute): void
{
if ($entity->getAttributeParam($attribute, 'isParentName')) {
/** @var ?string $relationName */
$relationName = $entity->getAttributeParam($attribute, OrmAttributeParam::RELATION);
if ($relationName) {
$entity->loadParentNameField($relationName);
}
return;
}
if ($entity->getAttributeParam($attribute, AttributeParam::IS_LINK_MULTIPLE_ID_LIST)) {
/** @var ?string $relationName */
$relationName = $entity->getAttributeParam($attribute, OrmAttributeParam::RELATION);
if ($relationName) {
$entity->loadLinkMultipleField($relationName);
}
return;
}
if ($entity->getAttributeParam($attribute, 'isEmailAddressData')) {
/** @var ?string $fieldName */
$fieldName = $entity->getAttributeParam($attribute, 'field');
if (!$fieldName) {
return;
}
/** @var EmailAddressRepository $emailAddressRepository */
$emailAddressRepository = $this->entityManager->getRepository(EmailAddress::ENTITY_TYPE);
$data = $emailAddressRepository->getEmailAddressData($entity);
$entity->set($attribute, $data);
$entity->setFetched($attribute, $data);
return;
}
if ($entity->getAttributeParam($attribute, 'isPhoneNumberData')) {
/** @var ?string $fieldName */
$fieldName = $entity->getAttributeParam($attribute, 'field');
if (!$fieldName) {
return;
}
/** @var PhoneNumberRepository $phoneNumberRepository */
$phoneNumberRepository = $this->entityManager->getRepository(PhoneNumber::ENTITY_TYPE);
$data = $phoneNumberRepository->getPhoneNumberData($entity);
$entity->set($attribute, $data);
$entity->setFetched($attribute, $data);
return;
}
$field = $this->fieldUtil->getFieldOfAttribute($entity->getEntityType(), $attribute);
if (!$field) {
return;
}
$this->specificFieldLoader->process($entity, $field);
}
public function resetRuntimeCache(): void
{
$this->relatedEntitiesCacheMap = [];
}
private function buildKey(Entity $entity, string $link): string
{
return spl_object_hash($entity) . '-' . $link;
}
}

View File

@@ -0,0 +1,37 @@
<?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\Core\Formula;
/**
* A data that can be evaluated.
*/
interface Evaluatable
{
}

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\Core\Formula;
use BadMethodCallException;
use OutOfBoundsException;
use Iterator;
use Countable;
use ArrayAccess;
use SeekableIterator;
/**
* A list of evaluated function arguments.
*
* @implements ArrayAccess<int, mixed>
* @implements Iterator<mixed>
* @implements SeekableIterator<int, mixed>
*/
class EvaluatedArgumentList implements Iterator, Countable, ArrayAccess, SeekableIterator
{
private int $position = 0;
/**
* @param array<int, mixed> $dataList
*/
public function __construct(private array $dataList)
{}
private function getLastValidKey(): int
{
$keys = array_keys($this->dataList);
$i = end($keys);
if ($i === false) {
return -1;
}
while ($i > 0) {
if (array_key_exists($i, $this->dataList)) {
break;
}
$i--;
}
return $i;
}
public function rewind(): void
{
$this->position = 0;
while (!$this->valid() && $this->position <= $this->getLastValidKey()) {
$this->position ++;
}
}
private function getArgumentByIndex(int $index): mixed
{
return $this->dataList[$index];
}
/**
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->getArgumentByIndex($this->position);
}
/**
* @return mixed
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
}
public function next(): void
{
do {
$this->position ++;
$next = false;
if (
!$this->valid() &&
$this->position <= $this->getLastValidKey()
) {
$next = true;
}
} while ($next);
}
public function valid(): bool
{
return array_key_exists($this->position, $this->dataList);
}
/**
* @param mixed $offset
*/
public function offsetExists($offset): bool
{
return array_key_exists($offset, $this->dataList);
}
/**
* @param mixed $offset
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
if (!$this->offsetExists($offset)) {
return null;
}
return $this->getArgumentByIndex($offset);
}
/**
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value): void
{
throw new BadMethodCallException('Setting is not allowed.');
}
/**
* @param mixed $offset
*/
public function offsetUnset($offset): void
{
throw new BadMethodCallException('Unsetting is not allowed.');
}
public function count(): int
{
return count($this->dataList);
}
/**
* @param int $offset
*/
public function seek($offset): void
{
$this->position = $offset;
if (!$this->valid()) {
throw new OutOfBoundsException("Invalid seek offset ($offset).");
}
}
}

View File

@@ -0,0 +1,163 @@
<?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\Core\Formula;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Exceptions\ExecutionException;
use Espo\Core\Formula\Exceptions\SyntaxError;
use Espo\Core\Formula\Exceptions\UnsafeFunction;
use Espo\Core\Formula\Functions\Base as DeprecatedBaseFunction;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Formula\Parser\Ast\Attribute;
use Espo\Core\Formula\Parser\Ast\Node;
use Espo\Core\Formula\Parser\Ast\Value;
use Espo\Core\Formula\Parser\Ast\Variable;
use Espo\ORM\Entity;
use Espo\Core\InjectableFactory;
use LogicException;
use stdClass;
/**
* Creates an instance of Processor and executes a script.
*
* @internal
*/
class Evaluator
{
private Parser $parser;
private AttributeFetcher $attributeFetcher;
/** @var array<string, (Node|Value|Attribute|Variable)> */
private $parsedHash;
/**
* @param array<string, class-string<BaseFunction|Func|DeprecatedBaseFunction>> $functionClassNameMap
* @param string[] $unsafeFunctionList
*/
public function __construct(
private InjectableFactory $injectableFactory,
private array $functionClassNameMap = [],
private array $unsafeFunctionList = []
) {
$this->attributeFetcher = $injectableFactory->create(AttributeFetcher::class);
$this->parser = new Parser();
$this->parsedHash = [];
}
/**
* Process expression.
*
* @throws SyntaxError
* @throws Error
*/
public function process(string $expression, ?Entity $entity = null, ?stdClass $variables = null): mixed
{
return $this->processInternal($expression, $entity, $variables, false);
}
/**
* Process expression in safe mode.
*
* @throws SyntaxError
* @throws Error
*/
public function processSafe(string $expression, ?Entity $entity = null, ?stdClass $variables = null): mixed
{
return $this->processInternal($expression, $entity, $variables, true);
}
/**
* @throws SyntaxError
* @throws Error
*/
private function processInternal(
string $expression,
?Entity $entity,
?stdClass $variables,
bool $safeMode,
): mixed {
$processor = new Processor(
$this->injectableFactory,
$this->attributeFetcher,
$this->functionClassNameMap,
$entity,
$variables
);
$item = $this->getParsedExpression($expression);
if ($safeMode) {
$this->checkIsSafe($item->getData());
}
try {
$result = $processor->process($item);
} catch (ExecutionException $e) {
throw new LogicException('Unexpected ExecutionException.', 0, $e);
}
$this->attributeFetcher->resetRuntimeCache();
return $result;
}
/**
* @throws SyntaxError
*/
private function getParsedExpression(string $expression): Argument
{
if (!array_key_exists($expression, $this->parsedHash)) {
$this->parsedHash[$expression] = $this->parser->parse($expression);
}
return new Argument($this->parsedHash[$expression]);
}
/**
* @throws UnsafeFunction
*/
private function checkIsSafe(mixed $data): void
{
if (!$data instanceof Node) {
return;
}
$name = $data->getType();
if (in_array($name, $this->unsafeFunctionList)) {
throw new UnsafeFunction("$name is not safe.");
}
foreach ($data->getChildNodes() as $subData) {
$this->checkIsSafe($subData);
}
}
}

View File

@@ -0,0 +1,62 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Exceptions;
/**
* A bad argument type.
*/
class BadArgumentType extends Error
{
private ?int $position = null;
private ?string $type = null;
/**
* Create.
*
* @param int $position An argument position. Starts from 1.
* @param string $type A required argument type.
*/
public static function create(int $position, string $type): self
{
$obj = new self();
$obj->position = $position;
$obj->type = $type;
return $obj;
}
public function getLogMessage(): string
{
$position = (string) ($this->position ?? '?');
$type = $this->type ?? '?';
return "Bad argument type on position {$position}, must be {$type}.";
}
}

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\Core\Formula\Exceptions;
/**
* A bad argument value.
*/
class BadArgumentValue extends Error
{
private ?int $position = null;
private ?string $logMessage = null;
/**
* Create.
*
* @param int $position An argument position.
*/
public static function create(int $position, ?string $message = null): self
{
$obj = new self();
$obj->position = $position;
$obj->logMessage = $message;
return $obj;
}
public function getLogMessage(): string
{
$position = (string) ($this->position ?? '?');
$message = "Bad argument value on position $position.";
if ($this->logMessage) {
$message .= " " . $this->logMessage;
}
return $message;
}
}

View File

@@ -0,0 +1,33 @@
<?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\Core\Formula\Exceptions;
class BreakLoop extends ExecutionException
{}

View File

@@ -0,0 +1,33 @@
<?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\Core\Formula\Exceptions;
class ContinueLoop extends ExecutionException
{}

View File

@@ -0,0 +1,38 @@
<?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\Core\Formula\Exceptions;
class Error extends \Exception
{
/**
* @var int
*/
protected $code = 500;
}

View File

@@ -0,0 +1,33 @@
<?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\Core\Formula\Exceptions;
class ExecutionException extends \Exception
{}

View File

@@ -0,0 +1,34 @@
<?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\Core\Formula\Exceptions;
class NotPassedEntity extends Error
{
}

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\Core\Formula\Exceptions;
use Throwable;
class SyntaxError extends Error
{
/**
* @var ?string
*/
private $shortMessage = null;
final public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
public static function create(string $message, ?string $shortMessage = null): self
{
$obj = new static($message);
$obj->shortMessage = $shortMessage;
return $obj;
}
public function getShortMessage(): ?string
{
return $this->shortMessage ?? $this->getMessage();
}
}

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\Core\Formula\Exceptions;
/**
* Too few function arguments passed.
*/
class TooFewArguments extends Error
{
private ?int $number = null;
/**
* Create.
*
* @param int $number A required number of arguments.
*/
public static function create(int $number): self
{
$obj = new self();
$obj->number = $number;
return $obj;
}
public function getLogMessage(): string
{
$number = (string) ($this->number ?? '?');
return "Too few arguments passed, must be {$number}.";
}
}

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\Core\Formula\Exceptions;
class UndefinedKey extends Error
{
private int $levelsRisen = 0;
public function getLevelsRisen(): int
{
return $this->levelsRisen;
}
public static function cloneWithLevelRisen(self $exception): UndefinedKey
{
$obj = new UndefinedKey($exception->getMessage(), $exception->getCode(), $exception);
$obj->levelsRisen = $exception->getLevelsRisen() + 1;
return $obj;
}
}

View File

@@ -0,0 +1,34 @@
<?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\Core\Formula\Exceptions;
class UnknownFunction extends Error
{
}

View File

@@ -0,0 +1,34 @@
<?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\Core\Formula\Exceptions;
class UnsafeFunction extends Error
{
}

View File

@@ -0,0 +1,46 @@
<?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\Core\Formula;
use Espo\Core\Formula\Exceptions\Error;
/**
* A function.
*/
interface Func
{
/**
* Process.
*
* @param EvaluatedArgumentList $arguments
* @throws Error
*/
public function process(EvaluatedArgumentList $arguments): mixed;
}

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\Core\Formula;
use Espo\Core\Formula\Exceptions\Error;
/**
* A function aware of variables.
*
* @since 8.3.0
*/
interface FuncVariablesAware
{
/**
* Process.
*
* @throws Error
*/
public function process(EvaluatedArgumentList $arguments, Variables $variables): mixed;
}

View File

@@ -0,0 +1,114 @@
<?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\Core\Formula;
use Espo\Core\Formula\Exceptions\UnknownFunction;
use Espo\Core\Formula\Functions\Base;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\ORM\Entity;
use Espo\Core\InjectableFactory;
use ReflectionClass;
use stdClass;
class FunctionFactory
{
/** @var array<string, class-string<BaseFunction|Func|Base>> */
private $classNameMap;
/**
* @param array<string, class-string<BaseFunction|Func|Base>> $classNameMap
*/
public function __construct(
private Processor $processor,
private InjectableFactory $injectableFactory,
private AttributeFetcher $attributeFetcher,
?array $classNameMap = null
) {
$this->classNameMap = $classNameMap ?? [];
}
/**
* @throws UnknownFunction
*/
public function create(
string $name,
?Entity $entity = null,
?stdClass $variables = null
): Func|FuncVariablesAware|BaseFunction|Base {
if ($this->classNameMap && array_key_exists($name, $this->classNameMap)) {
$className = $this->classNameMap[$name];
} else {
$arr = explode('\\', $name);
foreach ($arr as $i => $part) {
if ($i < count($arr) - 1) {
$part = $part . 'Group';
}
$arr[$i] = ucfirst($part);
}
$typeName = implode('\\', $arr);
/** @var class-string<Func|FuncVariablesAware|BaseFunction|Base> $className */
$className = 'Espo\\Core\\Formula\\Functions\\' . $typeName . 'Type';
}
if (!class_exists($className)) {
throw new UnknownFunction("Unknown function: " . $name);
}
$class = new ReflectionClass($className);
if (
$class->implementsInterface(Func::class) ||
$class->implementsInterface(FuncVariablesAware::class)
) {
return $this->injectableFactory->create($className);
}
$object = $this->injectableFactory->createWith($className, [
'name' => $name,
'processor' => $this->processor,
'entity' => $entity,
'variables' => $variables,
'attributeFetcher' => $this->attributeFetcher,
]);
if (method_exists($object, 'setAttributeFetcher')) {
$object->setAttributeFetcher($this->attributeFetcher);
}
/** @var BaseFunction|Base */
return $object;
}
}

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\Core\Formula\Functions;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Exceptions\Error;
/**
* @noinspection PhpUnused
*/
class ArrayAppendType extends BaseFunction
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$name = $this->evaluate($args[0]);
if (!is_string($name)) {
$this->throwBadArgumentValue(1, 'string');
}
$value = $this->evaluate($args[1]);
if (!property_exists($this->getVariables(), $name)) {
throw new Error("Cannot array-append to not existing variable.");
}
$array =& $this->getVariables()->$name;
if (!is_array($array)) {
throw new Error("Cannot array-append to non-array variable.");
}
$array[] = $value;
}
}

View File

@@ -0,0 +1,65 @@
<?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\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class AtType extends BaseFunction
{
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$array = $args[0] ?? [];
$index = $args[1];
if (!is_array($array)) {
$this->throwBadArgumentType(1, 'array');
}
if (!is_int($index)) {
$this->throwBadArgumentType(2, 'int');
}
if (!array_key_exists($index, $array)) {
$this->log("index doesn't exist");
return null;
}
return $array[$index];
}
}

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\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class IncludesType extends BaseFunction
{
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$list = $args[0] ?? [];
$needle = $args[1];
if (!is_array($list)) {
return false;
}
return in_array($needle, $list);
}
}

View File

@@ -0,0 +1,62 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class IndexOfType extends BaseFunction
{
public function process(ArgumentList $args)
{
$evaluatedArgs = $this->evaluate($args);
if (count($evaluatedArgs) < 2) {
$this->throwTooFewArguments();
}
$array = $evaluatedArgs[0] ?? [];
$needle = $evaluatedArgs[1];
if (!is_array($array)) {
$this->throwBadArgumentType(1, 'array');
}
$result = array_search($needle, $array, true);
if ($result === false || is_string($result)) {
return null;
}
return $result;
}
}

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\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class JoinType extends BaseFunction
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$list = $this->evaluate($args[0]) ?? [];
$separator = $this->evaluate($args[1]);
if (!is_string($separator)) {
$this->throwBadArgumentValue(2, 'string');
}
return implode($separator, $list);
}
}

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\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class LengthType extends BaseFunction
{
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$list = $args[0];
if (!is_array($list)) {
return 0;
}
return count($list);
}
}

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\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class PushType extends BaseFunction
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$list = $this->evaluate($args[0]) ?? [];
if (!is_array($list)) {
$this->throwError("Argument is non-array.");
}
foreach ($args as $i => $v) {
if ($i === 0) {
continue;
}
$element = $this->evaluate($args[$i]);
$list[] = $element;
}
return $list;
}
}

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\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class RemoveAtType extends BaseFunction
{
public function process(ArgumentList $args)
{
$evaluatedArgs = $this->evaluate($args);
if (count($evaluatedArgs) < 2) {
$this->throwTooFewArguments();
}
$array = $evaluatedArgs[0] ?? [];
$index = $evaluatedArgs[1];
if ($index === null) {
return $array;
}
if (!is_array($array)) {
$this->throwBadArgumentType(1, 'array');
}
if (!is_int($index)) {
$this->throwBadArgumentType(2, 'int');
}
if (!array_key_exists($index, $array)) {
return $array;
}
unset($array[$index]);
return array_values($array);
}
}

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\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class UniqueType extends BaseFunction
{
public function process(ArgumentList $args)
{
$evaluatedArgs = $this->evaluate($args);
if (count($evaluatedArgs) < 1) {
$this->throwTooFewArguments();
}
$array = $evaluatedArgs[0] ?? [];
if (!is_array($array)) {
$this->throwBadArgumentType(1, 'array');
}
return array_values(array_unique($array));
}
}

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\Core\Formula\Functions;
use Espo\Core\Formula\ArgumentList;
class AssignType extends BaseFunction
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$name = $this->evaluate($args[0]);
if (!is_string($name)) {
$this->throwBadArgumentValue(1, 'string');
}
$value = $this->evaluate($args[1]);
$this->getVariables()->$name = $value;
return $value;
}
}

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\Core\Formula\Functions;
use Espo\Core\Exceptions\Error;
use Espo\Core\Formula\AttributeFetcher;
class AttributeType extends Base
{
/**
* @var AttributeFetcher
*/
protected $attributeFetcher;
/**
* @return void
*/
public function setAttributeFetcher(AttributeFetcher $attributeFetcher)
{
$this->attributeFetcher = $attributeFetcher;
}
/**
* @return mixed
* @throws Error
*/
public function process(\stdClass $item)
{
if (!property_exists($item, 'value')) {
throw new Error();
}
return $this->getAttributeValue($item->value);
}
/**
* @param string $attribute
* @return mixed
* @throws Error
*/
protected function getAttributeValue($attribute)
{
return $this->attributeFetcher->fetch($this->getEntity(), $attribute);
}
}

View File

@@ -0,0 +1,132 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions;
use Espo\Core\Formula\Exceptions\Error;
use Espo\ORM\Entity;
use Espo\Core\Formula\Processor;
use Espo\Core\Formula\Argument;
use stdClass;
/**
* @deprecated Use Func interface instead.
* @todo Remove in v11.0.
*/
abstract class Base
{
/**
* @var ?string
*/
protected $name;
/**
* @var Processor
*/
protected $processor;
/**
* @var ?Entity
*/
private $entity;
/**
* @var ?\stdClass
*/
private $variables;
public function __construct(string $name, Processor $processor, ?Entity $entity = null, ?stdClass $variables = null)
{
$this->name = $name;
$this->processor = $processor;
$this->entity = $entity;
$this->variables = $variables;
}
protected function getVariables(): stdClass
{
return $this->variables ?? (object) [];
}
/**
* @throws Error
*/
protected function getEntity() /** @phpstan-ignore-line */
{
if (!$this->entity) {
throw new Error('Formula: Entity required but not passed.');
}
return $this->entity;
}
/**
* @return mixed
* @throws Error
*/
public abstract function process(stdClass $item);
/**
* @param mixed $item
* @return mixed
* @throws Error
*/
protected function evaluate($item)
{
$item = new Argument($item);
return $this->processor->process($item);
}
/**
* @return mixed[]
* @throws Error
*/
protected function fetchArguments(stdClass $item): array
{
$args = $item->value ?? [];
$eArgs = [];
foreach ($args as $item) {
$eArgs[] = $this->evaluate($item);
}
return $eArgs;
}
/**
* @return mixed[]
*/
protected function fetchRawArguments(stdClass $item): array
{
return $item->value ?? [];
}
}

View File

@@ -0,0 +1,199 @@
<?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\Core\Formula\Functions;
use Espo\ORM\Entity;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Evaluatable;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\BadArgumentValue;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Exceptions\ExecutionException;
use Espo\Core\Formula\Exceptions\NotPassedEntity;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Processor;
use Espo\Core\Utils\Log;
use stdClass;
/**
* A base abstract function. Avoid extending. Use Func interface instead.
*/
abstract class BaseFunction
{
protected function getVariables(): stdClass
{
return $this->variables ?? (object) [];
}
/**
* Get a target entity.
*
* @throws NotPassedEntity
*/
protected function getEntity(): Entity
{
if (!$this->entity) {
throw new NotPassedEntity('function: ' . $this->name);
}
return $this->entity;
}
public function __construct(
protected string $name,
protected Processor $processor,
private ?Entity $entity = null,
private ?stdClass $variables = null,
protected ?Log $log = null
) {}
/**
* Evaluates a function.
*
* @return mixed A result of the function.
* @throws Error
* @throws ExecutionException
*/
public abstract function process(ArgumentList $args);
/**
* Evaluates an argument or argument list.
*
* @param Evaluatable $item Argument or ArgumentList.
* @return mixed A result of evaluation. An array if an argument list was passed.
* @throws Error
* @throws ExecutionException
*/
protected function evaluate(Evaluatable $item)
{
return $this->processor->process($item);
}
/**
* Throws TooFewArguments exception.
*
* @return never
* @throws TooFewArguments
*/
protected function throwTooFewArguments(?int $number = null)
{
$msg = 'function: ' . $this->name;
if ($number !== null) {
$msg .= ', needs: ' . $number;
}
throw new TooFewArguments($msg);
}
/**
* Throw BadArgumentType exception.
*
* @return never
* @throws BadArgumentType
*/
protected function throwBadArgumentType(?int $index = null, ?string $type = null)
{
$msg = 'function: ' . $this->name;
if ($index !== null) {
$msg .= ', index: ' . $index;
if ($type) {
$msg .= ', should be: ' . $type;
}
}
throw new BadArgumentType($msg);
}
/**
* Throw BadArgumentValue exception.
*
* @return never
* @throws BadArgumentValue
*/
protected function throwBadArgumentValue(?int $index = null, ?string $msg = null)
{
$string = 'function: ' . $this->name;
if ($index !== null) {
$string .= ', index: ' . $index;
if ($msg) {
$string .= ', ' . $msg;
}
}
throw new BadArgumentValue($string);
}
/**
* Throw Error exception.
*
* @return never
* @throws Error
*/
protected function throwError(?string $msg = null)
{
$string = 'function: ' . $this->name;
if ($msg) {
$string .= ', ' . $msg;
}
throw new Error($string);
}
/**
* Log a bad argument type.
*/
protected function logBadArgumentType(int $index, string $type): void
{
if (!$this->log) {
return;
}
$this->log->warning("Formula function: {$this->name}, argument {$index} should be '{$type}'.");
}
/**
* Log a message.
*/
protected function log(string $msg, string $level = 'notice'): void
{
if (!$this->log) {
return;
}
$this->log->log($level, 'Formula function: ' . $this->name . ', ' . $msg);
}
}

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\Core\Formula\Functions;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Exceptions\BreakLoop;
class BreakType extends BaseFunction
{
public function process(ArgumentList $args)
{
throw new BreakLoop();
}
}

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\Core\Formula\Functions;
use Espo\Core\Formula\ArgumentList;
/**
* @noinspection PhpUnused
*/
class BundleType extends BaseFunction
{
public function process(ArgumentList $args)
{
$value = null;
foreach ($args as $item) {
$value = $this->evaluate($item);
}
return $value;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\ComparisonGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
abstract class Base extends BaseFunction
{
/**
* @return bool
* @throws \Espo\Core\Formula\Exceptions\TooFewArguments
* @throws \Espo\Core\Formula\Exceptions\Error
*/
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$left = $this->evaluate($args[0]);
$right = $this->evaluate($args[1]);
return $this->compare($left, $right);
}
/**
* @param mixed $left
* @param mixed $right
* @return bool
*/
abstract protected function compare($left, $right);
}

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\Core\Formula\Functions\ComparisonGroup;
class EqualsType extends Base
{
/**
* @param mixed $left
* @param mixed $right
* @return bool
*/
protected function compare($left, $right)
{
if (is_array($left) && is_array($right)) {
$result = true;
foreach ($left as $i => $value) {
if (!array_key_exists($i, $right)) {
$result = false;
break;
}
if ($value !== $right[$i]) {
$result = false;
break;
}
}
return $result;
}
return $left === $right;
}
}

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\Core\Formula\Functions\ComparisonGroup;
class GreaterThanOrEqualsType extends Base
{
/**
* @param mixed $left
* @param mixed $right
* @return bool
*/
protected function compare($left, $right)
{
return $left >= $right;
}
}

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\Core\Formula\Functions\ComparisonGroup;
class GreaterThanType extends Base
{
/**
* @param mixed $left
* @param mixed $right
* @return bool
*/
protected function compare($left, $right)
{
return $left > $right;
}
}

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\Core\Formula\Functions\ComparisonGroup;
class LessThanOrEqualsType extends Base
{
/**
* @param mixed $left
* @param mixed $right
* @return bool
*/
protected function compare($left, $right)
{
return $left <= $right;
}
}

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\Core\Formula\Functions\ComparisonGroup;
class LessThanType extends Base
{
/**
* @param mixed $left
* @param mixed $right
* @return bool
*/
protected function compare($left, $right)
{
return $left < $right;
}
}

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\Core\Formula\Functions\ComparisonGroup;
class NotEqualsType extends EqualsType
{
/**
* @param mixed $left
* @param mixed $right
* @return bool
*/
protected function compare($left, $right)
{
return !parent::compare($left, $right);
}
}

View File

@@ -0,0 +1,69 @@
<?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\Core\Formula\Functions\ComparisonGroup;
use Espo\Core\Formula\Exceptions\UndefinedKey;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Formula\ArgumentList;
class NullCoalescingType extends BaseFunction
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$array = [];
foreach ($args as $arg) {
$array[] = $arg;
}
foreach (array_slice($array, 0, -1) as $arg) {
try {
$value = $this->evaluate($arg);
} catch (UndefinedKey $e) {
if ($e->getLevelsRisen() > 1) {
throw $e;
}
$value = null;
}
if ($value !== null) {
return $value;
}
}
return $this->evaluate($array[count($array) - 1]);
}
}

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\Core\Formula\Functions;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class ConditionType extends BaseFunction
{
public function process(ArgumentList $args)
{
return $this->evaluate($args[0]);
}
}

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\Core\Formula\Functions;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Exceptions\ContinueLoop;
class ContinueType extends BaseFunction
{
public function process(ArgumentList $args)
{
throw new ContinueLoop();
}
}

View File

@@ -0,0 +1,35 @@
<?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\Core\Formula\Functions\DatetimeGroup;
class AddDaysType extends AddIntervalType
{
protected string $intervalTypeString = 'days';
}

View File

@@ -0,0 +1,36 @@
<?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\Core\Formula\Functions\DatetimeGroup;
class AddHoursType extends AddIntervalType
{
protected string $intervalTypeString = 'hours';
protected bool $timeOnly = true;
}

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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Utils\DateTime as DateTimeUtil;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Functions\BaseFunction;
use DateTime;
use Exception;
abstract class AddIntervalType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
protected bool $timeOnly = false;
protected string $intervalTypeString;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$dateTimeString = $args[0];
if (!$dateTimeString) {
return null;
}
if (!is_string($dateTimeString)) {
$this->throwBadArgumentType(1, 'string');
}
$interval = $args[1];
if (!is_numeric($interval)) {
$this->throwBadArgumentType(2, 'numeric');
}
$isTime = false;
if (strlen($dateTimeString) > 10) {
$isTime = true;
}
if ($this->timeOnly && !$isTime) {
$dateTimeString .= ' 00:00:00';
$isTime = true;
}
try {
$dateTime = new DateTime($dateTimeString);
} catch (Exception) {
$this->log('bad date-time value passed', 'warning');
return null;
}
$dateTime->modify(
($interval > 0 ? '+' : '') . strval($interval) . ' ' . $this->intervalTypeString
);
if ($isTime) {
return $dateTime->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT);
} else {
return $dateTime->format(DateTimeUtil::SYSTEM_DATE_FORMAT);
}
}
}

View File

@@ -0,0 +1,37 @@
<?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\Core\Formula\Functions\DatetimeGroup;
class AddMinutesType extends AddIntervalType
{
protected string $intervalTypeString = 'minutes';
protected bool $timeOnly = true;
}

View File

@@ -0,0 +1,35 @@
<?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\Core\Formula\Functions\DatetimeGroup;
class AddMonthsType extends AddIntervalType
{
protected string $intervalTypeString = 'months';
}

View File

@@ -0,0 +1,36 @@
<?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\Core\Formula\Functions\DatetimeGroup;
class AddSecondsType extends AddIntervalType
{
protected string $intervalTypeString = 'seconds';
protected bool $timeOnly = true;
}

View File

@@ -0,0 +1,35 @@
<?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\Core\Formula\Functions\DatetimeGroup;
class AddWeeksType extends AddIntervalType
{
protected string $intervalTypeString = 'weeks';
}

View File

@@ -0,0 +1,35 @@
<?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\Core\Formula\Functions\DatetimeGroup;
class AddYearsType extends AddIntervalType
{
protected string $intervalTypeString = 'years';
}

View File

@@ -0,0 +1,249 @@
<?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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
use DateTime;
use DateTimeZone;
class ClosestType extends BaseFunction implements Di\ConfigAware
{
use Di\ConfigSetter;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 3) {
$this->throwTooFewArguments();
}
$value = $args[0];
$type = $args[1];
$target = $args[2];
if (!in_array($type, ['time', 'minute', 'hour', 'date', 'dayOfWeek', 'month'])) {
$this->throwBadArgumentType(1);
}
$inPast = false;
if (count($args) > 3) {
$inPast = $args[3];
}
$timezone = null;
if (count($args) > 4) {
$timezone = $args[4];
}
if (!$value) {
return null;
}
if (!is_string($value)) {
$this->throwBadArgumentType(1, 'string');
}
if (!$timezone) {
$timezone = $this->config->get('timeZone');
}
$isDate = false;
if (strlen($value) === 10) {
$isDate = true;
$value .= ' 00:00:00';
}
if (strlen($value) === 16) {
$value .= ':00';
}
$format = 'Y-m-d H:i:s';
/** @var DateTime $dt */
$dt = DateTime::createFromFormat($format, $value, new DateTimeZone($timezone));
$valueTimestamp = $dt->getTimestamp();
if ($type === 'time') {
if (!is_string($target)) {
$this->throwBadArgumentType(3, 'string');
}
list($hour, $minute) = explode(':', $target);
if (!$hour) {
$hour = 0;
}
if (!$minute) {
$minute = 0;
}
$dt->setTime((int) $hour, (int) $minute, 0);
if ($valueTimestamp < $dt->getTimestamp()) {
if ($inPast) {
$dt->modify('-1 day');
}
} else if ($valueTimestamp > $dt->getTimestamp()) {
if (!$inPast) {
$dt->modify('+1 day');
}
}
} else if ($type === 'hour') {
$target = intval($target);
$dt->setTime($target, 0, 0);
if ($valueTimestamp < $dt->getTimestamp()) {
if ($inPast) {
$dt->modify('-1 day');
}
} else if ($valueTimestamp > $dt->getTimestamp()) {
if (!$inPast) {
$dt->modify('+1 day');
}
}
} else if ($type === 'minute') {
$target = intval($target);
$dt->setTime(intval($dt->format('G')), intval($target), 0);
if ($valueTimestamp < $dt->getTimestamp()) {
if ($inPast) {
$dt->modify('-1 hour');
}
} else if ($valueTimestamp > $dt->getTimestamp()) {
if (!$inPast) {
$dt->modify('+1 hour');
}
}
} else if ($type === 'dayOfWeek') {
$target = intval($target);
$dt->setTime(0, 0, 0);
$dayOfWeek = $dt->format('w');
$dt->modify('-' . $dayOfWeek . ' days');
$dt->modify('+' . $target . ' days');
if ($valueTimestamp < $dt->getTimestamp()) {
if ($inPast) {
$dt->modify('-1 week');
}
} else if ($valueTimestamp > $dt->getTimestamp()) {
if (!$inPast) {
$dt->modify('+1 week');
}
}
} else if ($type === 'date') {
$target = intval($target);
$dt->setTime(0, 0, 0);
if ($inPast) {
while (true) {
$date = intval($dt->format('d'));
if ($date === $target) {
break;
}
$dt->modify('-1 day');
}
} else {
if ($valueTimestamp > $dt->getTimestamp()) {
$dt->modify('+1 day');
}
while (true) {
$date = intval($dt->format('d'));
if ($date === $target) {
break;
}
$dt->modify('+1 day');
}
}
} else if ($type === 'month') {
$target = intval($target);
$dt->setTime(0, 0, 0);
$days = intval($dt->format('d')) - 1;
$dt->modify('-' . $days . ' days');
if ($inPast) {
while (true) {
$month = intval($dt->format('m'));
if ($month === $target) {
break;
}
$dt->modify('-1 month');
}
} else {
if ($valueTimestamp > $dt->getTimestamp()) {
$dt->modify('+1 month');
}
while (true) {
$month = intval($dt->format('m'));
if ($month === $target) {
break;
}
$dt->modify('+1 month');
}
}
}
if ($isDate && in_array($type, ['time', 'minute', 'hour'])) {
$isDate = false;
}
if (!$isDate) {
$dt->setTimezone(new DateTimeZone('UTC'));
return $dt->format('Y-m-d H:i');
}
return $dt->format('Y-m-d');
}
}

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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class DateType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $args[0];
$timezone = null;
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) {
return 0;
}
if (strlen($value) > 11) {
$resultString = $this->dateTime->convertSystemDateTime($value, $timezone, 'D');
} else {
$resultString = $this->dateTime->convertSystemDate($value, 'D');
}
return intval($resultString);
}
}

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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class DayOfWeekType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $args[0];
$timezone = null;
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) {
return -1;
}
if (strlen($value) > 11) {
$resultString = $this->dateTime->convertSystemDateTime($value, $timezone, 'd');
} else {
$resultString = $this->dateTime->convertSystemDate($value, 'd');
}
$result = intval($resultString);
return $result;
}
}

View File

@@ -0,0 +1,143 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Exceptions\ExecutionException;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Functions\BaseFunction;
use DateTime;
class DiffType extends BaseFunction
{
/**
* @var array<string, string>
*/
protected $intervalTypePropertyMap = [
'years' => 'y',
'months' => 'm',
'days' => 'd',
'hours' => 'h',
'minutes' => 'i',
'seconds' => 's',
];
/**
* @return ?int
* @throws TooFewArguments
* @throws Error
* @throws ExecutionException
*/
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$dateTime1String = $args[0];
$dateTime2String = $args[1];
if (!$dateTime1String) {
return null;
}
if (!$dateTime2String) {
return null;
}
if (!is_string($dateTime1String)) {
$this->throwBadArgumentType(1, 'string');
}
if (!is_string($dateTime2String)) {
$this->throwBadArgumentType(2, 'string');
}
$intervalType = 'days';
if (count($args) > 2) {
$intervalType = $args[2];
}
if (!is_string($intervalType)) {
$this->throwBadArgumentType(3, 'string');
}
if (!array_key_exists($intervalType, $this->intervalTypePropertyMap)) {
$this->throwBadArgumentValue(3, "not supported interval type '{$intervalType}'");
}
$isTime = false;
if (strlen($dateTime1String) > 10) {
$isTime = true;
}
try {
$dateTime1 = new DateTime($dateTime1String);
$dateTime2 = new DateTime($dateTime2String);
} catch (\Exception $e) {
return null;
}
$t1 = $dateTime1->getTimestamp();
$t2 = $dateTime2->getTimestamp();
$secondsDiff = $t1 - $t2;
if ($intervalType === 'seconds') {
$number = $secondsDiff;
} else if ($intervalType === 'minutes') {
$number = floor($secondsDiff / 60);
} else if ($intervalType === 'hours') {
$number = floor($secondsDiff / (60 * 60));
} else if ($intervalType === 'days') {
$number = floor($secondsDiff / (60 * 60 * 24));
} else {
$property = $this->intervalTypePropertyMap[$intervalType];
$interval = $dateTime2->diff($dateTime1);
$number = $interval->$property;
if ($interval->invert) {
$number *= -1;
}
if ($intervalType === 'months') {
$number += $interval->y * 12;
}
}
return $number;
}
}

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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Formula\{
Exceptions\Error,
Functions\BaseFunction,
ArgumentList};
use RuntimeException;
class FormatType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$timezone = null;
if (count($args) > 1) {
$timezone = $args[1];
}
$value = $args[0];
$format = null;
if (count($args) > 2) {
$format = $args[2];
}
try {
if (strlen($value) > 11) {
return $this->dateTime->convertSystemDateTime($value, $timezone, $format);
} else {
return $this->dateTime->convertSystemDate($value, $format);
}
} catch (RuntimeException $e) {
throw new Error($e->getMessage(), 500, $e);
}
}
}

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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class HourType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $args[0];
$timezone = null;
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) {
return -1;
}
if (strlen($value) > 11) {
$resultString = $this->dateTime->convertSystemDateTime($value, $timezone, 'H');
} else {
return 0;
}
return intval($resultString);
}
}

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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class MinuteType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $args[0];
$timezone = null;
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) {
return -1;
}
if (strlen($value) > 11) {
$resultString = $this->dateTime->convertSystemDateTime($value, $timezone, 'm');
} else {
return 0;
}
return intval($resultString);
}
}

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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class MonthType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $args[0];
$timezone = null;
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) {
return 0;
}
if (strlen($value) > 11) {
$resultString = $this->dateTime->convertSystemDateTime($value, $timezone, 'M');
} else {
$resultString = $this->dateTime->convertSystemDate($value, 'M');
}
return intval($resultString);
}
}

View File

@@ -0,0 +1,45 @@
<?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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Utils\DateTime;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class NowType extends BaseFunction
{
public function process(ArgumentList $args)
{
return DateTime::getSystemNowString();
}
}

View File

@@ -0,0 +1,62 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\DatetimeGroup;
use DateTimeZone;
use Espo\Core\Field\DateTime;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Func;
use Espo\Core\Utils\Config\ApplicationConfig;
use Espo\Core\Utils\DateTime as DateTimeUtil;
use Exception;
use RuntimeException;
/**
* @noinspection PhpUnused
*/
class TodayType implements Func
{
public function __construct(
private ApplicationConfig $applicationConfig
) {}
public function process(EvaluatedArgumentList $arguments): string
{
$timezone = $this->applicationConfig->getTimeZone();
try {
$today = DateTime::createNow()->withTimezone(new DateTimeZone($timezone));
} catch (Exception $e) {
throw new RuntimeException($e->getMessage());
}
return $today->toDateTime()->format(DateTimeUtil::SYSTEM_DATE_FORMAT);
}
}

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\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class YearType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(ArgumentList $args)
{
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $args[0];
$timezone = null;
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) {
return 0;
}
if (strlen($value) > 11) {
$resultString = $this->dateTime->convertSystemDateTime($value, $timezone, 'YYYY');
} else {
$resultString = $this->dateTime->convertSystemDate($value, 'YYYY');
}
return intval($resultString);
}
}

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\Core\Formula\Functions\EntityGroup;
use Espo\Core\Exceptions\Error;
class AddLinkMultipleIdType extends \Espo\Core\Formula\Functions\Base
{
/**
* @return void
* @throws Error
* @throws \Espo\Core\Formula\Exceptions\Error
*/
public function process(\stdClass $item)
{
if (count($item->value) < 2) {
throw new Error("addLinkMultipleId function: Too few arguments.");
}
$link = $this->evaluate($item->value[0]);
$id = $this->evaluate($item->value[1]);
if (!is_string($link)) {
throw new Error();
}
if (is_array($id)) {
$idList = $id;
foreach ($idList as $id) {
if (!is_string($id)) {
throw new Error();
}
$this->getEntity()->addLinkMultipleId($link, $id);
}
} else {
if (!is_string($id)) {
return;
}
$this->getEntity()->addLinkMultipleId($link, $id);
}
}
}

View File

@@ -0,0 +1,38 @@
<?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\Core\Formula\Functions\EntityGroup;
class AttributeFetchedType extends AttributeType
{
protected function getAttributeValue($attribute)
{
return $this->attributeFetcher->fetch($this->getEntity(), $attribute, true);
}
}

View File

@@ -0,0 +1,46 @@
<?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\Core\Formula\Functions\EntityGroup;
use Espo\Core\Exceptions\Error;
class AttributeType extends \Espo\Core\Formula\Functions\AttributeType
{
public function process(\stdClass $item)
{
if (count($item->value) < 1) {
throw new Error("attribute function: Too few arguments.");
}
$attribute = $this->evaluate($item->value[0]);
return $this->getAttributeValue($attribute);
}
}

View File

@@ -0,0 +1,60 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\EntityGroup;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Functions\BaseFunction;
/**
* @noinspection PhpUnused
*/
class ClearAttributeType extends BaseFunction
{
public function process(ArgumentList $args)
{
if (count($args) < 1) {
throw TooFewArguments::create(1);
}
$args = $this->evaluate($args);
$attribute = $args[0];
if (!is_string($attribute)) {
throw BadArgumentType::create(1, 'string');
}
$this->getEntity()->clear($attribute);
return null;
}
}

View File

@@ -0,0 +1,106 @@
<?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\Core\Formula\Functions\EntityGroup;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Functions\Base;
use Espo\Core\Formula\Functions\RecordGroup\Util\FindQueryUtil;
use Espo\Core\Select\SelectBuilderFactory;
use Espo\ORM\Defs\Params\RelationParam;
use Espo\Core\Di;
use stdClass;
/**
* @noinspection PhpUnused
*/
class CountRelatedType extends Base implements
Di\EntityManagerAware,
Di\InjectableFactoryAware,
Di\UserAware
{
use Di\EntityManagerSetter;
use Di\InjectableFactorySetter;
use Di\UserSetter;
/**
* @return int
* @throws Error
*/
public function process(stdClass $item)
{
if (count($item->value) < 1) {
throw new Error("countRelated: roo few arguments.");
}
$link = $this->evaluate($item->value[0]);
if (empty($link)) {
throw new Error("countRelated: no link passed.");
}
$filter = null;
if (count($item->value) > 1) {
$filter = $this->evaluate($item->value[1]);
}
$entity = $this->getEntity();
$entityManager = $this->entityManager;
$foreignEntityType = $entity->getRelationParam($link, RelationParam::ENTITY);
if (empty($foreignEntityType)) {
throw new Error();
}
$builder = $this->injectableFactory->create(SelectBuilderFactory::class)
->create()
->forUser($this->user)
->from($foreignEntityType);
if ($filter) {
(new FindQueryUtil())->applyFilter($builder, $filter, 2);
}
try {
$query = $builder->build();
} catch (BadRequest|Forbidden $e) {
throw new Error($e->getMessage());
}
return $entityManager->getRDBRepository($entity->getEntityType())
->getRelation($entity, $link)
->clone($query)
->count();
}
}

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\Core\Formula\Functions\EntityGroup;
use Espo\Core\Exceptions\Error;
use Espo\ORM\EntityManager;
use Espo\Core\Di;
class GetLinkColumnType extends \Espo\Core\Formula\Functions\Base implements
Di\EntityManagerAware
{
use Di\EntityManagerSetter;
/**
* @var EntityManager
*/
protected $entityManager;
/**
* @return mixed
* @throws Error
* @throws \Espo\Core\Formula\Exceptions\Error
*/
public function process(\stdClass $item)
{
$args = $item->value ?? [];
if (count($args) < 3) {
throw new Error("Formula: entity\\isRelated: no argument.");
}
$link = $this->evaluate($args[0]);
$id = $this->evaluate($args[1]);
$column = $this->evaluate($args[2]);
$entityType = $this->getEntity()->getEntityType();
$repository = $this->entityManager->getRDBRepository($entityType);
return $repository
->getRelation($this->getEntity(), $link)
->getColumnById($id, $column);
}
}

View File

@@ -0,0 +1,60 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\EntityGroup;
use Espo\Core\Exceptions\Error;
class HasLinkMultipleIdType extends \Espo\Core\Formula\Functions\Base
{
/**
* @return bool
* @throws Error
* @throws \Espo\Core\Formula\Exceptions\Error
*/
public function process(\stdClass $item)
{
if (count($item->value) < 2) {
throw new Error("hasLinkMultipleId: too few arguments.");
}
$link = $this->evaluate($item->value[0]);
$id = $this->evaluate($item->value[1]);
if (!is_string($link)) {
throw new Error();
}
if (!is_string($id)) {
throw new Error();
}
return $this->getEntity()->hasLinkMultipleId($link, $id);
}
}

View File

@@ -0,0 +1,61 @@
<?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\Core\Formula\Functions\EntityGroup;
use Espo\Core\Exceptions\Error;
class IsAttributeChangedType extends \Espo\Core\Formula\Functions\Base
{
/**
* @return bool
* @throws Error
* @throws \Espo\Core\Formula\Exceptions\Error
*/
public function process(\stdClass $item)
{
if (count($item->value) < 1) {
throw new Error("isAttributeChanged: too few arguments.");
}
$attribute = $this->evaluate($item->value[0]);
return $this->check($attribute);
}
/**
* @param string $attribute
* @return bool
* @throws Error
*/
protected function check($attribute)
{
return $this->getEntity()->isAttributeChanged($attribute);
}
}

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\Core\Formula\Functions\EntityGroup;
class IsAttributeNotChangedType extends IsAttributeChangedType
{
/**
* @param string $attribute
* @return bool
* @throws \Espo\Core\Exceptions\Error
*/
protected function check($attribute)
{
return !parent::check($attribute);
}
}

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\Core\Formula\Functions\EntityGroup;
class IsNewType extends \Espo\Core\Formula\Functions\Base
{
/**
* @return bool
* @throws \Espo\Core\Exceptions\Error
*/
public function process(\stdClass $item)
{
return $this->getEntity()->isNew();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\EntityGroup;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Functions\Base;
use Espo\Core\Di;
class IsRelatedType extends Base implements
Di\EntityManagerAware
{
use Di\EntityManagerSetter;
/**
* @return bool
* @throws Error
*/
public function process(\stdClass $item)
{
if (count($item->value) < 2) {
throw new Error("isRelated: roo few arguments.");
}
$link = $this->evaluate($item->value[0]);
$id = $this->evaluate($item->value[1]);
return $this->entityManager
->getRDBRepository($this->getEntity()->getEntityType())
->getRelation($this->getEntity(), $link)
->isRelatedById($id);
}
}

View File

@@ -0,0 +1,60 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\EntityGroup;
use Espo\Core\Exceptions\Error;
class RemoveLinkMultipleIdType extends \Espo\Core\Formula\Functions\Base
{
/**
* @return void
* @throws \Espo\Core\Formula\Exceptions\Error
* @throws Error
*/
public function process(\stdClass $item)
{
if (count($item->value) < 2) {
throw new Error("removeLinkMultipleId: roo few arguments.");
}
$link = $this->evaluate($item->value[0]);
$id = $this->evaluate($item->value[1]);
if (!is_string($link)) {
throw new Error();
}
if (!is_string($id)) {
throw new Error();
}
$this->getEntity()->removeLinkMultipleId($link, $id);
}
}

View File

@@ -0,0 +1,34 @@
<?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\Core\Formula\Functions\EntityGroup;
class SetAttributeType extends \Espo\Core\Formula\Functions\SetAttributeType
{
}

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\Core\Formula\Functions\EntityGroup;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\ORM\Entity;
class SetLinkMultipleColumnType extends BaseFunction
{
public function process(ArgumentList $args)
{
if (count($args) < 4) {
$this->throwTooFewArguments(4);
}
$link = $this->evaluate($args[0]);
$id = $this->evaluate($args[1]);
$column = $this->evaluate($args[2]);
$value = $this->evaluate($args[3]);
$entity = $this->getEntity();
if (!$entity instanceof Entity) {
throw new Error();
}
$entity->setLinkMultipleColumn($link, $column, $id, $value);
}
}

View File

@@ -0,0 +1,171 @@
<?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\Core\Formula\Functions\EntityGroup;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Di;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Formula\Functions\Base;
use Espo\Core\Formula\Functions\RecordGroup\Util\FindQueryUtil;
use Espo\Core\Select\SelectBuilderFactory;
use Espo\ORM\Defs\Params\RelationParam;
use Espo\ORM\Name\Attribute;
use stdClass;
use PDO;
/**
* @noinspection PhpUnused
*/
class SumRelatedType extends Base implements
Di\EntityManagerAware,
Di\InjectableFactoryAware,
Di\UserAware
{
use Di\EntityManagerSetter;
use Di\InjectableFactorySetter;
use Di\UserSetter;
/**
* @return float
* @throws Error
*/
public function process(stdClass $item)
{
if (count($item->value) < 2) {
throw new Error("sumRelated: Too few arguments.");
}
$link = $this->evaluate($item->value[0]);
if (empty($link)) {
throw new Error("No link passed to sumRelated function.");
}
$field = $this->evaluate($item->value[1]);
if (empty($field)) {
throw new Error("No field passed to sumRelated function.");
}
$filter = null;
if (count($item->value) > 2) {
$filter = $this->evaluate($item->value[2]);
}
$entity = $this->getEntity();
$entityManager = $this->entityManager;
$foreignEntityType = $entity->getRelationParam($link, RelationParam::ENTITY);
if (empty($foreignEntityType)) {
throw new Error();
}
$foreignLink = $entity->getRelationParam($link, RelationParam::FOREIGN);
$foreignLinkAlias = $foreignLink . 'SumRelated';
if (empty($foreignLink)) {
throw new Error("No foreign link for link {$link}.");
}
$builder = $this->injectableFactory->create(SelectBuilderFactory::class)
->create()
->forUser($this->user)
->from($foreignEntityType);
if ($filter) {
(new FindQueryUtil())->applyFilter($builder, $filter, 3);
}
try {
$queryBuilder = $builder->buildQueryBuilder();
} catch (BadRequest|Forbidden $e) {
throw new Error($e->getMessage(), $e->getCode(), $e);
}
$queryBuilder->select([
[$foreignLinkAlias . '.id', 'foreignId'],
'SUM:' . $field,
]);
if ($entity->getRelationType($link) === 'hasChildren') {
$queryBuilder
->join(
$entity->getEntityType(),
$foreignLinkAlias,
[
$foreignLinkAlias . '.id:' => $foreignLink . 'Id',
Attribute::DELETED => false,
$foreignLinkAlias . '.id!=' => null,
]
)
->where([
$foreignLink . 'Type' => $entity->getEntityType(),
]);
} else {
$queryBuilder->join($foreignLink, $foreignLinkAlias);
}
$queryBuilder->where([
$foreignLinkAlias . '.id' => $entity->getId(),
]);
if ($queryBuilder->build()->isDistinct()) {
// Use a sub-query to weed out duplicate rows.
$sqQueryBuilder = clone $queryBuilder;
$sqQueryBuilder
->order([])
->select([Attribute::ID]);
$queryBuilder->where([
'id=s' => $sqQueryBuilder->build(),
]);
}
$queryBuilder->group($foreignLinkAlias . '.id');
$sth = $entityManager->getQueryExecutor()->execute($queryBuilder->build());
$rowList = $sth->fetchAll(PDO::FETCH_ASSOC);
if (empty($rowList)) {
return 0.0;
}
$stringValue = $rowList[0]['SUM:' . $field];
return floatval($stringValue);
}
}

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\Core\Formula\Functions\EnvGroup;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\BadArgumentValue;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Espo\Entities\User;
class UserAttributeSafeType implements Func
{
/** @var string[] */
private array $allowedAttributeList = [
'id',
'userName',
'name',
'teamsIds',
'defaultTeamId',
'type',
];
public function __construct(
private User $user
) {}
public function process(EvaluatedArgumentList $arguments): mixed
{
if (count($arguments) < 1) {
throw TooFewArguments::create(1);
}
$attribute = $arguments[0];
if (!is_string($attribute)) {
throw BadArgumentType::create(1, 'string');
}
if (!in_array($attribute, $this->allowedAttributeList)) {
throw BadArgumentValue::create(1, "No allowed attribute.");
}
return $this->user->get($attribute);
}
}

View File

@@ -0,0 +1,69 @@
<?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\Core\Formula\Functions\EnvGroup;
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;
class UserAttributeType implements Func
{
/** @var string[] */
private array $forbiddenAttributeList = [
'password',
'apiKey',
'secretKey',
];
public function __construct(
private User $user
) {}
public function process(EvaluatedArgumentList $arguments): mixed
{
if (count($arguments) < 1) {
throw TooFewArguments::create(1);
}
$attribute = $arguments[0];
if (!is_string($attribute)) {
throw BadArgumentType::create(1, 'string');
}
if (in_array($attribute, $this->forbiddenAttributeList)) {
return null;
}
return $this->user->get($attribute);
}
}

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\Core\Formula\Functions\ExtGroup\AclGroup;
use Espo\Core\Acl\Table;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Espo\Core\Utils\Acl\UserAclManagerProvider;
use Espo\Entities\User;
use Espo\ORM\EntityManager;
/**
* @noinspection PhpUnused
*/
class CheckEntityType implements Func
{
public function __construct(
private UserAclManagerProvider $userAclManagerProvider,
private EntityManager $entityManager,
) {}
public function process(EvaluatedArgumentList $arguments): bool
{
if (count($arguments) < 3) {
throw TooFewArguments::create(3);
}
$userId = $arguments[0];
$entityType = $arguments[1];
$id = $arguments[2];
/** @var Table::ACTION_*|null $action */
$action = $arguments[3] ?? Table::ACTION_READ;
if (!is_string($userId)) {
throw BadArgumentType::create(1, 'string');
}
if (!is_string($entityType)) {
throw BadArgumentType::create(2, 'string');
}
if (!is_string($id)) {
throw BadArgumentType::create(3, 'string');
}
if (!is_string($action)) {
throw BadArgumentType::create(4, 'string');
}
$user = $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
if (!$user) {
return false;
}
$entity = $this->entityManager->getEntityById($entityType, $id);
if (!$entity) {
return false;
}
return $this->userAclManagerProvider->get($user)->checkEntity($user, $entity, $action);
}
}

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\Core\Formula\Functions\ExtGroup\AclGroup;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Espo\Core\Utils\Acl\UserAclManagerProvider;
use Espo\Entities\User;
use Espo\ORM\EntityManager;
/**
* @noinspection PhpUnused
*/
class CheckScopeType implements Func
{
public function __construct(
private UserAclManagerProvider $userAclManagerProvider,
private EntityManager $entityManager,
) {}
public function process(EvaluatedArgumentList $arguments): bool
{
if (count($arguments) < 2) {
throw TooFewArguments::create(2);
}
$userId = $arguments[0];
$scope = $arguments[1];
$action = $arguments[2] ?? null;
if (!is_string($userId)) {
throw BadArgumentType::create(1, 'string');
}
if (!is_string($scope)) {
throw BadArgumentType::create(2, 'string');
}
if ($action !== null && !is_string($action)) {
throw BadArgumentType::create(3, 'string');
}
$user = $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
if (!$user) {
return false;
}
return $this->userAclManagerProvider->get($user)->checkScope($user, $scope, $action);
}
}

View File

@@ -0,0 +1,96 @@
<?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\Core\Formula\Functions\ExtGroup\AclGroup;
use Espo\Core\Acl\Table;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\BadArgumentValue;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Espo\Core\Utils\Acl\UserAclManagerProvider;
use Espo\Entities\User;
use Espo\ORM\EntityManager;
/**
* @noinspection PhpUnused
*/
class GetLevelType implements Func
{
public function __construct(
private UserAclManagerProvider $userAclManagerProvider,
private EntityManager $entityManager,
) {}
public function process(EvaluatedArgumentList $arguments): string
{
if (count($arguments) < 3) {
throw TooFewArguments::create(3);
}
$userId = $arguments[0];
$scope = $arguments[1];
$action = $arguments[2];
if (!is_string($userId)) {
throw BadArgumentType::create(1, 'string');
}
if (!is_string($scope)) {
throw BadArgumentType::create(2, 'string');
}
if (!is_string($action)) {
throw BadArgumentType::create(3, 'string');
}
if (
!in_array($action, [
Table::ACTION_READ,
Table::ACTION_CREATE,
Table::ACTION_EDIT,
Table::ACTION_STREAM,
Table::ACTION_DELETE,
])
) {
throw BadArgumentValue::create(3);
}
/** @var Table::ACTION_* $action */
$user = $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
if (!$user) {
return Table::LEVEL_NO;
}
return $this->userAclManagerProvider->get($user)->getLevel($user, $scope, $action);
}
}

View File

@@ -0,0 +1,76 @@
<?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\Core\Formula\Functions\ExtGroup\AclGroup;
use Espo\Core\Acl\Table;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Espo\Core\Utils\Acl\UserAclManagerProvider;
use Espo\Entities\User;
use Espo\ORM\EntityManager;
/**
* @noinspection PhpUnused
*/
class GetPermissionLevelType implements Func
{
public function __construct(
private UserAclManagerProvider $userAclManagerProvider,
private EntityManager $entityManager,
) {}
public function process(EvaluatedArgumentList $arguments): string
{
if (count($arguments) < 2) {
throw TooFewArguments::create(2);
}
$userId = $arguments[0];
$permission = $arguments[1];
if (!is_string($userId)) {
throw BadArgumentType::create(1, 'string');
}
if (!is_string($permission)) {
throw BadArgumentType::create(2, 'string');
}
$user = $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
if (!$user) {
return Table::LEVEL_NO;
}
return $this->userAclManagerProvider->get($user)->getPermissionLevel($user, $permission);
}
}

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\Core\Formula\Functions\ExtGroup\CurrencyGroup;
use Espo\Core\Currency\ConfigDataProvider;
use Espo\Core\Currency\Converter;
use Espo\Core\Field\Currency;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
class ConvertType implements Func
{
public function __construct(
private ConfigDataProvider $configDataProvider,
private Converter $converter
) {}
public function process(EvaluatedArgumentList $arguments): string
{
if (count($arguments) < 2) {
throw TooFewArguments::create(2);
}
$amount = $arguments[0];
$fromCode = $arguments[1];
$toCode = $arguments[2] ?? $this->configDataProvider->getDefaultCurrency();
if (
!is_string($amount) &&
!is_int($amount) &&
!is_float($amount)
) {
throw BadArgumentType::create(1, 'int|float|string');
}
if (!is_string($fromCode)) {
throw BadArgumentType::create(2, 'string');
}
if (!is_string($toCode)) {
throw BadArgumentType::create(3, 'string');
}
assert(is_numeric($amount));
$value = Currency::create($amount, $fromCode);
$convertedValue = $this->converter->convert($value, $toCode);
return $convertedValue->getAmountAsString();
}
}

View File

@@ -0,0 +1,152 @@
<?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\Core\Formula\Functions\ExtGroup\EmailGroup;
use Espo\Core\Utils\SystemUser;
use Espo\Entities\Email;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Di;
use Espo\Entities\EmailTemplate;
use Espo\Tools\EmailTemplate\Data;
use Espo\Tools\EmailTemplate\Params;
use Espo\Tools\EmailTemplate\Processor;
class ApplyTemplateType extends BaseFunction implements
Di\EntityManagerAware,
Di\InjectableFactoryAware
{
use Di\EntityManagerSetter;
use Di\InjectableFactorySetter;
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments(2);
}
$args = $this->evaluate($args);
$id = $args[0];
$templateId = $args[1];
$parentType = $args[2] ?? null;
$parentId = $args[3] ?? null;
if (!$id || !is_string($id)) {
$this->throwBadArgumentType(1, 'string');
}
if (!$templateId || !is_string($templateId)) {
$this->throwBadArgumentType(2, 'string');
}
if ($parentType && !is_string($parentType)) {
$this->throwBadArgumentType(3, 'string');
}
if ($parentId && !is_string($parentId)) {
$this->throwBadArgumentType(4, 'string');
}
$em = $this->entityManager;
/** @var ?Email $email */
$email = $em->getEntityById(Email::ENTITY_TYPE, $id);
/** @var ?EmailTemplate $emailTemplate */
$emailTemplate = $em->getEntityById(EmailTemplate::ENTITY_TYPE, $templateId);
if (!$email) {
$this->log("Email {$id} does not exist.");
return false;
}
if (!$emailTemplate) {
$this->log("EmailTemplate {$templateId} does not exist.");
return false;
}
$status = $email->getStatus();
if ($status && $status === Email::STATUS_SENT) {
$this->log("Can't apply template to email with 'Sent' status.");
return false;
}
$processor = $this->injectableFactory->create(Processor::class);
$params = Params::create()
->withCopyAttachments(true)
->withApplyAcl(false);
$data = Data::create();
if (!$parentType || !$parentId) {
$parentType = $email->getParentType();
$parentId = $email->getParentId();
}
if ($parentType && $parentId) {
$data = $data
->withParentId($parentId)
->withParentType($parentType);
}
$data = $data->withEmailAddress(
$email->getToAddressList()[0] ?? null
);
$emailData = $processor->process($emailTemplate, $params, $data);
$attachmentsIdList = $email->getLinkMultipleIdList('attachments');
$attachmentsIdList = array_merge(
$attachmentsIdList,
$emailData->getAttachmentIdList()
);
$email
->setSubject($emailData->getSubject())
->setBody($emailData->getBody())
->setIsHtml($emailData->isHtml())
->setAttachmentIdList($attachmentsIdList);
$systemUserId = $this->injectableFactory->create(SystemUser::class)->getId();
$em->saveEntity($email, [
'modifiedById' => $systemUserId,
]);
return true;
}
}

View File

@@ -0,0 +1,136 @@
<?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\Core\Formula\Functions\ExtGroup\EmailGroup;
use Espo\Core\ApplicationUser;
use Espo\Core\Mail\ConfigDataProvider;
use Espo\Core\ORM\Repository\Option\SaveOption;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Utils\SystemUser;
use Espo\Entities\Email;
use Espo\Tools\Email\SendService;
use Espo\Core\Di;
use Exception;
class SendType extends BaseFunction implements
Di\EntityManagerAware,
Di\ServiceFactoryAware,
Di\ConfigAware,
Di\InjectableFactoryAware
{
use Di\EntityManagerSetter;
use Di\ServiceFactorySetter;
use Di\ConfigSetter;
use Di\InjectableFactorySetter;
public function process(ArgumentList $args)
{
if (count($args) < 1) {
$this->throwTooFewArguments(1);
}
$args = $this->evaluate($args);
$id = $args[0];
if (!$id || !is_string($id)) {
$this->throwBadArgumentType(1, 'string');
}
$em = $this->entityManager;
/** @var ?Email $email */
$email = $em->getEntityById(Email::ENTITY_TYPE, $id);
if (!$email) {
$this->log("Email '{$id}' does not exist.");
return false;
}
$status = $email->getStatus();
if ($status === Email::STATUS_SENT) {
$this->log("Can't send email that has 'Sent' status.");
return false;
}
/** @var \Espo\Services\Email $service */
$service = $this->serviceFactory->create(Email::ENTITY_TYPE);
$service->loadAdditionalFields($email);
$toSave = false;
if ($status !== Email::STATUS_SENDING) {
$email->set('status', Email::STATUS_SENDING);
$toSave = true;
}
if (!$email->get('from')) {
$from = $this->injectableFactory
->create(ConfigDataProvider::class)
->getSystemOutboundAddress();
if ($from) {
$email->set('from', $from);
$toSave = true;
}
}
$systemUserId = $this->injectableFactory->create(SystemUser::class)->getId();
if ($toSave) {
$em->saveEntity($email, [
SaveOption::SILENT => true,
SaveOption::MODIFIED_BY_ID => $systemUserId,
]);
}
$sendService = $this->injectableFactory->create(SendService::class);
try {
$sendService->send($email);
} catch (Exception $e) {
$message = $e->getMessage();
$this->log("Error while sending. Message: {$message}." , 'error');
return false;
}
return true;
}
}

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\Core\Formula\Functions\ExtGroup\MarkdownGroup;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Michelf\Markdown;
/**
* @noinspection PhpUnused
*/
class TransformType implements Func
{
public function process(EvaluatedArgumentList $arguments): string
{
if (count($arguments) < 1) {
throw TooFewArguments::create(1);
}
$string = $arguments[0] ?? '';
if (!is_string($string)) {
throw BadArgumentType::create(1, 'string');
}
return Markdown::defaultTransform($string);
}
}

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\Core\Formula\Functions\ExtGroup\OauthGroup;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Espo\Tools\OAuth\Exceptions\AccountNotFound;
use Espo\Tools\OAuth\Exceptions\NoToken;
use Espo\Tools\OAuth\Exceptions\ProviderNotAvailable;
use Espo\Tools\OAuth\Exceptions\TokenObtainingFailure;
use Espo\Tools\OAuth\TokensProvider;
/**
* @noinspection PhpUnused
*/
class GetAccessTokenType implements Func
{
public function __construct(
private TokensProvider $provider,
) {}
public function process(EvaluatedArgumentList $arguments): string
{
if (count($arguments) < 1) {
throw TooFewArguments::create(1);
}
$id = $arguments[0];
if (!is_string($id)) {
throw BadArgumentType::create(1, 'string');
}
try {
$tokens = $this->provider->get($arguments[0]);
} catch (AccountNotFound|NoToken|ProviderNotAvailable|TokenObtainingFailure $e) {
$message = "Could not obtain access token for OAuth account $id.";
throw new Error($message, 500, $e);
}
$accessToken = $tokens->getAccessToken();
if (!$accessToken) {
throw new Error("No access token.");
}
return $accessToken;
}
}

View File

@@ -0,0 +1,150 @@
<?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\Core\Formula\Functions\ExtGroup\PdfGroup;
use Espo\Core\Field\LinkParent;
use Espo\Core\Name\Field;
use Espo\Entities\Attachment;
use Espo\Entities\Template;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Utils\Util;
use Espo\Tools\Pdf\Params;
use Espo\Core\Di;
use Espo\Tools\Pdf\Service;
use Exception;
class GenerateType extends BaseFunction implements
Di\EntityManagerAware,
Di\InjectableFactoryAware,
Di\FileStorageManagerAware
{
use Di\EntityManagerSetter;
use Di\InjectableFactorySetter;
use Di\FileStorageManagerSetter;
public function process(ArgumentList $args)
{
if (count($args) < 3) {
$this->throwTooFewArguments(3);
}
$args = $this->evaluate($args);
$entityType = $args[0];
$id = $args[1];
$templateId = $args[2];
$fileName = $args[3];
if (!$entityType || !is_string($entityType)) {
$this->throwBadArgumentType(1, 'string');
}
if (!$id || !is_string($id)) {
$this->throwBadArgumentType(2, 'string');
}
if (!$templateId || !is_string($templateId)) {
$this->throwBadArgumentType(3, 'string');
}
if ($fileName && !is_string($fileName)) {
$this->throwBadArgumentType(4, 'string');
}
$em = $this->entityManager;
try {
$entity = $em->getEntityById($entityType, $id);
} catch (Exception $e) {
$this->log("Message: " . $e->getMessage() . ".");
throw new Error();
}
if (!$entity) {
$this->log("Record {$entityType} {$id} does not exist.");
throw new Error();
}
/** @var ?Template $template */
$template = $em->getEntityById(Template::ENTITY_TYPE, $templateId);
if (!$template) {
$this->log("Template {$templateId} does not exist.");
throw new Error();
}
if ($fileName) {
if (!str_ends_with($fileName, '.pdf')) {
$fileName .= '.pdf';
}
} else {
$fileName = Util::sanitizeFileName($entity->get(Field::NAME)) . '.pdf';
}
$params = Params::create()->withAcl(false);
try {
$service = $this->injectableFactory->create(Service::class);
$contents = $service->generate(
$entity->getEntityType(),
$entity->getId(),
$template->getId(),
$params
);
} catch (Exception $e) {
$this->log("Error while generating. Message: " . $e->getMessage() . ".", 'error');
throw new Error();
}
/** @var Attachment $attachment */
$attachment = $em->getNewEntity(Attachment::ENTITY_TYPE);
$attachment
->setName($fileName)
->setType('application/pdf')
->setSize($contents->getStream()->getSize())
->setRelated(LinkParent::create($entityType, $id))
->setRole(Attachment::ROLE_ATTACHMENT);
$em->saveEntity($attachment);
$this->fileStorageManager->putStream($attachment, $contents->getStream());
return $attachment->getId();
}
}

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\Core\Formula\Functions\ExtGroup\SecretGroup;
use Espo\Core\Formula\EvaluatedArgumentList;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Exceptions\TooFewArguments;
use Espo\Core\Formula\Func;
use Espo\Tools\AppSecret\SecretProvider;
/**
* @noinspection PhpUnused
*/
class GetType implements Func
{
public function __construct(private SecretProvider $secretProvider)
{}
public function process(EvaluatedArgumentList $arguments): string
{
if (count($arguments) < 1) {
throw TooFewArguments::create(1);
}
$string = $arguments[0];
if (!is_string($string)) {
throw BadArgumentType::create(1, 'string');
}
$secret = $this->secretProvider->get($string);
if ($secret === null) {
throw new Error("No secret '$string'.");
}
return $secret;
}
}

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\Core\Formula\Functions\ExtGroup\SmsGroup;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Sms\SmsSender;
use Espo\Entities\Sms;
use Espo\Core\Di;
use Exception;
class SendType extends BaseFunction implements
Di\EntityManagerAware,
Di\InjectableFactoryAware
{
use Di\EntityManagerSetter;
use Di\InjectableFactorySetter;
public function process(ArgumentList $args)
{
if (count($args) < 1) {
$this->throwTooFewArguments(1);
}
$evaluatedArgs = $this->evaluate($args);
$id = $evaluatedArgs[0];
if (!$id || !is_string($id)) {
$this->throwBadArgumentType(1, 'string');
}
/** @var Sms|null $sms */
$sms = $this->entityManager->getEntityById(Sms::ENTITY_TYPE, $id);
if (!$sms) {
$this->log("Sms '{$id}' does not exist.");
return false;
}
if ($sms->getStatus() === Sms::STATUS_SENT) {
$this->log("Can't send SMS that has 'Sent' status.");
return false;
}
try {
$this->createSender()->send($sms);
$this->entityManager->saveEntity($sms);
} catch (Exception $e) {
$message = $e->getMessage();
$this->log("Error while sending SMS. Message: {$message}." , 'error');
$sms->setStatus(Sms::STATUS_FAILED);
$this->entityManager->saveEntity($sms);
return false;
}
return true;
}
private function createSender(): SmsSender
{
return $this->injectableFactory->create(SmsSender::class);
}
}

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\Core\Formula\Functions\ExtGroup\UserGroup;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Job\JobSchedulerFactory;
use Espo\Tools\UserSecurity\Password\Jobs\SendAccessInfo as SendAccessInfoJob;
use Espo\Core\Job\Job\Data as JobData;
use Espo\Entities\User;
use Espo\Core\Di;
class SendAccessInfoType extends BaseFunction implements
Di\EntityManagerAware,
Di\InjectableFactoryAware
{
use Di\EntityManagerSetter;
use Di\InjectableFactorySetter;
public function process(ArgumentList $args)
{
if (count($args) < 1) {
$this->throwTooFewArguments(1);
}
$evaluatedArgs = $this->evaluate($args);
$userId = $evaluatedArgs[0];
if (!$userId || !is_string($userId)) {
$this->throwBadArgumentType(1, 'string');
}
$user = $this->entityManager->getEntityById(User::ENTITY_TYPE, $userId);
if (!$user) {
$this->log("User '$userId' does not exist.");
return;
}
$this->createJobScheduledFactory()
->create()
->setClassName(SendAccessInfoJob::class)
->setData(
JobData::create()->withTargetId($user->getId())
)
->schedule();
}
private function createJobScheduledFactory(): JobSchedulerFactory
{
return $this->injectableFactory->create(JobSchedulerFactory::class);
}
}

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\Core\Formula\Functions\ExtGroup\WorkingTimeGroup;
use Espo\Core\Field\DateTime;
use Espo\Core\Field\DateTimeOptional;
use Espo\Core\Formula\ArgumentList;
use Espo\Core\Utils\DateTime as DateTimeUtil;
class AddWorkingDaysType extends Base
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments(2);
}
/** @var mixed[] $evaluatedArgs */
$evaluatedArgs = $this->evaluate($args);
$stringValue = $evaluatedArgs[0];
$days = $evaluatedArgs[1];
if (!is_string($stringValue)) {
$this->throwBadArgumentType(1, 'string');
}
if (!is_int($days) && !is_float($days)) {
$this->throwBadArgumentType(2, 'int');
}
if (is_float($days)) {
$days = (int) $days;
}
if ($days <= 0) {
$this->throwBadArgumentValue(2, 'Days value should be greater than 0.');
}
$calendar = $this->createCalendar($evaluatedArgs, 2);
$dateTime = DateTimeOptional::fromString($stringValue);
$isAllDay = $dateTime->isAllDay();
if ($isAllDay) {
$dateTime = $dateTime->withTimezone($calendar->getTimezone());
}
$dateTime = DateTime::fromDateTime($dateTime->toDateTime());
$result = $this->createCalendarUtility($calendar)->addWorkingDays($dateTime, $days);
if (!$result) {
return null;
}
if ($isAllDay) {
return $result->toDateTime()->format(DateTimeUtil::SYSTEM_DATE_FORMAT);
}
return $result->toString();
}
}

View File

@@ -0,0 +1,126 @@
<?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\Core\Formula\Functions\ExtGroup\WorkingTimeGroup;
use Espo\Core\Binding\BindingContainerBuilder;
use Espo\Core\Formula\Exceptions\BadArgumentType;
use Espo\Core\Formula\Exceptions\BadArgumentValue;
use Espo\Core\Formula\Exceptions\Error;
use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Entities\Team;
use Espo\Entities\User;
use Espo\Core\Di;
use Espo\ORM\Entity;
use Espo\Tools\WorkingTime\Calendar;
use Espo\Tools\WorkingTime\CalendarFactory;
use Espo\Tools\WorkingTime\CalendarUtility;
abstract class Base extends BaseFunction implements
Di\EntityManagerAware,
Di\InjectableFactoryAware
{
use Di\EntityManagerSetter;
use Di\InjectableFactorySetter;
private function getCalendarFactory(): CalendarFactory
{
return $this->injectableFactory->create(CalendarFactory::class);
}
protected function createCalendarUtility(Calendar $calendar): CalendarUtility
{
return $this->injectableFactory->createWithBinding(
CalendarUtility::class,
BindingContainerBuilder::create()
->bindInstance(Calendar::class, $calendar)
->build()
);
}
/**
* @param mixed[] $evaluatedArgs
* @throws BadArgumentType
* @throws BadArgumentValue
* @throws Error
*/
protected function createCalendar(array $evaluatedArgs, int $argumentPosition = 1): Calendar
{
$target = $this->obtainTarget($evaluatedArgs, $argumentPosition);
if ($target instanceof User) {
return $this->getCalendarFactory()->createForUser($target);
}
if ($target instanceof Team) {
return $this->getCalendarFactory()->createForTeam($target);
}
return $this->getCalendarFactory()->createGlobal();
}
/**
* @param mixed[] $evaluatedArgs
* @throws BadArgumentType
* @throws BadArgumentValue
* @throws Error
*/
private function obtainTarget(array $evaluatedArgs, int $argumentPosition = 1): ?Entity
{
if (count($evaluatedArgs) < $argumentPosition + 2) {
return null;
}
$entityType = $evaluatedArgs[$argumentPosition];
$entityId = $evaluatedArgs[$argumentPosition + 1];
if (!is_string($entityType)) {
$this->throwBadArgumentType($argumentPosition + 1, 'string');
}
if (!is_string($entityId)) {
$this->throwBadArgumentType($argumentPosition + 2, 'string');
}
if (!in_array($entityType, [User::ENTITY_TYPE, Team::ENTITY_TYPE])) {
$this->throwBadArgumentValue($argumentPosition + 1);
}
$entity = $this->entityManager->getEntityById($entityType, $entityId);
if (!$entity) {
$this->throwError("Entity {$entityType} {$entityId} not found.");
}
return $entity;
}
}

View File

@@ -0,0 +1,71 @@
<?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\Core\Formula\Functions\ExtGroup\WorkingTimeGroup;
use Espo\Core\Field\DateTime;
use Espo\Core\Field\DateTimeOptional;
use Espo\Core\Formula\ArgumentList;
class FindClosestWorkingTimeType extends Base
{
public function process(ArgumentList $args)
{
if (count($args) < 1) {
$this->throwTooFewArguments(1);
}
/** @var mixed[] $evaluatedArgs */
$evaluatedArgs = $this->evaluate($args);
$stringValue = $evaluatedArgs[0];
if (!is_string($stringValue)) {
$this->throwBadArgumentType(1, 'string');
}
$calendar = $this->createCalendar($evaluatedArgs);
$dateTime = DateTimeOptional::fromString($stringValue);
if ($dateTime->isAllDay()) {
$dateTime = $dateTime->withTimezone($calendar->getTimezone());
}
$dateTime = DateTime::fromDateTime($dateTime->getDateTime());
$result = $this->createCalendarUtility($calendar)->findClosestWorkingTime($dateTime);
if (!$result) {
return null;
}
return $result->toString();
}
}

View File

@@ -0,0 +1,76 @@
<?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\Core\Formula\Functions\ExtGroup\WorkingTimeGroup;
use Espo\Core\Field\DateTime;
use Espo\Core\Field\DateTimeOptional;
use Espo\Core\Formula\ArgumentList;
class GetSummedWorkingHoursType extends Base
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments(2);
}
/** @var mixed[] $evaluatedArgs */
$evaluatedArgs = $this->evaluate($args);
$stringValue1 = $evaluatedArgs[0];
$stringValue2 = $evaluatedArgs[1];
if (!is_string($stringValue1)) {
$this->throwBadArgumentType(1, 'string');
}
if (!is_string($stringValue2)) {
$this->throwBadArgumentType(2, 'string');
}
$calendar = $this->createCalendar($evaluatedArgs, 2);
$dateTime1 = DateTimeOptional::fromString($stringValue1);
$dateTime2 = DateTimeOptional::fromString($stringValue2);
if ($dateTime1->isAllDay()) {
$dateTime1 = $dateTime1->withTimezone($calendar->getTimezone());
}
if ($dateTime2->isAllDay()) {
$dateTime2 = $dateTime2->withTimezone($calendar->getTimezone());
}
$dateTime1 = DateTime::fromDateTime($dateTime1->getDateTime());
$dateTime2 = DateTime::fromDateTime($dateTime2->getDateTime());
return $this->createCalendarUtility($calendar)->getSummedWorkingHours($dateTime1, $dateTime2);
}
}

View File

@@ -0,0 +1,76 @@
<?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\Core\Formula\Functions\ExtGroup\WorkingTimeGroup;
use Espo\Core\Field\DateTime;
use Espo\Core\Field\DateTimeOptional;
use Espo\Core\Formula\ArgumentList;
class GetWorkingDaysType extends Base
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments(2);
}
/** @var mixed[] $evaluatedArgs */
$evaluatedArgs = $this->evaluate($args);
$stringValue1 = $evaluatedArgs[0];
$stringValue2 = $evaluatedArgs[1];
if (!is_string($stringValue1)) {
$this->throwBadArgumentType(1, 'string');
}
if (!is_string($stringValue2)) {
$this->throwBadArgumentType(2, 'string');
}
$calendar = $this->createCalendar($evaluatedArgs, 2);
$dateTime1 = DateTimeOptional::fromString($stringValue1);
$dateTime2 = DateTimeOptional::fromString($stringValue2);
if ($dateTime1->isAllDay()) {
$dateTime1 = $dateTime1->withTimezone($calendar->getTimezone());
}
if ($dateTime2->isAllDay()) {
$dateTime2 = $dateTime2->withTimezone($calendar->getTimezone());
}
$dateTime1 = DateTime::fromDateTime($dateTime1->getDateTime());
$dateTime2 = DateTime::fromDateTime($dateTime2->getDateTime());
return $this->createCalendarUtility($calendar)->getWorkingDays($dateTime1, $dateTime2);
}
}

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