mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-10 10:47:01 +00:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
500e8ca89b | ||
|
|
f7fb3f50c3 | ||
|
|
4763cc80c3 | ||
|
|
0d744c5e7f | ||
|
|
629f410204 | ||
|
|
34b3f52276 | ||
|
|
4b508e3295 | ||
|
|
dfdde2ef83 | ||
|
|
88cac5c6c0 | ||
|
|
5e877dd374 | ||
|
|
bcf54d9fb0 | ||
|
|
5c42133e7b | ||
|
|
93c9afe338 | ||
|
|
9b12c5c9c1 | ||
|
|
1bd4468f57 | ||
|
|
e183bb4fa6 | ||
|
|
a79ece4a3d | ||
|
|
93bfa83b46 | ||
|
|
97fa72042e | ||
|
|
d5069487ae | ||
|
|
0a5f852d04 | ||
|
|
dcc2fd0382 | ||
|
|
bf7f0d5cbc | ||
|
|
a337aef8a7 | ||
|
|
30dec3be52 | ||
|
|
604aca50c8 | ||
|
|
949dc96e7b | ||
|
|
e4c2d4eae1 | ||
|
|
66bdb7b547 | ||
|
|
9fccc3c1f3 | ||
|
|
0effcab5c2 | ||
|
|
1f5ab5f45c | ||
|
|
4789fc15a5 | ||
|
|
6f2270eea7 | ||
|
|
a0f43c8f8f | ||
|
|
b36b5857c1 | ||
|
|
c6de42fd80 | ||
|
|
5e7f61b46d | ||
|
|
c5e41faa0f | ||
|
|
465377c67b | ||
|
|
f41cc85cba | ||
|
|
fdf08624cb | ||
|
|
5c89f4f389 | ||
|
|
acce8f1b1f | ||
|
|
f1382e802e | ||
|
|
f64e59b0de | ||
|
|
5b41abe76f | ||
|
|
f286d2277e | ||
|
|
b38c983e73 | ||
|
|
a70d133471 | ||
|
|
bc3d488bfe | ||
|
|
e9578d8d44 | ||
|
|
4c38d96730 | ||
|
|
b962d1572a | ||
|
|
3f1f686bd1 | ||
|
|
611bbb8fb2 | ||
|
|
b89b39cd44 | ||
|
|
9a6e2d6578 | ||
|
|
9f63c00f5c | ||
|
|
f11b9c0bbc | ||
|
|
6ea109f712 | ||
|
|
1c3dc61264 | ||
|
|
c415ce677d | ||
|
|
927a580dce | ||
|
|
8e3b44c2e1 | ||
|
|
4048b7207b | ||
|
|
a87231552b | ||
|
|
a234f503a1 | ||
|
|
55be5b12b2 | ||
|
|
1f400649f5 | ||
|
|
e6f65440f2 | ||
|
|
def8455d78 | ||
|
|
1ad2144432 | ||
|
|
c2828e3273 |
137
Gruntfile.js
137
Gruntfile.js
@@ -18,6 +18,15 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
************************************************************************/
|
||||
|
||||
/**
|
||||
* * `grunt` - full build
|
||||
* * `grunt dev` - build only items needed for development
|
||||
* * `grunt offline` - skips *composer install*
|
||||
* * `grant release` - full build plus upgrade packages`
|
||||
* * `grant tests` - build and run tests
|
||||
*/
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
var jsFilesToMinify = [
|
||||
@@ -83,6 +92,10 @@ module.exports = function (grunt) {
|
||||
}
|
||||
|
||||
var fs = require('fs');
|
||||
var cp = require('child_process');
|
||||
var path = require('path');
|
||||
|
||||
var currentPath = path.dirname(fs.realpathSync(__filename));
|
||||
|
||||
var themeList = [];
|
||||
fs.readdirSync('application/Espo/Resources/metadata/themes').forEach(function (file) {
|
||||
@@ -108,8 +121,10 @@ module.exports = function (grunt) {
|
||||
lessData[theme] = o;
|
||||
});
|
||||
|
||||
var pkg = grunt.file.readJSON('package.json');
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
pkg: pkg,
|
||||
|
||||
mkdir: {
|
||||
tmp: {
|
||||
@@ -125,8 +140,13 @@ module.exports = function (grunt) {
|
||||
clean: {
|
||||
start: ['build/EspoCRM-*'],
|
||||
final: ['build/tmp'],
|
||||
release: ['build/EspoCRM-' + pkg.version],
|
||||
beforeFinal: {
|
||||
src: ['build/tmp/custom/Espo/Custom/*', '!build/tmp/custom/Espo/Custom/.htaccess', 'build/tmp/install/config.php']
|
||||
src: [
|
||||
'build/tmp/custom/Espo/Custom/*',
|
||||
'!build/tmp/custom/Espo/Custom/.htaccess',
|
||||
'build/tmp/install/config.php',
|
||||
]
|
||||
}
|
||||
},
|
||||
less: lessData,
|
||||
@@ -146,6 +166,9 @@ module.exports = function (grunt) {
|
||||
})
|
||||
},
|
||||
copy: {
|
||||
options: {
|
||||
mode: true,
|
||||
},
|
||||
frontendFolders: {
|
||||
expand: true,
|
||||
cwd: 'client',
|
||||
@@ -207,9 +230,6 @@ module.exports = function (grunt) {
|
||||
},
|
||||
},
|
||||
chmod: {
|
||||
options: {
|
||||
mode: '755'
|
||||
},
|
||||
php: {
|
||||
options: {
|
||||
mode: '644'
|
||||
@@ -238,7 +258,21 @@ module.exports = function (grunt) {
|
||||
'build/EspoCRM-<%= pkg.version %>/api/v1/portal-access',
|
||||
'build/EspoCRM-<%= pkg.version %>',
|
||||
]
|
||||
}
|
||||
},
|
||||
foldersWritable: {
|
||||
options: {
|
||||
mode: '775'
|
||||
},
|
||||
src: [
|
||||
'build/EspoCRM-<%= pkg.version %>/data',
|
||||
'build/EspoCRM-<%= pkg.version %>/custom',
|
||||
'build/EspoCRM-<%= pkg.version %>/custom/Espo',
|
||||
'build/EspoCRM-<%= pkg.version %>/custom/Espo/Custom',
|
||||
'build/EspoCRM-<%= pkg.version %>/client/custom',
|
||||
'build/EspoCRM-<%= pkg.version %>/client/modules',
|
||||
'build/EspoCRM-<%= pkg.version %>/application/Espo/Modules',
|
||||
]
|
||||
},
|
||||
},
|
||||
replace: {
|
||||
version: {
|
||||
@@ -258,17 +292,53 @@ module.exports = function (grunt) {
|
||||
]
|
||||
}
|
||||
},
|
||||
compress: {
|
||||
final: {
|
||||
options: {
|
||||
archive: 'build/EspoCRM-<%= pkg.version %>.zip',
|
||||
mode: 'zip'
|
||||
},
|
||||
src: ['**'],
|
||||
cwd: 'build/EspoCRM-<%= pkg.version %>',
|
||||
dest: 'EspoCRM-<%= pkg.version %>'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask("chmod-folders", function() {
|
||||
cp.execSync("find . -type d -exec chmod 755 {} + ", {stdio: 'ignore', cwd: 'build/EspoCRM-' + pkg.version});
|
||||
});
|
||||
|
||||
grunt.registerTask("composer", function() {
|
||||
cp.execSync("composer install", {stdio: 'ignore'});
|
||||
});
|
||||
|
||||
grunt.registerTask("upgrade", function() {
|
||||
cp.execSync("node diff --all --vendor", {stdio: 'inherit'});
|
||||
});
|
||||
|
||||
grunt.registerTask("unit-tests", function() {
|
||||
cp.execSync("phpunit --bootstrap=vendor/autoload.php tests/unit", {stdio: 'inherit'});
|
||||
});
|
||||
|
||||
grunt.registerTask("integration-tests", function() {
|
||||
cp.execSync("phpunit --bootstrap=vendor/autoload.php tests/integration", {stdio: 'inherit'});
|
||||
});
|
||||
|
||||
grunt.registerTask("zip", function() {
|
||||
var fs = require('fs');
|
||||
|
||||
var resolve = this.async();
|
||||
|
||||
var folder = 'EspoCRM-' + pkg.version;
|
||||
|
||||
var zipPath = 'build/' + folder +'.zip';
|
||||
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
|
||||
|
||||
var archiver = require('archiver');
|
||||
var archive = archiver('zip');
|
||||
|
||||
archive.on('error', function (err) {
|
||||
grunt.fail.warn(err);
|
||||
});
|
||||
var zipOutput = fs.createWriteStream(zipPath);
|
||||
zipOutput.on('close', function () {
|
||||
console.log("EspoCRM package has been built.");
|
||||
resolve();
|
||||
});
|
||||
|
||||
archive.directory(currentPath + '/build/' + folder, folder).pipe(zipOutput);
|
||||
|
||||
archive.finalize();
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
@@ -278,10 +348,9 @@ module.exports = function (grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-replace');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-chmod');
|
||||
|
||||
grunt.registerTask('default', [
|
||||
grunt.registerTask('offline', [
|
||||
'clean:start',
|
||||
'mkdir:tmp',
|
||||
'less',
|
||||
@@ -293,7 +362,33 @@ module.exports = function (grunt) {
|
||||
'replace',
|
||||
'clean:beforeFinal',
|
||||
'copy:final',
|
||||
'chmod',
|
||||
'clean:final'
|
||||
'chmod-folders',
|
||||
'chmod:php',
|
||||
'chmod:folders',
|
||||
'chmod:foldersWritable',
|
||||
'clean:final',
|
||||
]);
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'composer',
|
||||
'offline',
|
||||
]);
|
||||
|
||||
grunt.registerTask('release', [
|
||||
'default',
|
||||
'upgrade',
|
||||
'zip',
|
||||
'clean:release',
|
||||
]);
|
||||
|
||||
grunt.registerTask('tests', [
|
||||
'default',
|
||||
'unit-tests',
|
||||
'integration-tests',
|
||||
]);
|
||||
|
||||
grunt.registerTask('dev', [
|
||||
'composer',
|
||||
'less',
|
||||
]);
|
||||
};
|
||||
|
||||
28
README.md
28
README.md
@@ -25,7 +25,7 @@ Create an issue [here](https://github.com/espocrm/espocrm/issues) or post on our
|
||||
|
||||
[Download](https://www.espocrm.com/download/) the latest version. See the [instructions](https://www.espocrm.com/documentation/administration/installation/) about installation.
|
||||
|
||||
### How to get started (for developers)
|
||||
### Getting started (for developers)
|
||||
|
||||
1. Clone repository to your local computer.
|
||||
2. Change to the project's root directory.
|
||||
@@ -48,15 +48,33 @@ You need to have nodejs and Grunt CLI installed.
|
||||
|
||||
The build will be created in the `build` directory.
|
||||
|
||||
### How to contribute
|
||||
Upgrade packages can be built with `grunt upgrade`.
|
||||
|
||||
### How to contribute (for developers)
|
||||
|
||||
Before we can merge your pull request you need to accept our CLA [here](https://github.com/espocrm/cla). It's very simple to do.
|
||||
|
||||
Branches:
|
||||
|
||||
* hotfix/* – upcoming maintenance release; fixes should be pushed to this branch;
|
||||
* master – develop branch; new features should be pushed to this branch;
|
||||
* stable – last stable release.
|
||||
* *hotfix/** – upcoming maintenance release; fixes should be pushed to this branch;
|
||||
* *master* – develop branch; new features should be pushed to this branch;
|
||||
* *stable* – last stable release.
|
||||
|
||||
### Running tests (for developers)
|
||||
|
||||
You need to have *phpunit* installed.
|
||||
|
||||
Unit tests:
|
||||
|
||||
```
|
||||
phpunit --bootstrap=vendor/autoload.php tests/unit
|
||||
```
|
||||
|
||||
Integration tests:
|
||||
|
||||
```
|
||||
phpunit --bootstrap=vendor/autoload.php tests/integration
|
||||
```
|
||||
|
||||
### How to make a translation
|
||||
|
||||
|
||||
@@ -39,6 +39,16 @@ class User extends \Espo\Core\Acl\Base
|
||||
return $user->id === $entity->id;
|
||||
}
|
||||
|
||||
public function checkEntityRead(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if (!$user->isAdmin() && $entity->isPortal()) {
|
||||
if ($this->getAclManager()->get($user, 'portalPermission') === 'yes') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return $this->checkEntity($user, $entity, $data, 'read');
|
||||
}
|
||||
|
||||
public function checkEntityCreate(EntityUser $user, Entity $entity, $data)
|
||||
{
|
||||
if (!$user->isAdmin()) {
|
||||
|
||||
@@ -105,19 +105,23 @@ abstract class OAuth2Abstract implements IClient
|
||||
|
||||
public function getAccessTokenFromAuthorizationCode($code)
|
||||
{
|
||||
$r = $this->client->getAccessToken($this->getParam('tokenEndpoint'), Client::GRANT_TYPE_AUTHORIZATION_CODE, array(
|
||||
$r = $this->client->getAccessToken($this->getParam('tokenEndpoint'), Client::GRANT_TYPE_AUTHORIZATION_CODE, [
|
||||
'code' => $code,
|
||||
'redirect_uri' => $this->getParam('redirectUri')
|
||||
));
|
||||
'redirect_uri' => $this->getParam('redirectUri'),
|
||||
]);
|
||||
|
||||
if ($r['code'] == 200) {
|
||||
$data = array();
|
||||
$data = [];
|
||||
if (!empty($r['result'])) {
|
||||
$data['accessToken'] = $r['result']['access_token'];
|
||||
$data['tokenType'] = $r['result']['token_type'];
|
||||
$data['refreshToken'] = $r['result']['refresh_token'];
|
||||
} else {
|
||||
$GLOBALS['log']->debug("OAuth getAccessTokenFromAuthorizationCode; Response: " . json_encode($r));
|
||||
}
|
||||
return $data;
|
||||
} else {
|
||||
$GLOBALS['log']->debug("OAuth getAccessTokenFromAuthorizationCode; Response: " . json_encode($r));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
35
application/Espo/Core/FieldValidators/ArrayIntType.php
Normal file
35
application/Espo/Core/FieldValidators/ArrayIntType.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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 Espo\Core\FieldValidators;
|
||||
|
||||
class ArrayIntType extends ArrayType
|
||||
{
|
||||
|
||||
}
|
||||
@@ -44,6 +44,13 @@ class ArrayType extends BaseType
|
||||
return true;
|
||||
}
|
||||
|
||||
public function checkArray(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool
|
||||
{
|
||||
if (!$entity->has($field) || $entity->get($field) === null) return true;
|
||||
|
||||
return is_array($entity->get($field));
|
||||
}
|
||||
|
||||
protected function isNotEmpty(\Espo\ORM\Entity $entity, $field)
|
||||
{
|
||||
if (!$entity->has($field) || $entity->get($field) === null) return false;
|
||||
|
||||
@@ -84,6 +84,7 @@ class SumRelatedType extends \Espo\Core\Formula\Functions\Base
|
||||
$foreignSelectManager = $this->getInjection('selectManagerFactory')->create($foreignEntityType);
|
||||
|
||||
$foreignLink = $entity->getRelationParam($link, 'foreign');
|
||||
$foreignLinkAlias = $foreignLink . 'SumRelated';
|
||||
|
||||
if (empty($foreignLink)) {
|
||||
throw new Error("No foreign link for link {$link}.");
|
||||
@@ -95,29 +96,29 @@ class SumRelatedType extends \Espo\Core\Formula\Functions\Base
|
||||
$foreignSelectManager->applyFilter($filter, $selectParams);
|
||||
}
|
||||
|
||||
$selectParams['select'] = [[$foreignLink . '.id', 'foreignId'], 'SUM:' . $field];
|
||||
$selectParams['select'] = [[$foreignLinkAlias . '.id', 'foreignId'], 'SUM:' . $field];
|
||||
|
||||
if ($entity->getRelationType($link) === 'hasChildren') {
|
||||
$foreignSelectManager->addJoin([
|
||||
$entity->getEntityType(),
|
||||
$foreignLink,
|
||||
$foreignLinkAlias,
|
||||
[
|
||||
$foreignLink . '.id:' => $foreignLink . 'Id',
|
||||
$foreignLinkAlias . '.id:' => $foreignLink . 'Id',
|
||||
'deleted' => false,
|
||||
$foreignLink . '.id!=' => null,
|
||||
$foreignLinkAlias . '.id!=' => null,
|
||||
]
|
||||
], $selectParams);
|
||||
$selectParams['whereClause'][] = [$foreignLink . 'Type' => $entity->getEntityType()];
|
||||
|
||||
} else {
|
||||
$foreignSelectManager->addJoin($foreignLink, $selectParams);
|
||||
$foreignSelectManager->addJoin([$foreignLink, $foreignLinkAlias], $selectParams);
|
||||
}
|
||||
|
||||
if (!empty($selectParams['distinct'])) {
|
||||
$sqSelectParams = $selectParams;
|
||||
|
||||
$sqSelectParams['whereClause'][] = [
|
||||
$foreignLink . '.id' => $entity->id
|
||||
$foreignLinkAlias . '.id' => $entity->id
|
||||
];
|
||||
|
||||
$sqSelectParams['select'] = ['id'];
|
||||
@@ -133,11 +134,11 @@ class SumRelatedType extends \Espo\Core\Formula\Functions\Base
|
||||
];
|
||||
} else {
|
||||
$selectParams['whereClause'][] = [
|
||||
$foreignLink . '.id' => $entity->id
|
||||
$foreignLinkAlias . '.id' => $entity->id
|
||||
];
|
||||
}
|
||||
|
||||
$selectParams['groupBy'] = [$foreignLink . '.id'];
|
||||
$selectParams['groupBy'] = [$foreignLinkAlias . '.id'];
|
||||
|
||||
$entityManager->getRepository($foreignEntityType)->handleSelectParams($selectParams);
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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 Espo\Core\Formula\Functions\RecordGroup;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class RelateType extends \Espo\Core\Formula\Functions\Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('entityManager');
|
||||
}
|
||||
|
||||
public function process(\StdClass $item)
|
||||
{
|
||||
$args = $item->value ?? [];
|
||||
|
||||
if (count($args) < 4) throw new Error("Formula: record\\relate: Not enough arguments.");
|
||||
|
||||
$entityType = $this->evaluate($args[0]);
|
||||
$id = $this->evaluate($args[1]);
|
||||
$link = $this->evaluate($item->value[2]);
|
||||
$foreignId = $this->evaluate($item->value[3]);
|
||||
|
||||
if (!$entityType) throw new Error("Formula record\\relate: Empty entityType.");
|
||||
if (!$id) return null;
|
||||
if (!$link) throw new Error("Formula record\\relate: Empty link.");
|
||||
if (!$foreignId) return null;
|
||||
|
||||
$em = $this->getInjection('entityManager');
|
||||
|
||||
if (!$em->hasRepository($entityType)) throw new Error("Formula: record\\relate: Repository does not exist.");
|
||||
|
||||
$entity = $em->getEntity($entityType, $id);
|
||||
if (!$entity) return null;
|
||||
|
||||
if ($em->getRepository($entityType)->isRelated($entity, $link, $foreignId))
|
||||
return true;
|
||||
|
||||
return $em->getRepository($entityType)->relate($entity, $link, $foreignId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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 Espo\Core\Formula\Functions\RecordGroup;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class UnrelateType extends \Espo\Core\Formula\Functions\Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('entityManager');
|
||||
}
|
||||
|
||||
public function process(\StdClass $item)
|
||||
{
|
||||
$args = $item->value ?? [];
|
||||
|
||||
if (count($args) < 4) throw new Error("Formula: record\\unrelate: Not enough arguments.");
|
||||
|
||||
$entityType = $this->evaluate($args[0]);
|
||||
$id = $this->evaluate($args[1]);
|
||||
$link = $this->evaluate($item->value[2]);
|
||||
$foreignId = $this->evaluate($item->value[3]);
|
||||
|
||||
if (!$entityType) throw new Error("Formula record\\unrelate: Empty entityType.");
|
||||
if (!$id) return null;
|
||||
if (!$link) throw new Error("Formula record\\unrelate: Empty link.");
|
||||
if (!$foreignId) return null;
|
||||
|
||||
$em = $this->getInjection('entityManager');
|
||||
|
||||
if (!$em->hasRepository($entityType)) throw new Error("Formula: record\\unrelate: Repository does not exist.");
|
||||
|
||||
$entity = $em->getEntity($entityType, $id);
|
||||
if (!$entity) return null;
|
||||
|
||||
if (!$em->getRepository($entityType)->isRelated($entity, $link, $foreignId))
|
||||
return true;
|
||||
|
||||
return $em->getRepository($entityType)->unrelate($entity, $link, $foreignId);
|
||||
}
|
||||
}
|
||||
41
application/Espo/Core/Loaders/AuthenticationFactory.php
Normal file
41
application/Espo/Core/Loaders/AuthenticationFactory.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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 Espo\Core\Loaders;
|
||||
|
||||
class AuthenticationFactory extends Base
|
||||
{
|
||||
public function load()
|
||||
{
|
||||
$obj = new \Espo\Core\Utils\Authentication\Utils\AuthenticationFactory(
|
||||
$this->getContainer()
|
||||
);
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
@@ -229,43 +229,53 @@ class Importer
|
||||
}
|
||||
|
||||
if ($parser->checkMessageAttribute($message, 'references') && $parser->getMessageAttribute($message, 'references')) {
|
||||
$arr = explode(' ', $parser->getMessageAttribute($message, 'references'));
|
||||
$reference = $arr[0];
|
||||
$reference = str_replace(array('/', '@'), " ", trim($reference, '<>'));
|
||||
$parentType = $parentId = null;
|
||||
$emailSent = PHP_INT_MAX;
|
||||
$number = null;
|
||||
$n = sscanf($reference, '%s %s %d %d espo', $parentType, $parentId, $emailSent, $number);
|
||||
if ($n != 4) {
|
||||
$n = sscanf($reference, '%s %s %d %d espo-system', $parentType, $parentId, $emailSent, $number);
|
||||
$references = $parser->getMessageAttribute($message, 'references');
|
||||
$delimiter = ' ';
|
||||
if (strpos($references, '>,')) {
|
||||
$delimiter = ',';
|
||||
}
|
||||
if ($n == 4 && $emailSent < time()) {
|
||||
if (!empty($parentType) && !empty($parentId)) {
|
||||
if ($parentType == 'Lead') {
|
||||
$parent = $this->getEntityManager()->getEntity('Lead', $parentId);
|
||||
if ($parent && $parent->get('status') == 'Converted') {
|
||||
if ($parent->get('createdAccountId')) {
|
||||
$account = $this->getEntityManager()->getEntity('Account', $parent->get('createdAccountId'));
|
||||
if ($account) {
|
||||
$parentType = 'Account';
|
||||
$parentId = $account->id;
|
||||
}
|
||||
} else {
|
||||
if ($this->getConfig()->get('b2cMode')) {
|
||||
if ($parent->get('createdContactId')) {
|
||||
$contact = $this->getEntityManager()->getEntity('Contact', $parent->get('createdContactId'));
|
||||
if ($contact) {
|
||||
$parentType = 'Contact';
|
||||
$parentId = $contact->id;
|
||||
|
||||
$arr = explode($delimiter, $references);
|
||||
|
||||
foreach ($arr as $reference) {
|
||||
$reference = trim($reference);
|
||||
$reference = str_replace(['/', '@'], " ", trim($reference, '<>'));
|
||||
$parentType = $parentId = null;
|
||||
$emailSent = PHP_INT_MAX;
|
||||
$number = null;
|
||||
$n = sscanf($reference, '%s %s %d %d espo', $parentType, $parentId, $emailSent, $number);
|
||||
if ($n != 4) {
|
||||
$n = sscanf($reference, '%s %s %d %d espo-system', $parentType, $parentId, $emailSent, $number);
|
||||
}
|
||||
|
||||
if ($n == 4 && $emailSent < time()) {
|
||||
if (!empty($parentType) && !empty($parentId)) {
|
||||
if ($parentType == 'Lead') {
|
||||
$parent = $this->getEntityManager()->getEntity('Lead', $parentId);
|
||||
if ($parent && $parent->get('status') == 'Converted') {
|
||||
if ($parent->get('createdAccountId')) {
|
||||
$account = $this->getEntityManager()->getEntity('Account', $parent->get('createdAccountId'));
|
||||
if ($account) {
|
||||
$parentType = 'Account';
|
||||
$parentId = $account->id;
|
||||
}
|
||||
} else {
|
||||
if ($this->getConfig()->get('b2cMode')) {
|
||||
if ($parent->get('createdContactId')) {
|
||||
$contact = $this->getEntityManager()->getEntity('Contact', $parent->get('createdContactId'));
|
||||
if ($contact) {
|
||||
$parentType = 'Contact';
|
||||
$parentId = $contact->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$email->set('parentType', $parentType);
|
||||
$email->set('parentId', $parentId);
|
||||
$parentFound = true;
|
||||
}
|
||||
$email->set('parentType', $parentType);
|
||||
$email->set('parentId', $parentId);
|
||||
$parentFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ abstract class Base
|
||||
$isInRange = false;
|
||||
try {
|
||||
$isInRange = Semver::satisfies($currentVersion, $version);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$GLOBALS['log']->error('SemVer: Version identification error: '.$e->getMessage().'.');
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ abstract class Base
|
||||
|
||||
try {
|
||||
$script->run($this->getContainer(), $this->scriptParams);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->throwErrorAndRemovePackage($e->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -494,7 +494,7 @@ abstract class Base
|
||||
{
|
||||
try {
|
||||
$res = $this->getFileManager()->copy($sourcePath, $destPath, $recursively, $fileList, $copyOnlyFiles);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->throwErrorAndRemovePackage($e->getMessage());
|
||||
}
|
||||
|
||||
@@ -706,7 +706,7 @@ abstract class Base
|
||||
{
|
||||
try {
|
||||
return $this->getContainer()->get('dataManager')->rebuild();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$GLOBALS['log']->error('Database rebuild failure, details: '.$e->getMessage().'.');
|
||||
}
|
||||
|
||||
|
||||
@@ -77,20 +77,7 @@ class Auth
|
||||
|
||||
protected function getAuthenticationImpl(string $method) : \Espo\Core\Utils\Authentication\Base
|
||||
{
|
||||
$className = $this->getMetadata()->get([
|
||||
'authenticationMethods', $method, 'implementationClassName'
|
||||
]);
|
||||
|
||||
if (!$className) {
|
||||
$sanitizedName = preg_replace('/[^a-zA-Z0-9]+/', '', $method);
|
||||
|
||||
$className = "\\Espo\\Custom\\Core\\Utils\\Authentication\\" . $sanitizedName;
|
||||
if (!class_exists($className)) {
|
||||
$className = "\\Espo\\Core\\Utils\\Authentication\\" . $sanitizedName;
|
||||
}
|
||||
}
|
||||
|
||||
return new $className($this->getConfig(), $this->getEntityManager(), $this, $this->getContainer());
|
||||
return $this->getContainer()->get('authenticationFactory')->create($method);
|
||||
}
|
||||
|
||||
protected function get2FAImpl(string $method) : \Espo\Core\Utils\Authentication\TwoFA\Base
|
||||
|
||||
@@ -29,11 +29,13 @@
|
||||
|
||||
namespace Espo\Core\Utils\Authentication;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Entities\AuthToken;
|
||||
|
||||
class ApiKey extends Base
|
||||
{
|
||||
public function login($username, $password, $authToken = null, $params = [], $request)
|
||||
public function login(string $username, $password, ?AuthToken $authToken = null, array $params = [], $request = null)
|
||||
{
|
||||
$apiKey = $username;
|
||||
|
||||
@@ -41,7 +43,7 @@ class ApiKey extends Base
|
||||
'whereClause' => [
|
||||
'type' => 'api',
|
||||
'apiKey' => $apiKey,
|
||||
'authMethod' => 'ApiKey'
|
||||
'authMethod' => 'ApiKey',
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
@@ -35,19 +35,16 @@ use \Espo\Core\Utils\Auth;
|
||||
|
||||
abstract class Base
|
||||
{
|
||||
private $config;
|
||||
protected $config;
|
||||
|
||||
private $entityManager;
|
||||
|
||||
private $auth;
|
||||
protected $entityManager;
|
||||
|
||||
private $passwordHash;
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager, Auth $auth)
|
||||
public function __construct(Config $config, EntityManager $entityManager)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
protected function getConfig()
|
||||
@@ -60,11 +57,6 @@ abstract class Base
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
protected function getAuth()
|
||||
{
|
||||
return $this->auth;
|
||||
}
|
||||
|
||||
protected function getPasswordHash()
|
||||
{
|
||||
if (!isset($this->passwordHash)) {
|
||||
@@ -74,4 +66,3 @@ abstract class Base
|
||||
return $this->passwordHash;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,13 @@
|
||||
|
||||
namespace Espo\Core\Utils\Authentication;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Entities\AuthToken;
|
||||
|
||||
class Espo extends Base
|
||||
{
|
||||
public function login($username, $password, \Espo\Entities\AuthToken $authToken = null, $params = [], $request)
|
||||
public function login(string $username, $password, ?AuthToken $authToken = null, array $params = [], $request = null)
|
||||
{
|
||||
if (!$password) return;
|
||||
|
||||
@@ -47,7 +49,7 @@ class Espo extends Base
|
||||
'whereClause' => [
|
||||
'userName' => $username,
|
||||
'password' => $hash,
|
||||
'type!=' => ['api', 'system']
|
||||
'type!=' => ['api', 'system'],
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
@@ -29,11 +29,13 @@
|
||||
|
||||
namespace Espo\Core\Utils\Authentication;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Entities\AuthToken;
|
||||
|
||||
class Hmac extends Base
|
||||
{
|
||||
public function login($username, $password, $authToken = null, $params = [], $request)
|
||||
public function login(string $username, $password, ?AuthToken $authToken = null, array $params = [], $request)
|
||||
{
|
||||
$apiKey = $username;
|
||||
$hash = $password;
|
||||
|
||||
@@ -33,6 +33,7 @@ use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Utils\Auth;
|
||||
use Espo\Entities\AuthToken;
|
||||
|
||||
class LDAP extends Espo
|
||||
{
|
||||
@@ -74,9 +75,9 @@ class LDAP extends Espo
|
||||
'portalRolesIds' => 'portalUserRolesIds',
|
||||
);
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager, Auth $auth)
|
||||
public function __construct(Config $config, EntityManager $entityManager)
|
||||
{
|
||||
parent::__construct($config, $entityManager, $auth);
|
||||
parent::__construct($config, $entityManager);
|
||||
|
||||
$this->utils = new LDAP\Utils($config);
|
||||
}
|
||||
@@ -101,16 +102,7 @@ class LDAP extends Espo
|
||||
return $this->ldapClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP login
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param \Espo\Entities\AuthToken $authToken
|
||||
*
|
||||
* @return \Espo\Entities\User | null
|
||||
*/
|
||||
public function login($username, $password, \Espo\Entities\AuthToken $authToken = null, $params = [], $request)
|
||||
public function login(string $username, $password, ?AuthToken $authToken = null, array $params = [], $request = null)
|
||||
{
|
||||
if (!$password) return;
|
||||
|
||||
@@ -279,9 +271,17 @@ class LDAP extends Espo
|
||||
$data[$fieldName] = $fieldValue;
|
||||
}
|
||||
|
||||
$this->getAuth()->useNoAuth();
|
||||
$entityManager = $this->entityManager;
|
||||
|
||||
$user = $this->getEntityManager()->getEntity('User');
|
||||
$systemUser = $entityManager->getRepository('User')->get('system');
|
||||
if (!$systemUser) {
|
||||
throw new Error("System user is not found");
|
||||
}
|
||||
$systemUser->set('isAdmin', true);
|
||||
$systemUser->set('ipAddress', $_SERVER['REMOTE_ADDR']);
|
||||
$entityManager->setUser($systemUser);
|
||||
|
||||
$user = $entityManager->getEntity('User');
|
||||
$user->set($data);
|
||||
|
||||
$this->getEntityManager()->saveEntity($user);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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 Espo\Core\Utils\Authentication\Utils;
|
||||
|
||||
use Espo\Core\Container;
|
||||
|
||||
class AuthenticationFactory
|
||||
{
|
||||
protected $container;
|
||||
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function create(string $method) : \Espo\Core\Utils\Authentication\Base
|
||||
{
|
||||
$metadata = $this->container->get('metadata');
|
||||
|
||||
$className = $metadata->get(['authenticationMethods', $method, 'implementationClassName']);
|
||||
$dependencyList = $metadata->get(['authenticationMethods', $method, 'dependencyList']) ?? [];
|
||||
|
||||
if (!$className) {
|
||||
$sanitizedName = preg_replace('/[^a-zA-Z0-9]+/', '', $method);
|
||||
|
||||
$className = "\\Espo\\Custom\\Core\\Utils\\Authentication\\" . $sanitizedName;
|
||||
if (!class_exists($className)) {
|
||||
$className = "\\Espo\\Core\\Utils\\Authentication\\" . $sanitizedName;
|
||||
}
|
||||
}
|
||||
|
||||
$config = $this->container->get('config');
|
||||
$entityManager = $this->container->get('entityManager');
|
||||
|
||||
$impl = new $className($config, $entityManager);
|
||||
|
||||
foreach ($dependencyList as $item) {
|
||||
$impl->inject($item, $this->container->get($item));
|
||||
}
|
||||
|
||||
return $impl;
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ class Autoload
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return Utill::getValueByKey($this->data, $key, $returns);
|
||||
return Util::getValueByKey($this->data, $key, $returns);
|
||||
}
|
||||
|
||||
public function getAll()
|
||||
|
||||
@@ -44,7 +44,6 @@ class Manager
|
||||
if (isset($config)) {
|
||||
$params = array(
|
||||
'defaultPermissions' => $config->get('defaultPermissions'),
|
||||
'permissionMap' => $config->get('permissionMap'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -381,11 +380,11 @@ class Manager
|
||||
{
|
||||
$fullPath = $this->concatPaths($path);
|
||||
|
||||
if (file_exists($fullPath) && is_dir($path)) {
|
||||
if (file_exists($fullPath) && is_dir($fullPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$defaultPermissions = $this->getPermissionUtils()->getDefaultPermissions();
|
||||
$defaultPermissions = $this->getPermissionUtils()->getRequiredPermissions($fullPath);
|
||||
|
||||
if (!isset($permission)) {
|
||||
$permission = (string) $defaultPermissions['dir'];
|
||||
@@ -393,7 +392,11 @@ class Manager
|
||||
}
|
||||
|
||||
try {
|
||||
$umask = @umask(0);
|
||||
$result = mkdir($fullPath, $permission, true);
|
||||
if ($umask) {
|
||||
@umask($umask);
|
||||
}
|
||||
|
||||
if (!empty($defaultPermissions['user'])) {
|
||||
$this->getPermissionUtils()->chown($fullPath);
|
||||
@@ -484,11 +487,11 @@ class Manager
|
||||
*/
|
||||
public function checkCreateFile($filePath)
|
||||
{
|
||||
$defaultPermissions = $this->getPermissionUtils()->getDefaultPermissions();
|
||||
$defaultPermissions = $this->getPermissionUtils()->getRequiredPermissions($filePath);
|
||||
|
||||
if (file_exists($filePath)) {
|
||||
if (!is_writable($filePath) && !in_array($this->getPermissionUtils()->getCurrentPermission($filePath), array($defaultPermissions['file'], $defaultPermissions['dir']))) {
|
||||
return $this->getPermissionUtils()->setDefaultPermissions($filePath, true);
|
||||
return $this->getPermissionUtils()->setDefaultPermissions($filePath);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -504,7 +507,7 @@ class Manager
|
||||
}
|
||||
|
||||
if (touch($filePath)) {
|
||||
return $this->getPermissionUtils()->setDefaultPermissions($filePath, true);
|
||||
return $this->getPermissionUtils()->setDefaultPermissions($filePath);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\File;
|
||||
use Espo\Core\Utils,
|
||||
Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Core\Utils;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
class Permission
|
||||
{
|
||||
@@ -44,47 +45,45 @@ class Permission
|
||||
|
||||
protected $permissionErrorRules = null;
|
||||
|
||||
protected $params = array(
|
||||
'defaultPermissions' => array (
|
||||
'dir' => '0775',
|
||||
'file' => '0664',
|
||||
'user' => '',
|
||||
'group' => '',
|
||||
),
|
||||
'permissionMap' => array(
|
||||
protected $writableMap = [
|
||||
'data' => [
|
||||
'recursive' => true,
|
||||
],
|
||||
'application/Espo/Modules' => [
|
||||
'recursive' => false,
|
||||
],
|
||||
'client/modules' => [
|
||||
'recursive' => false,
|
||||
],
|
||||
'custom/Espo/Custom' => [
|
||||
'recursive' => true,
|
||||
],
|
||||
];
|
||||
|
||||
/** array('0664', '0775') */
|
||||
'writable' => array(
|
||||
'data',
|
||||
'custom',
|
||||
),
|
||||
|
||||
/** array('0644', '0755') */
|
||||
'readable' => array(
|
||||
'api',
|
||||
'application',
|
||||
'client',
|
||||
'vendor',
|
||||
'index.php',
|
||||
'cron.php',
|
||||
'rebuild.php',
|
||||
'main.html',
|
||||
'reset.html',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
protected $permissionRules = array(
|
||||
'writable' => array('0664', '0775'),
|
||||
'readable' => array('0644', '0755'),
|
||||
);
|
||||
protected $defaultPermissions = [
|
||||
'dir' => '0755',
|
||||
'file' => '0644',
|
||||
'user' => null,
|
||||
'group' => null,
|
||||
];
|
||||
|
||||
protected $writablePermissions = [
|
||||
'file' => '0664',
|
||||
'dir' => '0775',
|
||||
];
|
||||
|
||||
public function __construct(Manager $fileManager, array $params = null)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
if (isset($params)) {
|
||||
$this->params = $params;
|
||||
|
||||
if ($params) {
|
||||
foreach ($params as $paramName => $paramValue) {
|
||||
switch ($paramName) {
|
||||
case 'defaultPermissions':
|
||||
$this->defaultPermissions = array_merge($this->defaultPermissions, $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,11 +92,6 @@ class Permission
|
||||
return $this->fileManager;
|
||||
}
|
||||
|
||||
protected function getParams()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default settings
|
||||
*
|
||||
@@ -105,14 +99,34 @@ class Permission
|
||||
*/
|
||||
public function getDefaultPermissions()
|
||||
{
|
||||
$params = $this->getParams();
|
||||
return $params['defaultPermissions'];
|
||||
return $this->defaultPermissions;
|
||||
}
|
||||
|
||||
|
||||
public function getPermissionRules()
|
||||
public function getWritableMap()
|
||||
{
|
||||
return $this->permissionRules;
|
||||
return $this->writableMap;
|
||||
}
|
||||
|
||||
public function getWritableList()
|
||||
{
|
||||
return array_keys($this->writableMap);
|
||||
}
|
||||
|
||||
public function getRequiredPermissions($path)
|
||||
{
|
||||
$permission = $this->getDefaultPermissions();
|
||||
|
||||
foreach ($this->getWritableMap() as $writablePath => $writableOptions) {
|
||||
if (!$writableOptions['recursive'] && $path == $writablePath) {
|
||||
return array_merge($permission, $this->writablePermissions);
|
||||
}
|
||||
|
||||
if ($writableOptions['recursive'] && substr($path, 0, strlen($writablePath)) == $writablePath) {
|
||||
return array_merge($permission, $this->writablePermissions);
|
||||
}
|
||||
}
|
||||
|
||||
return $permission;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +143,7 @@ class Permission
|
||||
return false;
|
||||
}
|
||||
|
||||
$permission = $this->getDefaultPermissions();
|
||||
$permission = $this->getRequiredPermissions($path);
|
||||
|
||||
$result = $this->chmod($path, array($permission['file'], $permission['dir']), $recurse);
|
||||
if (!empty($permission['user'])) {
|
||||
@@ -342,8 +356,8 @@ class Permission
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function chgrpRecurse($path, $group) {
|
||||
|
||||
protected function chgrpRecurse($path, $group)
|
||||
{
|
||||
if (!file_exists($path)) {
|
||||
return false;
|
||||
}
|
||||
@@ -461,60 +475,38 @@ class Permission
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMapPermission($mode = null)
|
||||
public function setMapPermission()
|
||||
{
|
||||
$this->permissionError = array();
|
||||
$this->permissionErrorRules = array();
|
||||
|
||||
$params = $this->getParams();
|
||||
|
||||
$permissionRules = $this->permissionRules;
|
||||
if (isset($mode)) {
|
||||
foreach ($permissionRules as &$value) {
|
||||
$value = $mode;
|
||||
}
|
||||
}
|
||||
|
||||
$result = true;
|
||||
foreach ($params['permissionMap'] as $type => $items) {
|
||||
|
||||
$permission = $permissionRules[$type];
|
||||
foreach ($this->getWritableMap() as $path => $options) {
|
||||
if (!file_exists($path)) continue;
|
||||
|
||||
foreach ($items as $item) {
|
||||
try {
|
||||
$this->chmod($path, $this->writablePermissions, $options['recursive']);
|
||||
} catch (\Throwable $e) {}
|
||||
|
||||
if (file_exists($item)) {
|
||||
/** check is writable */
|
||||
$res = is_writable($path);
|
||||
|
||||
try {
|
||||
$this->chmod($item, $permission, true);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
$res = is_readable($item);
|
||||
|
||||
/** check is wtitable */
|
||||
if ($type == 'writable') {
|
||||
|
||||
$res &= is_writable($item);
|
||||
|
||||
if (is_dir($item)) {
|
||||
$name = uniqid();
|
||||
|
||||
try {
|
||||
$res &= $this->getFileManager()->putContents(array($item, $name), 'test');
|
||||
$res &= $this->getFileManager()->removeFile($name, $item);
|
||||
} catch (\Exception $e) {
|
||||
$res = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
$result = false;
|
||||
$this->permissionError[] = $item;
|
||||
$this->permissionErrorRules[$item] = $permission;
|
||||
}
|
||||
if (is_dir($path)) {
|
||||
try {
|
||||
$name = uniqid();
|
||||
$res &= $this->getFileManager()->putContents([$path, $name], 'test');
|
||||
$res &= $this->getFileManager()->removeFile($name, $path);
|
||||
} catch (\Throwable $e) {
|
||||
$res = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
$result = false;
|
||||
$this->permissionError[] = $path;
|
||||
$this->permissionErrorRules[$path] = $this->writablePermissions;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -607,4 +599,3 @@ class Permission
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -159,11 +159,12 @@ class SystemRequirements
|
||||
{
|
||||
return $this->getRequiredList('permissionRequirements', [
|
||||
'permissionMap.writable',
|
||||
'permissionMap.readable',
|
||||
], $additionalData);
|
||||
], $additionalData, [
|
||||
'permissionMap.writable' => $this->getFileManager()->getPermissionUtils()->getWritableList(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRequiredList($type, $checkList, array $additionalData = null)
|
||||
protected function getRequiredList($type, $checkList, array $additionalData = null, array $predefinedData = [])
|
||||
{
|
||||
$config = $this->getConfig();
|
||||
|
||||
@@ -172,7 +173,8 @@ class SystemRequirements
|
||||
foreach ($checkList as $itemName) {
|
||||
$methodName = 'check' . ucfirst($type);
|
||||
if (method_exists($this, $methodName)) {
|
||||
$result = $this->$methodName($itemName, $config->get($itemName), $additionalData);
|
||||
$itemValue = isset($predefinedData[$itemName]) ? $predefinedData[$itemName] : $config->get($itemName);
|
||||
$result = $this->$methodName($itemName, $itemValue, $additionalData);
|
||||
$list = array_merge($list, $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,31 +29,11 @@
|
||||
|
||||
return [
|
||||
'defaultPermissions' => [
|
||||
'dir' => '0775',
|
||||
'file' => '0664',
|
||||
'dir' => '0755',
|
||||
'file' => '0644',
|
||||
'user' => '',
|
||||
'group' => ''
|
||||
],
|
||||
'permissionMap' => [
|
||||
/** array('0664', '0775') */
|
||||
'writable' => [
|
||||
'data',
|
||||
'custom',
|
||||
'application/Espo/Modules',
|
||||
'client/modules'
|
||||
],
|
||||
/** array('0644', '0755') */
|
||||
'readable' => [
|
||||
'api',
|
||||
'application',
|
||||
'client',
|
||||
'vendor',
|
||||
'index.php',
|
||||
'cron.php',
|
||||
'rebuild.php',
|
||||
'clear_cache.php'
|
||||
],
|
||||
],
|
||||
'jobMaxPortion' => 15, /** Max number of jobs per one execution. */
|
||||
'jobPeriod' => 7800, /** Max execution time (in seconds) allocated for a sinle job. If exceeded then set to Failed.*/
|
||||
'jobPeriodForActiveProcess' => 36000, /** Max execution time (in seconds) allocated for a sinle job with active process. If exceeded then set to Failed.*/
|
||||
@@ -89,10 +69,8 @@ return [
|
||||
'crud',
|
||||
'logger',
|
||||
'isInstalled',
|
||||
'defaultPermissions',
|
||||
'systemUser',
|
||||
'permissionMap',
|
||||
'permissionRules',
|
||||
'defaultPermissions',
|
||||
'passwordSalt',
|
||||
'cryptKey',
|
||||
'apiSecretKeys',
|
||||
|
||||
35
application/Espo/Entities/Settings.php
Normal file
35
application/Espo/Entities/Settings.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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 Espo\Entities;
|
||||
|
||||
class Settings extends \Espo\Core\ORM\Entity
|
||||
{
|
||||
|
||||
}
|
||||
@@ -371,48 +371,13 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
|
||||
if (
|
||||
$this->getMetadata()->get(['entityDefs', $entityType, 'links', $link, 'audited'])
|
||||
|
||||
) {
|
||||
$n = $this->getEntityManager()->getRepository('Note')->where(array(
|
||||
'type' => 'Relate',
|
||||
'parentId' => $entity->id,
|
||||
'parentType' => $entityType,
|
||||
'relatedId' => $foreignEntity->id,
|
||||
'relatedType' => $foreignEntity->getEntityType()
|
||||
))->findOne();
|
||||
if (!$n) {
|
||||
$note = $this->getEntityManager()->getEntity('Note');
|
||||
$note->set(array(
|
||||
'type' => 'Relate',
|
||||
'parentId' => $entity->id,
|
||||
'parentType' => $entityType,
|
||||
'relatedId' => $foreignEntity->id,
|
||||
'relatedType' => $foreignEntity->getEntityType()
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($note);
|
||||
}
|
||||
$this->getStreamService()->noteRelate($foreignEntity, $entityType, $entity->id);
|
||||
}
|
||||
|
||||
$foreignLink = $entity->getRelationParam($link, 'foreign');
|
||||
if ($this->getMetadata()->get(['entityDefs', $foreignEntity->getEntityType(), 'links', $foreignLink, 'audited'])) {
|
||||
$n = $this->getEntityManager()->getRepository('Note')->where(array(
|
||||
'type' => 'Relate',
|
||||
'parentId' => $foreignEntity->id,
|
||||
'parentType' => $foreignEntity->getEntityType(),
|
||||
'relatedId' => $entity->id,
|
||||
'relatedType' => $entityType
|
||||
))->findOne();
|
||||
if (!$n) {
|
||||
$note = $this->getEntityManager()->getEntity('Note');
|
||||
$note->set(array(
|
||||
'type' => 'Relate',
|
||||
'parentId' => $foreignEntity->id,
|
||||
'parentType' => $foreignEntity->getEntityType(),
|
||||
'relatedId' => $entity->id,
|
||||
'relatedType' => $entityType
|
||||
));
|
||||
$this->getEntityManager()->saveEntity($note);
|
||||
}
|
||||
$this->getStreamService()->noteRelate($entity, $foreignEntity->getEntityType(), $foreignEntity->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,39 +32,41 @@ namespace Espo\Modules\Crm\Business\Distribution\CaseObj;
|
||||
class LeastBusy
|
||||
{
|
||||
protected $entityManager;
|
||||
protected $metadata;
|
||||
|
||||
public function __construct($entityManager)
|
||||
public function __construct($entityManager, $metadata)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
protected function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
public function getUser($team, $targetUserPosition = null)
|
||||
{
|
||||
$params = array();
|
||||
$selectParams = [
|
||||
'whereClause' => ['isActive' => true],
|
||||
'orderBy' => 'id',
|
||||
];
|
||||
|
||||
if (!empty($targetUserPosition)) {
|
||||
$params['additionalColumnsConditions'] = array(
|
||||
'role' => $targetUserPosition
|
||||
);
|
||||
$selectParams['additionalColumnsConditions'] = ['role' => $targetUserPosition];
|
||||
}
|
||||
|
||||
$userList = $team->get('users', $params);
|
||||
$userList = $team->get('users', $selectParams);
|
||||
|
||||
if (count($userList) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$countHash = array();
|
||||
$countHash = [];
|
||||
|
||||
$notActualStatusList =
|
||||
$this->metadata->get(['entityDefs', 'Case', 'fields', 'status', 'notActualOptions']) ?? [];
|
||||
|
||||
foreach ($userList as $user) {
|
||||
$count = $this->getEntityManager()->getRepository('Case')->where(array(
|
||||
$count = $this->entityManager->getRepository('Case')->where([
|
||||
'assignedUserId' => $user->id,
|
||||
'status<>' => ['Closed', 'Rejected', 'Duplicated']
|
||||
))->count();
|
||||
'status!=' => $notActualStatusList,
|
||||
])->count();
|
||||
$countHash[$user->id] = $count;
|
||||
}
|
||||
|
||||
@@ -83,8 +85,7 @@ class LeastBusy
|
||||
}
|
||||
|
||||
if ($foundUserId !== false) {
|
||||
return $this->getEntityManager()->getEntity('User', $foundUserId);
|
||||
return $this->entityManager->getEntity('User', $foundUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,29 +45,30 @@ class RoundRobin
|
||||
|
||||
public function getUser($team, $targetUserPosition = null)
|
||||
{
|
||||
$params = array();
|
||||
$selectParams = [
|
||||
'whereClause' => ['isActive' => true],
|
||||
'orderBy' => 'id',
|
||||
];
|
||||
|
||||
if (!empty($targetUserPosition)) {
|
||||
$params['additionalColumnsConditions'] = array(
|
||||
'role' => $targetUserPosition
|
||||
);
|
||||
$selectParams['additionalColumnsConditions'] = ['role' => $targetUserPosition];
|
||||
}
|
||||
|
||||
$userList = $team->get('users', $params);
|
||||
$userList = $team->get('users', $selectParams);
|
||||
|
||||
if (count($userList) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$userIdList = array();
|
||||
$userIdList = [];
|
||||
|
||||
foreach ($userList as $user) {
|
||||
$userIdList[] = $user->id;
|
||||
}
|
||||
|
||||
|
||||
$case = $this->getEntityManager()->getRepository('Case')->where(array(
|
||||
$case = $this->getEntityManager()->getRepository('Case')->where([
|
||||
'assignedUserId' => $userIdList,
|
||||
))->order('createdAt', 'DESC')->findOne();
|
||||
])->order('number', 'DESC')->findOne();
|
||||
|
||||
if (empty($case)) {
|
||||
$num = 0;
|
||||
@@ -80,7 +81,8 @@ class RoundRobin
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getEntityManager()->getEntity('User', $userIdList[$num]);
|
||||
$id = $userIdList[$num];
|
||||
|
||||
return $this->getEntityManager()->getEntity('User', $id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ abstract class Entity implements IEntity
|
||||
}
|
||||
}
|
||||
|
||||
public function clear($name = null)
|
||||
public function clear(?string $name = null)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
$this->reset();
|
||||
@@ -126,7 +126,7 @@ abstract class Entity implements IEntity
|
||||
}
|
||||
}
|
||||
|
||||
public function get($name, $params = [])
|
||||
public function get(string $name, $params = [])
|
||||
{
|
||||
if ($name == 'id') {
|
||||
return $this->id;
|
||||
@@ -148,7 +148,7 @@ abstract class Entity implements IEntity
|
||||
return null;
|
||||
}
|
||||
|
||||
public function has($name)
|
||||
public function has(string $name)
|
||||
{
|
||||
if ($name == 'id') {
|
||||
return !!$this->id;
|
||||
|
||||
@@ -33,4 +33,3 @@ interface ICollection
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -56,37 +56,27 @@ interface IEntity
|
||||
const HAS_CHILDREN = 'hasChildren';
|
||||
|
||||
/**
|
||||
* Push values from the array.
|
||||
* E.g. insert values into the bean from a request data.
|
||||
* @param array $arr Array of field - value pairs
|
||||
*/
|
||||
function populateFromArray(array $arr);
|
||||
|
||||
/**
|
||||
* Resets all fields in the current model.
|
||||
* Resets all attributes in the current model.
|
||||
*/
|
||||
function reset();
|
||||
|
||||
/**
|
||||
* Set field.
|
||||
* Set attribute.
|
||||
*/
|
||||
function set($name, $value);
|
||||
|
||||
/**
|
||||
* Get field.
|
||||
* Get attribute.
|
||||
*/
|
||||
function get($name);
|
||||
function get(string $name);
|
||||
|
||||
/**
|
||||
* Check field is set.
|
||||
* Check attribute is set.
|
||||
*/
|
||||
function has($name);
|
||||
function has(string $name);
|
||||
|
||||
/**
|
||||
* Clear field.
|
||||
* Clear attribute.
|
||||
*/
|
||||
function clear($name);
|
||||
|
||||
function clear(?string $name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -65,4 +65,3 @@ class RepositoryFactory
|
||||
$this->defaultRepositoryClassName = $defaultRepositoryClassName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,9 +87,21 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
if ($entity->get('fromEmailAddressName')) {
|
||||
$entity->set('from', $entity->get('fromEmailAddressName'));
|
||||
} else {
|
||||
$entity->set('from', null);
|
||||
return;
|
||||
}
|
||||
if ($entity->get('fromEmailAddressId')) {
|
||||
$ea = $this->getEntityManager()->getRepository('EmailAddress')->get($entity->get('fromEmailAddressId'));
|
||||
if ($ea) {
|
||||
$entity->set('from', $ea->get('name'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$entity->has('fromEmailAddressId')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity->set('from', null);
|
||||
}
|
||||
|
||||
public function loadToField(Entity $entity)
|
||||
|
||||
168
application/Espo/Resources/i18n/en_US/Currency.json
Normal file
168
application/Espo/Resources/i18n/en_US/Currency.json
Normal file
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"names": {
|
||||
"AED":"United Arab Emirates Dirham",
|
||||
"AFN":"Afghan Afghani",
|
||||
"ALL":"Albanian Lek",
|
||||
"AMD":"Armenian Dram",
|
||||
"ANG":"Netherlands Antillean Guilder",
|
||||
"AOA":"Angolan Kwanza",
|
||||
"ARS":"Argentine Peso",
|
||||
"AUD":"Australian Dollar",
|
||||
"AWG":"Aruban Florin",
|
||||
"AZN":"Azerbaijani Manat",
|
||||
"BAM":"Bosnia-Herzegovina Convertible Mark",
|
||||
"BBD":"Barbadian Dollar",
|
||||
"BDT":"Bangladeshi Taka",
|
||||
"BGN":"Bulgarian Lev",
|
||||
"BHD":"Bahraini Dinar",
|
||||
"BIF":"Burundian Franc",
|
||||
"BMD":"Bermudan Dollar",
|
||||
"BND":"Brunei Dollar",
|
||||
"BOB":"Bolivian Boliviano",
|
||||
"BOV":"Bolivian Mvdol",
|
||||
"BRL":"Brazilian Real",
|
||||
"BSD":"Bahamian Dollar",
|
||||
"BTN":"Bhutanese Ngultrum",
|
||||
"BWP":"Botswanan Pula",
|
||||
"BYN":"Belarusian Ruble",
|
||||
"BZD":"Belize Dollar",
|
||||
"CAD":"Canadian Dollar",
|
||||
"CDF":"Congolese Franc",
|
||||
"CHE":"WIR Euro",
|
||||
"CHF":"Swiss Franc",
|
||||
"CHW":"WIR Franc",
|
||||
"CLF":"Chilean Unit of Account (UF)",
|
||||
"CLP":"Chilean Peso",
|
||||
"CNH":"Chinese Yuan (offshore)",
|
||||
"CNY":"Chinese Yuan",
|
||||
"COP":"Colombian Peso",
|
||||
"COU":"Colombian Real Value Unit",
|
||||
"CRC":"Costa Rican Colón",
|
||||
"CUC":"Cuban Convertible Peso",
|
||||
"CUP":"Cuban Peso",
|
||||
"CVE":"Cape Verdean Escudo",
|
||||
"CZK":"Czech Koruna",
|
||||
"DJF":"Djiboutian Franc",
|
||||
"DKK":"Danish Krone",
|
||||
"DOP":"Dominican Peso",
|
||||
"DZD":"Algerian Dinar",
|
||||
"EGP":"Egyptian Pound",
|
||||
"ERN":"Eritrean Nakfa",
|
||||
"ETB":"Ethiopian Birr",
|
||||
"EUR":"Euro",
|
||||
"FJD":"Fijian Dollar",
|
||||
"FKP":"Falkland Islands Pound",
|
||||
"GBP":"British Pound",
|
||||
"GEL":"Georgian Lari",
|
||||
"GHS":"Ghanaian Cedi",
|
||||
"GIP":"Gibraltar Pound",
|
||||
"GMD":"Gambian Dalasi",
|
||||
"GNF":"Guinean Franc",
|
||||
"GTQ":"Guatemalan Quetzal",
|
||||
"GYD":"Guyanaese Dollar",
|
||||
"HKD":"Hong Kong Dollar",
|
||||
"HNL":"Honduran Lempira",
|
||||
"HRK":"Croatian Kuna",
|
||||
"HTG":"Haitian Gourde",
|
||||
"HUF":"Hungarian Forint",
|
||||
"IDR":"Indonesian Rupiah",
|
||||
"ILS":"Israeli New Shekel",
|
||||
"INR":"Indian Rupee",
|
||||
"IQD":"Iraqi Dinar",
|
||||
"IRR":"Iranian Rial",
|
||||
"ISK":"Icelandic Króna",
|
||||
"JMD":"Jamaican Dollar",
|
||||
"JOD":"Jordanian Dinar",
|
||||
"JPY":"Japanese Yen",
|
||||
"KES":"Kenyan Shilling",
|
||||
"KGS":"Kyrgystani Som",
|
||||
"KHR":"Cambodian Riel",
|
||||
"KMF":"Comorian Franc",
|
||||
"KPW":"North Korean Won",
|
||||
"KRW":"South Korean Won",
|
||||
"KWD":"Kuwaiti Dinar",
|
||||
"KYD":"Cayman Islands Dollar",
|
||||
"KZT":"Kazakhstani Tenge",
|
||||
"LAK":"Laotian Kip",
|
||||
"LBP":"Lebanese Pound",
|
||||
"LKR":"Sri Lankan Rupee",
|
||||
"LRD":"Liberian Dollar",
|
||||
"LSL":"Lesotho Loti",
|
||||
"LYD":"Libyan Dinar",
|
||||
"MAD":"Moroccan Dirham",
|
||||
"MDL":"Moldovan Leu",
|
||||
"MGA":"Malagasy Ariary",
|
||||
"MKD":"Macedonian Denar",
|
||||
"MMK":"Myanmar Kyat",
|
||||
"MNT":"Mongolian Tugrik",
|
||||
"MOP":"Macanese Pataca",
|
||||
"MRO":"Mauritanian Ouguiya",
|
||||
"MUR":"Mauritian Rupee",
|
||||
"MWK":"Malawian Kwacha",
|
||||
"MXN":"Mexican Peso",
|
||||
"MXV":"Mexican Investment Unit",
|
||||
"MYR":"Malaysian Ringgit",
|
||||
"MZN":"Mozambican Metical",
|
||||
"NAD":"Namibian Dollar",
|
||||
"NGN":"Nigerian Naira",
|
||||
"NIO":"Nicaraguan Córdoba",
|
||||
"NOK":"Norwegian Krone",
|
||||
"NPR":"Nepalese Rupee",
|
||||
"NZD":"New Zealand Dollar",
|
||||
"OMR":"Omani Rial",
|
||||
"PAB":"Panamanian Balboa",
|
||||
"PEN":"Peruvian Sol",
|
||||
"PGK":"Papua New Guinean Kina",
|
||||
"PHP":"Philippine Piso",
|
||||
"PKR":"Pakistani Rupee",
|
||||
"PLN":"Polish Zloty",
|
||||
"PYG":"Paraguayan Guarani",
|
||||
"QAR":"Qatari Rial",
|
||||
"RON":"Romanian Leu",
|
||||
"RSD":"Serbian Dinar",
|
||||
"RUB":"Russian Ruble",
|
||||
"RWF":"Rwandan Franc",
|
||||
"SAR":"Saudi Riyal",
|
||||
"SBD":"Solomon Islands Dollar",
|
||||
"SCR":"Seychellois Rupee",
|
||||
"SDG":"Sudanese Pound",
|
||||
"SEK":"Swedish Krona",
|
||||
"SGD":"Singapore Dollar",
|
||||
"SHP":"St. Helena Pound",
|
||||
"SLL":"Sierra Leonean Leone",
|
||||
"SOS":"Somali Shilling",
|
||||
"SRD":"Surinamese Dollar",
|
||||
"SSP":"South Sudanese Pound",
|
||||
"STN":"São Tomé & Príncipe Dobra (2018)",
|
||||
"SYP":"Syrian Pound",
|
||||
"SZL":"Swazi Lilangeni",
|
||||
"SVC": "Salvadoran Colón",
|
||||
"THB":"Thai Baht",
|
||||
"TJS":"Tajikistani Somoni",
|
||||
"TND":"Tunisian Dinar",
|
||||
"TOP":"Tongan Paʻanga",
|
||||
"TRY":"Turkish Lira",
|
||||
"TTD":"Trinidad & Tobago Dollar",
|
||||
"TWD":"New Taiwan Dollar",
|
||||
"TZS":"Tanzanian Shilling",
|
||||
"UAH":"Ukrainian Hryvnia",
|
||||
"UGX":"Ugandan Shilling",
|
||||
"USD":"US Dollar",
|
||||
"USN":"US Dollar (Next day)",
|
||||
"UYI":"Uruguayan Peso (Indexed Units)",
|
||||
"UYU":"Uruguayan Peso",
|
||||
"UZS":"Uzbekistani Som",
|
||||
"VEF":"Venezuelan Bolívar",
|
||||
"VND":"Vietnamese Dong",
|
||||
"VUV":"Vanuatu Vatu",
|
||||
"WST":"Samoan Tala",
|
||||
"XAF":"Central African CFA Franc",
|
||||
"XCD":"East Caribbean Dollar",
|
||||
"XOF":"West African CFA Franc",
|
||||
"XPF":"CFP Franc",
|
||||
"YER":"Yemeni Rial",
|
||||
"ZAR":"South African Rand",
|
||||
"ZMW":"Zambian Kwacha",
|
||||
"ZWL": "Zimbabwe Dollar"
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@
|
||||
"View Users": "View Users"
|
||||
},
|
||||
"messages": {
|
||||
"noSmtpSetup": "No SMTP setup. {link}.",
|
||||
"noSmtpSetup": "SMTP is not configured: {link}",
|
||||
"testEmailSent": "Test email has been sent",
|
||||
"emailSent": "Email has been sent",
|
||||
"savedAsDraft": "Saved as draft",
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"attachments": "Attachments",
|
||||
"insertField": "Insert Field",
|
||||
"oneOff": "One-off",
|
||||
"category": "Category"
|
||||
"category": "Category",
|
||||
"insertField": "Placeholders"
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"removeDuplicates": "This will permanently remove all imported records that were recognized as duplicates.",
|
||||
"confirmRevert": "This will remove all imported records permanently. Are you sure?",
|
||||
"confirmRemoveDuplicates": "This will permanently remove all imported records that were recognized as duplicates. Are you sure?",
|
||||
"confirmRemoveImportLog" : "This will remove the import log. All imported records will be kept. You wan't be able to revert import results. Are you sure you?",
|
||||
"confirmRemoveImportLog" : "This will remove the import log. All imported records will be kept. You won't be able to revert import results. Are you sure you?",
|
||||
"removeImportLog": "This will remove the import log. All imported records will be kept. Use it if you are sure that import is fine."
|
||||
},
|
||||
"fields": {
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
{
|
||||
"name":"insertField",
|
||||
"view": "views/email-template/fields/insert-field",
|
||||
"customLabel": "",
|
||||
"fullWidth":true
|
||||
}
|
||||
],
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
{
|
||||
"name":"insertField",
|
||||
"view": "views/email-template/fields/insert-field",
|
||||
"customLabel": "",
|
||||
"fullWidth":true
|
||||
}
|
||||
],
|
||||
|
||||
@@ -163,5 +163,102 @@
|
||||
"ZAR":"R",
|
||||
"ZWD":"Z$",
|
||||
"BTC":"฿"
|
||||
}
|
||||
},
|
||||
"list": [
|
||||
"AFN",
|
||||
"AED",
|
||||
"ALL",
|
||||
"ANG",
|
||||
"AOA",
|
||||
"ARS",
|
||||
"AUD",
|
||||
"BAM",
|
||||
"BGN",
|
||||
"BHD",
|
||||
"BND",
|
||||
"BOB",
|
||||
"BRL",
|
||||
"BWP",
|
||||
"CAD",
|
||||
"CHF",
|
||||
"CLP",
|
||||
"CNY",
|
||||
"COP",
|
||||
"CRC",
|
||||
"CVE",
|
||||
"CZK",
|
||||
"DKK",
|
||||
"DOP",
|
||||
"DZD",
|
||||
"EGP",
|
||||
"EUR",
|
||||
"FJD",
|
||||
"GBP",
|
||||
"GNF",
|
||||
"HKD",
|
||||
"HNL",
|
||||
"HRK",
|
||||
"HUF",
|
||||
"IDR",
|
||||
"ILS",
|
||||
"INR",
|
||||
"IRR",
|
||||
"JMD",
|
||||
"JOD",
|
||||
"JPY",
|
||||
"KES",
|
||||
"KRW",
|
||||
"KWD",
|
||||
"KYD",
|
||||
"KZT",
|
||||
"LBP",
|
||||
"LKR",
|
||||
"MAD",
|
||||
"MDL",
|
||||
"MKD",
|
||||
"MMK",
|
||||
"MUR",
|
||||
"MXN",
|
||||
"MYR",
|
||||
"MZN",
|
||||
"NAD",
|
||||
"NGN",
|
||||
"NIO",
|
||||
"NOK",
|
||||
"NPR",
|
||||
"NZD",
|
||||
"OMR",
|
||||
"PEN",
|
||||
"PGK",
|
||||
"PHP",
|
||||
"PKR",
|
||||
"PLN",
|
||||
"PYG",
|
||||
"QAR",
|
||||
"RON",
|
||||
"RSD",
|
||||
"RUB",
|
||||
"SAR",
|
||||
"SCR",
|
||||
"SEK",
|
||||
"SGD",
|
||||
"SLL",
|
||||
"SVC",
|
||||
"THB",
|
||||
"TND",
|
||||
"TRY",
|
||||
"TTD",
|
||||
"TWD",
|
||||
"TZS",
|
||||
"UAH",
|
||||
"UGX",
|
||||
"USD",
|
||||
"UYU",
|
||||
"UZS",
|
||||
"VND",
|
||||
"YER",
|
||||
"ZAR",
|
||||
"ZMW",
|
||||
"ZWL"
|
||||
]
|
||||
}
|
||||
@@ -34,6 +34,39 @@
|
||||
],
|
||||
"dynamicLogic": {
|
||||
"fields": {
|
||||
"ldapHost": {
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "equals",
|
||||
"attribute": "authenticationMethod",
|
||||
"value": "LDAP"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapUserNameAttribute": {
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "equals",
|
||||
"attribute": "authenticationMethod",
|
||||
"value": "LDAP"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapUserObjectClass": {
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "equals",
|
||||
"attribute": "authenticationMethod",
|
||||
"value": "LDAP"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapUsername": {
|
||||
"visible": {
|
||||
"conditionGroup": [
|
||||
@@ -42,6 +75,14 @@
|
||||
"attribute": "ldapAuth"
|
||||
}
|
||||
]
|
||||
},
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "ldapAuth"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapPassword": {
|
||||
@@ -94,6 +135,14 @@
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
},
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapUserFirstNameAttribute": {
|
||||
@@ -104,6 +153,14 @@
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
},
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapUserLastNameAttribute": {
|
||||
@@ -114,6 +171,14 @@
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
},
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapUserEmailAddressAttribute": {
|
||||
@@ -124,6 +189,14 @@
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
},
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapUserPhoneNumberAttribute": {
|
||||
@@ -134,6 +207,14 @@
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
},
|
||||
"required": {
|
||||
"conditionGroup": [
|
||||
{
|
||||
"type": "isTrue",
|
||||
"attribute": "ldapCreateEspoUser"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ldapUserTeams": {
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
"currencyList": {
|
||||
"type": "multiEnum",
|
||||
"default": ["USD", "EUR"],
|
||||
"options": ["AED","ANG","ARS","AUD","BAM", "BGN","BHD","BND","BOB","BRL","BWP","CAD","CHF","CLP","CNY","COP","CRC","CZK","DKK","DOP","DZD","EEK","EGP","EUR","FJD","GBP","GNF","HKD","HNL","HRK","HUF","IDR","ILS","INR","IRR","JMD","JOD","JPY","KES","KRW","KWD","KYD","KZT","LBP","LKR","LTL","LVL","MAD","MDL","MKD","MMK","MUR","MXN","MYR","NAD","NGN","NIO","NOK","NPR","NZD","OMR","PEN","PGK","PHP","PKR","PLN","PYG","QAR","RON","RSD","RUB","SAR","SCR","SEK","SGD","SKK","SLL","SVC","THB","TND","TRY","TTD","TWD","TZS","UAH","UGX","USD","UYU","UZS","VND","YER","ZAR","ZMK"],
|
||||
"required": true
|
||||
"required": true,
|
||||
"view": "views/settings/fields/currency-list"
|
||||
},
|
||||
"defaultCurrency": {
|
||||
"type": "enum",
|
||||
@@ -101,12 +101,10 @@
|
||||
"type": "int",
|
||||
"min": 0,
|
||||
"max": 9999,
|
||||
"required": true,
|
||||
"default": 587
|
||||
},
|
||||
"smtpAuth": {
|
||||
"type": "bool",
|
||||
"default": true
|
||||
"type": "bool"
|
||||
},
|
||||
"smtpSecurity": {
|
||||
"type": "enum",
|
||||
@@ -114,8 +112,7 @@
|
||||
"options": ["", "SSL", "TLS"]
|
||||
},
|
||||
"smtpUsername": {
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
"type": "varchar"
|
||||
},
|
||||
"smtpPassword": {
|
||||
"type": "password"
|
||||
@@ -193,8 +190,7 @@
|
||||
"type": "bool"
|
||||
},
|
||||
"ldapHost": {
|
||||
"type": "varchar",
|
||||
"required": true
|
||||
"type": "varchar"
|
||||
},
|
||||
"ldapPort": {
|
||||
"type": "varchar",
|
||||
@@ -210,7 +206,6 @@
|
||||
},
|
||||
"ldapUsername": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapPassword": {
|
||||
@@ -265,37 +260,30 @@
|
||||
},
|
||||
"ldapUserNameAttribute": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserObjectClass": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserFirstNameAttribute": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserLastNameAttribute": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserTitleAttribute": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserEmailAddressAttribute": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserPhoneNumberAttribute": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"tooltip": true
|
||||
},
|
||||
"ldapUserDefaultTeam": {
|
||||
|
||||
@@ -44,9 +44,13 @@
|
||||
}
|
||||
],
|
||||
"validationList": [
|
||||
"array",
|
||||
"required",
|
||||
"maxCount"
|
||||
],
|
||||
"mandatoryValidationList": [
|
||||
"array"
|
||||
],
|
||||
"filter": true,
|
||||
"notCreatable": false,
|
||||
"notSortable": true,
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
"type":"bool"
|
||||
}
|
||||
],
|
||||
"validationList": [
|
||||
"array"
|
||||
],
|
||||
"mandatoryValidationList": [
|
||||
"array"
|
||||
],
|
||||
"filter": true,
|
||||
"notCreatable": true,
|
||||
"fieldDefs":{
|
||||
|
||||
@@ -36,9 +36,13 @@
|
||||
}
|
||||
],
|
||||
"validationList": [
|
||||
"array",
|
||||
"required",
|
||||
"maxCount"
|
||||
],
|
||||
"mandatoryValidationList": [
|
||||
"array"
|
||||
],
|
||||
"filter": true,
|
||||
"notCreatable": false,
|
||||
"notSortable": true,
|
||||
|
||||
@@ -51,9 +51,13 @@
|
||||
}
|
||||
],
|
||||
"validationList": [
|
||||
"array",
|
||||
"required",
|
||||
"maxCount"
|
||||
],
|
||||
"mandatoryValidationList": [
|
||||
"array"
|
||||
],
|
||||
"filter": true,
|
||||
"notCreatable": false,
|
||||
"notSortable": true,
|
||||
|
||||
@@ -125,6 +125,16 @@ class User extends \Espo\Core\SelectManagers\Base
|
||||
|
||||
protected function accessOnlyOwn(&$result)
|
||||
{
|
||||
if ($this->getAcl()->get('portalPermission') == 'yes') {
|
||||
$result['whereClause'][] = [
|
||||
'OR' => [
|
||||
'id' => $this->getUser()->id,
|
||||
'type' => 'portal',
|
||||
],
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
$result['whereClause'][] = [
|
||||
'id' => $this->getUser()->id
|
||||
];
|
||||
@@ -141,11 +151,18 @@ class User extends \Espo\Core\SelectManagers\Base
|
||||
{
|
||||
$this->setDistinct(true, $result);
|
||||
$this->addLeftJoin(['teams', 'teamsAccess'], $result);
|
||||
|
||||
$or = [
|
||||
'teamsAccess.id' => $this->getUser()->getLinkMultipleIdList('teams'),
|
||||
'id' => $this->getUser()->id,
|
||||
];
|
||||
|
||||
if ($this->getAcl()->get('portalPermission') == 'yes') {
|
||||
$or['type'] = 'portal';
|
||||
}
|
||||
|
||||
$result['whereClause'][] = [
|
||||
'OR' => [
|
||||
'teamsAccess.id' => $this->getUser()->getLinkMultipleIdList('teams'),
|
||||
'id' => $this->getUser()->id
|
||||
]
|
||||
'OR' => $or,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,8 +198,8 @@ class Email extends Record
|
||||
}
|
||||
|
||||
if ($smtpParams) {
|
||||
if ($emailAddress) {
|
||||
$this->applySmtpHandler($this->getUser()->id, $emailAddress, $smtpParams);
|
||||
if ($fromAddress) {
|
||||
$this->applySmtpHandler($this->getUser()->id, $fromAddress, $smtpParams);
|
||||
}
|
||||
$emailSender->useSmtp($smtpParams);
|
||||
}
|
||||
@@ -380,6 +380,12 @@ class Email extends Record
|
||||
}
|
||||
|
||||
$this->loadAdditionalFields($entity);
|
||||
|
||||
if (!isset($data->from) && !isset($data->to) && !isset($data->cc)) {
|
||||
$entity->clear('nameHash');
|
||||
$entity->clear('idHash');
|
||||
$entity->clear('typeHash');
|
||||
}
|
||||
}
|
||||
|
||||
public function loadFromField(Entity $entity)
|
||||
|
||||
@@ -550,7 +550,7 @@ class InboundEmail extends \Espo\Services\Record
|
||||
$className = '\\Espo\\Modules\\Crm\\Business\\Distribution\\CaseObj\\LeastBusy';
|
||||
}
|
||||
|
||||
$distribution = new $className($this->getEntityManager());
|
||||
$distribution = new $className($this->getEntityManager(), $this->getMetadata());
|
||||
|
||||
$user = $distribution->getUser($team, $targetUserPosition);
|
||||
|
||||
|
||||
@@ -60,7 +60,9 @@ class Layout extends \Espo\Core\Services\Base
|
||||
if ($name === 'relationships') {
|
||||
$data = json_decode($dataString);
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $i => $link) {
|
||||
foreach ($data as $i => $item) {
|
||||
$link = $item;
|
||||
if (is_object($item)) $link = $item->name ?? null;
|
||||
$foreignEntityType = $this->getMetadata()->get(['entityDefs', $scope, 'links', $link, 'entity']);
|
||||
if ($foreignEntityType) {
|
||||
if (!$this->getAcl()->check($foreignEntityType)) {
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
|
||||
namespace Espo\Services;
|
||||
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
@@ -190,6 +191,10 @@ class Settings extends \Espo\Core\Services\Base
|
||||
unset($data->$item);
|
||||
}
|
||||
|
||||
$entity = $this->getEntityManager()->getEntity('Settings');
|
||||
$entity->set($data);
|
||||
$this->processValidation($entity, $data);
|
||||
|
||||
if (
|
||||
(isset($data->useCache) && $data->useCache !== $this->getConfig()->get('useCache'))
|
||||
||
|
||||
@@ -297,4 +302,44 @@ class Settings extends \Espo\Core\Services\Base
|
||||
|
||||
return $itemList;
|
||||
}
|
||||
|
||||
protected function processValidation(Entity $entity, $data)
|
||||
{
|
||||
$fieldList = $this->getFieldManagerUtil()->getEntityTypeFieldList('Settings');
|
||||
|
||||
foreach ($fieldList as $field) {
|
||||
if (!$this->isFieldSetInData($data, $field)) continue;
|
||||
$this->processValidationField($entity, $field, $data);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processValidationField(Entity $entity, string $field, $data)
|
||||
{
|
||||
$fieldType = $this->getFieldManagerUtil()->getEntityTypeFieldParam('Settings', $field, 'type');
|
||||
$validationList = $this->getMetadata()->get(['fields', $fieldType, 'validationList'], []);
|
||||
$mandatoryValidationList = $this->getMetadata()->get(['fields', $fieldType, 'mandatoryValidationList'], []);
|
||||
$fieldValidatorManager = $this->getInjection('container')->get('fieldValidatorManager');
|
||||
|
||||
foreach ($validationList as $type) {
|
||||
$value = $this->getFieldManagerUtil()->getEntityTypeFieldParam('Settings', $field, $type);
|
||||
if (is_null($value) && !in_array($type, $mandatoryValidationList)) continue;
|
||||
|
||||
if (!$fieldValidatorManager->check($entity, $field, $type, $data)) {
|
||||
throw new BadRequest("Not valid data. Field: '{$field}', type: {$type}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function isFieldSetInData($data, $field)
|
||||
{
|
||||
$attributeList = $this->getFieldManagerUtil()->getActualAttributeList('Settings', $field);
|
||||
$isSet = false;
|
||||
foreach ($attributeList as $attribute) {
|
||||
if (property_exists($data, $attribute)) {
|
||||
$isSet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $isSet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +276,16 @@ class Stream extends \Espo\Core\Services\Base
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = $this->getEntityManager()->getRepository('User')
|
||||
->select(['id', 'type', 'isActive'])
|
||||
->where([
|
||||
'id' => $userId,
|
||||
'isActive' => true,
|
||||
])->findOne();
|
||||
|
||||
if (!$user) return false;
|
||||
if (!$this->getAclManager()->check($user, $entity, 'stream')) return false;
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
if (!$this->checkIsFollowed($entity, $userId)) {
|
||||
@@ -1311,6 +1321,39 @@ class Stream extends \Espo\Core\Services\Base
|
||||
$this->getEntityManager()->saveEntity($note, $o);
|
||||
}
|
||||
|
||||
public function noteRelate(Entity $entity, $parentType, $parentId, array $options = [])
|
||||
{
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
$existing = $this->getEntityManager()->getRepository('Note')->select(['id'])->where([
|
||||
'type' => 'Relate',
|
||||
'parentId' => $parentId,
|
||||
'parentType' => $parentType,
|
||||
'relatedId' => $entity->id,
|
||||
'relatedType' => $entityType,
|
||||
])->findOne();
|
||||
if ($existing) return false;
|
||||
|
||||
$note = $this->getEntityManager()->getEntity('Note');
|
||||
|
||||
$note->set([
|
||||
'type' => 'Relate',
|
||||
'parentId' => $parentId,
|
||||
'parentType' => $parentType,
|
||||
'relatedType' => $entityType,
|
||||
'relatedId' => $entity->id,
|
||||
]);
|
||||
|
||||
$this->processNoteTeamsUsers($note, $entity);
|
||||
|
||||
$o = [];
|
||||
if (!empty($options['createdById'])) {
|
||||
$o['createdById'] = $options['createdById'];
|
||||
}
|
||||
|
||||
$this->getEntityManager()->saveEntity($note, $o);
|
||||
}
|
||||
|
||||
public function noteAssign(Entity $entity, array $options = [])
|
||||
{
|
||||
$note = $this->getEntityManager()->getEntity('Note');
|
||||
|
||||
@@ -45,6 +45,7 @@ class UserSecurity extends \Espo\Core\Services\Base
|
||||
$this->addDependency('metadata');
|
||||
$this->addDependency('totp');
|
||||
$this->addDependency('config');
|
||||
$this->addDependency('container');
|
||||
}
|
||||
|
||||
protected function getUser()
|
||||
@@ -87,7 +88,7 @@ class UserSecurity extends \Espo\Core\Services\Base
|
||||
if (!$password) throw new Forbidden('Passport required.');
|
||||
|
||||
if (!$this->getUser()->isAdmin() || $this->getUser()->id === $id) {
|
||||
$this->checkPassport($id, $password);
|
||||
$this->checkPassword($id, $password);
|
||||
}
|
||||
|
||||
$userData = $this->getEntityManager()->getRepository('UserData')->getByUserId($id);
|
||||
@@ -136,7 +137,7 @@ class UserSecurity extends \Espo\Core\Services\Base
|
||||
if (!$password) throw new Forbidden('Passport required.');
|
||||
|
||||
if (!$this->getUser()->isAdmin() || $this->getUser()->id === $id) {
|
||||
$this->checkPassport($id, $password);
|
||||
$this->checkPassword($id, $password);
|
||||
}
|
||||
|
||||
foreach (get_object_vars($data) as $attribute => $v) {
|
||||
@@ -222,15 +223,21 @@ class UserSecurity extends \Espo\Core\Services\Base
|
||||
];
|
||||
}
|
||||
|
||||
protected function checkPassport(string $id, string $password)
|
||||
protected function checkPassword(string $id, string $password)
|
||||
{
|
||||
$passwordHash = new \Espo\Core\Utils\PasswordHash($this->getConfig());
|
||||
$method = $this->getConfig()->get('authenticationMethod', 'Espo');
|
||||
|
||||
$auth = $this->getInjection('container')->get('authenticationFactory')->create($method);
|
||||
|
||||
$user = $this->getEntityManager()->getRepository('User')->where([
|
||||
'id' => $id,
|
||||
'password' => $passwordHash->hash($password),
|
||||
])->findOne();
|
||||
if (!$user) {
|
||||
throw new Forbidden('Passport is incorrect.');
|
||||
|
||||
if (!$user) throw new Forbidden('User is not found.');
|
||||
|
||||
if (!$auth->login($user->get('userName'), $password)) {
|
||||
throw new Forbidden('Password is incorrect.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-xs-6">
|
||||
<div class="col-sm-4 col-xs-6">
|
||||
<select class="form-control" data-name="entityType"></select>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-6">
|
||||
<div class="col-sm-5 col-xs-6">
|
||||
<select class="form-control" data-name="field"></select>
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-6">
|
||||
<div class="col-sm-3 col-xs-6">
|
||||
<button class="btn btn-default" type="button" data-action="insert">{{translate 'Insert'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,13 +26,23 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('acl/user', 'acl', function (Dep) {
|
||||
define('acl/user', 'acl', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
checkModelRead: function (model, data, precise) {
|
||||
if (model.isPortal()) {
|
||||
if (this.get('portalPermission') === 'yes') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Dep.prototype.checkModelRead.call(this, model, data, precise);
|
||||
},
|
||||
|
||||
checkIsOwner: function (model) {
|
||||
return this.getUser().id === model.id;
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -313,7 +313,7 @@ define(
|
||||
controller.doAction(params.action, params.options);
|
||||
this.trigger('action:done');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.error(e);
|
||||
switch (e.name) {
|
||||
case 'AccessDenied':
|
||||
this.baseController.error403();
|
||||
|
||||
@@ -57,7 +57,15 @@ define('controllers/portal-user', 'controllers/record', function (Dep) {
|
||||
options.attributes = options.attributes || {};
|
||||
options.attributes.type = 'portal';
|
||||
Dep.prototype.actionCreate.call(this, options);
|
||||
}
|
||||
},
|
||||
|
||||
checkAccess: function (action) {
|
||||
|
||||
if (this.getAcl().get('portalPermission') === 'yes')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -160,9 +160,11 @@ define('ui', [], function () {
|
||||
diffHeight = diffHeight + options.bodyDiffHeight;
|
||||
}
|
||||
|
||||
var h = $window.height();
|
||||
|
||||
if (this.fitHeight || options.fullHeight) {
|
||||
var processResize = function () {
|
||||
var windowHeight = $window.height();
|
||||
var windowHeight = window.innerHeight;
|
||||
var windowWidth = $window.width();
|
||||
|
||||
if (!options.fullHeight && windowHeight < 512) {
|
||||
@@ -178,6 +180,7 @@ define('ui', [], function () {
|
||||
};
|
||||
if (options.fullHeight) {
|
||||
cssParams.height = (windowHeight - diffHeight) + 'px';
|
||||
|
||||
this.$el.css('paddingRight', 0);
|
||||
} else {
|
||||
if (windowWidth <= options.screenWidthXs) {
|
||||
@@ -337,6 +340,7 @@ define('ui', [], function () {
|
||||
this.onRemove();
|
||||
this.$el.remove();
|
||||
$(this).off();
|
||||
$(window).off('resize.modal-height');
|
||||
};
|
||||
|
||||
var Ui = Espo.Ui = Espo.ui = {
|
||||
|
||||
@@ -289,7 +289,10 @@ define('view-helper', ['lib!client/lib/purify.min.js'], function () {
|
||||
|
||||
for (var key in list) {
|
||||
var keyVal = list[key];
|
||||
html += "<option value=\"" + keyVal + "\" " + (checkOption(list[key]) ? 'selected' : '') + ">" + translate(list[key]) + "</option>"
|
||||
var label = translate(list[key]);
|
||||
keyVal = self.escapeString(keyVal);
|
||||
label = self.escapeString(label);
|
||||
html += "<option value=\"" + keyVal + "\" " + (checkOption(list[key]) ? 'selected' : '') + ">" + label + "</option>"
|
||||
}
|
||||
return new Handlebars.SafeString(html);
|
||||
});
|
||||
|
||||
@@ -46,6 +46,18 @@ define('views/admin/outbound-emails', 'views/settings/record/edit', function (De
|
||||
attribute: 'smtpAuth',
|
||||
}
|
||||
]
|
||||
},
|
||||
required: {
|
||||
conditionGroup: [
|
||||
{
|
||||
type: 'isNotEmpty',
|
||||
attribute: 'smtpServer',
|
||||
},
|
||||
{
|
||||
type: 'isTrue',
|
||||
attribute: 'smtpAuth',
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
smtpPassword: {
|
||||
@@ -70,6 +82,14 @@ define('views/admin/outbound-emails', 'views/settings/record/edit', function (De
|
||||
attribute: 'smtpServer',
|
||||
},
|
||||
]
|
||||
},
|
||||
required: {
|
||||
conditionGroup: [
|
||||
{
|
||||
type: 'isNotEmpty',
|
||||
attribute: 'smtpServer',
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
smtpSecurity: {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/email-template/record/detail', 'views/record/detail', function (Dep) {
|
||||
define('views/email-template/record/detail', 'views/record/detail', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -35,6 +35,17 @@ Espo.define('views/email-template/record/detail', 'views/record/detail', functio
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
this.listenToInsertField();
|
||||
|
||||
|
||||
this.hideField('insertField');
|
||||
|
||||
this.on('before:set-edit-mode', function () {
|
||||
this.showField('insertField');
|
||||
}, this);
|
||||
|
||||
this.on('before:set-detail-mode', function () {
|
||||
this.hideField('insertField');
|
||||
}, this);
|
||||
},
|
||||
|
||||
listenToInsertField: function () {
|
||||
|
||||
@@ -32,9 +32,18 @@ Espo.define('views/email/fields/compose-from-address', 'views/fields/base', func
|
||||
editTemplate: 'email/fields/compose-from-address/edit',
|
||||
|
||||
data: function () {
|
||||
var noSmtpMessage = this.translate('noSmtpSetup', 'messages', 'Email');
|
||||
|
||||
var linkHtml = '<a href="#EmailAccount">'+this.translate('EmailAccount', 'scopeNamesPlural')+'</a>';
|
||||
if (!this.getAcl().check('EmailAccount')) {
|
||||
linkHtml = '<a href="#Preferences">'+this.translate('Preferences')+'</a>';
|
||||
}
|
||||
|
||||
noSmtpMessage = noSmtpMessage.replace('{link}', linkHtml);
|
||||
|
||||
return _.extend({
|
||||
list: this.list,
|
||||
noSmtpMessage: this.translate('noSmtpSetup', 'messages', 'Email').replace('{link}', '<a href="#Preferences">'+this.translate('Preferences')+'</a>')
|
||||
noSmtpMessage: noSmtpMessage,
|
||||
}, Dep.prototype.data.call(this));
|
||||
},
|
||||
|
||||
|
||||
@@ -58,9 +58,8 @@ define('views/fields/link-multiple-with-role', 'views/fields/link-multiple', fun
|
||||
this.roleFieldScope = this.model.name;
|
||||
}
|
||||
|
||||
if (this.roleType == 'enum') {
|
||||
if (this.roleType == 'enum' && !this.forceRoles) {
|
||||
this.roleList = this.getMetadata().get('entityDefs.' + this.roleFieldScope + '.fields.' + this.roleField + '.options');
|
||||
|
||||
if (!this.roleList) {
|
||||
this.roleList = [];
|
||||
this.skipRoles = true;
|
||||
|
||||
@@ -490,6 +490,12 @@ define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], function
|
||||
if (code == '<p><br></p>') {
|
||||
code = '';
|
||||
}
|
||||
|
||||
var imageTagString = '<img src="' + window.location.origin + window.location.pathname + '?entryPoint=attachment';
|
||||
code = code.replace(
|
||||
new RegExp(imageTagString.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), 'g'),
|
||||
'<img src="?entryPoint=attachment'
|
||||
);
|
||||
data[this.name] = code;
|
||||
} else {
|
||||
data[this.name] = this.$element.val();
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -124,6 +124,21 @@ Espo.define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
}
|
||||
},
|
||||
|
||||
hasTextFilter: function () {
|
||||
if (this.collection.where) {
|
||||
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;
|
||||
},
|
||||
|
||||
hasIsExpandedStoredValue: function () {
|
||||
return this.getStorage().has('state', 'categories-expanded-' + this.scope);
|
||||
},
|
||||
@@ -263,7 +278,12 @@ Espo.define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
this.$listContainer.removeClass('hidden');
|
||||
return;
|
||||
}
|
||||
if (!this.collection.models.length && this.nestedCategoriesCollection && this.nestedCategoriesCollection.models.length) {
|
||||
if (
|
||||
!this.collection.models.length &&
|
||||
this.nestedCategoriesCollection &&
|
||||
this.nestedCategoriesCollection.models.length &&
|
||||
!this.hasTextFilter()
|
||||
) {
|
||||
this.$listContainer.addClass('hidden');
|
||||
} else {
|
||||
this.$listContainer.removeClass('hidden');
|
||||
@@ -396,27 +416,14 @@ Espo.define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
applyCategoryToCollection: function () {
|
||||
|
||||
this.collection.whereFunction = function () {
|
||||
var filter;
|
||||
var isExpanded = this.isExpanded;
|
||||
|
||||
var hasTextFilter = false;
|
||||
if (this.collection.where) {
|
||||
for (var i = 0; i < this.collection.where.length; i++) {
|
||||
if (this.collection.where[i].type === 'textFilter') {
|
||||
hasTextFilter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.collection.data && this.collection.data.textFilter) {
|
||||
hasTextFilter = true;
|
||||
}
|
||||
|
||||
if (!isExpanded && !hasTextFilter) {
|
||||
if (!isExpanded && !this.hasTextFilter()) {
|
||||
if (this.isCategoryMultiple()) {
|
||||
if (this.currentCategoryId) {
|
||||
filter = {
|
||||
@@ -491,7 +498,7 @@ Espo.define('views/list-with-categories', 'views/list', function (Dep) {
|
||||
actionManageCategories: function () {
|
||||
this.clearView('categories');
|
||||
this.getRouter().navigate('#' + this.categoryScope, {trigger: true});
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
49
client/src/views/settings/fields/currency-list.js
Normal file
49
client/src/views/settings/fields/currency-list.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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.
|
||||
************************************************************************/
|
||||
|
||||
define('views/settings/fields/currency-list', 'views/fields/multi-enum', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setupOptions: function () {
|
||||
this.params.options = this.getMetadata().get(['app', 'currency', 'list']) || [];
|
||||
|
||||
this.translatedOptions = {};
|
||||
|
||||
this.params.options.forEach(function (item) {
|
||||
var value = item
|
||||
var name = this.getLanguage().get('Currency', 'names', item);
|
||||
if (name) {
|
||||
value += ' - ' + name;
|
||||
}
|
||||
this.translatedOptions[item] = value;
|
||||
}, this);
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
@@ -42,7 +42,7 @@ define('views/stream/panel', ['views/record/panels/relationship', 'lib!Textcompl
|
||||
|
||||
events: _.extend({
|
||||
'focus textarea[data-name="post"]': function (e) {
|
||||
this.enablePostingMode();
|
||||
this.enablePostingMode(true);
|
||||
},
|
||||
'click button.post': function () {
|
||||
this.post();
|
||||
@@ -79,14 +79,19 @@ define('views/stream/panel', ['views/record/panels/relationship', 'lib!Textcompl
|
||||
return data;
|
||||
},
|
||||
|
||||
enablePostingMode: function () {
|
||||
enablePostingMode: function (byFocus) {
|
||||
this.$el.find('.buttons-panel').removeClass('hide');
|
||||
|
||||
if (!this.postingMode) {
|
||||
if (this.$textarea.val() && this.$textarea.val().length) {
|
||||
this.getView('postField').controlTextareaHeight();
|
||||
}
|
||||
var isClicked = false;
|
||||
$('body').on('click.stream-panel', function (e) {
|
||||
if (byFocus && !isClicked) {
|
||||
isClicked = true;
|
||||
return;
|
||||
}
|
||||
var $target = $(e.target);
|
||||
if ($target.parent().hasClass('remove-attachment')) return;
|
||||
if ($.contains(this.$postContainer.get(0), e.target)) return;
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/user/fields/teams', 'views/fields/link-multiple-with-role', function (Dep) {
|
||||
define('views/user/fields/teams', 'views/fields/link-multiple-with-role', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
forceRoles: true,
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
@@ -43,7 +45,6 @@ Espo.define('views/user/fields/teams', 'views/fields/link-multiple-with-role', f
|
||||
}
|
||||
}, this);
|
||||
|
||||
|
||||
this.listenTo(this.model, 'change:teamsIds', function () {
|
||||
var toLoad = false;
|
||||
this.ids.forEach(function (id) {
|
||||
@@ -85,7 +86,6 @@ Espo.define('views/user/fields/teams', 'views/fields/link-multiple-with-role', f
|
||||
|
||||
teams.fetch();
|
||||
}, this);
|
||||
|
||||
},
|
||||
|
||||
getDetailLinkHtml: function (id, name) {
|
||||
@@ -94,6 +94,7 @@ Espo.define('views/user/fields/teams', 'views/fields/link-multiple-with-role', f
|
||||
var role = (this.columns[id] || {})[this.columnName] || '';
|
||||
var roleHtml = '';
|
||||
if (role != '') {
|
||||
role = this.getHelper().escapeString(role);
|
||||
roleHtml = '<span class="text-muted small"> » ' + role + '</span>';
|
||||
}
|
||||
var lineHtml = '<div>' + '<a href="#' + this.foreignScope + '/view/' + id + '">' + name + '</a> ' + roleHtml + '</div>';
|
||||
@@ -101,21 +102,18 @@ Espo.define('views/user/fields/teams', 'views/fields/link-multiple-with-role', f
|
||||
},
|
||||
|
||||
getJQSelect: function (id, roleValue) {
|
||||
|
||||
|
||||
var roleList = Espo.Utils.clone((this.roleListMap[id] || []));
|
||||
|
||||
if (!roleList.length) {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
if (roleList.length || roleValue) {
|
||||
$role = $('<select class="role form-control input-sm pull-right" data-id="'+id+'">');
|
||||
|
||||
roleList.unshift('');
|
||||
roleList.forEach(function (role) {
|
||||
var selectedHtml = (role == roleValue) ? 'selected': '';
|
||||
role = this.getHelper().escapeString(role);
|
||||
var label = role;
|
||||
if (role == '') {
|
||||
label = '--' + this.translate('None', 'labels') + '--';
|
||||
@@ -127,8 +125,6 @@ Espo.define('views/user/fields/teams', 'views/fields/link-multiple-with-role', f
|
||||
} else {
|
||||
return $('<div class="small pull-right text-muted">').html(roleValue);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
478
diff.js
478
diff.js
@@ -25,153 +25,381 @@
|
||||
* 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.
|
||||
************************************************************************/
|
||||
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var versionFrom = process.argv[2];
|
||||
|
||||
if (process.argv.length < 3) {
|
||||
throw new Error("No 'version from' passed");
|
||||
}
|
||||
|
||||
var acceptedVersionName = versionFrom;
|
||||
|
||||
var isDev = false;
|
||||
var isAll = false;
|
||||
var withVendor = false;
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
if (process.argv.length > 1) {
|
||||
for (var i in process.argv) {
|
||||
if (process.argv[i] === '--dev') {
|
||||
isDev = true;
|
||||
}
|
||||
if (process.argv[i] === '--all') {
|
||||
isAll = true;
|
||||
}
|
||||
if (process.argv[i] === '--vendor') {
|
||||
withVendor = true;
|
||||
}
|
||||
if (~process.argv[i].indexOf('--acceptedVersion=')) {
|
||||
acceptedVersionName = process.argv[i].substr(('--acceptedVersion=').length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var sys = require('util')
|
||||
|
||||
var version = (require('./package.json') || {}).version;
|
||||
if (isAll) {
|
||||
var version = (require('./package.json') || {}).version;
|
||||
|
||||
var currentPath = path.dirname(fs.realpathSync(__filename));
|
||||
execute('git tag -l --sort=-v:refname # reverse', function (tagsString) {
|
||||
var tagList = tagsString.trim().split("\n");
|
||||
var versionFromList = [];
|
||||
|
||||
var buildRelPath = 'build/EspoCRM-' + version;
|
||||
var buildPath = currentPath + '/' + buildRelPath;
|
||||
var diffFilePath = currentPath + '/build/diff';
|
||||
var upgradePath = currentPath + '/build/EspoCRM-upgrade-' + acceptedVersionName + '-to-' + version;
|
||||
var minorVersionNumber = version.split('.')[1];
|
||||
var hotfixVersionNumber = version.split('.')[2];
|
||||
|
||||
var exec = require('child_process').exec;
|
||||
for (var i = 0; i < tagList.length; i++) {
|
||||
var tag = tagList[i];
|
||||
if (!~tag.indexOf('beta') && !~tag.indexOf('alpha')) {
|
||||
var minorVersionNumberI = tag.split('.')[1];
|
||||
if (minorVersionNumberI !== minorVersionNumber) {
|
||||
versionFromList.push(tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hotfixVersionNumber !== '0') {
|
||||
for (var i = 0; i < tagList.length; i++) {
|
||||
var tag = tagList[i];
|
||||
if (!~tag.indexOf('beta') && !~tag.indexOf('alpha')) {
|
||||
versionFromList.push(tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function buildMultiple () {
|
||||
for (const versionFrom of versionFromList) {
|
||||
await buildUpgradePackage(versionFrom, {
|
||||
isDev: isDev,
|
||||
withVendor: withVendor,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buildMultiple();
|
||||
});
|
||||
|
||||
} else {
|
||||
if (process.argv.length < 3) {
|
||||
throw new Error("No 'version' specified.");
|
||||
}
|
||||
buildUpgradePackage(versionFrom, {
|
||||
acceptedVersionName: acceptedVersionName,
|
||||
isDev: isDev,
|
||||
withVendor: withVendor,
|
||||
});
|
||||
}
|
||||
|
||||
function buildUpgradePackage(versionFrom, params)
|
||||
{
|
||||
return new Promise(function (resolve) {
|
||||
var acceptedVersionName = params.acceptedVersionName || versionFrom;
|
||||
var isDev = params.isDev;
|
||||
var withVendor = params.withVendor;
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var sys = require('util');
|
||||
var cp = require('child_process');
|
||||
var archiver = require('archiver');
|
||||
|
||||
var version = (require('./package.json') || {}).version;
|
||||
|
||||
var composerData = require('./composer.json') || {};
|
||||
|
||||
var currentPath = path.dirname(fs.realpathSync(__filename));
|
||||
|
||||
var buildRelPath = 'build/EspoCRM-' + version;
|
||||
var buildPath = currentPath + '/' + buildRelPath;
|
||||
var diffFilePath = currentPath + '/build/diff';
|
||||
var diffBeforeUpgradeFolderPath = currentPath + '/build/diffBeforeUpgrade';
|
||||
|
||||
var tempFolderPath = currentPath + '/build/upgradeTmp';
|
||||
|
||||
var folderName = 'EspoCRM-upgrade-' + acceptedVersionName + '-to-' + version;
|
||||
|
||||
var upgradePath = currentPath + '/build/' + folderName;
|
||||
|
||||
var zipPath = currentPath + '/build/' + folderName + '.zip';
|
||||
|
||||
var upgradeDataFolder = versionFrom + '-' + version;
|
||||
var isMinorVersion = false;
|
||||
if (versionFrom.split('.')[1] !== version.split('.')[1] || versionFrom.split('.')[0] !== version.split('.')[0]) {
|
||||
isMinorVersion = true;
|
||||
upgradeDataFolder = version.split('.')[0] + '.' + version.split('.')[1];
|
||||
}
|
||||
var upgradeDataFolderPath = currentPath + '/upgrades/' + upgradeDataFolder;
|
||||
var upgradeFolderExists = fs.existsSync(upgradeDataFolderPath);
|
||||
|
||||
var upgradeData = {};
|
||||
if (upgradeFolderExists) {
|
||||
upgradeData = require(upgradeDataFolderPath + '/data.json') || {};
|
||||
}
|
||||
|
||||
var beforeUpgradeFileList = upgradeData.beforeUpgradeFiles || [];
|
||||
|
||||
var deleteDirRecursively = function (path) {
|
||||
if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) {
|
||||
fs.readdirSync(path).forEach(function(file, index) {
|
||||
var curPath = path + "/" + file;
|
||||
if (fs.lstatSync(curPath).isDirectory()) {
|
||||
deleteDirRecursively(curPath);
|
||||
} else {
|
||||
fs.unlinkSync(curPath);
|
||||
}
|
||||
});
|
||||
fs.rmdirSync(path);
|
||||
} else if (fs.existsSync(path) && fs.lstatSync(path).isFile()) {
|
||||
fs.unlinkSync(path);
|
||||
}
|
||||
};
|
||||
|
||||
deleteDirRecursively(diffFilePath);
|
||||
deleteDirRecursively(diffBeforeUpgradeFolderPath);
|
||||
deleteDirRecursively(upgradePath);
|
||||
deleteDirRecursively(tempFolderPath);
|
||||
|
||||
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
|
||||
|
||||
execute('git rev-parse --abbrev-ref HEAD', function (branch) {
|
||||
branch = branch.trim();
|
||||
if (branch !== 'master' && branch !== 'stable' && branch.indexOf('hotfix/') !== 0) {
|
||||
console.log('\x1b[33m%s\x1b[0m', "Warning! You are on " + branch + " branch.");
|
||||
}
|
||||
});
|
||||
|
||||
execute('git diff --name-only ' + versionFrom, function (stdout) {
|
||||
if (!fs.existsSync(buildPath)) {
|
||||
throw new Error("EspoCRM is not built. Need to run grunt before.");
|
||||
}
|
||||
|
||||
if (!fs.existsSync(upgradePath)) {
|
||||
fs.mkdirSync(upgradePath);
|
||||
}
|
||||
if (!fs.existsSync(upgradePath + '/files')) {
|
||||
fs.mkdirSync(upgradePath + '/files');
|
||||
}
|
||||
|
||||
if (beforeUpgradeFileList.length) {
|
||||
if (!fs.existsSync(upgradePath + '/beforeUpgradeFiles')) {
|
||||
fs.mkdirSync(upgradePath + '/beforeUpgradeFiles');
|
||||
}
|
||||
}
|
||||
|
||||
process.chdir(buildPath);
|
||||
|
||||
var fileList = [];
|
||||
|
||||
(stdout || '').split('\n').forEach(function (file) {
|
||||
if (file == '') {
|
||||
return;
|
||||
}
|
||||
fileList.push(file);
|
||||
});
|
||||
|
||||
fileList.push('client/espo.min.js');
|
||||
fileList.push('client/espo.min.js.map');
|
||||
|
||||
fs.readdirSync('client/css/espo/').forEach(function (file) {
|
||||
fileList.push('client/css/espo/' + file);
|
||||
});
|
||||
|
||||
fs.writeFileSync(diffFilePath, fileList.join('\n'));
|
||||
|
||||
if (beforeUpgradeFileList.length) {
|
||||
fs.writeFileSync(diffBeforeUpgradeFolderPath, beforeUpgradeFileList.join('\n'));
|
||||
}
|
||||
|
||||
execute('git diff --name-only --diff-filter=D ' + versionFrom, function (stdout) {
|
||||
var deletedFileList = [];
|
||||
|
||||
(stdout || '').split('\n').forEach(function (file) {
|
||||
if (file == '') {
|
||||
return;
|
||||
}
|
||||
deletedFileList.push(file);
|
||||
});
|
||||
|
||||
if (beforeUpgradeFileList.length) {
|
||||
cp.execSync('xargs -a ' + diffBeforeUpgradeFolderPath + ' cp -p --parents -t ' + upgradePath + '/beforeUpgradeFiles');
|
||||
}
|
||||
|
||||
if (!isDev) {
|
||||
if (fs.existsSync(upgradeDataFolderPath + '/scripts')) {
|
||||
cp.execSync('cp -r ' + upgradeDataFolderPath + '/scripts ' + upgradePath + '/scripts');
|
||||
}
|
||||
}
|
||||
|
||||
execute('xargs -a ' + diffFilePath + ' cp -p --parents -t ' + upgradePath + '/files' , function (stdout) {
|
||||
var d = new Date();
|
||||
|
||||
var monthN = ((d.getMonth() + 1).toString());
|
||||
monthN = monthN.length == 1 ? '0' + monthN : monthN;
|
||||
|
||||
var dateN = d.getDate().toString();
|
||||
dateN = dateN.length == 1 ? '0' + dateN : dateN;
|
||||
|
||||
var date = d.getFullYear().toString() + '-' + monthN + '-' + dateN.toString();
|
||||
|
||||
execute('git tag', function (stdout) {
|
||||
var versionList = [];
|
||||
tagList = stdout.split('\n').forEach(function (tag) {
|
||||
if (tag == versionFrom) {
|
||||
versionList.push(tag);
|
||||
}
|
||||
if (!tag || tag == version) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (isDev) {
|
||||
versionList = [];
|
||||
}
|
||||
|
||||
var name = acceptedVersionName+" to "+version;
|
||||
|
||||
var manifestData = {
|
||||
"name": "EspoCRM Upgrade "+name,
|
||||
"type": "upgrade",
|
||||
"version": version,
|
||||
"acceptableVersions": versionList,
|
||||
"php": [composerData.require.php],
|
||||
"releaseDate": date,
|
||||
"author": "EspoCRM",
|
||||
"description": "",
|
||||
"delete": deletedFileList,
|
||||
};
|
||||
|
||||
var additionalManifestData = upgradeData.manifest || {};
|
||||
for (var item in additionalManifestData) {
|
||||
manifestData[item] = additionalManifestData[item];
|
||||
}
|
||||
|
||||
fs.writeFileSync(upgradePath + '/manifest.json', JSON.stringify(manifestData, null, ' '));
|
||||
|
||||
if (fs.existsSync(diffFilePath)) {
|
||||
fs.unlinkSync(diffFilePath);
|
||||
}
|
||||
if (fs.existsSync(diffBeforeUpgradeFolderPath)) {
|
||||
fs.unlinkSync(diffBeforeUpgradeFolderPath);
|
||||
}
|
||||
|
||||
new Promise(function (resolve) {
|
||||
if (!withVendor) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var output = cp.execSync("git show "+versionFrom+" --format=%H").toString();
|
||||
var commitHash = output.split("\n")[3];
|
||||
if (!commitHash) throw new Error("Couldn't find commit hash.");
|
||||
var composerLockOldContents = cp.execSync("git show "+commitHash+":composer.lock").toString();
|
||||
var composerLockNewContents = cp.execSync("cat "+currentPath+"/composer.lock").toString();
|
||||
var composerNewContents = cp.execSync("cat "+currentPath+"/composer.json").toString();
|
||||
|
||||
if (composerLockNewContents === composerLockOldContents) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var newPackages = JSON.parse(composerLockNewContents).packages;
|
||||
var oldPackages = JSON.parse(composerLockOldContents).packages;
|
||||
|
||||
cp.execSync("mkdir "+tempFolderPath);
|
||||
cp.execSync("mkdir "+tempFolderPath + "/new");
|
||||
|
||||
var vendorPath = tempFolderPath + "/new/vendor/";
|
||||
|
||||
fs.writeFileSync(tempFolderPath + "/new/composer.lock", composerLockNewContents);
|
||||
fs.writeFileSync(tempFolderPath + "/new/composer.json", composerNewContents);
|
||||
|
||||
cp.execSync("composer install", {cwd: tempFolderPath + "/new", stdio: 'ignore'});
|
||||
|
||||
fs.mkdirSync(upgradePath + '/vendorFiles');
|
||||
|
||||
cp.execSync("mv "+vendorPath+"/autoload.php "+ upgradePath + "/vendorFiles/autoload.php");
|
||||
cp.execSync("mv "+vendorPath+"/composer "+ upgradePath + "/vendorFiles/composer");
|
||||
cp.execSync("mv "+vendorPath+"/bin "+ upgradePath + "/vendorFiles/bin");
|
||||
|
||||
var folderList = [];
|
||||
|
||||
for (var item of newPackages) {
|
||||
var name = item.name;
|
||||
if (name.indexOf('composer/') === 0) continue;
|
||||
|
||||
var isFound = false;
|
||||
var toAdd = false;
|
||||
|
||||
for (var oItem of oldPackages) {
|
||||
if (oItem.name !== name) continue;
|
||||
isFound = true;
|
||||
if (item.version !== oItem.version)
|
||||
toAdd = true;
|
||||
}
|
||||
|
||||
if (!isFound) {
|
||||
toAdd = true;
|
||||
}
|
||||
|
||||
if (toAdd) {
|
||||
var folder = name.split('/')[0];
|
||||
if (!~folderList.indexOf(folder))
|
||||
folderList.push(folder);
|
||||
}
|
||||
}
|
||||
|
||||
for (var folder of folderList) {
|
||||
if (fs.existsSync(vendorPath + '/'+ folder)) {
|
||||
cp.execSync("mv "+vendorPath + '/'+ folder+" "+ upgradePath + '/vendorFiles/' + folder);
|
||||
}
|
||||
}
|
||||
|
||||
deleteDirRecursively(tempFolderPath);
|
||||
|
||||
resolve();
|
||||
|
||||
}).then(function () {
|
||||
var zipOutput = fs.createWriteStream(zipPath);
|
||||
var archive = archiver('zip');
|
||||
archive.on('error', function (err) {
|
||||
throw err;
|
||||
});
|
||||
zipOutput.on('close', function () {
|
||||
console.log("Upgrade package has been built: "+name+"");
|
||||
deleteDirRecursively(upgradePath);
|
||||
resolve();
|
||||
});
|
||||
archive.directory(upgradePath, false).pipe(zipOutput);
|
||||
archive.finalize();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function execute(command, callback) {
|
||||
exec(command, function(error, stdout, stderr) {
|
||||
callback(stdout);
|
||||
});
|
||||
};
|
||||
|
||||
var deleteDirRecursively = function (path) {
|
||||
if (fs.existsSync(path)) {
|
||||
fs.readdirSync(path).forEach(function(file, index) {
|
||||
var curPath = path + "/" + file;
|
||||
if (fs.lstatSync(curPath).isDirectory()) {
|
||||
deleteDirRecursively(curPath);
|
||||
} else {
|
||||
fs.unlinkSync(curPath);
|
||||
}
|
||||
});
|
||||
fs.rmdirSync(path);
|
||||
}
|
||||
};
|
||||
|
||||
deleteDirRecursively(diffFilePath);
|
||||
deleteDirRecursively(upgradePath);
|
||||
|
||||
execute('git rev-parse --abbrev-ref HEAD', function (branch) {
|
||||
branch = branch.trim();
|
||||
if (branch !== 'master' && branch !== 'stable' && branch.indexOf('hotfix/') !== 0) {
|
||||
console.log('\x1b[33m%s\x1b[0m', "Warning! You are on " + branch + " branch.");
|
||||
}
|
||||
});
|
||||
|
||||
execute('git diff --name-only ' + versionFrom, function (stdout) {
|
||||
|
||||
if (!fs.existsSync(upgradePath)) {
|
||||
fs.mkdirSync(upgradePath);
|
||||
}
|
||||
if (!fs.existsSync(upgradePath + '/files')) {
|
||||
fs.mkdirSync(upgradePath + '/files');
|
||||
}
|
||||
process.chdir(buildPath);
|
||||
|
||||
var fileList = [];
|
||||
|
||||
(stdout || '').split('\n').forEach(function (file) {
|
||||
if (file == '') {
|
||||
return;
|
||||
}
|
||||
fileList.push(file);
|
||||
});
|
||||
|
||||
fileList.push('client/espo.min.js');
|
||||
fileList.push('client/espo.min.js.map');
|
||||
|
||||
fs.readdirSync('client/css/espo/').forEach(function (file) {
|
||||
fileList.push('client/css/espo/' + file);
|
||||
});
|
||||
|
||||
fs.writeFileSync(diffFilePath, fileList.join('\n'));
|
||||
|
||||
execute('git diff --name-only --diff-filter=D ' + versionFrom, function (stdout) {
|
||||
var deletedFileList = [];
|
||||
|
||||
(stdout || '').split('\n').forEach(function (file) {
|
||||
if (file == '') {
|
||||
return;
|
||||
}
|
||||
deletedFileList.push(file);
|
||||
});
|
||||
|
||||
execute('xargs -a ' + diffFilePath + ' cp --parents -t ' + upgradePath + '/files ' , function (stdout) {
|
||||
var d = new Date();
|
||||
|
||||
var monthN = ((d.getMonth() + 1).toString());
|
||||
monthN = monthN.length == 1 ? '0' + monthN : monthN;
|
||||
|
||||
var dateN = d.getDate().toString();
|
||||
dateN = dateN.length == 1 ? '0' + dateN : dateN;
|
||||
|
||||
var date = d.getFullYear().toString() + '-' + monthN + '-' + dateN.toString();
|
||||
|
||||
execute('git tag', function (stdout) {
|
||||
var versionList = [];
|
||||
tagList = stdout.split('\n').forEach(function (tag) {
|
||||
if (tag == versionFrom) {
|
||||
versionList.push(tag);
|
||||
}
|
||||
if (!tag || tag == version) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (isDev) {
|
||||
versionList = [];
|
||||
}
|
||||
|
||||
var manifest = {
|
||||
"name": "EspoCRM Upgrade "+acceptedVersionName+" to "+version,
|
||||
"type": "upgrade",
|
||||
"version": version,
|
||||
"acceptableVersions": versionList,
|
||||
"releaseDate": date,
|
||||
"author": "EspoCRM",
|
||||
"description": "",
|
||||
"delete": deletedFileList
|
||||
}
|
||||
|
||||
fs.writeFileSync(upgradePath + '/manifest.json', JSON.stringify(manifest, null, ' '));
|
||||
|
||||
console.log("Upgrade package is built.");
|
||||
});
|
||||
|
||||
fs.unlinkSync(diffFilePath);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -962,12 +962,27 @@ ul.dropdown-menu > li.checkbox:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-group.col-sm-12 > .field .input-group > .input-group-btn > select {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.col-sm-12 > .field .input-group-link-parent > .input-group-btn > select {
|
||||
.form-group > .field .input-group > .input-group-btn > select {
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
.form-group.col-sm-12 > .field .input-group-link-parent.input-group > .input-group-btn > select {
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
.col-sm-6 > .field .input-group-link-parent > .input-group-btn > select {
|
||||
.form-group.col-sm-3 > .field .input-group-link-parent.input-group > .input-group-btn > select {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.form-group.col-sm-4 > .field .input-group-link-parent.input-group > .input-group-btn > select {
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
.form-group > .field .input-group-link-parent.input-group > .input-group-btn > select {
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
@@ -1586,10 +1601,14 @@ table.no-margin {
|
||||
border-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.panel-body > .list-container > .list > div.show-more,
|
||||
.list.no-bottom-margin > div.show-more {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
.panel-body > .list-container > .list,
|
||||
.panel-body.dashlet-body > div > .list,
|
||||
.list.no-bottom-margin
|
||||
{
|
||||
> div.show-more {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-body > div:first-child > .list-expanded:first-child {
|
||||
@@ -1787,6 +1806,10 @@ table.less-padding td.cell[data-name="buttons"] > .btn-group {
|
||||
}
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
|
||||
.panel.dashlet > .panel-body {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.panel.dashlet > .panel-body {
|
||||
|
||||
100
install/cli.php
Normal file
100
install/cli.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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.
|
||||
************************************************************************/
|
||||
|
||||
if (substr(php_sapi_name(), 0, 3) != 'cli') die('The file can be run only via CLI.');
|
||||
|
||||
$options = getopt("a:d:");
|
||||
|
||||
if (empty($options['a'])) {
|
||||
fwrite(\STDOUT, "Error: the option [-a] is required.\n");
|
||||
exit;
|
||||
}
|
||||
|
||||
$allPostData = [];
|
||||
|
||||
if (!empty($options['d'])) {
|
||||
parse_str($options['d'], $allPostData);
|
||||
|
||||
if (empty($allPostData) || !is_array($allPostData)) {
|
||||
fwrite(\STDOUT, "Error: Incorrect input data.\n");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$action = $options['a'];
|
||||
$allPostData['action'] = $action;
|
||||
|
||||
chdir(dirname(__FILE__));
|
||||
set_include_path(dirname(__FILE__));
|
||||
require_once('../bootstrap.php');
|
||||
|
||||
$_SERVER['SERVER_SOFTWARE'] = 'Cli';
|
||||
|
||||
require_once('core/PostData.php');
|
||||
$postData = new PostData();
|
||||
$postData->set($allPostData);
|
||||
|
||||
require_once('core/InstallerConfig.php');
|
||||
$installerConfig = new InstallerConfig();
|
||||
|
||||
if ($installerConfig->get('isInstalled')) {
|
||||
fwrite(\STDOUT, "Error: EspoCRM is already installed.\n");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$installerConfig->get('cliSessionId')) {
|
||||
session_start();
|
||||
$installerConfig->set('cliSessionId', session_id());
|
||||
$installerConfig->save();
|
||||
}
|
||||
|
||||
$sessonId = session_id($installerConfig->get('cliSessionId'));
|
||||
|
||||
ob_start();
|
||||
|
||||
try {
|
||||
require('install/index.php');
|
||||
} catch (\Throwable $e) {
|
||||
fwrite(\STDOUT, "Error: ". $e->getMessage() .".\n");
|
||||
exit;
|
||||
}
|
||||
|
||||
$result = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
if (preg_match('/"success":false/i', $result)) {
|
||||
$resultData = json_decode($result, true);
|
||||
if (empty($resultData)) {
|
||||
fwrite(\STDOUT, "Error: Unexpected error occurred.\n");
|
||||
exit;
|
||||
}
|
||||
|
||||
fwrite(\STDOUT, "Error: ". (!empty($resultData['errors']) ? print_r($resultData['errors'], true) : $resultData['errorMsg']) ."\n");
|
||||
exit;
|
||||
}
|
||||
@@ -45,25 +45,32 @@ class Installer
|
||||
|
||||
protected $isAuth = false;
|
||||
|
||||
protected $permissionMap;
|
||||
|
||||
protected $permissionError;
|
||||
|
||||
private $passwordHash;
|
||||
|
||||
protected $settingList = array(
|
||||
protected $defaultSettings;
|
||||
|
||||
protected $permittedSettingList = array(
|
||||
'dateFormat',
|
||||
'timeFormat',
|
||||
'timeZone',
|
||||
'weekStart',
|
||||
'defaultCurrency' => array(
|
||||
'currencyList', 'defaultCurrency',
|
||||
),
|
||||
'smtpSecurity',
|
||||
'defaultCurrency',
|
||||
'language',
|
||||
'thousandSeparator',
|
||||
'decimalMark',
|
||||
'smtpServer',
|
||||
'smtpPort',
|
||||
'smtpAuth',
|
||||
'smtpSecurity',
|
||||
'smtpUsername',
|
||||
'smtpPassword',
|
||||
'outboundEmailFromName',
|
||||
'outboundEmailFromAddress',
|
||||
'outboundEmailIsShared',
|
||||
);
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->initialize();
|
||||
@@ -79,10 +86,6 @@ class Installer
|
||||
require_once('install/core/SystemHelper.php');
|
||||
$this->systemHelper = new SystemHelper();
|
||||
|
||||
$configPath = $this->getConfig()->getConfigPath();
|
||||
$this->permissionMap = $this->getConfig()->get('permissionMap');
|
||||
$this->permissionMap['writable'][] = $configPath;
|
||||
|
||||
$this->databaseHelper = new \Espo\Core\Utils\Database\Helper($this->getConfig());
|
||||
}
|
||||
|
||||
@@ -189,15 +192,20 @@ class Installer
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
public function getLanguageList()
|
||||
public function getLanguageList($isTranslated = true)
|
||||
{
|
||||
$config = $this->app->getContainer()->get('config');
|
||||
$languageList = $this->app->getContainer()->get('config')->get('languageList');
|
||||
|
||||
$languageList = $config->get('languageList');
|
||||
if ($isTranslated) {
|
||||
return $this->getLanguage()->translate('language', 'options', 'Global', $languageList);
|
||||
}
|
||||
|
||||
$translated = $this->getLanguage()->translate('language', 'options', 'Global', $languageList);
|
||||
return $languageList;
|
||||
}
|
||||
|
||||
return $translated;
|
||||
protected function getCurrencyList()
|
||||
{
|
||||
return $this->app->getMetadata()->get('app.currency.list');
|
||||
}
|
||||
|
||||
public function getInstallerConfigData()
|
||||
@@ -248,29 +256,33 @@ class Installer
|
||||
* @param string $language
|
||||
* @return bool
|
||||
*/
|
||||
public function saveData($database, $language)
|
||||
public function saveData(array $saveData)
|
||||
{
|
||||
$initData = include('install/core/afterInstall/config.php');
|
||||
$siteUrl = $this->getSystemHelper()->getBaseUrl();
|
||||
$databaseDefaults = $this->app->getContainer()->get('config')->get('database');
|
||||
|
||||
$data = [
|
||||
'database' => array_merge($databaseDefaults, $database),
|
||||
'language' => $language,
|
||||
'siteUrl' => $siteUrl,
|
||||
'database' => array_merge($databaseDefaults, $saveData['database']),
|
||||
'language' => $saveData['language'],
|
||||
'siteUrl' => !empty($saveData['siteUrl']) ? $saveData['siteUrl'] : $this->getSystemHelper()->getBaseUrl(),
|
||||
'passwordSalt' => $this->getPasswordHash()->generateSalt(),
|
||||
'cryptKey' => $this->getContainer()->get('crypt')->generateKey(),
|
||||
'hashSecretKey' => \Espo\Core\Utils\Util::generateSecretKey(),
|
||||
];
|
||||
|
||||
$owner = $this->getFileManager()->getPermissionUtils()->getDefaultOwner(true);
|
||||
$group = $this->getFileManager()->getPermissionUtils()->getDefaultGroup(true);
|
||||
|
||||
if (!empty($owner)) {
|
||||
$data['defaultPermissions']['user'] = $owner;
|
||||
if (empty($saveData['defaultPermissions']['user'])) {
|
||||
$saveData['defaultPermissions']['user'] = $this->getFileManager()->getPermissionUtils()->getDefaultOwner(true);
|
||||
}
|
||||
if (!empty($group)) {
|
||||
$data['defaultPermissions']['group'] = $group;
|
||||
|
||||
if (empty($saveData['defaultPermissions']['group'])) {
|
||||
$saveData['defaultPermissions']['group'] = $this->getFileManager()->getPermissionUtils()->getDefaultGroup(true);
|
||||
}
|
||||
|
||||
if (!empty($saveData['defaultPermissions']['user'])) {
|
||||
$data['defaultPermissions']['user'] = $saveData['defaultPermissions']['user'];
|
||||
}
|
||||
if (!empty($saveData['defaultPermissions']['group'])) {
|
||||
$data['defaultPermissions']['group'] = $saveData['defaultPermissions']['group'];
|
||||
}
|
||||
|
||||
$data = array_merge($data, $initData);
|
||||
@@ -303,11 +315,13 @@ class Installer
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setPreferences($preferences)
|
||||
public function savePreferences($preferences)
|
||||
{
|
||||
$currencyList = $this->getConfig()->get('currencyList', array());
|
||||
if (isset($preferences['defaultCurrency']) && !in_array($preferences['defaultCurrency'], $currencyList)) {
|
||||
$preferences = $this->normalizeSettingParams($preferences);
|
||||
|
||||
$currencyList = $this->getConfig()->get('currencyList', []);
|
||||
|
||||
if (isset($preferences['defaultCurrency']) && !in_array($preferences['defaultCurrency'], $currencyList)) {
|
||||
$preferences['currencyList'] = array($preferences['defaultCurrency']);
|
||||
$preferences['baseCurrency'] = $preferences['defaultCurrency'];
|
||||
}
|
||||
@@ -320,7 +334,6 @@ class Installer
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
protected function createRecords()
|
||||
{
|
||||
$records = include('install/core/afterInstall/records.php');
|
||||
@@ -452,34 +465,76 @@ class Installer
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getSettingDefaults()
|
||||
public function getDefaultSettings()
|
||||
{
|
||||
$defaults = array();
|
||||
if (!$this->defaultSettings) {
|
||||
|
||||
$settingDefs = $this->app->getMetadata()->get('entityDefs.Settings.fields');
|
||||
$settingDefs = $this->app->getMetadata()->get('entityDefs.Settings.fields');
|
||||
|
||||
foreach ($this->settingList as $fieldName => $field) {
|
||||
$defaults = array();
|
||||
foreach ($this->permittedSettingList as $fieldName) {
|
||||
|
||||
if (is_array($field)) {
|
||||
$fieldDefaults = array();
|
||||
foreach ($field as $subField) {
|
||||
if (isset($settingDefs[$subField])) {
|
||||
$fieldDefaults = array_merge($fieldDefaults, $this->translateSetting($subField, $settingDefs[$subField]));
|
||||
}
|
||||
if (!isset($settingDefs[$fieldName])) continue;
|
||||
|
||||
switch ($fieldName) {
|
||||
case 'defaultCurrency':
|
||||
$settingDefs['defaultCurrency']['options'] = $this->getCurrencyList();
|
||||
break;
|
||||
|
||||
case 'language':
|
||||
$settingDefs['language']['options'] = $this->getLanguageList(false);
|
||||
break;
|
||||
}
|
||||
$defaults[$fieldName] = $fieldDefaults;
|
||||
|
||||
} else if (isset($settingDefs[$field])) {
|
||||
$defaults[$fieldName] = $this->translateSetting($fieldName, $settingDefs[$fieldName]);
|
||||
}
|
||||
|
||||
$defaults[$field] = $this->translateSetting($field, $settingDefs[$field]);
|
||||
$this->defaultSettings = $defaults;
|
||||
}
|
||||
|
||||
return $this->defaultSettings;
|
||||
}
|
||||
|
||||
protected function normalizeSettingParams(array $params)
|
||||
{
|
||||
$defaultSettings = $this->getDefaultSettings();
|
||||
|
||||
$normalizedParams = [];
|
||||
foreach ($params as $name => $value) {
|
||||
if (!isset($defaultSettings[$name])) continue;
|
||||
|
||||
$paramDefs = $defaultSettings[$name];
|
||||
$paramType = isset($paramDefs['type']) ? $paramDefs['type'] : 'varchar';
|
||||
|
||||
switch ($paramType) {
|
||||
case 'enumInt':
|
||||
$value = (int) $value;
|
||||
|
||||
case 'enum':
|
||||
if (isset($paramDefs['options']) && array_key_exists($value, $paramDefs['options'])) {
|
||||
$normalizedParams[$name] = $value;
|
||||
} else if (array_key_exists('default', $paramDefs)) {
|
||||
$normalizedParams[$name] = $paramDefs['default'];
|
||||
$GLOBALS['log']->warning('Incorrect value ['. $value .'] for Settings parameter ['. $name .']. Use default value ['. $paramDefs['default'] .'].');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bool':
|
||||
$normalizedParams[$name] = (bool) $value;
|
||||
break;
|
||||
|
||||
case 'int':
|
||||
$normalizedParams[$name] = (int) $value;
|
||||
break;
|
||||
|
||||
case 'varchar':
|
||||
default:
|
||||
$normalizedParams[$name] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($defaults['language'])) {
|
||||
$defaults['language']['options'] = $this->getLanguageList();
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
return $normalizedParams;
|
||||
}
|
||||
|
||||
protected function translateSetting($name, array $settingDefs)
|
||||
|
||||
45
install/core/PostData.php
Normal file
45
install/core/PostData.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
class PostData
|
||||
{
|
||||
protected $data = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
if (isset($_POST) && is_array($_POST)) {
|
||||
$this->data = $_POST;
|
||||
}
|
||||
}
|
||||
|
||||
public function set($name, $value = null)
|
||||
{
|
||||
if (!is_array($name)) {
|
||||
$name = [
|
||||
$name => $value
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($name as $key => $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function get($name, $default = null)
|
||||
{
|
||||
if (array_key_exists($name, $this->data)) {
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function getAll()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ class SystemHelper extends \Espo\Core\Utils\System
|
||||
|
||||
protected $combineOperator = '&&';
|
||||
|
||||
protected $writableMap;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = include('config.php');
|
||||
@@ -50,6 +52,9 @@ class SystemHelper extends \Espo\Core\Utils\System
|
||||
}
|
||||
|
||||
$this->apiPath = $this->config['apiPath'];
|
||||
|
||||
$permission = new \Espo\Core\Utils\File\Permission(new \Espo\Core\Utils\File\Manager());
|
||||
$this->writableMap = $permission->getWritableMap();
|
||||
}
|
||||
|
||||
protected function getMainConfig($optionName, $returns = null)
|
||||
@@ -124,7 +129,7 @@ class SystemHelper extends \Espo\Core\Utils\System
|
||||
return $cd.$sudoStr.'chown -R '.$owner.':'.$group.' '.$path;
|
||||
}
|
||||
|
||||
public function getChmodCommand($path, $permissions = array('755'), $isSudo = false, $isFile = null, $isCd = true)
|
||||
public function getChmodCommand($path, $permissions = ['755'], $isRecursive = true, $isSudo = false, $isFile = null)
|
||||
{
|
||||
$path = empty($path) ? '.' : $path;
|
||||
if (is_array($path)) {
|
||||
@@ -133,27 +138,32 @@ class SystemHelper extends \Espo\Core\Utils\System
|
||||
|
||||
$sudoStr = $isSudo ? 'sudo ' : '';
|
||||
|
||||
$cd = $isCd ? $this->getCd(true) : '';
|
||||
|
||||
if (is_string($permissions)) {
|
||||
$permissions = (array) $permissions;
|
||||
}
|
||||
|
||||
if (!isset($isFile) && count($permissions) == 1) {
|
||||
return $cd. $sudoStr . 'find '.$path.' -type d -exec ' . $sudoStr . 'chmod '.$permissions[0].' {} +';
|
||||
if ($isRecursive) {
|
||||
return $sudoStr . 'find '. $path .' -type d -exec ' . $sudoStr . 'chmod '. $permissions[0] .' {} +';
|
||||
}
|
||||
return $sudoStr . 'chmod '. $permissions[0] .' '. $path;
|
||||
}
|
||||
|
||||
$bufPerm = (count($permissions) == 1) ? array_fill(0, 2, $permissions[0]) : $permissions;
|
||||
|
||||
$commands = array();
|
||||
|
||||
if ($isCd) {
|
||||
$commands[] = $this->getCd();
|
||||
if ($isRecursive) {
|
||||
$commands[] = $sudoStr. 'find '.$path.' -type f -exec ' .$sudoStr.'chmod '.$bufPerm[0].' {} +';
|
||||
$commands[] = $sudoStr . 'find '.$path.' -type d -exec ' .$sudoStr. 'chmod '.$bufPerm[1].' {} +';
|
||||
} else {
|
||||
if (file_exists($path) && is_file($path)) {
|
||||
$commands[] = $sudoStr. 'chmod '. $bufPerm[0] .' '. $path;
|
||||
} else {
|
||||
$commands[] = $sudoStr. 'chmod '. $bufPerm[1] .' '. $path;
|
||||
}
|
||||
}
|
||||
|
||||
$commands[] = $sudoStr. 'find '.$path.' -type f -exec ' .$sudoStr.'chmod '.$bufPerm[0].' {} +';//.'chmod '.$bufPerm[0].' $(find '.$path.' -type f)';
|
||||
$commands[] = $sudoStr . 'find '.$path.' -type d -exec ' .$sudoStr. 'chmod '.$bufPerm[1].' {} +';//.'chmod '.$bufPerm[1].' $(find '.$path.' -type d)';
|
||||
|
||||
if (count($permissions) >= 2) {
|
||||
return implode(' ' . $this->combineOperator . ' ', $commands);
|
||||
}
|
||||
@@ -187,7 +197,7 @@ class SystemHelper extends \Espo\Core\Utils\System
|
||||
* @param bool $isFile
|
||||
* @return string
|
||||
*/
|
||||
public function getPermissionCommands($path, $permissions = array('644', '755'), $isSudo = false, $isFile = null, $changeOwner = true)
|
||||
public function getPermissionCommands($path, $permissions = ['644', '755'], $isSudo = false, $isFile = null, $changeOwner = true, $isCd = true)
|
||||
{
|
||||
if (is_string($path)) {
|
||||
$path = array_fill(0, 2, $path);
|
||||
@@ -195,7 +205,30 @@ class SystemHelper extends \Espo\Core\Utils\System
|
||||
list($chmodPath, $chownPath) = $path;
|
||||
|
||||
$commands = array();
|
||||
$commands[] = $this->getChmodCommand($chmodPath, $permissions, $isSudo, $isFile);
|
||||
|
||||
if ($isCd) {
|
||||
$commands[] = $this->getCd();
|
||||
}
|
||||
|
||||
$chmodPath = (array) $chmodPath;
|
||||
|
||||
$pathList = [];
|
||||
$recursivePathList = [];
|
||||
foreach ($chmodPath as $pathItem) {
|
||||
if (isset($this->writableMap[$pathItem]) && !$this->writableMap[$pathItem]['recursive']) {
|
||||
$pathList[] = $pathItem;
|
||||
continue;
|
||||
}
|
||||
$recursivePathList[] = $pathItem;
|
||||
}
|
||||
|
||||
if (!empty($pathList)) {
|
||||
$commands[] = $this->getChmodCommand($pathList, $permissions, false, $isSudo, $isFile);
|
||||
}
|
||||
|
||||
if (!empty($recursivePathList)) {
|
||||
$commands[] = $this->getChmodCommand($recursivePathList, $permissions, true, $isSudo, $isFile);
|
||||
}
|
||||
|
||||
if ($changeOwner) {
|
||||
$chown = $this->getChownCommand($chownPath, $isSudo, false);
|
||||
|
||||
@@ -34,15 +34,15 @@ class Utils
|
||||
static public function checkActionExists($actionName)
|
||||
{
|
||||
return in_array($actionName, [
|
||||
'applySett',
|
||||
'saveSettings',
|
||||
'buildDatabase',
|
||||
'checkPermission',
|
||||
'createUser',
|
||||
'errors',
|
||||
'finish',
|
||||
'main',
|
||||
'setEmailSett',
|
||||
'setPreferences',
|
||||
'saveEmailSettings',
|
||||
'savePreferences',
|
||||
'settingsTest',
|
||||
'setupConfirmation',
|
||||
'step1',
|
||||
@@ -52,7 +52,6 @@ class Utils
|
||||
'step5'
|
||||
]);
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ if (!$installer->checkPermission()) {
|
||||
$changeOwner = false;
|
||||
}
|
||||
}
|
||||
$result['errorMsg'] = $langs['messages']['Permission denied to'] . ':<br><pre>/'.implode('<br>/', $urls).'</pre>';
|
||||
$result['errorMsg'] = $langs['messages']['Permission denied to'] . ':<br><pre>'.implode('<br>', $urls).'</pre>';
|
||||
$result['errorFixInstruction'] = str_replace( '"{C}"' , $instruction, $langs['messages']['permissionInstruction']) . "<br>" .
|
||||
str_replace( '{CSU}' , $instructionSU, $langs['messages']['operationNotPermitted']);
|
||||
}
|
||||
|
||||
@@ -25,24 +25,47 @@
|
||||
*
|
||||
* 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.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
ob_start();
|
||||
$result = array('success' => false, 'errorMsg' => '');
|
||||
|
||||
if (!empty($_SESSION['install'])) {
|
||||
$preferences = array(
|
||||
'smtpServer' => $_SESSION['install']['smtpServer'],
|
||||
'smtpPort' => $_SESSION['install']['smtpPort'],
|
||||
'smtpAuth' => (empty($_SESSION['install']['smtpAuth']) || $_SESSION['install']['smtpAuth'] == 'false' || !$_SESSION['install']['smtpAuth'])? false : true,
|
||||
'smtpSecurity' => $_SESSION['install']['smtpSecurity'],
|
||||
'smtpUsername' => $_SESSION['install']['smtpUsername'],
|
||||
'smtpPassword' => $_SESSION['install']['smtpPassword'],
|
||||
'outboundEmailFromName' => $_SESSION['install']['outboundEmailFromName'],
|
||||
'outboundEmailFromAddress' => $_SESSION['install']['outboundEmailFromAddress'],
|
||||
'outboundEmailIsShared' => (empty($_SESSION['install']['smtpAuth']) || $_SESSION['install']['outboundEmailIsShared'] == 'false' || !$_SESSION['install']['smtpAuth'])? false : true,
|
||||
);
|
||||
$res = $installer->setPreferences($preferences);
|
||||
|
||||
$paramList = [
|
||||
'smtpServer',
|
||||
'smtpPort',
|
||||
'smtpAuth',
|
||||
'smtpSecurity',
|
||||
'smtpUsername',
|
||||
'smtpPassword',
|
||||
'outboundEmailFromName',
|
||||
'outboundEmailFromAddress',
|
||||
'outboundEmailIsShared',
|
||||
];
|
||||
|
||||
$preferences = [];
|
||||
foreach ($paramList as $paramName) {
|
||||
|
||||
switch ($paramName) {
|
||||
case 'smtpAuth':
|
||||
$preferences['smtpAuth'] = (empty($_SESSION['install']['smtpAuth']) || $_SESSION['install']['smtpAuth'] == 'false' || !$_SESSION['install']['smtpAuth']) ? false : true;
|
||||
break;
|
||||
|
||||
case 'outboundEmailIsShared':
|
||||
$preferences['outboundEmailIsShared'] = (empty($_SESSION['install']['smtpAuth']) || $_SESSION['install']['outboundEmailIsShared'] == 'false' || !$_SESSION['install']['smtpAuth']) ? false : true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (array_key_exists($paramName, $_SESSION['install'])) {
|
||||
$preferences[$paramName] = $_SESSION['install'][$paramName];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$res = $installer->savePreferences($preferences);
|
||||
if (!empty($res)) {
|
||||
$result['success'] = true;
|
||||
}
|
||||
@@ -25,23 +25,31 @@
|
||||
*
|
||||
* 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.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
ob_start();
|
||||
$result = array('success' => false, 'errorMsg' => '');
|
||||
|
||||
if (!empty($_SESSION['install'])) {
|
||||
$preferences = array(
|
||||
'dateFormat' => $_SESSION['install']['dateFormat'],
|
||||
'timeFormat' => $_SESSION['install']['timeFormat'],
|
||||
'timeZone' => $_SESSION['install']['timeZone'],
|
||||
'weekStart' => (int)$_SESSION['install']['weekStart'],
|
||||
'defaultCurrency' => $_SESSION['install']['defaultCurrency'],
|
||||
'thousandSeparator' => $_SESSION['install']['thousandSeparator'],
|
||||
'decimalMark' => $_SESSION['install']['decimalMark'],
|
||||
'language' => $_SESSION['install']['language'],
|
||||
);
|
||||
$res = $installer->setPreferences($preferences);
|
||||
$paramList = [
|
||||
'dateFormat',
|
||||
'timeFormat',
|
||||
'timeZone',
|
||||
'weekStart',
|
||||
'defaultCurrency',
|
||||
'thousandSeparator',
|
||||
'decimalMark',
|
||||
'language',
|
||||
];
|
||||
|
||||
$preferences = [];
|
||||
foreach ($paramList as $paramName) {
|
||||
if (array_key_exists($paramName, $_SESSION['install'])) {
|
||||
$preferences[$paramName] = $_SESSION['install'][$paramName];
|
||||
}
|
||||
}
|
||||
|
||||
$res = $installer->savePreferences($preferences);
|
||||
if (!empty($res)) {
|
||||
$result['success'] = true;
|
||||
}
|
||||
@@ -25,13 +25,13 @@
|
||||
*
|
||||
* 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.
|
||||
************************************************************************/
|
||||
************************************************************************/
|
||||
|
||||
ob_start();
|
||||
$result = array('success' => true, 'errorMsg' => '');
|
||||
|
||||
// save settings
|
||||
$data = array(
|
||||
$database = array(
|
||||
'driver' => 'pdo_mysql',
|
||||
'dbname' => $_SESSION['install']['db-name'],
|
||||
'user' => $_SESSION['install']['db-user-name'],
|
||||
@@ -41,10 +41,22 @@ $host = $_SESSION['install']['host-name'];
|
||||
if (strpos($host,':') === false) {
|
||||
$host .= ":";
|
||||
}
|
||||
list($data['host'], $data['port']) = explode(':', $host);
|
||||
list($database['host'], $database['port']) = explode(':', $host);
|
||||
|
||||
$lang = (!empty($_SESSION['install']['user-lang']))? $_SESSION['install']['user-lang'] : 'en_US';
|
||||
if (!$installer->saveData($data, $lang)) {
|
||||
$saveData = [
|
||||
'database' => $database,
|
||||
'language' => !empty($_SESSION['install']['user-lang']) ? $_SESSION['install']['user-lang'] : 'en_US',
|
||||
'siteUrl' => !empty($_SESSION['install']['site-url']) ? $_SESSION['install']['site-url'] : null,
|
||||
];
|
||||
|
||||
if (!empty($_SESSION['install']['default-permissions-user']) && !empty($_SESSION['install']['default-permissions-group'])) {
|
||||
$saveData['defaultPermissions'] = [
|
||||
'user' => $_SESSION['install']['default-permissions-user'],
|
||||
'group' => $_SESSION['install']['default-permissions-group'],
|
||||
];
|
||||
}
|
||||
|
||||
if (!$installer->saveData($saveData)) {
|
||||
$result['success'] = false;
|
||||
$result['errorMsg'] = $langs['messages']['Can not save settings'];
|
||||
}
|
||||
@@ -50,16 +50,18 @@ foreach ($phpRequiredList as $name => $details) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($result['success'] && !empty($_REQUEST['dbName']) && !empty($_REQUEST['hostName']) && !empty($_REQUEST['dbUserName'])) {
|
||||
$allPostData = $postData->getAll();
|
||||
|
||||
if ($result['success'] && !empty($allPostData['dbName']) && !empty($allPostData['hostName']) && !empty($allPostData['dbUserName'])) {
|
||||
$connect = false;
|
||||
|
||||
$dbName = trim($_REQUEST['dbName']);
|
||||
if (strpos($_REQUEST['hostName'],':') === false) {
|
||||
$_REQUEST['hostName'] .= ":";
|
||||
$dbName = trim($allPostData['dbName']);
|
||||
if (strpos($allPostData['hostName'],':') === false) {
|
||||
$allPostData['hostName'] .= ":";
|
||||
}
|
||||
list($hostName, $port) = explode(':', trim($_REQUEST['hostName']));
|
||||
$dbUserName = trim($_REQUEST['dbUserName']);
|
||||
$dbUserPass = trim($_REQUEST['dbUserPass']);
|
||||
list($hostName, $port) = explode(':', trim($allPostData['hostName']));
|
||||
$dbUserName = trim($allPostData['dbUserName']);
|
||||
$dbUserPass = trim($allPostData['dbUserPass']);
|
||||
|
||||
$databaseParams = [
|
||||
'host' => $hostName,
|
||||
@@ -84,7 +86,7 @@ if ($result['success'] && !empty($_REQUEST['dbName']) && !empty($_REQUEST['hostN
|
||||
$databaseRequiredList = $installer->getSystemRequirementList('database', true, ['database' => $databaseParams]);
|
||||
|
||||
foreach ($databaseRequiredList as $name => $details) {
|
||||
if (!$details['acceptable']) {
|
||||
if (!$details['acceptable']) {
|
||||
|
||||
switch ($details['type']) {
|
||||
case 'version':
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
},
|
||||
"modRewriteInstruction": {
|
||||
"apache": {
|
||||
"linux": "<br><br>1. Enable \"mod_rewrite\". To do it run those commands in a Terminal:<pre>{APACHE1}</pre><br>2. Enable .htaccess support. Add/edit the Server configuration settings (/etc/apache/apache2.conf, /etc/httpd/conf/httpd.conf):<pre>{APACHE2}</pre>\n Afterwards run this command in a Terminal:<pre>{APACHE3}</pre><br>3. Try to add the RewriteBase path, open a file {API_PATH}.htaccess and replace the following line:<pre>{APACHE4}</pre>To<pre>{APACHE5}</pre><br> For more information please visit the guideline <a href=\"https://www.espocrm.com/documentation/administration/apache-server-configuration/\" target=\"_blank\">Apache server configuration for EspoCRM</a>.<br><br>",
|
||||
"linux": "<br><br>1. Enable \"mod_rewrite\". To do it run those commands in a Terminal:<pre>{APACHE1}</pre><br>2. If the previous step did not help, try to enable .htaccess support. Add/edit the Server configuration settings (/etc/apache/apache2.conf, /etc/httpd/conf/httpd.conf):<pre>{APACHE2}</pre>\n Afterwards run this command in a Terminal:<pre>{APACHE3}</pre><br>3. If the previous step did not help, try to add the RewriteBase path. Open a file {API_PATH}.htaccess and replace the following line:<pre>{APACHE4}</pre>To<pre>{APACHE5}</pre><br> For more information please visit the guideline <a href=\"https://www.espocrm.com/documentation/administration/apache-server-configuration/\" target=\"_blank\">Apache server configuration for EspoCRM</a>.<br><br>",
|
||||
"windows": "<br> <pre>1. Find the httpd.conf file (usually you will find it in a folder called conf, config or something along those lines)<br>\n2. Inside the httpd.conf file uncomment the line LoadModule rewrite_module modules/mod_rewrite.so (remove the pound '#' sign from in front of the line)<br>\n3. Also find the line ClearModuleList is uncommented then find and make sure that the line AddModule mod_rewrite.c is not commented out.\n</pre>"
|
||||
},
|
||||
"nginx": {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</label>
|
||||
<div class="field field-dateFormat">
|
||||
<select name="dateFormat" class="form-control main-element">
|
||||
{foreach from=$settingsDefaults['dateFormat'].options item=lbl key=val}
|
||||
{foreach from=$defaultSettings['dateFormat'].options item=lbl key=val}
|
||||
{if $val == $fields['dateFormat'].value}
|
||||
<option selected="selected" value="{$val}">{$lbl}</option>
|
||||
{else}
|
||||
@@ -27,7 +27,7 @@
|
||||
</label>
|
||||
<div class="field field-timeFormat">
|
||||
<select name="timeFormat" class="form-control main-element">
|
||||
{foreach from=$settingsDefaults['timeFormat'].options item=lbl key=val}
|
||||
{foreach from=$defaultSettings['timeFormat'].options item=lbl key=val}
|
||||
{if $val == $fields['timeFormat'].value}
|
||||
<option selected="selected" value="{$val}">{$lbl}</option>
|
||||
{else}
|
||||
@@ -46,7 +46,7 @@
|
||||
</label>
|
||||
<div class="field field-timeZone">
|
||||
<select name="timeZone" class="form-control main-element">
|
||||
{foreach from=$settingsDefaults['timeZone'].options item=lbl key=val}
|
||||
{foreach from=$defaultSettings['timeZone'].options item=lbl key=val}
|
||||
{if $val == $fields['timeZone'].value}
|
||||
<option selected="selected" value="{$val}">{$lbl}</option>
|
||||
{else}
|
||||
@@ -63,7 +63,7 @@
|
||||
</label>
|
||||
<div class="field field-weekStart">
|
||||
<select name="weekStart" class="form-control main-element">
|
||||
{foreach from=$settingsDefaults['weekStart'].options item=lbl key=val}
|
||||
{foreach from=$defaultSettings['weekStart'].options item=lbl key=val}
|
||||
{if $val == $fields['weekStart'].value}
|
||||
<option selected="selected" value="{$val}">{$lbl}</option>
|
||||
{else}
|
||||
@@ -82,7 +82,7 @@
|
||||
</label>
|
||||
<div class="field field-defaultCurrency">
|
||||
<select name="defaultCurrency" class="form-control main-element">
|
||||
{foreach from=$settingsDefaults['defaultCurrency'].options item=lbl key=val}
|
||||
{foreach from=$defaultSettings['defaultCurrency'].options item=lbl key=val}
|
||||
{if $val == $fields['defaultCurrency'].value}
|
||||
<option selected="selected" value="{$val}">{$lbl}</option>
|
||||
{else}
|
||||
@@ -120,7 +120,7 @@
|
||||
</label>
|
||||
<div class="field field-language">
|
||||
<select name="language" class="form-control main-element">
|
||||
{foreach from=$settingsDefaults['language'].options item=lbl key=val}
|
||||
{foreach from=$defaultSettings['language'].options item=lbl key=val}
|
||||
{if $val == $fields['language'].value}
|
||||
<option selected="selected" value="{$val}">{$lbl}</option>
|
||||
{else}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</label>
|
||||
<div class="field field-smtpSecurity">
|
||||
<select name="smtpSecurity" class="form-control main-element">
|
||||
{foreach from=$settingsDefaults['smtpSecurity'].options item=lbl key=val}
|
||||
{foreach from=$defaultSettings['smtpSecurity'].options item=lbl key=val}
|
||||
{if $val == $fields['smtpSecurity'].value}
|
||||
<option selected="selected" value="{$val}">{$lbl}</option>
|
||||
{else}
|
||||
|
||||
@@ -27,11 +27,23 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
session_start();
|
||||
require_once('../bootstrap.php');
|
||||
if (session_status() !== \PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (file_exists('../bootstrap.php')) {
|
||||
require_once('../bootstrap.php');
|
||||
}
|
||||
|
||||
if (!isset($postData)) {
|
||||
require_once('core/PostData.php');
|
||||
$postData = new PostData();
|
||||
}
|
||||
|
||||
$allPostData = $postData->getAll();
|
||||
|
||||
//action
|
||||
$action = (!empty($_POST['action']))? $_POST['action'] : 'main';
|
||||
$action = (!empty($allPostData['action']))? $allPostData['action'] : 'main';
|
||||
require_once('core/Utils.php');
|
||||
if (!Utils::checkActionExists($action)) {
|
||||
die('This page does not exist.');
|
||||
@@ -40,8 +52,8 @@ if (!Utils::checkActionExists($action)) {
|
||||
// temp save all settings
|
||||
$ignoredFields = array('installProcess', 'dbName', 'hostName', 'dbUserName', 'dbUserPass', 'dbDriver');
|
||||
|
||||
if (!empty($_POST)) {
|
||||
foreach ($_POST as $key => $val) {
|
||||
if (!empty($allPostData)) {
|
||||
foreach ($allPostData as $key => $val) {
|
||||
if (!in_array($key, $ignoredFields)) {
|
||||
$_SESSION['install'][$key] = trim($val);
|
||||
}
|
||||
@@ -64,7 +76,7 @@ $systemHelper = new SystemHelper();
|
||||
|
||||
$systemConfig = include('application/Espo/Core/defaults/systemConfig.php');
|
||||
if (isset($systemConfig['requiredPhpVersion']) && version_compare(PHP_VERSION, $systemConfig['requiredPhpVersion'], '<')) {
|
||||
die(str_replace('{minVersion}', $systemConfig['requiredPhpVersion'], $sanitizedLangs['messages']['phpVersion']) . '.');
|
||||
die(str_replace("{minVersion}", $systemConfig['requiredPhpVersion'], $sanitizedLangs['messages']['phpVersion']) . ".\n");
|
||||
}
|
||||
|
||||
if (!$systemHelper->initWritable()) {
|
||||
@@ -74,7 +86,7 @@ if (!$systemHelper->initWritable()) {
|
||||
$message = str_replace('{*}', $dir, $message);
|
||||
$message = str_replace('{C}', $systemHelper->getPermissionCommands(array($dir, ''), '775'), $message);
|
||||
$message = str_replace('{CSU}', $systemHelper->getPermissionCommands(array($dir, ''), '775', true), $message);
|
||||
die($message);
|
||||
die($message . "\n");
|
||||
}
|
||||
|
||||
require_once ('install/vendor/smarty/libs/Smarty.class.php');
|
||||
@@ -130,13 +142,13 @@ switch ($action) {
|
||||
break;
|
||||
|
||||
case 'step4':
|
||||
$settingsDefaults = $installer->getSettingDefaults();
|
||||
$smarty->assign("settingsDefaults", $settingsDefaults);
|
||||
$defaultSettings = $installer->getDefaultSettings();
|
||||
$smarty->assign("defaultSettings", $defaultSettings);
|
||||
break;
|
||||
|
||||
case 'step5':
|
||||
$settingsDefaults = $installer->getSettingDefaults();
|
||||
$smarty->assign("settingsDefaults", $settingsDefaults);
|
||||
$defaultSettings = $installer->getDefaultSettings();
|
||||
$smarty->assign("defaultSettings", $defaultSettings);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ var InstallScript = function(opt) {
|
||||
'break': true
|
||||
},
|
||||
{
|
||||
'action': 'applySett',
|
||||
'action': 'saveSettings',
|
||||
'break': true
|
||||
},
|
||||
{
|
||||
@@ -256,7 +256,7 @@ InstallScript.prototype.step4 = function() {
|
||||
}
|
||||
var data = self.systemSettings;
|
||||
|
||||
data.action = 'setPreferences';
|
||||
data.action = 'savePreferences';
|
||||
$.ajax({
|
||||
url: "index.php",
|
||||
type: "POST",
|
||||
@@ -287,14 +287,14 @@ InstallScript.prototype.step5 = function() {
|
||||
|
||||
$("#next").click(function(){
|
||||
$(this).attr('disabled', 'disabled');
|
||||
self.setEmailSett();
|
||||
self.saveEmailSettings();
|
||||
if (!self.validate()) {
|
||||
$(this).removeAttr('disabled');
|
||||
return;
|
||||
}
|
||||
var data = self.emailSettings;
|
||||
|
||||
data.action = 'setEmailSett';
|
||||
data.action = 'saveEmailSettings';
|
||||
$.ajax({
|
||||
url: "index.php",
|
||||
type: "POST",
|
||||
@@ -384,7 +384,7 @@ InstallScript.prototype.setSystemSett = function() {
|
||||
this.systemSettings.language = $('[name="language"]').val();
|
||||
}
|
||||
|
||||
InstallScript.prototype.setEmailSett = function() {
|
||||
InstallScript.prototype.saveEmailSettings = function() {
|
||||
this.emailSettings.smtpServer = $('[name="smtpServer"]').val();
|
||||
this.emailSettings.smtpPort = $('[name="smtpPort"]').val();
|
||||
this.emailSettings.smtpAuth = $('[name="smtpAuth"]').is(':checked');
|
||||
@@ -626,7 +626,7 @@ InstallScript.prototype.checkAction = function(dataMain) {
|
||||
this.checkModRewrite();
|
||||
return;
|
||||
}
|
||||
if (checkAction == 'applySett') {
|
||||
if (checkAction == 'saveSettings') {
|
||||
data['user-name'] = this.userSett.name;
|
||||
data['user-pass'] = this.userSett.pass;
|
||||
}
|
||||
|
||||
948
package-lock.json
generated
948
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "5.7.7",
|
||||
"version": "5.7.10",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
@@ -10,11 +10,11 @@
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"archiver": "^3.1.1",
|
||||
"fstream": ">=1.0.12",
|
||||
"grunt": "~1.0.3",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-chmod": "~1.1.0",
|
||||
"grunt-contrib-clean": "~2.0.0",
|
||||
"grunt-contrib-compress": "~1.4.3",
|
||||
"grunt-contrib-concat": "~1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-cssmin": "~3.0.0",
|
||||
@@ -25,5 +25,6 @@
|
||||
"js-yaml": "^3.13.1",
|
||||
"pofile": "~1.0.11",
|
||||
"tar": "^4.4.8"
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
@@ -116,8 +116,6 @@ abstract class BaseTestCase extends \PHPUnit\Framework\TestCase
|
||||
|
||||
protected function setUp() : void
|
||||
{
|
||||
$this->beforeSetUp();
|
||||
|
||||
$params = array(
|
||||
'className' => get_class($this),
|
||||
'dataFile' => $this->dataFile,
|
||||
@@ -126,6 +124,9 @@ abstract class BaseTestCase extends \PHPUnit\Framework\TestCase
|
||||
);
|
||||
|
||||
$this->espoTester = new Tester($params);
|
||||
|
||||
$this->beforeSetUp();
|
||||
|
||||
$this->espoTester->initialize();
|
||||
$this->auth($this->userName, $this->password, null, $this->authenticationMethod);
|
||||
|
||||
@@ -190,7 +191,7 @@ abstract class BaseTestCase extends \PHPUnit\Framework\TestCase
|
||||
$this->espoTester->setData($data);
|
||||
}
|
||||
|
||||
protected function enableFullReset()
|
||||
protected function fullReset()
|
||||
{
|
||||
$this->espoTester->setParam('fullReset', true);
|
||||
}
|
||||
|
||||
@@ -251,15 +251,18 @@ class Tester
|
||||
$fullReset = false;
|
||||
|
||||
$modifiedTime = filemtime($latestEspoDir . '/application');
|
||||
if (!isset($configData['lastModifiedTime']) || $configData['lastModifiedTime'] != $modifiedTime) {
|
||||
if ($this->getParam('fullReset') || !isset($configData['lastModifiedTime']) || $configData['lastModifiedTime'] != $modifiedTime) {
|
||||
$fullReset = true;
|
||||
$this->saveTestConfigData('lastModifiedTime', $modifiedTime);
|
||||
}
|
||||
|
||||
if ($fullReset) {
|
||||
Utils::dropTables($configData['database']);
|
||||
$fileManager->removeInDir($this->installPath);
|
||||
$fileManager->copy($latestEspoDir, $this->installPath, true);
|
||||
|
||||
//$fileManager->removeInDir($this->installPath);
|
||||
//$fileManager->copy($latestEspoDir, $this->installPath, true);
|
||||
shell_exec('rm -rf "' . $this->installPath . '"');
|
||||
shell_exec('cp -r "' . $latestEspoDir . '" "' . $this->installPath . '"');
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -301,8 +304,8 @@ class Tester
|
||||
{
|
||||
$applyChanges = false;
|
||||
|
||||
if (!empty($this->params['pathToFiles'])) {
|
||||
$this->getDataLoader()->loadFiles($this->params['pathToFiles']);
|
||||
if (!empty($this->params['pathToFiles']) && file_exists($this->params['pathToFiles'])) {
|
||||
$result = $this->getDataLoader()->loadFiles($this->params['pathToFiles']);
|
||||
$this->getApplication(true, true)->runRebuild();
|
||||
}
|
||||
|
||||
|
||||
@@ -377,4 +377,44 @@ class FormulaTest extends \tests\integration\Core\BaseTestCase
|
||||
$result = $fm->run($script, $lead);
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testRecordRelate()
|
||||
{
|
||||
$fm = $this->getContainer()->get('formulaManager');
|
||||
$em = $this->getContainer()->get('entityManager');
|
||||
|
||||
$a = $em->createEntity('Account', [
|
||||
'name' => '1',
|
||||
]);
|
||||
$o = $em->createEntity('Opportunity', [
|
||||
'name' => '1',
|
||||
]);
|
||||
|
||||
$script = "record\\relate('Account', '".$a->id."', 'opportunities', '".$o->id."')";
|
||||
$result = $fm->run($script, $contact);
|
||||
|
||||
$this->assertTrue($result);
|
||||
$this->assertTrue($em->getRepository('Account')->isRelated($a, 'opportunities', $o));
|
||||
}
|
||||
|
||||
public function testRecordUnrelate()
|
||||
{
|
||||
$fm = $this->getContainer()->get('formulaManager');
|
||||
$em = $this->getContainer()->get('entityManager');
|
||||
|
||||
$a = $em->createEntity('Account', [
|
||||
'name' => '1',
|
||||
]);
|
||||
$o = $em->createEntity('Opportunity', [
|
||||
'name' => '1',
|
||||
]);
|
||||
|
||||
$em->getRepository('Account')->relate($a, 'opportunities', $o);
|
||||
|
||||
$script = "record\\unrelate('Account', '".$a->id."', 'opportunities', '".$o->id."')";
|
||||
$result = $fm->run($script, $contact);
|
||||
|
||||
$this->assertTrue($result);
|
||||
$this->assertFalse($em->getRepository('Account')->isRelated($a, 'opportunities', $o));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,11 @@ class GeneralTest extends \tests\integration\Core\BaseTestCase
|
||||
|
||||
protected $packagePath = 'Extension/General.zip';
|
||||
|
||||
protected function beforeSetUp()
|
||||
{
|
||||
$this->fullReset();
|
||||
}
|
||||
|
||||
public function testUpload()
|
||||
{
|
||||
$fileData = file_get_contents($this->normalizePath($this->packagePath));
|
||||
@@ -50,8 +55,6 @@ class GeneralTest extends \tests\integration\Core\BaseTestCase
|
||||
$this->assertFileExists('data/upload/extensions/' . $extensionId . 'z');
|
||||
$this->assertFileExists('data/upload/extensions/' . $extensionId); //directory
|
||||
|
||||
$this->enableFullReset();
|
||||
|
||||
return $extensionId;
|
||||
}
|
||||
|
||||
@@ -75,8 +78,6 @@ class GeneralTest extends \tests\integration\Core\BaseTestCase
|
||||
$this->assertFileNotExists('extension.php');
|
||||
$this->assertFileNotExists('upgrade.php');
|
||||
|
||||
$this->enableFullReset();
|
||||
|
||||
return $extensionId;
|
||||
}
|
||||
|
||||
@@ -100,8 +101,6 @@ class GeneralTest extends \tests\integration\Core\BaseTestCase
|
||||
$this->assertFileExists('extension.php');
|
||||
$this->assertFileExists('upgrade.php');
|
||||
|
||||
$this->enableFullReset();
|
||||
|
||||
return $extensionId;
|
||||
}
|
||||
|
||||
@@ -124,7 +123,5 @@ class GeneralTest extends \tests\integration\Core\BaseTestCase
|
||||
$this->assertFileExists('vendor/zendframework'); //directory
|
||||
$this->assertFileExists('extension.php');
|
||||
$this->assertFileExists('upgrade.php');
|
||||
|
||||
$this->enableFullReset();
|
||||
}
|
||||
}
|
||||
|
||||
7
upgrades/5.7/data.json
Normal file
7
upgrades/5.7/data.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"beforeUpgradeFiles": [
|
||||
"application/Espo/Core/Utils/Database/Helper.php"
|
||||
],
|
||||
"manifest": {
|
||||
}
|
||||
}
|
||||
41
upgrades/5.7/scripts/AfterUpgrade.php
Normal file
41
upgrades/5.7/scripts/AfterUpgrade.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://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/.
|
||||
************************************************************************/
|
||||
|
||||
class AfterUpgrade
|
||||
{
|
||||
public function run($container)
|
||||
{
|
||||
$entityManager = $container->get('entityManager');
|
||||
|
||||
$entityManager->createEntity('ScheduledJob', [
|
||||
'job' => 'ProcessWebhookQueue',
|
||||
'name' => 'Process Webhook Queue',
|
||||
'scheduling' => '*/5 * * * *',
|
||||
'status' => 'Active',
|
||||
]);
|
||||
|
||||
$config = $container->get('config');
|
||||
|
||||
$config->set('hashSecretKey', \Espo\Core\Utils\Util::generateApiKey());
|
||||
$config->save();
|
||||
}
|
||||
}
|
||||
61
upgrades/5.7/scripts/BeforeUpgrade.php
Normal file
61
upgrades/5.7/scripts/BeforeUpgrade.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://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/.
|
||||
************************************************************************/
|
||||
|
||||
class BeforeUpgrade
|
||||
{
|
||||
public function run($container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->checkDatabaseRequirements();
|
||||
|
||||
$pdo = $container->get('entityManager')->getPDO();
|
||||
|
||||
try {
|
||||
$pdo->query("TRUNCATE TABLE `scheduled_job_log_record`");
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
protected function checkDatabaseRequirements()
|
||||
{
|
||||
$databaseRequirements = [
|
||||
'mysql' => '5.6.0',
|
||||
'mariadb' => '10.0.0',
|
||||
];
|
||||
|
||||
$databaseHelper = new \Espo\Core\Utils\Database\Helper($this->container->get('config'));
|
||||
|
||||
$databaseType = $databaseHelper->getDatabaseType();
|
||||
$fullVersion = $databaseHelper->getPdoDatabaseVersion($this->container->get('entityManager')->getPDO());
|
||||
|
||||
if (preg_match('/[0-9]+\.[0-9]+\.[0-9]+/', $fullVersion, $match)) {
|
||||
$version = $match[0];
|
||||
$databaseTypeLc = strtolower($databaseType);
|
||||
|
||||
if (isset($databaseRequirements[$databaseTypeLc])) {
|
||||
if (version_compare($version, $databaseRequirements[$databaseTypeLc], '<')) {
|
||||
$msg = "Your {$databaseType} version is not supported. Please upgrade {$databaseType} to a newer version (5.6 or later).";
|
||||
throw new \Espo\Core\Exceptions\Error($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
upgrades/5.8/data.json
Normal file
7
upgrades/5.8/data.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"manifest": {
|
||||
"delete": [
|
||||
"vendor/php-mime-mail-parser"
|
||||
]
|
||||
}
|
||||
}
|
||||
66
upgrades/5.8/scripts/AfterUpgrade.php
Normal file
66
upgrades/5.8/scripts/AfterUpgrade.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://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/.
|
||||
************************************************************************/
|
||||
|
||||
class AfterUpgrade
|
||||
{
|
||||
public function run($container)
|
||||
{
|
||||
$entityManager = $container->get('entityManager');
|
||||
|
||||
$this->populateOpportunityContactId($entityManager);
|
||||
}
|
||||
|
||||
protected function populateOpportunityContactId($entityManager)
|
||||
{
|
||||
$pdo = $entityManager->getPdo();
|
||||
|
||||
$sql = "
|
||||
SELECT opportunity.id AS 'opportunityId', contact.id AS `contactId` FROM `opportunity`
|
||||
JOIN contact_opportunity ON contact_opportunity.opportunity_id = opportunity.id AND contact_opportunity.deleted = 0
|
||||
JOIN contact ON contact.id = contact_opportunity.contact_id AND contact.deleted = 0
|
||||
WHERE
|
||||
contact.id IN (
|
||||
SELECT MIN(contact.id)
|
||||
FROM `opportunity`
|
||||
JOIN contact_opportunity ON contact_opportunity.opportunity_id = opportunity.id AND contact_opportunity.deleted = 0
|
||||
JOIN contact ON contact.id = contact_opportunity.contact_id AND contact.deleted = 0
|
||||
GROUP BY opportunity.id
|
||||
) AND
|
||||
opportunity.contact_id IS NULL AND
|
||||
opportunity.deleted = 0
|
||||
";
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
|
||||
while ($row = $sth->fetch()) {
|
||||
$cId = $row['contactId'] ?? null;
|
||||
$oId = $row['opportunityId'] ?? null;
|
||||
if (!$cId || !$oId) continue;
|
||||
|
||||
$q = "
|
||||
UPDATE `opportunity` SET contact_id = ".$pdo->quote($cId)." WHERE id = ".$pdo->quote($oId)."
|
||||
";
|
||||
$pdo->query($q);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
upgrades/5.8/scripts/BeforeUpgrade.php
Normal file
56
upgrades/5.8/scripts/BeforeUpgrade.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://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/.
|
||||
************************************************************************/
|
||||
|
||||
class BeforeUpgrade
|
||||
{
|
||||
public function run($container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->checkDatabaseRequirements();
|
||||
}
|
||||
|
||||
protected function checkDatabaseRequirements()
|
||||
{
|
||||
$databaseRequirements = [
|
||||
'mysql' => '5.7.0',
|
||||
'mariadb' => '10.1.0',
|
||||
];
|
||||
|
||||
$databaseHelper = new \Espo\Core\Utils\Database\Helper($this->container->get('config'));
|
||||
|
||||
$databaseType = $databaseHelper->getDatabaseType();
|
||||
$fullVersion = $databaseHelper->getPdoDatabaseVersion($this->container->get('entityManager')->getPDO());
|
||||
|
||||
if (preg_match('/[0-9]+\.[0-9]+\.[0-9]+/', $fullVersion, $match)) {
|
||||
$version = $match[0];
|
||||
$databaseTypeLc = strtolower($databaseType);
|
||||
|
||||
if (isset($databaseRequirements[$databaseTypeLc])) {
|
||||
$requiredVersion = $databaseRequirements[$databaseTypeLc];
|
||||
if (version_compare($version, $requiredVersion, '<')) {
|
||||
$msg = "Your {$databaseType} version is not supported. Please upgrade {$databaseType} to a newer version ({$requiredVersion} or later).";
|
||||
throw new \Espo\Core\Exceptions\Error($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user