Initial commit
This commit is contained in:
46
application/Espo/Tools/Export/Format/CellValuePreparator.php
Normal file
46
application/Espo/Tools/Export/Format/CellValuePreparator.php
Normal 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\Tools\Export\Format;
|
||||
|
||||
use Espo\Core\Field\Currency;
|
||||
use Espo\Core\Field\Date;
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
interface CellValuePreparator
|
||||
{
|
||||
/**
|
||||
* @param string $name A field name.
|
||||
*/
|
||||
public function prepare(
|
||||
Entity $entity,
|
||||
string $name
|
||||
): string|bool|int|float|Date|DateTime|Currency|null;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?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\Tools\Export\Format;
|
||||
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Tools\Export\Format\Xlsx\CellValuePreparators\General;
|
||||
|
||||
class CellValuePreparatorFactory
|
||||
{
|
||||
public function __construct(
|
||||
private InjectableFactory $injectableFactory,
|
||||
private Metadata $metadata
|
||||
) {}
|
||||
|
||||
public function create(string $format, string $fieldType): CellValuePreparator
|
||||
{
|
||||
/** @var class-string<CellValuePreparator> $className */
|
||||
$className = $this->metadata
|
||||
->get(['app', 'export', 'formatDefs', $format, 'cellValuePreparatorClassNameMap', $fieldType]) ??
|
||||
General::class;
|
||||
|
||||
return $this->injectableFactory->create($className);
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Csv;
|
||||
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\AdditionalFieldsLoader as AdditionalFieldsLoaderInterface;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class AdditionalFieldsLoader implements AdditionalFieldsLoaderInterface
|
||||
{
|
||||
public function __construct(private Metadata $metadata) {}
|
||||
|
||||
public function load(Entity $entity, array $fieldList): void
|
||||
{
|
||||
if (!$entity instanceof CoreEntity) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
$fieldType = $this->metadata
|
||||
->get(['entityDefs', $entity->getEntityType(), 'fields', $field, 'type']);
|
||||
|
||||
if (
|
||||
$fieldType === FieldType::LINK_MULTIPLE ||
|
||||
$fieldType === FieldType::ATTACHMENT_MULTIPLE
|
||||
) {
|
||||
if (!$entity->has($field . 'Ids') && $entity->hasLinkMultipleField($field)) {
|
||||
$entity->loadLinkMultipleField($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
application/Espo/Tools/Export/Format/Csv/Processor.php
Normal file
123
application/Espo/Tools/Export/Format/Csv/Processor.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?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\Tools\Export\Format\Csv;
|
||||
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Json;
|
||||
use Espo\Entities\Preferences;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Collection;
|
||||
use Espo\Tools\Export\Processor as ProcessorInterface;
|
||||
use Espo\Tools\Export\Processor\Params;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
use const JSON_UNESCAPED_UNICODE;
|
||||
|
||||
class Processor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
private Preferences $preferences
|
||||
) {}
|
||||
|
||||
public function process(Params $params, Collection $collection): StreamInterface
|
||||
{
|
||||
$attributeList = $params->getAttributeList();
|
||||
|
||||
$delimiterRaw =
|
||||
$this->preferences->get('exportDelimiter') ??
|
||||
$this->config->get('exportDelimiter') ??
|
||||
',';
|
||||
|
||||
$delimiter = str_replace('\t', "\t", $delimiterRaw);
|
||||
|
||||
$fp = fopen('php://temp', 'w');
|
||||
|
||||
if ($fp === false) {
|
||||
throw new RuntimeException("Could not open temp.");
|
||||
}
|
||||
|
||||
fputcsv($fp, $attributeList, $delimiter);
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
$preparedRow = $this->prepareRow($entity, $attributeList);
|
||||
|
||||
fputcsv($fp, $preparedRow, $delimiter, '"' , "\0");
|
||||
}
|
||||
|
||||
rewind($fp);
|
||||
|
||||
return new Stream($fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $attributeList
|
||||
* @return string[]
|
||||
*/
|
||||
private function prepareRow(Entity $entity, array $attributeList): array
|
||||
{
|
||||
$preparedRow = [];
|
||||
|
||||
foreach ($attributeList as $attribute) {
|
||||
$value = $entity->get($attribute);
|
||||
|
||||
if (is_array($value) || is_object($value)) {
|
||||
$value = Json::encode($value, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
$value = (string) $value;
|
||||
|
||||
$preparedRow[] = $this->sanitizeCellValue($value);
|
||||
}
|
||||
|
||||
return $preparedRow;
|
||||
}
|
||||
|
||||
private function sanitizeCellValue(string $value): string
|
||||
{
|
||||
if ($value === '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (in_array($value[0], ['+', '-', '@', '='])) {
|
||||
return "'" . $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Export\Format\Xlsx;
|
||||
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\AdditionalFieldsLoader as AdditionalFieldsLoaderInterface;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class AdditionalFieldsLoader implements AdditionalFieldsLoaderInterface
|
||||
{
|
||||
public function __construct(private Metadata $metadata)
|
||||
{}
|
||||
|
||||
public function load(Entity $entity, array $fieldList): void
|
||||
{
|
||||
if (!$entity instanceof CoreEntity) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($entity->getRelationList() as $link) {
|
||||
if (!in_array($link, $fieldList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($entity->getRelationType($link) === Entity::BELONGS_TO_PARENT) {
|
||||
if (!$entity->get($link . 'Name')) {
|
||||
$entity->loadParentNameField($link);
|
||||
}
|
||||
} else if (
|
||||
(
|
||||
(
|
||||
$entity->getRelationType($link) === Entity::BELONGS_TO &&
|
||||
$entity->getRelationParam($link, RelationParam::NO_JOIN)
|
||||
) ||
|
||||
$entity->getRelationType($link) === Entity::HAS_ONE
|
||||
) &&
|
||||
$entity->hasAttribute($link . 'Name')
|
||||
) {
|
||||
if (!$entity->get($link . 'Name') || !$entity->get($link . 'Id')) {
|
||||
$entity->loadLinkField($link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
$fieldType = $this->metadata
|
||||
->get(['entityDefs', $entity->getEntityType(), 'fields', $field, 'type']);
|
||||
|
||||
if ($fieldType === FieldType::LINK_MULTIPLE || $fieldType === FieldType::ATTACHMENT_MULTIPLE) {
|
||||
if (!$entity->has($field . 'Ids') && $entity->hasLinkMultipleField($field)) {
|
||||
$entity->loadLinkMultipleField($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Field\Address\AddressFactory;
|
||||
use Espo\Core\Field\Address\AddressFormatterFactory;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class Address implements CellValuePreparator
|
||||
{
|
||||
public function __construct(
|
||||
private AddressFormatterFactory $formatterFactory
|
||||
) {}
|
||||
|
||||
public function prepare(Entity $entity, string $name): ?string
|
||||
{
|
||||
$address = (new AddressFactory())->createFromEntity($entity, $name);
|
||||
|
||||
$formatter = $this->formatterFactory->createDefault();
|
||||
|
||||
return $formatter->format($address) ?: null;
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class Boolean implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): bool
|
||||
{
|
||||
return (bool) $entity->get($name);
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Field\Currency as CurrencyValue;
|
||||
use Espo\Core\Field\Currency\CurrencyFactory;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class Currency implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): ?CurrencyValue
|
||||
{
|
||||
$factory = new CurrencyFactory();
|
||||
|
||||
if (!$factory->isCreatableFromEntity($entity, $name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $factory->createFromEntity($entity, $name);
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Field\Currency as CurrencyValue;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class CurrencyConverted implements CellValuePreparator
|
||||
{
|
||||
private string $code;
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->code = $config->get('defaultCurrency');
|
||||
}
|
||||
|
||||
public function prepare(Entity $entity, string $name): ?CurrencyValue
|
||||
{
|
||||
$value = $entity->get($name);
|
||||
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CurrencyValue::create($value, $this->code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Field\Date as DateValue;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class Date implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): ?DateValue
|
||||
{
|
||||
$value = $entity->get($name);
|
||||
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return DateValue::fromString($value);
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Field\DateTime as DateTimeValue;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
use DateTimeZone;
|
||||
|
||||
class DateTime implements CellValuePreparator
|
||||
{
|
||||
private string $timezone;
|
||||
|
||||
public function __construct(Config\ApplicationConfig $applicationConfig)
|
||||
{
|
||||
$this->timezone = $applicationConfig->getTimeZone();
|
||||
}
|
||||
|
||||
public function prepare(Entity $entity, string $name): ?DateTimeValue
|
||||
{
|
||||
$value = $entity->get($name);
|
||||
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return DateTimeValue::fromString($value)
|
||||
->withTimezone(
|
||||
new DateTimeZone($this->timezone)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Field\DateTime as DateTimeValue;
|
||||
use Espo\Core\Field\Date as DateValue;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
use DateTimeZone;
|
||||
|
||||
class DateTimeOptional implements CellValuePreparator
|
||||
{
|
||||
private string $timezone;
|
||||
|
||||
public function __construct(Config\ApplicationConfig $applicationConfig)
|
||||
{
|
||||
$this->timezone = $applicationConfig->getTimeZone();
|
||||
}
|
||||
|
||||
public function prepare(Entity $entity, string $name): DateTimeValue|DateValue|null
|
||||
{
|
||||
$dateValue = $entity->get($name . 'Date');
|
||||
|
||||
if ($dateValue !== null) {
|
||||
return DateValue::fromString($dateValue);
|
||||
}
|
||||
|
||||
$value = $entity->get($name);
|
||||
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return DateTimeValue::fromString($value)
|
||||
->withTimezone(
|
||||
new DateTimeZone($this->timezone)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Utils\Language;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class Duration implements CellValuePreparator
|
||||
{
|
||||
public function __construct(private Language $language)
|
||||
{}
|
||||
|
||||
public function prepare(Entity $entity, string $name): ?string
|
||||
{
|
||||
$value = $entity->get($name);
|
||||
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$seconds = intval($value);
|
||||
|
||||
$days = intval(floor($seconds / 86400));
|
||||
$seconds = $seconds - $days * 86400;
|
||||
$hours = intval(floor($seconds / 3600));
|
||||
$seconds = $seconds - $hours * 3600;
|
||||
$minutes = intval(floor($seconds / 60));
|
||||
|
||||
$value = '';
|
||||
|
||||
if ($days) {
|
||||
$value .= $days . $this->language->translateLabel('d', 'durationUnits');
|
||||
|
||||
if ($minutes || $hours) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if ($hours) {
|
||||
$value .= $hours . $this->language->translateLabel('h', 'durationUnits');
|
||||
|
||||
if ($minutes) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if ($minutes) {
|
||||
$value .= $minutes . $this->language->translateLabel('m', 'durationUnits');
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Utils\Language;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
use Espo\Tools\Export\Format\Xlsx\FieldHelper;
|
||||
|
||||
class Enumeration implements CellValuePreparator
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $ormDefs,
|
||||
private Language $language,
|
||||
private FieldHelper $fieldHelper
|
||||
) {}
|
||||
|
||||
public function prepare(Entity $entity, string $name): ?string
|
||||
{
|
||||
if (!$entity->has($name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = $entity->get($name);
|
||||
|
||||
$fieldData = $this->fieldHelper->getData($entity->getEntityType(), $name);
|
||||
|
||||
if (!$fieldData) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$entityType = $fieldData->getEntityType();
|
||||
$field = $fieldData->getField();
|
||||
|
||||
$translation = $this->ormDefs
|
||||
->getEntity($entityType)
|
||||
->getField($field)
|
||||
->getParam('translation');
|
||||
|
||||
if (!$translation) {
|
||||
return $this->language->translateOption($value, $field, $entityType);
|
||||
}
|
||||
|
||||
$map = $this->language->get($translation);
|
||||
|
||||
if (!is_array($map)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $map[$value] ?? $value;
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class Floating implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): float
|
||||
{
|
||||
return $entity->get($name) ?? 0.0;
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class General implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): string|bool|int|float|null
|
||||
{
|
||||
$value = $entity->get($name);
|
||||
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
!is_string($value) &&
|
||||
!is_int($value) &&
|
||||
!is_float($value) &&
|
||||
!is_bool($value)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class Integer implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): int
|
||||
{
|
||||
/** @var int */
|
||||
return $entity->get($name) ?? 0;
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class Link implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): ?string
|
||||
{
|
||||
/** @var ?string */
|
||||
return $entity->get($name . 'Name');
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
use stdClass;
|
||||
|
||||
class LinkMultiple implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): ?string
|
||||
{
|
||||
if (
|
||||
!$entity->has($name . 'Ids') ||
|
||||
!$entity->has($name . 'Names')
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string[] $ids */
|
||||
$ids = $entity->get($name . 'Ids');
|
||||
/** @var ?stdClass $names */
|
||||
$names = $entity->get($name . 'Names');
|
||||
|
||||
$nameList = array_map(function ($id) use ($names) {
|
||||
return $names->$id ?? $id;
|
||||
}, $ids);
|
||||
|
||||
return implode(',', $nameList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\Core\Utils\Language;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
use Espo\Tools\Export\Format\Xlsx\FieldHelper;
|
||||
|
||||
class MultiEnum implements CellValuePreparator
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $ormDefs,
|
||||
private Language $language,
|
||||
private FieldHelper $fieldHelper
|
||||
) {}
|
||||
|
||||
public function prepare(Entity $entity, string $name): ?string
|
||||
{
|
||||
if (!$entity->has($name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$list = $entity->get($name);
|
||||
|
||||
if (!is_array($list)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string[] $list */
|
||||
|
||||
$fieldData = $this->fieldHelper->getData($entity->getEntityType(), $name);
|
||||
|
||||
if (!$fieldData) {
|
||||
return $this->joinList($list);
|
||||
}
|
||||
|
||||
$entityType = $fieldData->getEntityType();
|
||||
$field = $fieldData->getField();
|
||||
|
||||
$translation = $this->ormDefs
|
||||
->getEntity($entityType)
|
||||
->getField($field)
|
||||
->getParam('translation');
|
||||
|
||||
if (!$translation) {
|
||||
return $this->joinList(
|
||||
array_map(
|
||||
function ($item) use ($field, $entityType) {
|
||||
return $this->language->translateOption($item, $field, $entityType);
|
||||
},
|
||||
$list
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$map = $this->language->get($translation);
|
||||
|
||||
if (!is_array($map)) {
|
||||
return $this->joinList($list);
|
||||
}
|
||||
|
||||
return $this->joinList(
|
||||
array_map(
|
||||
function ($item) use ($map) {
|
||||
return $map[$item] ?? $item;
|
||||
},
|
||||
$list
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $list
|
||||
*/
|
||||
private function joinList(array $list): string
|
||||
{
|
||||
return implode(', ', $list);
|
||||
}
|
||||
}
|
||||
@@ -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\Tools\Export\Format\Xlsx\CellValuePreparators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
|
||||
class PersonName implements CellValuePreparator
|
||||
{
|
||||
public function prepare(Entity $entity, string $name): ?string
|
||||
{
|
||||
$value = $entity->get($name);
|
||||
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$arr = [];
|
||||
|
||||
$firstName = $entity->get('first' . ucfirst($name));
|
||||
$lastName = $entity->get('last' . ucfirst($name));
|
||||
|
||||
if ($firstName) {
|
||||
$arr[] = $firstName;
|
||||
}
|
||||
|
||||
if ($lastName) {
|
||||
$arr[] = $lastName;
|
||||
}
|
||||
|
||||
return implode(' ', $arr) ?: null;
|
||||
}
|
||||
}
|
||||
60
application/Espo/Tools/Export/Format/Xlsx/FieldData.php
Normal file
60
application/Espo/Tools/Export/Format/Xlsx/FieldData.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Export\Format\Xlsx;
|
||||
|
||||
class FieldData
|
||||
{
|
||||
public function __construct(
|
||||
private string $entityType,
|
||||
private string $field,
|
||||
private string $type,
|
||||
private ?string $link
|
||||
) {}
|
||||
|
||||
public function getEntityType(): string
|
||||
{
|
||||
return $this->entityType;
|
||||
}
|
||||
|
||||
public function getField(): string
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getLink(): ?string
|
||||
{
|
||||
return $this->link;
|
||||
}
|
||||
}
|
||||
115
application/Espo/Tools/Export/Format/Xlsx/FieldHelper.php
Normal file
115
application/Espo/Tools/Export/Format/Xlsx/FieldHelper.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?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\Tools\Export\Format\Xlsx;
|
||||
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\ORM\Defs;
|
||||
|
||||
class FieldHelper
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $ormDefs
|
||||
) {}
|
||||
|
||||
public function isForeignReference(string $name): bool
|
||||
{
|
||||
return str_contains($name, '_');
|
||||
}
|
||||
|
||||
private function isForeign(string $entityType, string $name): bool
|
||||
{
|
||||
if ($this->isForeignReference($name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$entityDefs = $this->ormDefs->getEntity($entityType);
|
||||
|
||||
return
|
||||
$entityDefs->hasField($name) &&
|
||||
$entityDefs->getField($name)->getType() === FieldType::FOREIGN;
|
||||
}
|
||||
|
||||
public function getData(string $entityType, string $name): ?FieldData
|
||||
{
|
||||
$entityDefs = $this->ormDefs->getEntity($entityType);
|
||||
|
||||
if (!$this->isForeign($entityType, $name)) {
|
||||
if (!$entityDefs->hasField($name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = $entityDefs
|
||||
->getField($name)
|
||||
->getType();
|
||||
|
||||
return new FieldData($entityType, $name, $type, null);
|
||||
}
|
||||
|
||||
$link = null;
|
||||
$field = null;
|
||||
|
||||
if (
|
||||
$entityDefs->hasField($name) &&
|
||||
$entityDefs->getField($name)->getType() === FieldType::FOREIGN
|
||||
) {
|
||||
$fieldDefs = $entityDefs->getField($name);
|
||||
|
||||
$link = $fieldDefs->getParam('link');
|
||||
$field = $fieldDefs->getParam('field');
|
||||
} else if (str_contains($name, '_')) {
|
||||
[$link, $field] = explode('_', $name);
|
||||
}
|
||||
|
||||
if (!$link || !$field) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$entityDefs = $this->ormDefs->getEntity($entityType);
|
||||
|
||||
if (!$entityDefs->hasRelation($link)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$relationDefs = $entityDefs->getRelation($link);
|
||||
|
||||
if (!$relationDefs->hasForeignEntityType()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$foreignEntityType = $relationDefs->getForeignEntityType();
|
||||
|
||||
$type = $this->ormDefs
|
||||
->getEntity($foreignEntityType)
|
||||
->getField($field)
|
||||
->getType();
|
||||
|
||||
return new FieldData($foreignEntityType, $field, $type, $link);
|
||||
}
|
||||
}
|
||||
296
application/Espo/Tools/Export/Format/Xlsx/OpenSpoutProcessor.php
Normal file
296
application/Espo/Tools/Export/Format/Xlsx/OpenSpoutProcessor.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?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\Tools\Export\Format\Xlsx;
|
||||
|
||||
use Espo\Core\Field\Currency;
|
||||
use Espo\Core\Field\Date;
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\DateTime as DateTimeUtil;
|
||||
use Espo\Core\Utils\Language;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Collection;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
use Espo\Tools\Export\Format\CellValuePreparatorFactory;
|
||||
use Espo\Tools\Export\Processor as ProcessorInterface;
|
||||
use Espo\Tools\Export\Processor\Params;
|
||||
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
use LogicException;
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
use OpenSpout\Common\Exception\InvalidArgumentException;
|
||||
use OpenSpout\Common\Exception\IOException;
|
||||
use OpenSpout\Writer\Exception\WriterNotOpenedException;
|
||||
use OpenSpout\Writer\XLSX\Writer;
|
||||
use OpenSpout\Writer\XLSX\Entity\SheetView;
|
||||
use OpenSpout\Writer\XLSX\Options;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class OpenSpoutProcessor implements ProcessorInterface
|
||||
{
|
||||
private const FORMAT = 'xlsx';
|
||||
|
||||
/** @var array<string, CellValuePreparator> */
|
||||
private array $preparatorsCache = [];
|
||||
/** @var array<string, string> */
|
||||
private array $typesCache = [];
|
||||
|
||||
public function __construct(
|
||||
private FieldHelper $fieldHelper,
|
||||
private CellValuePreparatorFactory $cellValuePreparatorFactory,
|
||||
private Language $language,
|
||||
private DateTimeUtil $dateTime,
|
||||
private Config $config,
|
||||
private Metadata $metadata
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws IOException
|
||||
* @throws WriterNotOpenedException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function process(Params $params, Collection $collection): StreamInterface
|
||||
{
|
||||
if (!$params->getFieldList()) {
|
||||
throw new LogicException("No field list.");
|
||||
}
|
||||
|
||||
$filePath = tempnam(sys_get_temp_dir(), 'espo-export');
|
||||
|
||||
if (!$filePath) {
|
||||
throw new RuntimeException("Could not create a temp file.");
|
||||
}
|
||||
|
||||
$options = new Options();
|
||||
$options->setColumnWidthForRange(20, 1, count($params->getFieldList()));
|
||||
|
||||
$writer = new Writer($options);
|
||||
|
||||
$writer->openToFile($filePath);
|
||||
|
||||
$sheetView = new SheetView();
|
||||
$sheetView->setFreezeRow(2);
|
||||
|
||||
$writer->getCurrentSheet()->setSheetView($sheetView);
|
||||
|
||||
$headerCells = [];
|
||||
|
||||
foreach ($params->getFieldList() as $name) {
|
||||
$label = $this->translateLabel($params->getEntityType(), $name);
|
||||
|
||||
$headerCells[] = Cell::fromValue($label, (new Style())->setFontBold());
|
||||
}
|
||||
|
||||
$writer->addRow(new Row($headerCells));
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
$this->processRow($params, $entity, $writer);
|
||||
}
|
||||
|
||||
$writer->close();
|
||||
|
||||
$resource = fopen($filePath, 'r+');
|
||||
|
||||
if ($resource === false) {
|
||||
throw new RuntimeException("Could not open temp.");
|
||||
}
|
||||
|
||||
$stream = new Stream($resource);
|
||||
$stream->seek(0);
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
private function translateLabel(string $entityType, string $name): string
|
||||
{
|
||||
$label = $name;
|
||||
|
||||
$fieldData = $this->fieldHelper->getData($entityType, $name);
|
||||
$isForeignReference = $this->fieldHelper->isForeignReference($name);
|
||||
|
||||
if ($isForeignReference && $fieldData && $fieldData->getLink()) {
|
||||
$label =
|
||||
$this->language->translateLabel($fieldData->getLink(), 'links', $entityType) . '.' .
|
||||
$this->language->translateLabel($fieldData->getField(), 'fields', $fieldData->getEntityType());
|
||||
}
|
||||
|
||||
if (!$isForeignReference) {
|
||||
$label = $this->language->translateLabel($name, 'fields', $entityType);
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
private function processRow(Params $params, Entity $entity, Writer $writer): void
|
||||
{
|
||||
$cells = [];
|
||||
|
||||
foreach ($params->getFieldList() ?? [] as $name) {
|
||||
$cells[] = $this->prepareCell($params, $entity, $name);
|
||||
}
|
||||
|
||||
$writer->addRow(new Row($cells));
|
||||
}
|
||||
|
||||
private function prepareCell(Params $params, Entity $entity, mixed $name): Cell
|
||||
{
|
||||
$type = $this->getFieldType($params->getEntityType(), $name);
|
||||
|
||||
$value = $this->getPreparator($type)
|
||||
->prepare($entity, $name);
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = $this->sanitizeCellValue($value);
|
||||
|
||||
return Cell\StringCell::fromValue($value);
|
||||
}
|
||||
|
||||
if (is_int($value)) {
|
||||
return Cell\NumericCell::fromValue($value);
|
||||
}
|
||||
|
||||
if (is_float($value)) {
|
||||
return Cell\NumericCell::fromValue($value);
|
||||
}
|
||||
|
||||
if (is_bool($value)) {
|
||||
return Cell\BooleanCell::fromValue($value);
|
||||
}
|
||||
|
||||
if ($value instanceof Date) {
|
||||
$dateFormat = self::convertDateFormat($this->dateTime->getDateFormat());
|
||||
|
||||
$style = new Style();
|
||||
$style->setFormat($dateFormat);
|
||||
|
||||
return Cell\DateTimeCell::fromValue($value->toDateTime(), $style);
|
||||
}
|
||||
|
||||
if ($value instanceof DateTime) {
|
||||
$dateTimeFormat = self::convertDateFormat($this->dateTime->getDateTimeFormat());
|
||||
|
||||
$style = new Style();
|
||||
$style->setFormat($dateTimeFormat);
|
||||
|
||||
return Cell\DateTimeCell::fromValue($value->toDateTime(), $style);
|
||||
}
|
||||
|
||||
if ($value instanceof Currency) {
|
||||
$format = $this->getCurrencyFormat($value->getCode());
|
||||
|
||||
$style = new Style();
|
||||
$style->setFormat($format);
|
||||
|
||||
return Cell\NumericCell::fromValue($value->getAmount(), $style);
|
||||
}
|
||||
|
||||
return Cell::fromValue('');
|
||||
}
|
||||
|
||||
private function getFieldType(string $entityType, string $name): string
|
||||
{
|
||||
$key = $entityType . '-' . $name;
|
||||
|
||||
$type = $this->typesCache[$key] ?? null;
|
||||
|
||||
if (!$type) {
|
||||
$fieldData = $this->fieldHelper->getData($entityType, $name);
|
||||
$type = $fieldData ? $fieldData->getType() : 'base';
|
||||
$this->typesCache[$key] = $type;
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function getPreparator(string $type): CellValuePreparator
|
||||
{
|
||||
if (!array_key_exists($type, $this->preparatorsCache)) {
|
||||
$this->preparatorsCache[$type] = $this->cellValuePreparatorFactory->create(self::FORMAT, $type);
|
||||
}
|
||||
|
||||
return $this->preparatorsCache[$type];
|
||||
}
|
||||
|
||||
private static function convertDateFormat(string $format): string
|
||||
{
|
||||
$map = [
|
||||
'MM' => 'mm',
|
||||
'DD' => 'dd',
|
||||
'YYYY' => 'yyyy',
|
||||
'HH' => 'hh',
|
||||
'mm' => 'mm',
|
||||
'hh' => 'hh',
|
||||
'A' => 'AM/PM',
|
||||
'a' => 'AM/PM',
|
||||
'ss' => 'ss',
|
||||
];
|
||||
|
||||
return str_replace(
|
||||
array_keys($map),
|
||||
array_values($map),
|
||||
$format
|
||||
);
|
||||
}
|
||||
|
||||
private function getCurrencyFormat(string $code): string
|
||||
{
|
||||
$currencySymbol = $this->metadata->get(['app', 'currency', 'symbolMap', $code], '');
|
||||
|
||||
$currencyFormat = $this->config->get('currencyFormat') ?? 2;
|
||||
|
||||
if ($currencyFormat === 3) {
|
||||
return '#,##0.00_-"' . $currencySymbol . '"';
|
||||
}
|
||||
|
||||
return '[$' . $currencySymbol . '-409]#,##0.00;-[$' . $currencySymbol . '-409]#,##0.00';
|
||||
}
|
||||
|
||||
private function sanitizeCellValue(string $value): string
|
||||
{
|
||||
if ($value === '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (in_array($value[0], ['+', '-', '@', '='])) {
|
||||
return "'" . $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
134
application/Espo/Tools/Export/Format/Xlsx/ParamsHandler.php
Normal file
134
application/Espo/Tools/Export/Format/Xlsx/ParamsHandler.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?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\Tools\Export\Format\Xlsx;
|
||||
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Params;
|
||||
use Espo\Tools\Export\Processor;
|
||||
use Espo\Tools\Export\ProcessorParamsHandler;
|
||||
|
||||
class ParamsHandler implements ProcessorParamsHandler
|
||||
{
|
||||
public function __construct(
|
||||
private Metadata $metadata
|
||||
) {}
|
||||
|
||||
public function handle(Params $params, Processor\Params $processorParams): Processor\Params
|
||||
{
|
||||
$fieldList = $processorParams->getFieldList();
|
||||
|
||||
if ($fieldList === null) {
|
||||
return $processorParams;
|
||||
}
|
||||
|
||||
$fieldList = $this->filterFieldList($params->getEntityType(), $fieldList, $params->allFields());
|
||||
|
||||
$attributeList = $processorParams->getAttributeList();
|
||||
|
||||
$this->addAdditionalAttributes($params->getEntityType(), $attributeList, $fieldList);
|
||||
|
||||
return $processorParams
|
||||
->withAttributeList($attributeList)
|
||||
->withFieldList($fieldList);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $fieldList
|
||||
* @return string[]
|
||||
*/
|
||||
private function filterFieldList(string $entityType, array $fieldList, bool $exportAllFields): array
|
||||
{
|
||||
if ($exportAllFields) {
|
||||
foreach ($fieldList as $i => $field) {
|
||||
$type = $this->metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
|
||||
if (in_array($type, [FieldType::LINK_MULTIPLE, FieldType::ATTACHMENT_MULTIPLE])) {
|
||||
unset($fieldList[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($fieldList);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $attributeList
|
||||
* @param string[] $fieldList
|
||||
*/
|
||||
private function addAdditionalAttributes(string $entityType, array &$attributeList, array $fieldList): void
|
||||
{
|
||||
$linkList = [];
|
||||
|
||||
if (!in_array('id', $attributeList)) {
|
||||
$attributeList[] = 'id';
|
||||
}
|
||||
|
||||
$linkDefs = $this->metadata->get(['entityDefs', $entityType, 'links']) ?? [];
|
||||
|
||||
foreach ($linkDefs as $link => $defs) {
|
||||
$linkType = $defs['type'] ?? null;
|
||||
|
||||
if (!$linkType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($linkType === Entity::BELONGS_TO_PARENT) {
|
||||
$linkList[] = $link;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($linkType === Entity::BELONGS_TO && !empty($defs[RelationParam::NO_JOIN])) {
|
||||
if ($this->metadata->get(['entityDefs', $entityType, 'fields', $link])) {
|
||||
$linkList[] = $link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($linkList as $item) {
|
||||
if (in_array($item, $fieldList) && !in_array($item . 'Name', $attributeList)) {
|
||||
$attributeList[] = $item . 'Name';
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
$type = $this->metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
|
||||
if ($type === FieldType::CURRENCY_CONVERTED) {
|
||||
if (!in_array($field, $attributeList)) {
|
||||
$attributeList[] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,657 @@
|
||||
<?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\Tools\Export\Format\Xlsx;
|
||||
|
||||
use Espo\Core\Field\Currency;
|
||||
use Espo\Core\Field\Date;
|
||||
use Espo\Core\Field\DateTime as DateTimeValue;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Config\ApplicationConfig;
|
||||
use Espo\Entities\Attachment;
|
||||
use Espo\Core\FileStorage\Manager as FileStorageManager;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\DateTime as DateTimeUtil;
|
||||
use Espo\Core\Utils\Language;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Export\Collection;
|
||||
use Espo\Tools\Export\Format\CellValuePreparator;
|
||||
use Espo\Tools\Export\Format\CellValuePreparatorFactory;
|
||||
use Espo\Tools\Export\Processor as ProcessorInterface;
|
||||
use Espo\Tools\Export\Processor\Params;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate;
|
||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use RuntimeException;
|
||||
|
||||
class PhpSpreadsheetProcessor implements ProcessorInterface
|
||||
{
|
||||
private const FORMAT = 'xlsx';
|
||||
private const PARAM_RECORD_LINKS = 'recordLinks';
|
||||
private const PARAM_TITLE = 'title';
|
||||
|
||||
/** @var array<string, CellValuePreparator> */
|
||||
private array $preparatorsCache = [];
|
||||
|
||||
/** @var array<string, mixed> */
|
||||
private array $titleStyle = [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 12,
|
||||
]
|
||||
];
|
||||
/** @var array<string, mixed> */
|
||||
private array $dateStyle = [
|
||||
'font' => [
|
||||
'size' => 12,
|
||||
]
|
||||
];
|
||||
/** @var array<string, mixed> */
|
||||
private array $headerStyle = [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 12,
|
||||
]
|
||||
];
|
||||
/** @var array<string, mixed> */
|
||||
private array $linkStyle = [
|
||||
'font' => [
|
||||
'color' => ['rgb' => '345b7c'],
|
||||
'underline' => 'single',
|
||||
]
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
private Metadata $metadata,
|
||||
private Language $language,
|
||||
private DateTimeUtil $dateTime,
|
||||
private EntityManager $entityManager,
|
||||
private FileStorageManager $fileStorageManager,
|
||||
private FieldHelper $fieldHelper,
|
||||
private CellValuePreparatorFactory $cellValuePreparatorFactory,
|
||||
private ApplicationConfig $applicationConfig,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws SpreadsheetException
|
||||
* @throws WriterException
|
||||
*/
|
||||
public function process(Params $params, Collection $collection): StreamInterface
|
||||
{
|
||||
$entityType = $params->getEntityType();
|
||||
$fieldList = $params->getFieldList();
|
||||
|
||||
if ($fieldList === null) {
|
||||
throw new RuntimeException("Field list is required.");
|
||||
}
|
||||
|
||||
$sheetName = $this->getSheetNameFromParams($params);
|
||||
$exportName = $params->getName() ??
|
||||
$this->language->translate($entityType, 'scopeNamesPlural');
|
||||
|
||||
$phpExcel = new Spreadsheet();
|
||||
|
||||
$headerRowNumber = $params->getParam(self::PARAM_TITLE) ? 3 : 1;
|
||||
|
||||
$sheet = $phpExcel->setActiveSheetIndex(0)
|
||||
->setTitle($sheetName)
|
||||
->freezePane('A' . ($headerRowNumber + 1));
|
||||
|
||||
$now = new DateTime();
|
||||
$now->setTimezone(new DateTimeZone($this->config->get('timeZone', 'UTC')));
|
||||
|
||||
if ($params->getParam(self::PARAM_TITLE)) {
|
||||
$sheet
|
||||
->setCellValue('A1', $this->sanitizeCellValue($exportName))
|
||||
->setCellValue('A2',
|
||||
SharedDate::PHPToExcel(strtotime($now->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT)))
|
||||
);
|
||||
|
||||
$sheet->getStyle('A1')->applyFromArray($this->titleStyle);
|
||||
$sheet->getStyle('A2')->applyFromArray($this->dateStyle);
|
||||
$sheet->getStyle('A2')
|
||||
->getNumberFormat()
|
||||
->setFormatCode($this->dateTime->getDateTimeFormat());
|
||||
}
|
||||
|
||||
$azRange = $this->getColumnsRange($fieldList);
|
||||
|
||||
$rowNumber = $headerRowNumber;
|
||||
$linkColList = [];
|
||||
$lastIndex = 0;
|
||||
|
||||
foreach ($fieldList as $i => $name) {
|
||||
$col = $azRange[$i];
|
||||
$type = 'base';
|
||||
|
||||
$label = $this->translateLabel($entityType, $name);
|
||||
|
||||
$fieldData = $this->fieldHelper->getData($entityType, $name);
|
||||
|
||||
if ($fieldData) {
|
||||
$type = $fieldData->getType();
|
||||
}
|
||||
|
||||
$sheet->setCellValue($col . $rowNumber, $this->sanitizeCellValue($label));
|
||||
$sheet->getColumnDimension($col)->setAutoSize(true);
|
||||
|
||||
$linkTypeList = $params->getParam(self::PARAM_RECORD_LINKS) ?
|
||||
[FieldType::URL, FieldType::PHONE, FieldType::EMAIL, FieldType::LINK, FieldType::LINK_PARENT] :
|
||||
['url'];
|
||||
|
||||
if (
|
||||
in_array($type, $linkTypeList) ||
|
||||
$params->getParam(self::PARAM_RECORD_LINKS) && $name === 'name'
|
||||
) {
|
||||
$linkColList[] = $col;
|
||||
}
|
||||
|
||||
$lastIndex = $i;
|
||||
}
|
||||
|
||||
$col = $azRange[$lastIndex];
|
||||
|
||||
$sheet->getStyle("A$rowNumber:$col$rowNumber")->applyFromArray($this->headerStyle);
|
||||
$sheet->setAutoFilter("A$rowNumber:$col$rowNumber");
|
||||
|
||||
$typesCache = [];
|
||||
|
||||
$rowNumber++;
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
$this->processRow(
|
||||
$entity,
|
||||
$sheet,
|
||||
$rowNumber,
|
||||
$fieldList,
|
||||
$azRange,
|
||||
$typesCache
|
||||
);
|
||||
|
||||
$rowNumber++;
|
||||
}
|
||||
|
||||
$sheet->getStyle("A$headerRowNumber:A$rowNumber")
|
||||
->getNumberFormat()
|
||||
->setFormatCode(NumberFormat::FORMAT_TEXT);
|
||||
|
||||
$startingRowNumber = 2;
|
||||
|
||||
if ($params->getParam(self::PARAM_TITLE)) {
|
||||
$startingRowNumber += 2;
|
||||
}
|
||||
|
||||
foreach ($fieldList as $i => $name) {
|
||||
$col = $azRange[$i];
|
||||
|
||||
if (!array_key_exists($name, $typesCache)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$type = $typesCache[$name];
|
||||
|
||||
$coordinate = "$col$startingRowNumber:$col$rowNumber";
|
||||
|
||||
switch ($type) {
|
||||
case FieldType::CURRENCY:
|
||||
case FieldType::CURRENCY_CONVERTED:
|
||||
break;
|
||||
|
||||
case FieldType::INT:
|
||||
$sheet->getStyle($coordinate)
|
||||
->getNumberFormat()
|
||||
->setFormatCode('0');
|
||||
|
||||
break;
|
||||
|
||||
case FieldType::FLOAT:
|
||||
$sheet->getStyle($coordinate)
|
||||
->getNumberFormat()
|
||||
->setFormatCode(NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1);
|
||||
break;
|
||||
|
||||
case FieldType::DATE:
|
||||
$sheet->getStyle($coordinate)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($this->dateTime->getDateFormat());
|
||||
|
||||
break;
|
||||
|
||||
case FieldType::DATETIME_OPTIONAL:
|
||||
case FieldType::DATETIME:
|
||||
$sheet->getStyle($coordinate)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($this->dateTime->getDateTimeFormat());
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$sheet->getStyle($coordinate)
|
||||
->getNumberFormat()
|
||||
->setFormatCode('@');
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($linkColList as $linkColumn) {
|
||||
$sheet
|
||||
->getStyle($linkColumn . $startingRowNumber . ':' . $linkColumn . $rowNumber)
|
||||
->applyFromArray($this->linkStyle);
|
||||
}
|
||||
|
||||
$objWriter = IOFactory::createWriter($phpExcel, 'Xlsx');
|
||||
|
||||
$resource = fopen('php://temp', 'r+');
|
||||
|
||||
if ($resource === false) {
|
||||
throw new RuntimeException("Could not open temp.");
|
||||
}
|
||||
|
||||
$objWriter->save($resource);
|
||||
|
||||
$stream = new Stream($resource);
|
||||
$stream->seek(0);
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
private function translateLabel(string $entityType, string $name): string
|
||||
{
|
||||
$label = $name;
|
||||
|
||||
$fieldData = $this->fieldHelper->getData($entityType, $name);
|
||||
$isForeignReference = $this->fieldHelper->isForeignReference($name);
|
||||
|
||||
if ($isForeignReference && $fieldData && $fieldData->getLink()) {
|
||||
$label =
|
||||
$this->language->translateLabel($fieldData->getLink(), 'links', $entityType) . '.' .
|
||||
$this->language->translateLabel($fieldData->getField(), 'fields', $fieldData->getEntityType());
|
||||
}
|
||||
|
||||
if (!$isForeignReference) {
|
||||
$label = $this->language->translateLabel($name, 'fields', $entityType);
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $fieldList
|
||||
* @return string[]
|
||||
*/
|
||||
private function getColumnsRange(array $fieldList): array
|
||||
{
|
||||
$azRange = range('A', 'Z');
|
||||
$azRangeCopied = $azRange;
|
||||
|
||||
foreach ($azRangeCopied as $i => $char1) {
|
||||
foreach ($azRangeCopied as $j => $char2) {
|
||||
$azRange[] = $char1 . $char2;
|
||||
|
||||
if ($i * count($azRangeCopied) + $j === count($fieldList)) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $azRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $fieldList
|
||||
* @param string[] $azRange
|
||||
* @param array<string, string> $typesCache
|
||||
* @throws SpreadsheetException
|
||||
*/
|
||||
private function processRow(
|
||||
Entity $entity,
|
||||
Worksheet $sheet,
|
||||
int $rowNumber,
|
||||
array $fieldList,
|
||||
array $azRange,
|
||||
array &$typesCache
|
||||
): void {
|
||||
|
||||
foreach ($fieldList as $i => $name) {
|
||||
$col = $azRange[$i];
|
||||
|
||||
$coordinate = $col . $rowNumber;
|
||||
|
||||
$this->processCell(
|
||||
$entity,
|
||||
$sheet,
|
||||
$rowNumber,
|
||||
$coordinate,
|
||||
$name,
|
||||
$typesCache
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $typesCache
|
||||
* @throws SpreadsheetException
|
||||
*/
|
||||
private function processCell(
|
||||
Entity $entity,
|
||||
Worksheet $sheet,
|
||||
int $rowNumber,
|
||||
string $coordinate,
|
||||
string $name,
|
||||
array &$typesCache
|
||||
): void {
|
||||
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
$type = $typesCache[$name] ?? null;
|
||||
|
||||
if (!$type) {
|
||||
$fieldData = $this->fieldHelper->getData($entityType, $name);
|
||||
$type = $fieldData ? $fieldData->getType() : 'base';
|
||||
$typesCache[$name] = $type;
|
||||
}
|
||||
|
||||
$preparator = $this->getPreparator($type);
|
||||
|
||||
$value = $preparator->prepare($entity, $name);
|
||||
|
||||
if ($type === FieldType::IMAGE) {
|
||||
$this->applyImage(
|
||||
$entity,
|
||||
$coordinate,
|
||||
$sheet,
|
||||
$rowNumber,
|
||||
$name
|
||||
);
|
||||
|
||||
$value = null;
|
||||
}
|
||||
|
||||
$value = $this->sanitizeCellValue($value);
|
||||
|
||||
if (is_string($value)) {
|
||||
$sheet->setCellValueExplicit($coordinate, $value, DataType::TYPE_STRING);
|
||||
} else if (is_int($value) || is_float($value)) {
|
||||
$sheet->setCellValueExplicit($coordinate, $value, DataType::TYPE_NUMERIC);
|
||||
}
|
||||
|
||||
if (is_bool($value)) {
|
||||
$sheet->setCellValueExplicit($coordinate, $value, DataType::TYPE_BOOL);
|
||||
} else if ($value instanceof Date) {
|
||||
$sheet->setCellValue(
|
||||
$coordinate,
|
||||
SharedDate::PHPToExcel(
|
||||
strtotime($value->toString())
|
||||
)
|
||||
);
|
||||
} else if ($value instanceof DateTimeValue) {
|
||||
$sheet->setCellValue(
|
||||
$coordinate,
|
||||
SharedDate::PHPToExcel(
|
||||
strtotime($value->toDateTime()->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT))
|
||||
)
|
||||
);
|
||||
} else if ($value instanceof Currency) {
|
||||
$sheet->setCellValue($coordinate, $value->getAmount());
|
||||
|
||||
$sheet->getStyle($coordinate)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($this->getCurrencyFormatCode($value->getCode()));
|
||||
}
|
||||
|
||||
$this->applyLinks(
|
||||
$type,
|
||||
$entity,
|
||||
$sheet,
|
||||
$coordinate,
|
||||
$name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SpreadsheetException
|
||||
*/
|
||||
private function applyImage(
|
||||
Entity $entity,
|
||||
string $coordinate,
|
||||
Worksheet $sheet,
|
||||
int $rowNumber,
|
||||
string $name
|
||||
): void {
|
||||
|
||||
$attachmentId = $entity->get($name . 'Id');
|
||||
|
||||
if (!$attachmentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var ?Attachment $attachment */
|
||||
$attachment = $this->entityManager->getEntityById(Attachment::ENTITY_TYPE, $attachmentId);
|
||||
|
||||
if (!$attachment) {
|
||||
return;
|
||||
}
|
||||
|
||||
$objDrawing = new Drawing();
|
||||
$filePath = $this->fileStorageManager->getLocalFilePath($attachment);
|
||||
|
||||
if (!$filePath || !file_exists($filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$objDrawing->setPath($filePath);
|
||||
$objDrawing->setHeight(100);
|
||||
$objDrawing->setCoordinates($coordinate);
|
||||
$objDrawing->setWorksheet($sheet);
|
||||
|
||||
$sheet->getRowDimension($rowNumber)->setRowHeight(100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SpreadsheetException
|
||||
*/
|
||||
private function applyLinks(
|
||||
string $type,
|
||||
Entity $entity,
|
||||
Worksheet $sheet,
|
||||
string $coordinate,
|
||||
string $name
|
||||
): void {
|
||||
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
$link = null;
|
||||
|
||||
$foreignLink = null;
|
||||
$foreignField = null;
|
||||
|
||||
if (strpos($name, '_')) {
|
||||
[$foreignLink, $foreignField] = explode('_', $name);
|
||||
}
|
||||
|
||||
$siteUrl = $this->applicationConfig->getSiteUrl();
|
||||
|
||||
if ($name === 'name') {
|
||||
if ($entity->hasId()) {
|
||||
$link = "$siteUrl/#$entityType/view/{$entity->getId()}";
|
||||
}
|
||||
} else if ($type === FieldType::URL) {
|
||||
$value = $entity->get($name);
|
||||
|
||||
if ($value) {
|
||||
$link = $this->sanitizeUrl($value);
|
||||
}
|
||||
} else if ($type === FieldType::LINK) {
|
||||
$idValue = $entity->get($name . 'Id');
|
||||
|
||||
if ($idValue && $foreignField) {
|
||||
if (!$foreignLink) {
|
||||
$foreignEntity =
|
||||
$this->metadata->get(['entityDefs', $entityType, 'links', $name, RelationParam::ENTITY]);
|
||||
} else {
|
||||
$foreignEntity1 = $this->metadata
|
||||
->get(['entityDefs', $entityType, 'links', $foreignLink, 'entity']);
|
||||
|
||||
$foreignEntity = $this->metadata
|
||||
->get(['entityDefs', $foreignEntity1, 'links', $foreignField, 'entity']);
|
||||
}
|
||||
|
||||
if ($foreignEntity) {
|
||||
$link = "$siteUrl/#$foreignEntity/view/$idValue";
|
||||
}
|
||||
}
|
||||
} else if ($type === FieldType::FILE) {
|
||||
$idValue = $entity->get($name . 'Id');
|
||||
|
||||
if ($idValue) {
|
||||
$link = "$siteUrl/?entryPoint=download&id=$idValue";
|
||||
}
|
||||
} else if ($type === FieldType::LINK_PARENT) {
|
||||
$idValue = $entity->get($name . 'Id');
|
||||
$typeValue = $entity->get($name . 'Type');
|
||||
|
||||
if ($idValue && $typeValue) {
|
||||
$link = "$siteUrl/#$typeValue/view/$idValue";
|
||||
}
|
||||
} else if ($type === FieldType::PHONE) {
|
||||
$value = $entity->get($name);
|
||||
|
||||
if ($value) {
|
||||
$link = "tel:$value";
|
||||
}
|
||||
} else if ($type === FieldType::EMAIL) {
|
||||
$value = $entity->get($name);
|
||||
|
||||
if ($value) {
|
||||
$link = "mailto:$value";
|
||||
}
|
||||
}
|
||||
|
||||
if (!$link) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cell = $sheet->getCell($coordinate);
|
||||
|
||||
$hyperLink = $cell->getHyperlink();
|
||||
|
||||
$hyperLink->setUrl($link);
|
||||
$hyperLink->setTooltip($link);
|
||||
}
|
||||
|
||||
private function getPreparator(string $type): CellValuePreparator
|
||||
{
|
||||
if (!array_key_exists($type, $this->preparatorsCache)) {
|
||||
$this->preparatorsCache[$type] = $this->cellValuePreparatorFactory->create(self::FORMAT, $type);
|
||||
}
|
||||
|
||||
return $this->preparatorsCache[$type];
|
||||
}
|
||||
|
||||
private function getCurrencyFormatCode(string $currency): string
|
||||
{
|
||||
$currencySymbol = $this->metadata->get(['app', 'currency', 'symbolMap', $currency], '');
|
||||
|
||||
$currencyFormat = $this->config->get('currencyFormat') ?? 2;
|
||||
|
||||
if ($currencyFormat == 3) {
|
||||
return '#,##0.00_-"' . $currencySymbol . '"';
|
||||
}
|
||||
|
||||
return '[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00';
|
||||
}
|
||||
|
||||
private function getSheetNameFromParams(Params $params): string
|
||||
{
|
||||
$exportName =
|
||||
$params->getName() ??
|
||||
$this->language->translateLabel($params->getEntityType(), 'scopeNamesPlural');
|
||||
|
||||
$badCharList = ['*', ':', '/', '\\', '?', '[', ']'];
|
||||
|
||||
$sheetName = mb_substr($exportName, 0, 30, 'utf-8');
|
||||
$sheetName = str_replace($badCharList, ' ', $sheetName);
|
||||
|
||||
return str_replace('\'', '', $sheetName);
|
||||
}
|
||||
|
||||
private function sanitizeCellValue(mixed $value): mixed
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value === '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (in_array($value[0], ['+', '-', '@', '='])) {
|
||||
return "'" . $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function sanitizeUrl(string $value): ?string
|
||||
{
|
||||
$link = $value;
|
||||
|
||||
if (!preg_match("/[a-z]+:\/\//", $link)) {
|
||||
$link = 'https://' . $link;
|
||||
}
|
||||
|
||||
if (filter_var($link, FILTER_VALIDATE_URL)) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
83
application/Espo/Tools/Export/Format/Xlsx/Processor.php
Normal file
83
application/Espo/Tools/Export/Format/Xlsx/Processor.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Export\Format\Xlsx;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Tools\Export\Collection;
|
||||
use Espo\Tools\Export\Processor as ProcessorInterface;
|
||||
use Espo\Tools\Export\Processor\Params;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
class Processor implements ProcessorInterface
|
||||
{
|
||||
private const PARAM_LITE = 'lite';
|
||||
|
||||
public function __construct(
|
||||
private PhpSpreadsheetProcessor $phpSpreadsheetProcessor,
|
||||
private OpenSpoutProcessor $openSpoutProcessor,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
public function process(Params $params, Collection $collection): StreamInterface
|
||||
{
|
||||
return $params->getParam(self::PARAM_LITE) ?
|
||||
$this->processOpenSpout($params, $collection) :
|
||||
$this->processPhpSpreadsheet($params, $collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
private function processPhpSpreadsheet(Params $params, Collection $collection): StreamInterface
|
||||
{
|
||||
try {
|
||||
return $this->phpSpreadsheetProcessor->process($params, $collection);
|
||||
} catch (SpreadsheetException|WriterException $e) {
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
private function processOpenSpout(Params $params, Collection $collection): StreamInterface
|
||||
{
|
||||
try {
|
||||
return $this->openSpoutProcessor->process($params, $collection);
|
||||
} catch (\Throwable $e) {
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user