mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-09 07:37:01 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d755d3795 | ||
|
|
9fe61d2342 | ||
|
|
1f17d79e2d | ||
|
|
3b0c7f1d5b | ||
|
|
d30edee790 | ||
|
|
0815cc3b5e | ||
|
|
ddff78756d | ||
|
|
f8c01808d4 | ||
|
|
e073c82992 | ||
|
|
aa82f96993 | ||
|
|
867a0225f1 | ||
|
|
4ab636ded8 | ||
|
|
f764375e0e | ||
|
|
98b20a3e24 | ||
|
|
ca17a5453a | ||
|
|
641382dc66 | ||
|
|
8f89fa4597 | ||
|
|
543eb21a8b | ||
|
|
9fbc62837f |
@@ -41,6 +41,7 @@ use Espo\Core\{
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class Upgrade implements Command
|
||||
{
|
||||
@@ -127,7 +128,7 @@ class Upgrade implements Command
|
||||
try {
|
||||
$this->runUpgradeProcess($upgradeId, $params);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
catch (Throwable $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
}
|
||||
|
||||
@@ -137,7 +138,8 @@ class Upgrade implements Command
|
||||
$this->fileManager->unlink($packageFile);
|
||||
}
|
||||
|
||||
if (!empty($errorMessage)) {
|
||||
if (isset($errorMessage)) {
|
||||
$errorMessage = !empty($errorMessage) ? $errorMessage : "Error: An unexpected error occurred.";
|
||||
fwrite(\STDOUT, $errorMessage . "\n");
|
||||
|
||||
return;
|
||||
@@ -287,8 +289,11 @@ class Upgrade implements Command
|
||||
$upgradeManager = $this->getUpgradeManager(true);
|
||||
$upgradeManager->runInstallStep($stepName, ['id' => $upgradeId]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$GLOBALS['log']->error('Upgrade Error: ' . $e->getMessage());
|
||||
} catch (Throwable $e) {
|
||||
try {
|
||||
$GLOBALS['log']->error('Upgrade Error: ' . $e->getMessage());
|
||||
}
|
||||
catch (Throwable $t) {}
|
||||
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
@@ -308,7 +313,10 @@ class Upgrade implements Command
|
||||
$shellResult = shell_exec($command);
|
||||
|
||||
if ($shellResult !== 'true') {
|
||||
$GLOBALS['log']->error('Upgrade Error: ' . $shellResult);
|
||||
try {
|
||||
$GLOBALS['log']->error('Upgrade Error: ' . $shellResult);
|
||||
}
|
||||
catch (Throwable $t) {}
|
||||
|
||||
throw new Error($shellResult);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class UpgradeStep implements Command
|
||||
try {
|
||||
$result = $upgradeManager->runInstallStep($stepName, $params); // throw Exception on error
|
||||
} catch (\Exception $e) {
|
||||
die("Error: " . $e->getMessage() . "\n");
|
||||
die("Error: " . $e->getMessage());
|
||||
}
|
||||
|
||||
if (is_bool($result)) {
|
||||
|
||||
@@ -462,7 +462,7 @@ abstract class Base
|
||||
{
|
||||
if (!isset($this->data['restoreFileList'])) {
|
||||
$backupPath = $this->getPath('backupPath');
|
||||
$this->data['restoreFileList'] = $this->getFileList($backupPath);
|
||||
$this->data['restoreFileList'] = $this->getFileList($backupPath, true);
|
||||
}
|
||||
|
||||
return $this->data['restoreFileList'];
|
||||
@@ -496,7 +496,7 @@ abstract class Base
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getFileList($dirPath)
|
||||
protected function getFileList($dirPath, $skipVendorFileList = false)
|
||||
{
|
||||
$fileList = array();
|
||||
|
||||
@@ -508,10 +508,11 @@ abstract class Base
|
||||
}
|
||||
}
|
||||
|
||||
//vendor file list
|
||||
$vendorFileList = $this->getVendorFileList('copy');
|
||||
if (!empty($vendorFileList)) {
|
||||
$fileList = array_merge($fileList, $vendorFileList);
|
||||
if (!$skipVendorFileList) {
|
||||
$vendorFileList = $this->getVendorFileList('copy');
|
||||
if (!empty($vendorFileList)) {
|
||||
$fileList = array_merge($fileList, $vendorFileList);
|
||||
}
|
||||
}
|
||||
|
||||
return $fileList;
|
||||
@@ -738,7 +739,11 @@ abstract class Base
|
||||
return true;
|
||||
}
|
||||
catch (Throwable $e) {
|
||||
$GLOBALS['log']->error('Database rebuild failure, details: '. $e->getMessage() .'.');
|
||||
|
||||
try {
|
||||
$GLOBALS['log']->error('Database rebuild failure, details: '. $e->getMessage() .'.');
|
||||
}
|
||||
catch (Throwable $e) {}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -77,8 +77,6 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
protected function initPackage(array $data)
|
||||
{
|
||||
$GLOBALS['log']->setLevel('info');
|
||||
|
||||
$processId = $data['id'];
|
||||
|
||||
if (empty($processId)) {
|
||||
@@ -150,19 +148,23 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
|
||||
|
||||
$GLOBALS['log']->info('Installation process ['. $this->getProcessId() .']: Start "copy" step.');
|
||||
|
||||
/* remove files defined in a manifest "deleteBeforeCopy" */
|
||||
$this->deleteFiles('deleteBeforeCopy', true);
|
||||
/* remove files defined in a manifest */
|
||||
if (!$this->deleteFiles('delete', true)) {
|
||||
$this->throwErrorAndRemovePackage('Cannot delete files.');
|
||||
}
|
||||
|
||||
/* copy files from directory "Files" to EspoCRM files */
|
||||
if (!$this->copyFiles()) {
|
||||
$this->throwErrorAndRemovePackage('Cannot copy files.');
|
||||
}
|
||||
|
||||
/* remove files defined in a manifest */
|
||||
$this->deleteFiles('delete', true);
|
||||
if (!$this->deleteFiles('vendor')) {
|
||||
$this->throwErrorAndRemovePackage('Cannot delete vendor files.');
|
||||
}
|
||||
|
||||
$this->deleteFiles('vendor');
|
||||
$this->copyFiles('vendor');
|
||||
if (!$this->copyFiles('vendor')) {
|
||||
$this->throwErrorAndRemovePackage('Cannot copy vendor files.');
|
||||
}
|
||||
|
||||
$GLOBALS['log']->info('Installation process ['. $this->getProcessId() .']: End "copy" step.');
|
||||
}
|
||||
|
||||
@@ -66,22 +66,13 @@ class Autoload
|
||||
$this->loader = $loader;
|
||||
}
|
||||
|
||||
public function get($key = null, $returns = null)
|
||||
protected function getData() : array
|
||||
{
|
||||
if (!isset($this->data)) {
|
||||
$this->init();
|
||||
}
|
||||
|
||||
if (!isset($key)) {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return Util::getValueByKey($this->data, $key, $returns);
|
||||
}
|
||||
|
||||
public function getAll()
|
||||
{
|
||||
return $this->get();
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
protected function init()
|
||||
@@ -94,46 +85,48 @@ class Autoload
|
||||
return;
|
||||
}
|
||||
|
||||
$this->data = $this->unify();
|
||||
$this->data = $this->loadData();
|
||||
|
||||
if ($useCache) {
|
||||
$result = $this->dataCache->store($this->cacheKey, $this->data);
|
||||
}
|
||||
}
|
||||
|
||||
protected function unify()
|
||||
protected function loadData() : array
|
||||
{
|
||||
$data = $this->loadData($this->paths['corePath']);
|
||||
$data = $this->loadDataFromFile($this->paths['corePath']);
|
||||
|
||||
foreach ($this->metadata->getModuleList() as $moduleName) {
|
||||
$modulePath = str_replace('{*}', $moduleName, $this->paths['modulePath']);
|
||||
|
||||
$data = array_merge($data, $this->loadData($modulePath));
|
||||
$data = array_merge_recursive($data, $this->loadDataFromFile($modulePath));
|
||||
}
|
||||
|
||||
$data = array_merge($data, $this->loadData($this->paths['customPath']));
|
||||
$data = array_merge_recursive($data, $this->loadDataFromFile($this->paths['customPath']));
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function loadData($autoloadFile, $returns = [])
|
||||
protected function loadDataFromFile(string $filePath) : array
|
||||
{
|
||||
if (file_exists($autoloadFile)) {
|
||||
$content = $this->fileManager->getContents($autoloadFile);
|
||||
|
||||
$arrayContent = Json::getArrayData($content);
|
||||
|
||||
if (!empty($arrayContent)) {
|
||||
return $this->normalizeData($arrayContent);
|
||||
}
|
||||
|
||||
$GLOBALS['log']->error('Autoload: Empty file or syntax error ['.$autoloadFile.'].');
|
||||
if (!$this->fileManager->isFile($filePath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $returns;
|
||||
$content = $this->fileManager->getContents($filePath);
|
||||
|
||||
$arrayContent = Json::getArrayData($content);
|
||||
|
||||
if (empty($arrayContent)) {
|
||||
$GLOBALS['log']->error("Autoload: Empty file or syntax error in '{$filePath}'.");
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->normalizeData($arrayContent);
|
||||
}
|
||||
|
||||
protected function normalizeData(array $data)
|
||||
protected function normalizeData(array $data) : array
|
||||
{
|
||||
$normalizedData = [];
|
||||
|
||||
@@ -161,12 +154,14 @@ class Autoload
|
||||
public function register()
|
||||
{
|
||||
try {
|
||||
$autoloadList = $this->getAll();
|
||||
$data = $this->getData();
|
||||
}
|
||||
catch (Exception $e) {} //bad permissions
|
||||
catch (Exception $e) {} // bad permissions
|
||||
|
||||
if (!empty($autoloadList)) {
|
||||
$this->loader->register($autoloadList);
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loader->register($data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,42 +38,42 @@ class Loader
|
||||
$this->namespaceLoader = $namespaceLoader;
|
||||
}
|
||||
|
||||
public function register(array $autoloadList)
|
||||
public function register(array $data)
|
||||
{
|
||||
/* load "psr-4", "psr-0", "classmap" */
|
||||
$this->namespaceLoader->register($autoloadList);
|
||||
$this->namespaceLoader->register($data);
|
||||
|
||||
/* load "autoloadFileList" */
|
||||
$this->registerAutoloadFileList($autoloadList);
|
||||
$this->registerAutoloadFileList($data);
|
||||
|
||||
/* load "files" */
|
||||
$this->registerFiles($autoloadList);
|
||||
$this->registerFiles($data);
|
||||
}
|
||||
|
||||
protected function registerAutoloadFileList(array $autoloadList)
|
||||
protected function registerAutoloadFileList(array $data)
|
||||
{
|
||||
$keyName = 'autoloadFileList';
|
||||
|
||||
if (!isset($autoloadList[$keyName])) {
|
||||
if (!isset($data[$keyName])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($autoloadList[$keyName] as $filePath) {
|
||||
foreach ($data[$keyName] as $filePath) {
|
||||
if (file_exists($filePath)) {
|
||||
require_once($filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function registerFiles(array $autoloadList)
|
||||
protected function registerFiles(array $data)
|
||||
{
|
||||
$keyName = 'files';
|
||||
|
||||
if (!isset($autoloadList[$keyName])) {
|
||||
if (!isset($data[$keyName])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($autoloadList[$keyName] as $id => $filePath) {
|
||||
foreach ($data[$keyName] as $id => $filePath) {
|
||||
if (file_exists($filePath)) {
|
||||
require_once($filePath);
|
||||
}
|
||||
|
||||
@@ -74,9 +74,9 @@ class NamespaceLoader
|
||||
$this->classLoader = new ClassLoader();
|
||||
}
|
||||
|
||||
public function register(array $autoloadList)
|
||||
public function register(array $data)
|
||||
{
|
||||
$this->addListToClassLoader($this->classLoader, $autoloadList);
|
||||
$this->addListToClassLoader($this->classLoader, $data);
|
||||
|
||||
$this->classLoader->register(true);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ class NamespaceLoader
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function addListToClassLoader($classLoader, array $list, $skipVendorNamespaces = false)
|
||||
protected function addListToClassLoader(ClassLoader $classLoader, array $list, bool $skipVendorNamespaces = false)
|
||||
{
|
||||
foreach ($this->methodNameMap as $type => $methodName) {
|
||||
if (!isset($list[$type])) {
|
||||
|
||||
@@ -262,7 +262,7 @@ class Manager
|
||||
}
|
||||
|
||||
if ($result && function_exists('opcache_invalidate')) {
|
||||
@opcache_invalidate($fullPath);
|
||||
opcache_invalidate($fullPath);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -516,6 +516,12 @@ class Manager
|
||||
return true;
|
||||
}
|
||||
|
||||
$parentDirPath = dirname($fullPath);
|
||||
|
||||
if (!file_exists($parentDirPath)) {
|
||||
$this->mkdir($parentDirPath, $permission);
|
||||
}
|
||||
|
||||
$defaultPermissions = $this->getPermissionUtils()->getRequiredPermissions($fullPath);
|
||||
|
||||
if (!isset($permission)) {
|
||||
@@ -524,11 +530,11 @@ class Manager
|
||||
}
|
||||
|
||||
try {
|
||||
$umask = @umask(0);
|
||||
$result = mkdir($fullPath, $permission, true);
|
||||
$umask = umask(0);
|
||||
$result = mkdir($fullPath, $permission);
|
||||
|
||||
if ($umask) {
|
||||
@umask($umask);
|
||||
umask($umask);
|
||||
}
|
||||
|
||||
if (!empty($defaultPermissions['user'])) {
|
||||
@@ -615,7 +621,7 @@ class Manager
|
||||
$this->getPermissionUtils()->setDefaultPermissions($destFile);
|
||||
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
@opcache_invalidate($destFile);
|
||||
opcache_invalidate($destFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -719,7 +725,7 @@ class Manager
|
||||
|
||||
if (file_exists($filePath) && is_file($filePath)) {
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
@opcache_invalidate($filePath, true);
|
||||
opcache_invalidate($filePath, true);
|
||||
}
|
||||
|
||||
$result &= unlink($filePath);
|
||||
@@ -821,7 +827,7 @@ class Manager
|
||||
|
||||
/**
|
||||
* Remove empty parent directories if they are empty.
|
||||
* @param string $path
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function removeEmptyDirs($path)
|
||||
@@ -838,29 +844,19 @@ class Manager
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $dirname is directory.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param string $basePath
|
||||
*
|
||||
* @return boolean
|
||||
* Check whether a path is a directory.
|
||||
*/
|
||||
public function isDir($dirname, $basePath = null)
|
||||
public function isDir(string $dirPath) : bool
|
||||
{
|
||||
if (!empty($basePath)) {
|
||||
$dirname = $this->concatPaths([$basePath, $dirname]);
|
||||
}
|
||||
|
||||
return is_dir($dirname);
|
||||
return is_dir($dirPath);
|
||||
}
|
||||
|
||||
public function isFile($filename, $basePath = null)
|
||||
/**
|
||||
* Check whether a path is a file.
|
||||
*/
|
||||
public function isFile(string $filePath) : bool
|
||||
{
|
||||
if (!empty($basePath)) {
|
||||
$filename = $this->concatPaths([$basePath, $filename]);
|
||||
}
|
||||
|
||||
return is_file($filename);
|
||||
return is_file($filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -891,12 +887,9 @@ class Manager
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if directory is empty.
|
||||
*
|
||||
* @param string $path
|
||||
* @return boolean
|
||||
* Check whether a directory is empty.
|
||||
*/
|
||||
public function isDirEmpty($path)
|
||||
public function isDirEmpty(string $path) : bool
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
$fileList = $this->getFileList($path, true);
|
||||
|
||||
@@ -390,7 +390,7 @@ class Permission
|
||||
protected function chmodReal($filename, $mode)
|
||||
{
|
||||
try {
|
||||
$result = @chmod($filename, $mode);
|
||||
$result = chmod($filename, $mode);
|
||||
} catch (\Exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
@@ -400,7 +400,7 @@ class Permission
|
||||
$this->chgrp($filename, $this->getDefaultGroup(true));
|
||||
|
||||
try {
|
||||
$result = @chmod($filename, $mode);
|
||||
$result = chmod($filename, $mode);
|
||||
} catch (\Exception $e) {
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
@@ -412,7 +412,7 @@ class Permission
|
||||
protected function chownReal($path, $user)
|
||||
{
|
||||
try {
|
||||
$result = @chown($path, $user);
|
||||
$result = chown($path, $user);
|
||||
} catch (\Exception $e) {
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
@@ -423,7 +423,7 @@ class Permission
|
||||
protected function chgrpReal($path, $group)
|
||||
{
|
||||
try {
|
||||
$result = @chgrp($path, $group);
|
||||
$result = chgrp($path, $group);
|
||||
} catch (\Exception $e) {
|
||||
throw new Error($e->getMessage());
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ class Lead extends PersonService implements
|
||||
|
||||
$ignoreAttributeList = ['createdAt', 'modifiedAt', 'modifiedById', 'modifiedByName', 'createdById', 'createdByName'];
|
||||
|
||||
$convertFieldsDefs = $this->getMetadata()->get('entityDefs.Lead.convertFields', array());
|
||||
$convertFieldsDefs = $this->getMetadata()->get('entityDefs.Lead.convertFields', []);
|
||||
|
||||
foreach ($entityList as $entityType) {
|
||||
if (!$this->getAcl()->checkScope($entityType, 'edit')) {
|
||||
@@ -141,7 +141,10 @@ class Lead extends PersonService implements
|
||||
$attachment = $lead->get($field);
|
||||
|
||||
if ($attachment) {
|
||||
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
|
||||
$attachment = $this->getEntityManager()
|
||||
->getRepository('Attachment')
|
||||
->getCopiedAttachment($attachment);
|
||||
|
||||
$idAttribute = $field . 'Id';
|
||||
$nameAttribute = $field . 'Name';
|
||||
|
||||
@@ -189,7 +192,11 @@ class Lead extends PersonService implements
|
||||
continue;
|
||||
}
|
||||
|
||||
$leadAttribute = $leadAttributeList[$i];
|
||||
$leadAttribute = $leadAttributeList[$i] ?? null;
|
||||
|
||||
if (!$leadAttribute) {
|
||||
throw new Error("Not compatible fields in 'convertFields' map.");
|
||||
}
|
||||
|
||||
if (!$lead->has($leadAttribute)) {
|
||||
continue;
|
||||
|
||||
@@ -297,12 +297,6 @@ class RDBRepository extends Repository
|
||||
$additionalColumnsConditions = $params['additionalColumnsConditions'] ?? [];
|
||||
unset($params['additionalColumnsConditions']);
|
||||
|
||||
if ($type === Entity::MANY_MANY && count($additionalColumnsConditions)) {
|
||||
$select = $this->applyRelationAdditionalColumnsConditions(
|
||||
$entity, $relationName, $additionalColumnsConditions, $select
|
||||
);
|
||||
}
|
||||
|
||||
$select = null;
|
||||
|
||||
if ($entityType) {
|
||||
@@ -310,6 +304,12 @@ class RDBRepository extends Repository
|
||||
$select = Select::fromRaw($params);
|
||||
}
|
||||
|
||||
if ($type === Entity::MANY_MANY && count($additionalColumnsConditions)) {
|
||||
$select = $this->applyRelationAdditionalColumnsConditions(
|
||||
$entity, $relationName, $additionalColumnsConditions, $select
|
||||
);
|
||||
}
|
||||
|
||||
return (int) $this->getMapper()->countRelated($entity, $relationName, $select);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"tooltips": {
|
||||
"name": "Give the filter a descriptive name.",
|
||||
"subject": "Use a wildcard *:\n\ntext* - starts with text,\n*text* - contains text,\n*text - ends with text.",
|
||||
"subject": "Use a wildcard *: \n\n * `text*` – starts with text,\n * `*text*` – contains text,\n * `*text` – ends with text.",
|
||||
"bodyContains": "Body of the email contains any of the specified words or phrases.",
|
||||
"from": "Emails being sent from the specified address. Leave empty if not needed. You can use wildcard *.",
|
||||
"to": "Emails being sent to the specified address. Leave empty if not needed. You can use wildcard *.",
|
||||
|
||||
@@ -706,7 +706,7 @@ class Record implements Crud,
|
||||
}
|
||||
}
|
||||
|
||||
protected function processValidationField(Entity $entity, $field, $data)
|
||||
protected function processValidationField(Entity $entity, string $field, $data)
|
||||
{
|
||||
$fieldType = $this->fieldUtil->getEntityTypeFieldParam($this->entityType, $field, 'type');
|
||||
|
||||
@@ -724,10 +724,11 @@ class Record implements Crud,
|
||||
}
|
||||
|
||||
$skipPropertyName = 'validate' . ucfirst($type) . 'SkipFieldList';
|
||||
|
||||
if (property_exists($this, $skipPropertyName)) {
|
||||
$skipList = $this->$skipPropertyName;
|
||||
|
||||
if (in_array($type, $skipList)) {
|
||||
if (in_array($field, $skipList)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,12 +36,23 @@ use Espo\Core\{
|
||||
Utils\Config,
|
||||
Utils\Metadata,
|
||||
Utils\Language,
|
||||
Utils\DateTime,
|
||||
Utils\DateTime as DateTimeUtil,
|
||||
FileStorage\Manager as FileStorageManager,
|
||||
Utils\File\Manager as FileManager,
|
||||
ORM\EntityManager,
|
||||
};
|
||||
|
||||
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 DateTime;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
|
||||
class Xlsx
|
||||
{
|
||||
protected $config;
|
||||
@@ -56,7 +67,7 @@ class Xlsx
|
||||
Config $config,
|
||||
Metadata $metadata,
|
||||
Language $language,
|
||||
DateTime $dateTime,
|
||||
DateTimeUtil $dateTime,
|
||||
EntityManager $entityManager,
|
||||
FileStorageManager $fileStorageManager,
|
||||
FileManager $fileManager
|
||||
@@ -146,18 +157,24 @@ class Xlsx
|
||||
}
|
||||
|
||||
$linkDefs = $this->getMetadata()->get(['entityDefs', $entityType, 'links']);
|
||||
|
||||
if (is_array($linkDefs)) {
|
||||
foreach ($linkDefs as $link => $defs) {
|
||||
if (empty($defs['type'])) continue;
|
||||
if (empty($defs['type'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($defs['type'] === 'belongsToParent') {
|
||||
$linkList[] = $link;
|
||||
} else if ($defs['type'] === 'belongsTo' && !empty($defs['noJoin'])) {
|
||||
}
|
||||
else if ($defs['type'] === 'belongsTo' && !empty($defs['noJoin'])) {
|
||||
if ($this->getMetadata()->get(['entityDefs', $entityType, 'fields', $link])) {
|
||||
$linkList[] = $link;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($linkList as $item) {
|
||||
if (in_array($item, $fieldList) && !in_array($item . 'Name', $attributeList)) {
|
||||
$attributeList[] = $item . 'Name';
|
||||
@@ -166,6 +183,7 @@ class Xlsx
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
$type = $this->getMetadata()->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
|
||||
if ($type === 'currencyConverted') {
|
||||
if (!in_array($field, $attributeList)) {
|
||||
$attributeList[] = $field;
|
||||
@@ -180,43 +198,49 @@ class Xlsx
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$phpExcel = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
|
||||
$phpExcel = new Spreadsheet();
|
||||
|
||||
$sheet = $phpExcel->setActiveSheetIndex(0);
|
||||
|
||||
if (isset($params['exportName'])) {
|
||||
$exportName = $params['exportName'];
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$exportName = $this->language->translate($entityType, 'scopeNamesPlural');
|
||||
}
|
||||
|
||||
$sheetName = mb_substr($exportName, 0, 30, 'utf-8');
|
||||
$badCharList = ['*', ':', '/', '\\', '?', '[', ']'];
|
||||
|
||||
foreach ($badCharList as $badChar) {
|
||||
$sheetName = str_replace($badCharList, ' ', $sheetName);
|
||||
}
|
||||
|
||||
$sheetName = str_replace('\'', '', $sheetName);
|
||||
|
||||
$sheet->setTitle($sheetName);
|
||||
|
||||
$fieldList = $params['fieldList'];
|
||||
|
||||
$titleStyle = array(
|
||||
'font' => array(
|
||||
$titleStyle = [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 12
|
||||
)
|
||||
);
|
||||
$dateStyle = array(
|
||||
'font' => array(
|
||||
'size' => 12
|
||||
)
|
||||
);
|
||||
'size' => 12,
|
||||
],
|
||||
];
|
||||
|
||||
$now = new \DateTime();
|
||||
$now->setTimezone(new \DateTimeZone($this->config->get('timeZone', 'UTC')));
|
||||
$dateStyle = [
|
||||
'font' => [
|
||||
'size' => 12,
|
||||
],
|
||||
];
|
||||
|
||||
$now = new DateTime();
|
||||
|
||||
$now->setTimezone(new DateTimeZone($this->config->get('timeZone', 'UTC')));
|
||||
|
||||
$sheet->setCellValue('A1', $exportName);
|
||||
$sheet->setCellValue('B1', \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($now->format('Y-m-d H:i:s'))));
|
||||
$sheet->setCellValue('B1', SharedDate::PHPToExcel(strtotime($now->format('Y-m-d H:i:s'))));
|
||||
|
||||
$sheet->getStyle('A1')->applyFromArray($titleStyle);
|
||||
$sheet->getStyle('B1')->applyFromArray($dateStyle);
|
||||
@@ -229,6 +253,7 @@ class Xlsx
|
||||
foreach ($azRangeCopied as $i => $char1) {
|
||||
foreach ($azRangeCopied as $j => $char2) {
|
||||
$azRange[] = $char1 . $char2;
|
||||
|
||||
if ($i * count($azRangeCopied) + $j === count($fieldList)) {
|
||||
break 2;
|
||||
}
|
||||
@@ -251,39 +276,45 @@ class Xlsx
|
||||
}
|
||||
|
||||
$label = $name;
|
||||
|
||||
if (strpos($name, '_') !== false) {
|
||||
list($linkName, $foreignField) = explode('_', $name);
|
||||
$foreignScope = $this->metadata->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
|
||||
if ($foreignScope) {
|
||||
$label = $this->language->translate($linkName, 'links', $entityType) . '.' . $this->language->translate($foreignField, 'fields', $foreignScope);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$label = $this->language->translate($name, 'fields', $entityType);
|
||||
}
|
||||
|
||||
$sheet->setCellValue($col . $rowNumber, $label);
|
||||
|
||||
$sheet->getColumnDimension($col)->setAutoSize(true);
|
||||
|
||||
if (in_array($defs['type'], ['phone', 'email', 'url', 'link', 'linkParent'])) {
|
||||
$linkColList[] = $col;
|
||||
} else if ($name == 'name') {
|
||||
}
|
||||
else if ($name == 'name') {
|
||||
$linkColList[] = $col;
|
||||
}
|
||||
|
||||
$lastIndex = $i;
|
||||
}
|
||||
|
||||
$col = $azRange[$i];
|
||||
|
||||
$headerStyle = array(
|
||||
'font' => array(
|
||||
$headerStyle = [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 12
|
||||
)
|
||||
);
|
||||
'size' => 12,
|
||||
]
|
||||
];
|
||||
|
||||
$sheet->getStyle("A$rowNumber:$col$rowNumber")->applyFromArray($headerStyle);
|
||||
$sheet->setAutoFilter("A$rowNumber:$col$rowNumber");
|
||||
|
||||
$typesCache = array();
|
||||
$typesCache = [];
|
||||
|
||||
$rowNumber++;
|
||||
|
||||
@@ -297,18 +328,28 @@ class Xlsx
|
||||
|
||||
if ($dataFp) {
|
||||
$line = fgets($dataFp);
|
||||
if ($line === false) break;
|
||||
|
||||
if ($line === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$row = unserialize(base64_decode($line));
|
||||
} else {
|
||||
if ($lineIndex >= $lineCount) break;
|
||||
}
|
||||
else {
|
||||
if ($lineIndex >= $lineCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
$row = $dataList[$lineIndex];
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
|
||||
foreach ($fieldList as $i => $name) {
|
||||
$col = $azRange[$i];
|
||||
|
||||
$defs = $this->metadata->get(['entityDefs', $entityType, 'fields', $name]);
|
||||
|
||||
if (!$defs) {
|
||||
$defs = array();
|
||||
$defs['type'] = 'base';
|
||||
@@ -317,17 +358,22 @@ class Xlsx
|
||||
$type = $defs['type'];
|
||||
$foreignField = $name;
|
||||
$linkName = null;
|
||||
|
||||
if (strpos($name, '_') !== false) {
|
||||
list($linkName, $foreignField) = explode('_', $name);
|
||||
|
||||
$foreignScope = $this->metadata->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
|
||||
|
||||
if ($foreignScope) {
|
||||
$type = $this->metadata->get(['entityDefs', $foreignScope, 'fields', $foreignField, 'type'], $type);
|
||||
}
|
||||
}
|
||||
|
||||
if ($type === 'foreign') {
|
||||
$linkName = $this->metadata->get(['entityDefs', $entityType, 'fields', $name, 'link']);
|
||||
$foreignField = $this->metadata->get(['entityDefs', $entityType, 'fields', $name, 'field']);
|
||||
$foreignScope = $this->metadata->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
|
||||
|
||||
if ($foreignScope) {
|
||||
$type = $this->metadata->get(['entityDefs', $foreignScope, 'fields', $foreignField, 'type'], $type);
|
||||
}
|
||||
@@ -335,19 +381,24 @@ class Xlsx
|
||||
$typesCache[$name] = $type;
|
||||
|
||||
$link = null;
|
||||
|
||||
if ($type == 'link') {
|
||||
if (array_key_exists($name.'Name', $row)) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
|
||||
}
|
||||
} else if ($type == 'linkParent') {
|
||||
}
|
||||
else if ($type == 'linkParent') {
|
||||
if (array_key_exists($name.'Name', $row)) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
|
||||
}
|
||||
} else if ($type == 'int') {
|
||||
}
|
||||
else if ($type == 'int') {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
|
||||
} else if ($type == 'float') {
|
||||
}
|
||||
else if ($type == 'float') {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
|
||||
} else if ($type == 'currency') {
|
||||
}
|
||||
else if ($type == 'currency') {
|
||||
if (array_key_exists($name.'Currency', $row) && array_key_exists($name, $row)) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
|
||||
|
||||
@@ -359,7 +410,8 @@ class Xlsx
|
||||
$this->getCurrencyFormatCode($currency)
|
||||
);
|
||||
}
|
||||
} else if ($type == 'currencyConverted') {
|
||||
}
|
||||
else if ($type == 'currencyConverted') {
|
||||
if (array_key_exists($name, $row)) {
|
||||
$currency = $this->getConfig()->get('defaultCurrency');
|
||||
|
||||
@@ -371,7 +423,8 @@ class Xlsx
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
|
||||
}
|
||||
} else if ($type == 'personName') {
|
||||
}
|
||||
else if ($type == 'personName') {
|
||||
if (!empty($row['name'])) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row['name']);
|
||||
} else {
|
||||
@@ -387,41 +440,51 @@ class Xlsx
|
||||
}
|
||||
$sheet->setCellValue("$col$rowNumber", $personName);
|
||||
}
|
||||
} else if ($type == 'date') {
|
||||
}
|
||||
else if ($type == 'date') {
|
||||
if (isset($row[$name])) {
|
||||
$sheet->setCellValue("$col$rowNumber", \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($row[$name])));
|
||||
$sheet->setCellValue("$col$rowNumber", SharedDate::PHPToExcel(strtotime($row[$name])));
|
||||
}
|
||||
} else if ($type == 'datetime' || $type == 'datetimeOptional') {
|
||||
}
|
||||
else if ($type == 'datetime' || $type == 'datetimeOptional') {
|
||||
$value = null;
|
||||
|
||||
if ($type == 'datetimeOptional') {
|
||||
if (isset($row[$name . 'Date']) && $row[$name . 'Date']) {
|
||||
$value = $row[$name . 'Date'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$value) {
|
||||
if (isset($row[$name])) {
|
||||
$value = $row[$name];
|
||||
}
|
||||
}
|
||||
|
||||
if ($value && strlen($value) > 11) {
|
||||
try {
|
||||
$timeZone = $this->config->get('timeZone');
|
||||
$dt = new \DateTime($value);
|
||||
$dt->setTimezone(new \DateTimeZone($timeZone));
|
||||
|
||||
$dt = new DateTime($value);
|
||||
$dt->setTimezone(new DateTimeZone($timeZone));
|
||||
|
||||
$value = $dt->format($this->dateTime->getInternalDateTimeFormat());
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$value = '';
|
||||
}
|
||||
}
|
||||
|
||||
if ($value) {
|
||||
$sheet->setCellValue("$col$rowNumber", \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($value)));
|
||||
$sheet->setCellValue("$col$rowNumber", SharedDate::PHPToExcel(strtotime($value)));
|
||||
}
|
||||
} else if ($type == 'image') {
|
||||
}
|
||||
else if ($type == 'image') {
|
||||
if (isset($row[$name . 'Id']) && $row[$name . 'Id']) {
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment', $row[$name . 'Id']);
|
||||
|
||||
if ($attachment) {
|
||||
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
|
||||
$objDrawing = new Drawing();
|
||||
$filePath = $this->fileStorageManager->getLocalFilePath($attachment);
|
||||
|
||||
if ($filePath && file_exists($filePath)) {
|
||||
@@ -434,66 +497,87 @@ class Xlsx
|
||||
}
|
||||
}
|
||||
|
||||
} else if ($type == 'file') {
|
||||
}
|
||||
else if ($type == 'file') {
|
||||
if (array_key_exists($name.'Name', $row)) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
|
||||
}
|
||||
} else if ($type == 'enum') {
|
||||
}
|
||||
else if ($type == 'enum') {
|
||||
if (array_key_exists($name, $row)) {
|
||||
if ($linkName) {
|
||||
$value = $this->language->translateOption($row[$name], $foreignField, $foreignScope);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$value = $this->language->translateOption($row[$name], $name, $entityType);
|
||||
}
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", $value);
|
||||
}
|
||||
} else if ($type == 'linkMultiple' || $type == 'attachmentMultiple') {
|
||||
}
|
||||
else if ($type == 'linkMultiple' || $type == 'attachmentMultiple') {
|
||||
if (array_key_exists($name . 'Ids', $row) && array_key_exists($name . 'Names', $row)) {
|
||||
$nameList = [];
|
||||
|
||||
foreach ($row[$name . 'Ids'] as $relatedId) {
|
||||
$relatedName = $relatedId;
|
||||
|
||||
if (property_exists($row[$name . 'Names'], $relatedId)) {
|
||||
$relatedName = $row[$name . 'Names']->$relatedId;
|
||||
}
|
||||
|
||||
$nameList[] = $relatedName;
|
||||
}
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", implode(', ', $nameList));
|
||||
}
|
||||
} else if ($type == 'address') {
|
||||
}
|
||||
else if ($type == 'address') {
|
||||
$value = '';
|
||||
|
||||
if (!empty($row[$name . 'Street'])) {
|
||||
$value = $value .= $row[$name.'Street'];
|
||||
}
|
||||
|
||||
if (!empty($row[$name.'City']) || !empty($row[$name.'State']) || !empty($row[$name.'PostalCode'])) {
|
||||
if ($value) {
|
||||
$value .= "\n";
|
||||
}
|
||||
|
||||
if (!empty($row[$name.'City'])) {
|
||||
$value .= $row[$name.'City'];
|
||||
|
||||
if (
|
||||
!empty($row[$name.'State']) || !empty($row[$name.'PostalCode'])
|
||||
) {
|
||||
$value .= ', ';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($row[$name.'State'])) {
|
||||
$value .= $row[$name.'State'];
|
||||
|
||||
if (!empty($row[$name.'PostalCode'])) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($row[$name.'PostalCode'])) {
|
||||
$value .= $row[$name.'PostalCode'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($row[$name.'Country'])) {
|
||||
if ($value) {
|
||||
$value .= "\n";
|
||||
}
|
||||
|
||||
$value .= $row[$name.'Country'];
|
||||
}
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", $value);
|
||||
} else if ($type == 'duration') {
|
||||
}
|
||||
else if ($type == 'duration') {
|
||||
if (!empty($row[$name])) {
|
||||
$seconds = intval($row[$name]);
|
||||
|
||||
@@ -504,35 +588,58 @@ class Xlsx
|
||||
$minutes = intval(floor($seconds / 60));
|
||||
|
||||
$value = '';
|
||||
|
||||
if ($days) {
|
||||
$value .= (string) $days . $this->language->translate('d', 'durationUnits');
|
||||
|
||||
if ($minutes || $hours) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if ($hours) {
|
||||
$value .= (string) $hours . $this->language->translate('h', 'durationUnits');
|
||||
|
||||
if ($minutes) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if ($minutes) {
|
||||
$value .= (string) $minutes . $this->language->translate('m', 'durationUnits');
|
||||
}
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", $value);
|
||||
}
|
||||
} else if ($type == 'multiEnum' || $type == 'array') {
|
||||
}
|
||||
else if ($type == 'multiEnum' || $type == 'array') {
|
||||
if (!empty($row[$name])) {
|
||||
$array = json_decode($row[$name]);
|
||||
if (is_array($array)) {
|
||||
$value = implode(', ', $array);
|
||||
$sheet->setCellValue("$col$rowNumber", $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
|
||||
|
||||
if (!is_array($array)) {
|
||||
$array = [];
|
||||
}
|
||||
|
||||
foreach ($array as $i => $item) {
|
||||
if ($linkName) {
|
||||
$itemValue = $this->language
|
||||
->translateOption($item, $foreignField, $foreignScope);
|
||||
}
|
||||
else {
|
||||
$itemValue = $this->language
|
||||
->translateOption($item, $name, $entityType);
|
||||
}
|
||||
$array[$i] = $itemValue;
|
||||
}
|
||||
|
||||
$value = implode(', ', $array);
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", $value, DataType::TYPE_STRING);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (array_key_exists($name, $row)) {
|
||||
$sheet->setCellValueExplicit("$col$rowNumber", $row[$name], \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
|
||||
$sheet->setCellValueExplicit("$col$rowNumber", $row[$name], DataType::TYPE_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,8 +647,10 @@ class Xlsx
|
||||
|
||||
$foreignLink = null;
|
||||
$isForeign = false;
|
||||
|
||||
if (strpos($name, '_')) {
|
||||
$isForeign = true;
|
||||
|
||||
list($foreignLink, $foreignField) = explode('_', $name);
|
||||
}
|
||||
|
||||
@@ -549,59 +658,80 @@ class Xlsx
|
||||
if (array_key_exists('id', $row)) {
|
||||
$link = $this->getConfig()->getSiteUrl() . "/#".$entityType . "/view/" . $row['id'];
|
||||
}
|
||||
} else if ($type == 'url') {
|
||||
}
|
||||
else if ($type == 'url') {
|
||||
if (array_key_exists($name, $row) && filter_var($row[$name], FILTER_VALIDATE_URL)) {
|
||||
$link = $row[$name];
|
||||
}
|
||||
} else if ($type == 'link') {
|
||||
}
|
||||
else if ($type == 'link') {
|
||||
if (array_key_exists($name.'Id', $row)) {
|
||||
$foreignEntity = null;
|
||||
|
||||
if (!$isForeign) {
|
||||
$foreignEntity = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $name, 'entity']);
|
||||
} else {
|
||||
$foreignEntity1 = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $foreignLink, 'entity']);
|
||||
$foreignEntity = $this->getMetadata()->get(['entityDefs', $foreignEntity1, 'links', $foreignField, 'entity']);
|
||||
$foreignEntity = $this->getMetadata()->get(
|
||||
['entityDefs', $entityType, 'links', $name, 'entity']
|
||||
);
|
||||
}
|
||||
else {
|
||||
$foreignEntity1 = $this->getMetadata()->get(
|
||||
['entityDefs', $entityType, 'links', $foreignLink, 'entity']
|
||||
);
|
||||
|
||||
$foreignEntity = $this->getMetadata()->get(
|
||||
['entityDefs', $foreignEntity1, 'links', $foreignField, 'entity']
|
||||
);
|
||||
}
|
||||
|
||||
if ($foreignEntity) {
|
||||
$link = $this->getConfig()->getSiteUrl() . "/#" . $foreignEntity. "/view/". $row[$name.'Id'];
|
||||
}
|
||||
}
|
||||
} else if ($type == 'file') {
|
||||
}
|
||||
else if ($type == 'file') {
|
||||
if (array_key_exists($name.'Id', $row)) {
|
||||
$link = $this->getConfig()->getSiteUrl() . "/?entryPoint=download&id=" . $row[$name.'Id'];
|
||||
}
|
||||
} else if ($type == 'linkParent') {
|
||||
}
|
||||
else if ($type == 'linkParent') {
|
||||
if (array_key_exists($name.'Id', $row) && array_key_exists($name.'Type', $row)) {
|
||||
$link = $this->getConfig()->getSiteUrl() . "/#".$row[$name.'Type']."/view/". $row[$name.'Id'];
|
||||
}
|
||||
} else if ($type == 'phone') {
|
||||
}
|
||||
else if ($type == 'phone') {
|
||||
if (array_key_exists($name, $row)) {
|
||||
$link = "tel:".$row[$name];
|
||||
}
|
||||
} else if ($type == 'email' && array_key_exists($name, $row)) {
|
||||
}
|
||||
|
||||
else if ($type == 'email' && array_key_exists($name, $row)) {
|
||||
if (array_key_exists($name, $row)) {
|
||||
$link = "mailto:".$row[$name];
|
||||
}
|
||||
}
|
||||
|
||||
if ($link) {
|
||||
$sheet->getCell("$col$rowNumber")->getHyperlink()->setUrl($link);
|
||||
$sheet->getCell("$col$rowNumber")->getHyperlink()->setTooltip($link);
|
||||
}
|
||||
}
|
||||
|
||||
$rowNumber++;
|
||||
}
|
||||
|
||||
$sheet->getStyle("A2:A$rowNumber")
|
||||
->getNumberFormat()
|
||||
->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
|
||||
->setFormatCode(NumberFormat::FORMAT_TEXT);
|
||||
|
||||
$startingRowNumber = 4;
|
||||
|
||||
foreach ($fieldList as $i => $name) {
|
||||
$col = $azRange[$i];
|
||||
|
||||
if (!array_key_exists($name, $typesCache)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$type = $typesCache[$name];
|
||||
|
||||
switch ($type) {
|
||||
@@ -609,31 +739,37 @@ class Xlsx
|
||||
case 'currencyConverted': {
|
||||
|
||||
} break;
|
||||
|
||||
case 'int': {
|
||||
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
|
||||
->getNumberFormat()
|
||||
->setFormatCode('0');
|
||||
} break;
|
||||
|
||||
case 'float': {
|
||||
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
|
||||
->getNumberFormat()
|
||||
->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1);
|
||||
->setFormatCode(NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1);
|
||||
} break;
|
||||
|
||||
case 'date': {
|
||||
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($this->dateTime->getDateFormat());
|
||||
} break;
|
||||
|
||||
case 'datetime': {
|
||||
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($this->dateTime->getDateTimeFormat());
|
||||
} break;
|
||||
|
||||
case 'datetimeOptional': {
|
||||
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($this->dateTime->getDateTimeFormat());
|
||||
} break;
|
||||
|
||||
default: {
|
||||
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
|
||||
->getNumberFormat()
|
||||
@@ -645,23 +781,27 @@ class Xlsx
|
||||
$linkStyle = [
|
||||
'font' => [
|
||||
'color' => ['rgb' => '345b7c'],
|
||||
'underline' => 'single'
|
||||
'underline' => 'single',
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($linkColList as $linkColumn) {
|
||||
$sheet->getStyle($linkColumn.$startingRowNumber.':'.$linkColumn.$rowNumber)->applyFromArray($linkStyle);
|
||||
}
|
||||
|
||||
$objWriter = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($phpExcel, 'Xlsx');
|
||||
$objWriter = IOFactory::createWriter($phpExcel, 'Xlsx');
|
||||
|
||||
if (!$this->fileManager->isDir('data/cache/')) {
|
||||
$this->fileManager->mkdir('data/cache/');
|
||||
}
|
||||
|
||||
$tempFileName = 'data/cache/' . 'export_' . substr(md5(rand()), 0, 7);
|
||||
|
||||
$objWriter->save($tempFileName);
|
||||
|
||||
$fp = fopen($tempFileName, 'r');
|
||||
$xlsx = stream_get_contents($fp);
|
||||
|
||||
$this->fileManager->unlink($tempFileName);
|
||||
|
||||
return $xlsx;
|
||||
|
||||
@@ -201,6 +201,8 @@ class LeadCapture
|
||||
$lead = $this->getLeadWithPopulatedData($leadCapture, $data);
|
||||
}
|
||||
|
||||
$campaign = null;
|
||||
|
||||
$campaingService = $this->serviceFactory->create('Campaign');
|
||||
|
||||
if ($leadCapture->get('campaignId')) {
|
||||
|
||||
@@ -5237,16 +5237,19 @@ Flotr.addPlugin('hit', {
|
||||
|
||||
// EspoCRM fix start
|
||||
if (n.mouse.autoPositionHorizontal) {
|
||||
|
||||
if (n.xaxis.d2p(n.x) > this.plotWidth * 2 / 3) {
|
||||
p = 'w';
|
||||
} else {
|
||||
p = 'e';
|
||||
}
|
||||
|
||||
if (n.x < 0 && n.xaxis.d2p(n.x) < this.plotWidth * 1 / 4) {
|
||||
p = 'e';
|
||||
} else {
|
||||
p = 'w';
|
||||
if (n.x < 0) {
|
||||
if (n.xaxis.d2p(n.x) < this.plotWidth * 1 / 4) {
|
||||
p = 'e';
|
||||
} else {
|
||||
p = 'w';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n.mouse.autoPositionVertical) {
|
||||
|
||||
@@ -58,9 +58,11 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
|
||||
data: function () {
|
||||
var data = {};
|
||||
|
||||
data.categoriesDisabled = this.categoriesDisabled;
|
||||
data.isExpanded = this.isExpanded;
|
||||
data.hasExpandedToggler = this.hasExpandedToggler;
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -101,7 +103,10 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
this.applyCategoryToCollection();
|
||||
|
||||
this.listenTo(this.collection, 'sync', function (c, d, o) {
|
||||
if (o && o.openCategory) return;
|
||||
if (o && o.openCategory) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.controlListVisibility();
|
||||
}, this);
|
||||
},
|
||||
@@ -120,6 +125,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
this.openCategory(params.categoryId, params.categoryName);
|
||||
}
|
||||
}
|
||||
|
||||
this.selectCurrentCategory();
|
||||
}
|
||||
},
|
||||
@@ -129,13 +135,14 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
for (var i = 0; i < this.collection.where.length; i++) {
|
||||
if (this.collection.where[i].type === 'textFilter') {
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.collection.data && this.collection.data.textFilter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
@@ -161,9 +168,11 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
} else {
|
||||
this.controlListVisibility();
|
||||
}
|
||||
|
||||
if (!this.categoriesDisabled && !this.hasView('categories')) {
|
||||
this.loadCategories();
|
||||
}
|
||||
|
||||
if (!this.isExpanded && !this.hasView('nestedCategories')) {
|
||||
this.loadNestedCategories();
|
||||
}
|
||||
@@ -171,6 +180,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
|
||||
actionExpand: function () {
|
||||
this.isExpanded = true;
|
||||
|
||||
this.setIsExpandedStoredValue(true);
|
||||
|
||||
this.applyCategoryToCollection();
|
||||
@@ -307,12 +317,15 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
this.listenToOnce(collection, 'sync', function () {
|
||||
callback.call(this, collection);
|
||||
}, this);
|
||||
|
||||
collection.fetch();
|
||||
}, this);
|
||||
},
|
||||
|
||||
applyCategoryToNestedCategoriesCollection: function () {
|
||||
if (!this.nestedCategoriesCollection) return;
|
||||
if (!this.nestedCategoriesCollection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.nestedCategoriesCollection.where = null;
|
||||
|
||||
@@ -335,6 +348,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
collection.maxDepth = 1;
|
||||
|
||||
collection.data.checkIfEmpty = true;
|
||||
|
||||
if (!this.getAcl().checkScope(this.scope, 'create')) {
|
||||
collection.data.onlyNotEmpty = true;
|
||||
}
|
||||
@@ -344,6 +358,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
collection.fetch().then(function () {
|
||||
this.controlListVisibility();
|
||||
this.controlNestedCategoriesVisibility();
|
||||
|
||||
callback.call(this, collection);
|
||||
}.bind(this));
|
||||
}, this);
|
||||
@@ -353,7 +368,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
this.getNestedCategoriesCollection(function (collection) {
|
||||
this.createView('nestedCategories', 'views/record/list-nested-categories', {
|
||||
collection: collection,
|
||||
el: this.options.el + ' .nested-categories-container'
|
||||
el: this.options.el + ' .nested-categories-container',
|
||||
}, function (view) {
|
||||
view.render();
|
||||
});
|
||||
@@ -378,18 +393,22 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
if (this.currentCategoryId) {
|
||||
view.setSelected(this.currentCategoryId);
|
||||
}
|
||||
|
||||
view.render();
|
||||
|
||||
this.listenTo(view, 'select', function (model) {
|
||||
if (!this.isExpanded) {
|
||||
var id = null;
|
||||
var name = null;
|
||||
|
||||
if (model && model.id) {
|
||||
id = model.id;
|
||||
name = model.get('name');
|
||||
}
|
||||
|
||||
this.openCategory(id, name);
|
||||
this.navigateToCurrentCategory();
|
||||
|
||||
return;
|
||||
}
|
||||
this.currentCategoryId = null;
|
||||
@@ -409,6 +428,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
this.listenToOnce(this.collection, 'sync', function () {
|
||||
this.notify(false);
|
||||
}, this);
|
||||
|
||||
this.collection.fetch();
|
||||
|
||||
}, this);
|
||||
@@ -417,9 +437,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
applyCategoryToCollection: function () {
|
||||
|
||||
this.collection.whereFunction = function () {
|
||||
var filter;
|
||||
var isExpanded = this.isExpanded;
|
||||
@@ -457,14 +475,14 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
filter = {
|
||||
field: this.categoryField,
|
||||
type: this.categoryFilterType,
|
||||
value: this.currentCategoryId
|
||||
value: this.currentCategoryId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
return [filter];
|
||||
}
|
||||
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
@@ -479,19 +497,26 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
if (this.currentCategoryId) {
|
||||
var names = {};
|
||||
names[this.currentCategoryId] = this.currentCategoryName;
|
||||
|
||||
var data = {};
|
||||
|
||||
var idsAttribute = this.categoryField + 'Ids';
|
||||
var namesAttribute = this.categoryField + 'Names';
|
||||
|
||||
data[idsAttribute] = [this.currentCategoryId],
|
||||
data[namesAttribute] = names;
|
||||
|
||||
return data;
|
||||
}
|
||||
} else {
|
||||
var idAttribute = this.categoryField + 'Id';
|
||||
var nameAttribute = this.categoryField + 'Name';
|
||||
|
||||
var data = {};
|
||||
|
||||
data[idAttribute] = this.currentCategoryId;
|
||||
data[nameAttribute] = this.currentCategoryName;
|
||||
|
||||
return data;
|
||||
}
|
||||
},
|
||||
@@ -500,6 +525,5 @@ define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
this.clearView('categories');
|
||||
this.getRouter().navigate('#' + this.categoryScope, {trigger: true});
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,8 +62,12 @@ define('views/modals/compose-email', 'views/modals/edit', function (Dep) {
|
||||
var attributes = this.options.attributes || {};
|
||||
|
||||
require('email-helper', function (EmailHelper) {
|
||||
this.getRouter().confirmLeaveOut = false;
|
||||
|
||||
var emailHelper = new EmailHelper();
|
||||
|
||||
var link = emailHelper.composeMailToLink(attributes, this.getConfig().get('outboundEmailBccAddress'));
|
||||
|
||||
document.location.href = link;
|
||||
}.bind(this));
|
||||
|
||||
@@ -80,7 +84,9 @@ define('views/modals/compose-email', 'views/modals/edit', function (Dep) {
|
||||
},
|
||||
|
||||
createRecordView: function (model, callback) {
|
||||
var viewName = this.getMetadata().get('clientDefs.' + model.name + '.recordViews.compose') || 'views/email/record/compose';
|
||||
var viewName = this.getMetadata().get('clientDefs.' + model.name + '.recordViews.compose') ||
|
||||
'views/email/record/compose';
|
||||
|
||||
var options = {
|
||||
model: model,
|
||||
el: this.containerSelector + ' .edit-container',
|
||||
@@ -93,6 +99,7 @@ define('views/modals/compose-email', 'views/modals/edit', function (Dep) {
|
||||
appendSignature: this.options.appendSignature,
|
||||
exit: function () {}
|
||||
};
|
||||
|
||||
this.createView('edit', viewName, options, callback);
|
||||
},
|
||||
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "6.0.8",
|
||||
"version": "6.0.10",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
64
package.json
64
package.json
@@ -1,32 +1,32 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "6.0.8",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/espocrm/espocrm.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"archiver": "^3.1.1",
|
||||
"fstream": ">=1.0.12",
|
||||
"grunt": "^1.3.0",
|
||||
"grunt-chmod": "~1.1.0",
|
||||
"grunt-contrib-clean": "~2.0.0",
|
||||
"grunt-contrib-concat": "~1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-cssmin": "~3.0.0",
|
||||
"grunt-contrib-less": "^2.0.0",
|
||||
"grunt-contrib-uglify": "~4.0.0",
|
||||
"grunt-mkdir": "~1.0.0",
|
||||
"grunt-replace": "~1.0.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.20",
|
||||
"minimist": ">=1.2.2",
|
||||
"pofile": "~1.0.11",
|
||||
"tar": "^6.0.5"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "6.0.10",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/espocrm/espocrm.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"archiver": "^3.1.1",
|
||||
"fstream": ">=1.0.12",
|
||||
"grunt": "^1.3.0",
|
||||
"grunt-chmod": "~1.1.0",
|
||||
"grunt-contrib-clean": "~2.0.0",
|
||||
"grunt-contrib-concat": "~1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-cssmin": "~3.0.0",
|
||||
"grunt-contrib-less": "^2.0.0",
|
||||
"grunt-contrib-uglify": "~4.0.0",
|
||||
"grunt-mkdir": "~1.0.0",
|
||||
"grunt-replace": "~1.0.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.20",
|
||||
"minimist": ">=1.2.2",
|
||||
"pofile": "~1.0.11",
|
||||
"tar": "^6.0.5"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace tests\integration\Espo\Extensions;
|
||||
namespace tests\integration\Espo\Extension;
|
||||
|
||||
class GeneralTest extends \tests\integration\Core\BaseTestCase
|
||||
{
|
||||
|
||||
Binary file not shown.
Binary file not shown.
129
tests/unit/Espo/Core/Utils/AutoloadTest.php
Normal file
129
tests/unit/Espo/Core/Utils/AutoloadTest.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://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 General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace tests\unit\Espo\Core\Utils;
|
||||
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Autoload,
|
||||
Utils\Config,
|
||||
Utils\Metadata,
|
||||
Utils\Autoload\Loader,
|
||||
Utils\DataCache,
|
||||
Utils\File\Manager as FileManager,
|
||||
};
|
||||
|
||||
class AutoloadTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected function setUp() : void
|
||||
{
|
||||
$this->config = $this->createMock(Config::class);
|
||||
$this->metadata = $this->createMock(Metadata::class);
|
||||
$this->dataCache = $this->createMock(DataCache::class);
|
||||
$this->fileManager = $this->createMock(FileManager::class);
|
||||
$this->loader = $this->createMock(Loader::class);
|
||||
|
||||
$this->autoload = new Autoload(
|
||||
$this->config,
|
||||
$this->metadata,
|
||||
$this->dataCache,
|
||||
$this->fileManager,
|
||||
$this->loader
|
||||
);
|
||||
}
|
||||
|
||||
public function testMerge()
|
||||
{
|
||||
$this->metadata
|
||||
->expects($this->once())
|
||||
->method('getModuleList')
|
||||
->willReturn(['M1', 'M2']);
|
||||
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('useCache')
|
||||
->willReturn(false);
|
||||
|
||||
$this->fileManager
|
||||
->expects($this->any())
|
||||
->method('isFile')
|
||||
->will(
|
||||
$this->returnValueMap(
|
||||
[
|
||||
['application/Espo/Resources/autoload.json', false],
|
||||
['application/Espo/Modules/M1/Resources/autoload.json', true],
|
||||
['application/Espo/Modules/M2/Resources/autoload.json', true],
|
||||
['custom/Espo/Custom/Resources/autoload.json', false],
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
$data1 = [
|
||||
'autoloadFileList' => ['f1.php'],
|
||||
'psr-4' => [
|
||||
't1' => 'r1',
|
||||
],
|
||||
];
|
||||
|
||||
$data2 = [
|
||||
'autoloadFileList' => ['f2.php'],
|
||||
'psr-4' => [
|
||||
't2' => 'r2',
|
||||
],
|
||||
];
|
||||
|
||||
$expectedData = [
|
||||
'autoloadFileList' => ['f1.php', 'f2.php'],
|
||||
'psr-4' => [
|
||||
't1' => 'r1',
|
||||
't2' => 'r2',
|
||||
],
|
||||
];
|
||||
|
||||
$this->fileManager
|
||||
->expects($this->any())
|
||||
->method('getContents')
|
||||
->will(
|
||||
$this->returnValueMap(
|
||||
[
|
||||
['application/Espo/Modules/M1/Resources/autoload.json', json_encode($data1)],
|
||||
['application/Espo/Modules/M2/Resources/autoload.json', json_encode($data2)],
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
$this->loader
|
||||
->expects($this->once())
|
||||
->method('register')
|
||||
->with($expectedData);
|
||||
|
||||
$this->autoload->register();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user