Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 02e5c1fc44 | |||
| 844e9dd8e3 | |||
| 39f2ec83d5 | |||
| e4d2af03a5 | |||
| 37616d69bf | |||
| 4e36f71dd8 | |||
| b5877ac32a | |||
| 92de588a6e | |||
| 700f4b5b10 | |||
| 9c0a7b6778 | |||
| 9a54c66f68 | |||
| 83332178a0 | |||
| fb33c2acf1 | |||
| 3c5914b5d9 | |||
| 8c48f72a3c | |||
| a8ce780718 | |||
| 31eff86578 | |||
| 892f9f01f6 | |||
| 860a58847d | |||
| 1ed90d2306 | |||
| aff0b350f3 | |||
| 651b0be5ed | |||
| cead33c09d | |||
| 80602c084f | |||
| b0420b8b93 | |||
| b47018cbab | |||
| ea69ab6eaf | |||
| 92887401b2 | |||
| df0bda6324 | |||
| 9d67808496 | |||
| cf33a98f20 | |||
| afbda344ec | |||
| 1079597584 | |||
| af9b411a4c | |||
| cd49d951d8 | |||
| 7d63c114aa | |||
| af6f05ba07 | |||
| 46f8eb0cdb | |||
| c543bb5a5b |
@@ -39,6 +39,7 @@ use Espo\Core\{
|
||||
Api\ResponseWrapper,
|
||||
Api\RouteProcessor,
|
||||
Utils\Route,
|
||||
Utils\Log,
|
||||
};
|
||||
|
||||
use Slim\{
|
||||
@@ -63,12 +64,15 @@ class Api implements ApplicationRunner
|
||||
protected $injectableFactory;
|
||||
protected $applicationUser;
|
||||
protected $routeUtil;
|
||||
protected $log;
|
||||
|
||||
public function __construct(InjectableFactory $injectableFactory, ApplicationUser $applicationUser, Route $routeUtil)
|
||||
{
|
||||
public function __construct(
|
||||
InjectableFactory $injectableFactory, ApplicationUser $applicationUser, Route $routeUtil, Log $log
|
||||
) {
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
$this->applicationUser = $applicationUser;
|
||||
$this->routeUtil = $routeUtil;
|
||||
$this->log = $log;
|
||||
}
|
||||
|
||||
public function run()
|
||||
@@ -85,7 +89,7 @@ class Api implements ApplicationRunner
|
||||
$this->addRoute($slim, $item);
|
||||
}
|
||||
|
||||
$slim->addErrorMiddleware(false, true, true);
|
||||
$slim->addErrorMiddleware(false, true, true, $this->log);
|
||||
|
||||
$slim->run();
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ class Authentication
|
||||
|
||||
if ($code) {
|
||||
if (!$impl->verifyCode($loggedUser, $code)) {
|
||||
Result::fail('Code not verified');
|
||||
return Result::fail('Code not verified');
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -103,7 +103,7 @@ class CommandManager
|
||||
|
||||
$skipIndex = 1;
|
||||
|
||||
if (isset($argv[0]) && $argv[0] === 'command.php') {
|
||||
if (isset($argv[0]) && preg_match('/command\.php$/', $argv[0])) {
|
||||
$skipIndex = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,37 +48,57 @@ class AclCheck implements Command
|
||||
$id = $options['id'] ?? null;
|
||||
$action = $options['action'] ?? null;
|
||||
|
||||
if (empty($userId)) return null;
|
||||
if (empty($scope)) return null;
|
||||
if (empty($id)) return null;
|
||||
if (empty($userId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($scope)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$container = $this->container;
|
||||
|
||||
$entityManager = $container->get('entityManager');
|
||||
|
||||
$user = $entityManager->getEntity('User', $userId);
|
||||
if (!$user) return null;
|
||||
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($user->isPortal()) {
|
||||
$portalIdList = $user->getLinkMultipleIdList('portals');
|
||||
|
||||
foreach ($portalIdList as $portalId) {
|
||||
$application = new PortalApplication($portalId);
|
||||
$containerPortal = $application->getContainer();
|
||||
$entityManager = $containerPortal->get('entityManager');
|
||||
|
||||
$user = $entityManager->getEntity('User', $userId);
|
||||
if (!$user) return null;
|
||||
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $this->check($user, $scope, $id, $action, $containerPortal);
|
||||
|
||||
if ($result) {
|
||||
return 'true';
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->check($user, $scope, $id, $action, $container)) {
|
||||
return 'true';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function check($user, $scope, $id, $action, $container)
|
||||
@@ -86,7 +106,10 @@ class AclCheck implements Command
|
||||
$entityManager = $container->get('entityManager');
|
||||
|
||||
$entity = $entityManager->getEntity($scope, $id);
|
||||
if (!$entity) return false;
|
||||
|
||||
if (!$entity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$aclManager = $container->get('aclManager');
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ class Upgrade implements Command
|
||||
$versionInfo = $this->getVersionInfo($toVersion);
|
||||
|
||||
$nextVersion = $versionInfo->nextVersion ?? null;
|
||||
$lastVersion = $infoData->lastVersion ?? null;
|
||||
$lastVersion = $versionInfo->lastVersion ?? null;
|
||||
|
||||
$packageFile = $this->getPackageFile($params, $versionInfo);
|
||||
|
||||
|
||||
@@ -38,6 +38,6 @@ class NotType extends BaseFunction
|
||||
{
|
||||
public function process(ArgumentList $args)
|
||||
{
|
||||
return !$this->evaluate($args);
|
||||
return !$this->evaluate($args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ class Parser
|
||||
{
|
||||
protected $priorityList = [
|
||||
['='],
|
||||
['||', '&&'],
|
||||
['||'],
|
||||
['&&'],
|
||||
['==', '!=', '>', '<', '>=', '<='],
|
||||
['+', '-'],
|
||||
['*', '/', '%'],
|
||||
|
||||
@@ -120,11 +120,11 @@ class Tcpdf extends \TCPDF
|
||||
$html = $this->headerHtml;
|
||||
|
||||
if ($this->useGroupNumbers) {
|
||||
$html = str_replace('{pageNumber}', '{:png:}', $html);
|
||||
$html = str_replace('{pageAbsoluteNumber}', '{:pnp:}', $html);
|
||||
$html = str_replace('{pageNumber}', '{{:png:}}', $html);
|
||||
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
|
||||
} else {
|
||||
$html = str_replace('{pageNumber}', '{:pnp:}', $html);
|
||||
$html = str_replace('{pageAbsoluteNumber}', '{:pnp:}', $html);
|
||||
$html = str_replace('{pageNumber}', '{{:pnp:}', $html);
|
||||
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
|
||||
}
|
||||
|
||||
if ($this->isUnicodeFont()) {
|
||||
@@ -148,11 +148,11 @@ class Tcpdf extends \TCPDF
|
||||
$html = $this->footerHtml;
|
||||
|
||||
if ($this->useGroupNumbers) {
|
||||
$html = str_replace('{pageNumber}', '{:png:}', $html);
|
||||
$html = str_replace('{pageAbsoluteNumber}', '{:pnp:}', $html);
|
||||
$html = str_replace('{pageNumber}', '{{:png:}}', $html);
|
||||
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
|
||||
} else {
|
||||
$html = str_replace('{pageNumber}', '{:pnp:}', $html);
|
||||
$html = str_replace('{pageAbsoluteNumber}', '{:pnp:}', $html);
|
||||
$html = str_replace('{pageNumber}', '{{:pnp:}}', $html);
|
||||
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
|
||||
}
|
||||
|
||||
if ($this->isUnicodeFont()) {
|
||||
|
||||
@@ -34,12 +34,13 @@ use Espo\Core\{
|
||||
InjectableFactory,
|
||||
ApplicationUser,
|
||||
Portal\Utils\Route,
|
||||
Utils\Log,
|
||||
};
|
||||
|
||||
class Api extends ApiBase
|
||||
{
|
||||
public function __construct(InjectableFactory $injectableFactory, ApplicationUser $applicationUser, Route $routeUtil)
|
||||
public function __construct(InjectableFactory $injectableFactory, ApplicationUser $applicationUser, Route $routeUtil, Log $log)
|
||||
{
|
||||
parent::__construct($injectableFactory, $applicationUser, $routeUtil);
|
||||
parent::__construct($injectableFactory, $applicationUser, $routeUtil, $log);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2506,6 +2506,14 @@ class SelectManager
|
||||
$attributeType = $seed->getAttributeType($foreignField);
|
||||
} else {
|
||||
$attributeType = $seed->getAttributeType($field);
|
||||
|
||||
if ($attributeType === 'foreign') {
|
||||
$link = $seed->getAttributeParam($field, 'relation');
|
||||
|
||||
if ($link) {
|
||||
$this->addLeftJoin($link, $result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($attributeType === 'int') {
|
||||
@@ -2686,11 +2694,6 @@ class SelectManager
|
||||
$selectParams1['havingClause'][] = $selectParams2['havingClause'];
|
||||
}
|
||||
|
||||
if (!empty($selectParams2['leftJoins'])) {
|
||||
foreach ($selectParams2['leftJoins'] as $item) {
|
||||
$this->addLeftJoin($item, $selectParams1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($selectParams2['joins'])) {
|
||||
foreach ($selectParams2['joins'] as $item) {
|
||||
@@ -2698,6 +2701,16 @@ class SelectManager
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($selectParams2['leftJoins'])) {
|
||||
foreach ($selectParams2['leftJoins'] as $item) {
|
||||
if ($this->hasJoin($item, $selectParams1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->addLeftJoin($item, $selectParams1);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($selectParams2['select'])) {
|
||||
$selectParams1['select'] = $selectParams2['select'];
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class PersonName extends Base
|
||||
$whereItems[] = "CONCAT:({$firstName}, ' ', {$middleName}, ' ', {$lastName})";
|
||||
} else
|
||||
if ($format === 'lastFirstMiddle') {
|
||||
$whereItems[] = "CONCAT:({$lastName}, ' ', {$firstColumn}, ' ', {$middleName})";
|
||||
$whereItems[] = "CONCAT:({$lastName}, ' ', {$firstName}, ' ', {$middleName})";
|
||||
}
|
||||
|
||||
$selectExpression = $this->getSelect($fullList);
|
||||
|
||||
@@ -53,10 +53,10 @@ class ScheduledJob
|
||||
protected $checkingCronPeriod = '25 hours';
|
||||
|
||||
protected $cronSetup = [
|
||||
'linux' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BIN-DIR} -f {CRON-FILE} > /dev/null 2>&1',
|
||||
'linux' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BINARY} -f {CRON-FILE} > /dev/null 2>&1',
|
||||
'windows' => '{PHP-BINARY} -f {FULL-CRON-PATH}',
|
||||
'mac' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BIN-DIR} -f {CRON-FILE} > /dev/null 2>&1',
|
||||
'default' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BIN-DIR} -f {CRON-FILE} > /dev/null 2>&1',
|
||||
'mac' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BINARY} -f {CRON-FILE} > /dev/null 2>&1',
|
||||
'default' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BINARY} -f {CRON-FILE} > /dev/null 2>&1',
|
||||
];
|
||||
|
||||
protected $classFinder;
|
||||
@@ -105,7 +105,6 @@ class ScheduledJob
|
||||
$desc = $language->translate('cronSetup', 'options', 'ScheduledJob');
|
||||
|
||||
$data = array(
|
||||
'PHP-BIN-DIR' => $this->getSystemUtil()->getPhpBin(),
|
||||
'PHP-BINARY' => $this->getSystemUtil()->getPhpBinary(),
|
||||
'CRON-FILE' => $this->cronFile,
|
||||
'DOCUMENT_ROOT' => $this->getSystemUtil()->getRootDir(),
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
|
||||
class System
|
||||
{
|
||||
/**
|
||||
@@ -100,22 +102,11 @@ class System
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to PHP
|
||||
*
|
||||
* @return string
|
||||
* Deprecated. Use getPhpBinary()
|
||||
*/
|
||||
public function getPhpBin()
|
||||
{
|
||||
if (isset($_SERVER['PHP_PATH']) && !empty($_SERVER['PHP_PATH'])) {
|
||||
return $_SERVER['PHP_PATH'];
|
||||
}
|
||||
|
||||
$phpBin = @exec('which php');
|
||||
if (!empty($phpBin)) {
|
||||
return $phpBin;
|
||||
}
|
||||
|
||||
return defined("PHP_BINDIR") ? PHP_BINDIR . DIRECTORY_SEPARATOR . 'php' : 'php';
|
||||
return $this->getPhpBinary();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,7 +116,7 @@ class System
|
||||
*/
|
||||
public function getPhpBinary()
|
||||
{
|
||||
return defined("PHP_BINARY") ? PHP_BINARY : 'php';
|
||||
return (new PhpExecutableFinder)->find();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -873,7 +873,7 @@ class Activities implements
|
||||
|
||||
$service->handleListParams($params);
|
||||
|
||||
$selectParams = $selectManager->getSelectParams($params, false, true);
|
||||
$selectParams = $selectManager->getSelectParams($params, false, true, true);
|
||||
|
||||
$offset = $selectParams['offset'];
|
||||
$limit = $selectParams['limit'];
|
||||
|
||||
@@ -144,7 +144,8 @@
|
||||
},
|
||||
"currencyFormat": {
|
||||
"1": "10 USD",
|
||||
"2": "$10"
|
||||
"2": "$10",
|
||||
"3": "10 $"
|
||||
},
|
||||
"personNameFormat": {
|
||||
"firstLast": "First Last",
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
"uninstallConfirmation": "Ви впевнені, що хочете видалити розширення?",
|
||||
"cronIsNotConfigured": "Заплановані завдання не працюють. Отже, вхідні електронні листи, сповіщення та нагадування не працюють. Будь ласка, дотримуйтесь інструкцій (https://www.espocrm.com/documentation/administration/server-configuration/#user-content-setup-a-crontab), щоб налаштувати cron job.",
|
||||
"newExtensionVersionIsAvailable": "Нова {extensionName} версія {latestVersion} доступна.",
|
||||
"upgradeVersion": "EspoCRM буде оновлено до версії **{версія}**. Будьте терплячі, оскільки це може зайняти деякий час.",
|
||||
"upgradeVersion": "EspoCRM буде оновлено до версії **{version}**. Будьте терплячі, оскільки це може зайняти деякий час.",
|
||||
"upgradeDone": "EspoCRM оновлено до версії **{version}**.",
|
||||
"downloadUpgradePackage": "Завантажте апгрейд [звідси]({url})",
|
||||
"upgradeInfo": "Перегляньте [документацію]({url}) про те, як оновити EspoCRM.\n",
|
||||
@@ -257,4 +257,4 @@
|
||||
"noteStatus": "Повідомлення про оновлення статусу",
|
||||
"passwordChangeLink": "Посилання на зміну паролю"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,10 @@
|
||||
"parent": false
|
||||
},
|
||||
"EmailFolder": {
|
||||
"assignedUser": false
|
||||
"assignedUser": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
}
|
||||
},
|
||||
"Email": {
|
||||
"inboundEmails": false,
|
||||
|
||||
@@ -53,11 +53,21 @@
|
||||
{
|
||||
"label":"Folders",
|
||||
"link":"#EmailFolder",
|
||||
"configCheck": "!emailFoldersDisabled"
|
||||
"configCheck": "!emailFoldersDisabled",
|
||||
"accessDataList": [
|
||||
{
|
||||
"inPortalDisabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label":"Filters",
|
||||
"link":"#EmailFilter"
|
||||
"link":"#EmailFilter",
|
||||
"accessDataList": [
|
||||
{
|
||||
"inPortalDisabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"fields": {
|
||||
"timeZone": {
|
||||
"type": "enum",
|
||||
"detault": "",
|
||||
"default": "",
|
||||
"view": "views/preferences/fields/time-zone"
|
||||
},
|
||||
"dateFormat": {
|
||||
@@ -124,7 +124,8 @@
|
||||
"assignmentNotificationsIgnoreEntityTypeList": {
|
||||
"type": "checklist",
|
||||
"translation": "Global.scopeNamesPlural",
|
||||
"view": "views/preferences/fields/assignment-notifications-ignore-entity-type-list"
|
||||
"view": "views/preferences/fields/assignment-notifications-ignore-entity-type-list",
|
||||
"default": []
|
||||
},
|
||||
"assignmentEmailNotificationsIgnoreEntityTypeList": {
|
||||
"type": "checklist",
|
||||
@@ -140,14 +141,17 @@
|
||||
},
|
||||
"signature": {
|
||||
"type": "wysiwyg",
|
||||
"view": "views/preferences/fields/signature"
|
||||
"view": "views/preferences/fields/signature",
|
||||
"default": ""
|
||||
},
|
||||
"defaultReminders": {
|
||||
"type": "jsonArray",
|
||||
"view": "crm:views/meeting/fields/reminders"
|
||||
"view": "crm:views/meeting/fields/reminders",
|
||||
"default": []
|
||||
},
|
||||
"theme": {
|
||||
"type": "enum",
|
||||
"default": "",
|
||||
"view": "views/preferences/fields/theme",
|
||||
"translation": "Global.themes"
|
||||
},
|
||||
@@ -173,7 +177,8 @@
|
||||
},
|
||||
"doNotFillAssignedUserIfNotRequired": {
|
||||
"type": "bool",
|
||||
"tooltip": true
|
||||
"tooltip": true,
|
||||
"default": false
|
||||
},
|
||||
"followEntityOnStreamPost": {
|
||||
"type": "bool",
|
||||
@@ -187,16 +192,20 @@
|
||||
"type": "multiEnum",
|
||||
"view": "views/preferences/fields/auto-follow-entity-type-list",
|
||||
"translation": "Global.scopeNamesPlural",
|
||||
"default": [],
|
||||
"tooltip": true
|
||||
},
|
||||
"emailUseExternalClient": {
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
"scopeColorsDisabled": {
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
"tabColorsDisabled": {
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"noDeletedAttribute": true
|
||||
|
||||
@@ -496,7 +496,7 @@
|
||||
},
|
||||
"currencyFormat": {
|
||||
"type": "enumInt",
|
||||
"options": [1, 2]
|
||||
"options": [1, 2, 3]
|
||||
},
|
||||
"currencyDecimalPlaces": {
|
||||
"type": "int",
|
||||
|
||||
@@ -162,8 +162,6 @@ class Email extends \Espo\Core\Select\SelectManager
|
||||
$result['whereClause'][] = [
|
||||
'usersMiddle.userId' => $this->getUser()->id
|
||||
];
|
||||
|
||||
$this->addUsersColumns($result);
|
||||
}
|
||||
|
||||
protected function boolFilterOnlyMy(&$result)
|
||||
|
||||
@@ -46,6 +46,7 @@ use Espo\Core\{
|
||||
Utils\Config,
|
||||
Utils\Util,
|
||||
Utils\Language,
|
||||
Utils\FieldUtil,
|
||||
};
|
||||
|
||||
use Espo\Entities\User;
|
||||
@@ -57,8 +58,24 @@ use Espo\ORM\{
|
||||
Entity,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use Throwable;
|
||||
|
||||
class App
|
||||
{
|
||||
protected $config;
|
||||
protected $entityManager;
|
||||
protected $metadata;
|
||||
protected $acl;
|
||||
protected $aclManager;
|
||||
protected $dataManager;
|
||||
protected $selectManagerFactory;
|
||||
protected $injectableFactory;
|
||||
protected $serviceFactory;
|
||||
protected $user;
|
||||
protected $preferences;
|
||||
protected $fieldUtil;
|
||||
|
||||
public function __construct(
|
||||
Config $config,
|
||||
EntityManager $entityManager,
|
||||
@@ -70,7 +87,8 @@ class App
|
||||
InjectableFactory $injectableFactory,
|
||||
ServiceFactory $serviceFactory,
|
||||
User $user,
|
||||
Preferences $preferences
|
||||
Preferences $preferences,
|
||||
FieldUtil $fieldUtil
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
@@ -83,12 +101,14 @@ class App
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
$this->user = $user;
|
||||
$this->preferences = $preferences;
|
||||
$this->fieldUtil = $fieldUtil;
|
||||
}
|
||||
|
||||
public function getUserData()
|
||||
{
|
||||
$preferencesData = $this->preferences->getValueMap();
|
||||
unset($preferencesData->smtpPassword);
|
||||
|
||||
$this->filterPreferencesData($preferencesData);
|
||||
|
||||
$settingsService = $this->serviceFactory->create('Settings');
|
||||
|
||||
@@ -134,11 +154,16 @@ class App
|
||||
|
||||
foreach (($this->metadata->get(['app', 'appParams']) ?? []) as $paramKey => $item) {
|
||||
$className = $item['className'] ?? null;
|
||||
if (!$className) continue;
|
||||
|
||||
if (!$className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$itemParams = $this->injectableFactory->create($className)->get();
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
$GLOBALS['log']->error("appParam {$paramKey}: " . $e->getMessage());
|
||||
|
||||
continue;
|
||||
}
|
||||
$appParams[$paramKey] = $itemParams;
|
||||
@@ -452,19 +477,41 @@ class App
|
||||
$entityTypeList = ['Contact', 'Lead'];
|
||||
|
||||
foreach ($entityTypeList as $entityType) {
|
||||
$entityList = $this->entityManager->getRepository($entityType)->where([
|
||||
'doNotCall' => true,
|
||||
'phoneNumber!=' => null,
|
||||
])->select(['id', 'phoneNumber'])->find();
|
||||
$entityList = $this->entityManager
|
||||
->getRepository($entityType)
|
||||
->where([
|
||||
'doNotCall' => true,
|
||||
'phoneNumber!=' => null,
|
||||
])
|
||||
->select(['id', 'phoneNumber'])
|
||||
->find();
|
||||
|
||||
foreach ($entityList as $entity) {
|
||||
$phoneNumber = $entity->get('phoneNumber');
|
||||
if (!$phoneNumber) continue;
|
||||
$phoneNumberEntity = $this->entityManager->getRepository('PhoneNumber')->getByNumber($phoneNumber);
|
||||
if (!$phoneNumberEntity) continue;
|
||||
$phoneNumberEntity->set('optOut', true);
|
||||
$this->entityManager->saveEntity($phoneNumberEntity);
|
||||
|
||||
if (!$phoneNumber) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phoneNumberEntity = $this->entityManager->getRepository('PhoneNumber')->getByNumber($phoneNumber);
|
||||
|
||||
if (!$phoneNumberEntity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phoneNumberEntity->set('optOut', true);
|
||||
|
||||
$this->entityManager->saveEntity($phoneNumberEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function filterPreferencesData(StdClass $data)
|
||||
{
|
||||
$passwordFieldList = $this->fieldUtil->getFieldByTypeList('Preferences', 'password');
|
||||
|
||||
foreach ($passwordFieldList as $field) {
|
||||
unset($data->$field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ class Attachment extends Record
|
||||
|
||||
protected $inlineAttachmentFieldTypeList = ['wysiwyg'];
|
||||
|
||||
protected $adminOnlyHavingInlineAttachmentsEntityTypeList = [
|
||||
'TemplateManager',
|
||||
];
|
||||
|
||||
protected $imageTypeList = [
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
@@ -160,12 +164,24 @@ class Attachment extends Record
|
||||
|
||||
protected function checkAttachmentField($relatedEntityType, $field, $role = 'Attachment')
|
||||
{
|
||||
if (
|
||||
$this->getUser()->isAdmin()
|
||||
&&
|
||||
$role === 'Inline Attachment'
|
||||
&&
|
||||
in_array($relatedEntityType, $this->adminOnlyHavingInlineAttachmentsEntityTypeList)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fieldType = $this->getMetadata()->get(['entityDefs', $relatedEntityType, 'fields', $field, 'type']);
|
||||
|
||||
if (!$fieldType) {
|
||||
throw new Error("Field '{$field}' does not exist.");
|
||||
}
|
||||
|
||||
$attachmentFieldTypeListParam = lcfirst(str_replace(' ', '', $role)) . 'FieldTypeList';
|
||||
|
||||
if (!in_array($fieldType, $this->$attachmentFieldTypeListParam)) {
|
||||
throw new Error("Field type '{$fieldType}' is not allowed for {$role}.");
|
||||
}
|
||||
|
||||
@@ -2550,6 +2550,10 @@ class Record implements Crud,
|
||||
->getRepository('Attachment')
|
||||
->getCopiedAttachment($attachment);
|
||||
|
||||
$attachment->set('field', $field);
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
if ($attachment) {
|
||||
$idList[] = $attachment->id;
|
||||
$nameHash->{$attachment->id} = $attachment->get('name');
|
||||
|
||||
@@ -131,6 +131,8 @@ class Export
|
||||
|
||||
$collection = null;
|
||||
|
||||
$exportAllFields = !array_key_exists('fieldList', $params);
|
||||
|
||||
if (array_key_exists('collection', $params)) {
|
||||
$collection = $params['collection'];
|
||||
} else {
|
||||
@@ -162,9 +164,7 @@ class Export
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($searchParams['select']) && is_string($searchParams['select'])) {
|
||||
$searchParams['select'] = explode(',', $searchParams['select']);
|
||||
}
|
||||
unset($searchParams['select']);
|
||||
|
||||
$selectParams = $selectManager->getSelectParams($searchParams, true, true, true);
|
||||
}
|
||||
@@ -218,15 +218,12 @@ class Export
|
||||
}
|
||||
}
|
||||
|
||||
if (!array_key_exists('fieldList', $params)) {
|
||||
$exportAllFields = true;
|
||||
|
||||
if ($exportAllFields) {
|
||||
$fieldDefs = $this->metadata->get(['entityDefs', $this->entityType, 'fields'], []);
|
||||
$fieldList = array_keys($fieldDefs);
|
||||
|
||||
array_unshift($fieldList, 'id');
|
||||
} else {
|
||||
$exportAllFields = false;
|
||||
$fieldList = $params['fieldList'];
|
||||
}
|
||||
|
||||
|
||||
@@ -350,21 +350,24 @@ class Xlsx
|
||||
} else if ($type == 'currency') {
|
||||
if (array_key_exists($name.'Currency', $row) && array_key_exists($name, $row)) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
|
||||
|
||||
$currency = $row[$name . 'Currency'];
|
||||
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
|
||||
|
||||
$sheet->getStyle("$col$rowNumber")
|
||||
->getNumberFormat()
|
||||
->setFormatCode('[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00');
|
||||
->setFormatCode(
|
||||
$this->getCurrencyFormatCode($currency)
|
||||
);
|
||||
}
|
||||
} else if ($type == 'currencyConverted') {
|
||||
if (array_key_exists($name, $row)) {
|
||||
$currency = $this->getConfig()->get('defaultCurrency');
|
||||
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
|
||||
|
||||
$sheet->getStyle("$col$rowNumber")
|
||||
->getNumberFormat()
|
||||
->setFormatCode('[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00');
|
||||
->setFormatCode(
|
||||
$this->getCurrencyFormatCode($currency)
|
||||
);
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
|
||||
}
|
||||
@@ -663,4 +666,17 @@ class Xlsx
|
||||
|
||||
return $xlsx;
|
||||
}
|
||||
|
||||
protected function getCurrencyFormatCode(string $currency) : string
|
||||
{
|
||||
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
|
||||
|
||||
$currencyFormat = $this->getConfig()->get('currencyFormat') ?? 2;
|
||||
|
||||
if ($currencyFormat == 3) {
|
||||
return '#,##0.00_-"' . $currencySymbol . '"';
|
||||
}
|
||||
|
||||
return '[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,14 +68,14 @@ define('crm:views/record/panels/tasks', 'views/record/panels/relationship', func
|
||||
name: 'name',
|
||||
link: true,
|
||||
},
|
||||
{
|
||||
name: 'isOverdue'
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'isOverdue'
|
||||
},
|
||||
{name: 'assignedUser'},
|
||||
{name: 'status'},
|
||||
{name: 'dateEnd'},
|
||||
{name: 'status'},
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{{#if isNotEmpty}}
|
||||
{{value}} {{currencySymbol}}
|
||||
{{else}}
|
||||
{{#if valueIsSet}}{{{translate 'None'}}}{{else}}...{{/if}}
|
||||
{{/if}}
|
||||
@@ -0,0 +1,3 @@
|
||||
{{#if isNotEmpty}}
|
||||
<span title="{{currencySymbol}}{{value}}">{{value}} {{currencySymbol}}</span>
|
||||
{{/if}}
|
||||
@@ -356,11 +356,6 @@ define('views/fields/address', 'views/fields/base', function (Dep) {
|
||||
this.$country.attr('autocomplete', 'espo-country');
|
||||
}
|
||||
|
||||
this.controlStreetTextareaHeight();
|
||||
this.$street.on('input', function (e) {
|
||||
this.controlStreetTextareaHeight();
|
||||
}.bind(this));
|
||||
|
||||
var cityList = this.getConfig().get('addressCityList') || [];
|
||||
if (cityList.length) {
|
||||
this.$city.autocomplete({
|
||||
@@ -394,11 +389,6 @@ define('views/fields/address', 'views/fields/base', function (Dep) {
|
||||
this.$city.attr('autocomplete', 'espo-city');
|
||||
}
|
||||
|
||||
this.controlStreetTextareaHeight();
|
||||
this.$street.on('input', function (e) {
|
||||
this.controlStreetTextareaHeight();
|
||||
}.bind(this));
|
||||
|
||||
var stateList = this.getConfig().get('addressStateList') || [];
|
||||
if (stateList.length) {
|
||||
this.$state.autocomplete({
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/fields/currency', 'views/fields/float', function (Dep) {
|
||||
define('views/fields/currency', 'views/fields/float', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -40,12 +40,16 @@ Espo.define('views/fields/currency', 'views/fields/float', function (Dep) {
|
||||
|
||||
detailTemplate2: 'fields/currency/detail-2',
|
||||
|
||||
detailTemplate3: 'fields/currency/detail-3',
|
||||
|
||||
listTemplate: 'fields/currency/list',
|
||||
|
||||
listTemplate1: 'fields/currency/list-1',
|
||||
|
||||
listTemplate2: 'fields/currency/list-2',
|
||||
|
||||
listTemplate3: 'fields/currency/list-3',
|
||||
|
||||
detailTemplateNoCurrency: 'fields/currency/detail-no-currency',
|
||||
|
||||
maxDecimalPlaces: 3,
|
||||
|
||||
@@ -44,6 +44,8 @@ define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], function (De
|
||||
|
||||
translatedOptions: null,
|
||||
|
||||
fetchEmptyValueAsNull: false,
|
||||
|
||||
searchTypeList: ['anyOf', 'noneOf', 'isEmpty', 'isNotEmpty'],
|
||||
|
||||
data: function () {
|
||||
|
||||
@@ -253,10 +253,6 @@ define('views/fields/link-parent', 'views/fields/base', function (Dep) {
|
||||
e.currentTarget.value = this.model.get(this.nameName);
|
||||
}
|
||||
}.bind(this));
|
||||
} else if (this.mode == 'search') {
|
||||
this.$elementName.on('blur', function (e) {
|
||||
e.currentTarget.value = '';
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
if (!this.autocompleteDisabled) {
|
||||
|
||||
@@ -260,10 +260,6 @@ define('views/fields/link', 'views/fields/base', function (Dep) {
|
||||
e.currentTarget.value = this.model.get(this.nameName);
|
||||
}
|
||||
}.bind(this));
|
||||
} else if (this.mode == 'search') {
|
||||
this.$elementName.on('blur', function (e) {
|
||||
e.currentTarget.value = '';
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
var $elementName = this.$elementName;
|
||||
|
||||
@@ -44,6 +44,8 @@ define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], function
|
||||
|
||||
seeMoreDisabled: true,
|
||||
|
||||
fetchEmptyValueAsNull: false,
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
@@ -532,6 +534,12 @@ define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], function
|
||||
data[this.name] = code;
|
||||
} else {
|
||||
data[this.name] = this.$element.val();
|
||||
|
||||
if (this.fetchEmptyValueAsNull) {
|
||||
if (!data[this.name]) {
|
||||
data[this.name] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.model.has('isHtml') && this.hasBodyPlainField) {
|
||||
|
||||
@@ -30,6 +30,8 @@ define('views/preferences/fields/signature', 'views/fields/wysiwyg', function (D
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
fetchEmptyValueAsNull: true,
|
||||
|
||||
toolbar: [
|
||||
["style", ["bold", "italic", "underline", "clear"]],
|
||||
["color", ["color"]],
|
||||
|
||||
@@ -998,7 +998,7 @@ define('views/record/list', 'view', function (Dep) {
|
||||
}, this);
|
||||
|
||||
if (
|
||||
this.getConfig().get('exportDisabled') && !this.getUser().get('isAdmin')
|
||||
this.getConfig().get('exportDisabled') && !this.getUser().isAdmin()
|
||||
||
|
||||
this.getAcl().get('exportPermission') === 'no'
|
||||
||
|
||||
|
||||
+6
-1
@@ -93,7 +93,12 @@ class Diff
|
||||
|
||||
if (!~tag.indexOf('beta') && !~tag.indexOf('alpha')) {
|
||||
versionFromList.push(tag);
|
||||
break;
|
||||
|
||||
var patchVersionNumberI = tag.split('.')[2];
|
||||
|
||||
if (patchVersionNumberI === '0') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+1484
-764
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "6.0.1",
|
||||
"version": "6.0.5",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
@@ -12,21 +12,21 @@
|
||||
"devDependencies": {
|
||||
"archiver": "^3.1.1",
|
||||
"fstream": ">=1.0.12",
|
||||
"grunt": "^1.0.4",
|
||||
"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-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.19",
|
||||
"lodash": "^4.17.20",
|
||||
"minimist": ">=1.2.2",
|
||||
"pofile": "~1.0.11",
|
||||
"tar": "^4.4.8"
|
||||
"tar": "^6.0.5"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ class Tester
|
||||
{
|
||||
$configData = $this->getTestConfigData();
|
||||
|
||||
if ($configData[$optionName] === $data) {
|
||||
if (array_key_exists($optionName, $configData) && $configData[$optionName] === $data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -434,4 +434,22 @@ class EvaluatorTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
$this->assertEquals('test', $result);
|
||||
}
|
||||
|
||||
public function testNegate1()
|
||||
{
|
||||
$expression = "!string\contains('hello', 'test')";
|
||||
|
||||
$result = $this->evaluator->process($expression);
|
||||
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function testLogicalProority()
|
||||
{
|
||||
$expression = "0 && 0 || 1";
|
||||
|
||||
$result = $this->evaluator->process($expression);
|
||||
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ class SystemTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertEquals($rootDir, $this->object->getRootDir());
|
||||
}
|
||||
|
||||
public function testGetPhpBin()
|
||||
public function testGetPhpBinary()
|
||||
{
|
||||
$phpBin = @exec('which php');
|
||||
$phpBinary = (new \Symfony\Component\Process\PhpExecutableFinder)->find();
|
||||
|
||||
if (isset($phpBin)) {
|
||||
$this->assertEquals($phpBin, $this->object->getPhpBin());
|
||||
if (isset($phpBinary)) {
|
||||
$this->assertEquals($phpBinary, $this->object->getPhpBinary());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user