Merge branch 'stable'

This commit is contained in:
Yuri Kuznetsov
2020-11-16 15:00:33 +02:00
18 changed files with 1639 additions and 826 deletions

View File

@@ -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;
}

View File

@@ -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');

View File

@@ -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);

View File

@@ -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') {

View File

@@ -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);

View File

@@ -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(),

View File

@@ -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();
}
/**

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -2480,6 +2480,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');

View File

@@ -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'},
]
]
},

View File

@@ -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 () {

View File

@@ -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) {

View File

@@ -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"]],

View File

@@ -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'
||

2248
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "espocrm",
"version": "6.0.4",
"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": {}
}

View File

@@ -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());
}
}