Compare commits

..

122 Commits

Author SHA1 Message Date
Yuri Kuznetsov 4d755d3795 fix tests 2021-01-25 11:46:56 +02:00
Yuri Kuznetsov 9fe61d2342 v 2021-01-25 11:12:38 +02:00
Taras Machyshyn 1f17d79e2d Upgrade fixes 2021-01-21 17:53:54 +02:00
Taras Machyshyn 3b0c7f1d5b Upgrade bug fixes 2021-01-21 17:39:44 +02:00
Taras Machyshyn d30edee790 Upgrade bug fixes 2021-01-21 16:14:14 +02:00
Yuri Kuznetsov 0815cc3b5e xlsx export fix and refactoring 2021-01-18 13:58:13 +02:00
Yuri Kuznetsov ddff78756d fix label 2021-01-11 12:24:07 +02:00
Taras Machyshyn f8c01808d4 Wrong directory owner fixes 2021-01-05 16:16:49 +02:00
Yuri Kuznetsov e073c82992 fix orm repository 2021-01-05 10:27:57 +02:00
Yuri Kuznetsov aa82f96993 Fix reply email with external account 2021-01-04 12:17:57 +02:00
Yuri Kuznetsov 867a0225f1 v 2021-01-04 11:47:18 +02:00
Yuri Kuznetsov 4ab636ded8 autoload test and refactoring 2020-12-31 09:59:29 +02:00
Yuri Kuznetsov f764375e0e file manager method cleanups 2020-12-31 09:33:12 +02:00
Arkady 98b20a3e24 fix autoload path merging (#1875)
Co-authored-by: Arkadiy Asuratov <a.asuratov@dubas.pro>
2020-12-31 08:56:24 +02:00
Yuri Kuznetsov ca17a5453a fix warning 2020-12-28 11:51:56 +02:00
Yuri Kuznetsov 641382dc66 cs fix and error throwing 2020-12-21 11:50:07 +02:00
Yuri Kuznetsov 8f89fa4597 cs fix 2020-12-21 11:33:51 +02:00
Yuri Kuznetsov 543eb21a8b fix flotr 2020-12-21 11:27:48 +02:00
Yuri Kuznetsov 9fbc62837f fix validation 2020-12-21 11:07:31 +02:00
Yuri Kuznetsov d3d710b2b9 v 2020-12-04 15:04:36 +02:00
Yuri Kuznetsov 4426d81e77 fix category tree 2020-12-04 14:28:04 +02:00
Yuri Kuznetsov 6b3e81e3fd settings currency validation 2020-12-03 11:56:44 +02:00
Yuri Kuznetsov dd708778d6 field clone validations 2020-12-03 11:56:15 +02:00
Yuri Kuznetsov 0bc06f3cae fix autofollow 2020-12-02 11:04:33 +02:00
Yuri Kuznetsov 0ab8853455 fix notification for portal user 2020-12-01 13:14:17 +02:00
Yuri Kuznetsov a612ffd2d2 fix pdf google maps 2020-12-01 11:40:23 +02:00
Yuri Kuznetsov bc19a6bb5f flotr2 fix 2020-11-30 12:48:53 +02:00
Eymen Elkum 8cc6a2ddf0 fix bc (#1853) 2020-11-29 11:25:07 +02:00
Yuri Kuznetsov c1f0c47cf7 bc fix 2020-11-28 19:33:12 +02:00
Yuri Kuznetsov 7e71eb0aeb fix warning 2020-11-27 18:42:52 +02:00
Taras Machyshyn 196f787a1f Fix installer for PHP 8 2020-11-27 17:32:00 +02:00
Taras Machyshyn b7bec1793c Fix installer for PHP 8 2020-11-27 12:26:57 +02:00
Taras Machyshyn b97aa8e3a7 Fix gitignore 2020-11-27 12:26:30 +02:00
Taras Machyshyn 21dcf59f21 Fix gitignore 2020-11-27 12:25:06 +02:00
Taras Machyshyn 326b6c8c53 Merge branch 'hotfix/6.0.7' of https://github.com/espocrm/espocrm into hotfix/6.0.7 2020-11-27 12:16:34 +02:00
Taras Machyshyn 7ebf0e7bf8 Fix installer for PHP 8 2020-11-27 12:16:01 +02:00
Yuri Kuznetsov 014df8b9d3 Merge branch 'hotfix/6.0.7' of github.com:espocrm/espocrm into hotfix/6.0.7 2020-11-27 12:12:28 +02:00
Yuri Kuznetsov 6d85055147 fix loaders 2020-11-27 12:12:16 +02:00
Taras Machyshyn 077f7d8855 Fix installer for PHP 8 2020-11-27 12:03:04 +02:00
Taras Machyshyn 296484360f Fix installer for PHP 8 2020-11-27 11:03:40 +02:00
Yuri Kuznetsov 1071f8a045 v 2020-11-26 12:09:45 +02:00
Yuri Kuznetsov 962d3a049c disable edit access to user email address 2020-11-26 09:44:22 +02:00
Yuri Kuznetsov 161859606a ceanup 2020-11-21 14:20:14 +02:00
Yuri Kuznetsov a71df0cff6 fix layout 2020-11-20 09:52:54 +02:00
Yuri Kuznetsov fb9ff0b8c0 fix deprecated 2020-11-18 13:16:24 +02:00
Yuri Kuznetsov d1a62baa12 fix deprecated 2020-11-18 12:53:17 +02:00
Yuri Kuznetsov cdec7ccfe8 v 2020-11-18 11:34:14 +02:00
Yuri Kuznetsov 25bc3a40f7 fix export currency 2020-11-18 09:41:54 +02:00
Yuri Kuznetsov 02e5c1fc44 v 2020-11-16 13:40:49 +02:00
Yuri Kuznetsov 844e9dd8e3 Merge branch 'hotfix/6.0.5' of github.com:espocrm/espocrm into hotfix/6.0.5 2020-11-16 13:38:16 +02:00
Yuri Kuznetsov 39f2ec83d5 fix duplicate attachments multiple 2020-11-16 13:35:53 +02:00
Taras Machyshyn e4d2af03a5 Fixed PHP binary path 2020-11-16 13:34:24 +02:00
Taras Machyshyn 37616d69bf Merge branch 'hotfix/6.0.5' of https://github.com/espocrm/espocrm into hotfix/6.0.5 2020-11-16 13:28:03 +02:00
Taras Machyshyn 4e36f71dd8 Fixed PHP binary path 2020-11-16 13:27:16 +02:00
Yuri Kuznetsov b5877ac32a fix person name 2020-11-16 13:03:56 +02:00
Yuri Kuznetsov 92de588a6e update npm packages 2020-11-16 11:07:12 +02:00
Yuri Kuznetsov 700f4b5b10 fix export 2020-11-14 10:24:22 +02:00
Yuri Kuznetsov 9c0a7b6778 app service changes 2020-11-13 12:39:27 +02:00
Yuri Kuznetsov 9a54c66f68 preferences fixes 2020-11-13 12:32:13 +02:00
Yuri Kuznetsov 83332178a0 fix acl check command 2020-11-12 15:05:03 +02:00
Yuri Kuznetsov fb33c2acf1 fix task panel layout 2020-11-10 12:48:40 +02:00
Yuri Kuznetsov 3c5914b5d9 fix text filter by foreign field 2020-11-10 09:22:20 +02:00
Taras Machyshyn 8c48f72a3c Command fix 2020-11-09 15:16:30 +02:00
Taras Machyshyn a8ce780718 Upgrade fix 2020-11-04 10:23:07 +02:00
Yuri Kuznetsov 31eff86578 fix link search blur 2020-11-03 16:44:20 +02:00
Yuri Kuznetsov 892f9f01f6 export xlsx currency format 2020-11-03 10:43:33 +02:00
Yuri Kuznetsov 860a58847d currency format 10 $ 2020-11-03 10:03:12 +02:00
Yuri Kuznetsov 1ed90d2306 v 2020-11-02 12:55:01 +02:00
Yuri Kuznetsov aff0b350f3 fix portal runner 2020-11-02 12:54:13 +02:00
Yuri Kuznetsov 651b0be5ed add log to slim 2020-10-31 13:38:11 +02:00
Yuri Kuznetsov cead33c09d template manager image 2020-10-30 09:26:37 +02:00
Yuri Kuznetsov 80602c084f fix pdf page number 2020-10-29 16:34:49 +02:00
Yuri Kuznetsov b0420b8b93 fix email folder 2020-10-28 13:19:49 +02:00
Yuri Kuznetsov b47018cbab hide email menu items in portal 2020-10-28 11:58:44 +02:00
Yuri Kuznetsov ea69ab6eaf v 2020-10-28 11:12:51 +02:00
Eymen Elkum 92887401b2 address view remove duplicated codes (#1823) 2020-10-28 11:10:52 +02:00
Yuri Kuznetsov df0bda6324 Merge branch 'hotfix/6.0.3' of github.com:espocrm/espocrm into hotfix/6.0.3 2020-10-28 11:05:37 +02:00
Yuri Kuznetsov 9d67808496 fix tester 2020-10-28 11:02:24 +02:00
Yuri Kuznetsov cf33a98f20 fix activities and select manager 2020-10-27 17:04:49 +02:00
Yuri Kuznetsov afbda344ec fix export specific fields 2020-10-27 14:25:47 +02:00
Yuri Kuznetsov 1079597584 fix 2fa 2020-10-27 14:13:00 +02:00
Yuri Kuznetsov af9b411a4c diff all patch versions 2020-10-27 12:07:39 +02:00
Yuri Kuznetsov cd49d951d8 v 2020-10-27 11:59:55 +02:00
Yuri Kuznetsov 7d63c114aa fix export all fields 2020-10-27 11:06:52 +02:00
Yuri Kuznetsov af6f05ba07 formula fix logical operator priority 2020-10-27 10:31:42 +02:00
Yuri Kuznetsov 46f8eb0cdb formula fix logical not 2020-10-27 10:23:03 +02:00
Pomazan Bogdan c543bb5a5b Update Admin.json (#1814) 2020-10-26 15:36:46 +02:00
Yuri Kuznetsov d1997089e4 v 2020-10-26 13:06:11 +02:00
Yuri Kuznetsov 57dd0177f9 fix export 2020-10-25 18:56:57 +02:00
Yuri Kuznetsov ba76e5ee3d fix language manager 2020-10-25 18:54:34 +02:00
Yuri Kuznetsov 76c93b842c case close reject actions only if available in status list 2020-10-23 10:25:26 +03:00
Yuri Kuznetsov e73bff5ddf v 2020-10-22 12:14:40 +03:00
Yuri Kuznetsov 7d5c0d754e fix upgrade script 2020-10-22 11:47:59 +03:00
Taras Machyshyn e08a74b129 Bug fixes 2020-10-22 11:30:00 +03:00
Taras Machyshyn be07aafaf4 Fix 2020-10-21 15:33:42 +03:00
Taras Machyshyn e2bf27e524 Fix 2020-10-21 12:00:47 +03:00
Yuri Kuznetsov b0157adbe2 cs fix 2020-10-21 09:40:34 +03:00
Yuri Kuznetsov 239593afb5 fix mass actions 2020-10-21 09:31:42 +03:00
Yuri Kuznetsov b97d764588 fix error msg 2020-10-19 15:30:36 +03:00
Yuri Kuznetsov bf8e7af984 export fixes 2020-10-19 15:19:24 +03:00
Yuri Kuznetsov bf68c475e5 email queue items filters email 2020-10-19 13:37:06 +03:00
Yuri Kuznetsov 29624239ba search queue items 2020-10-19 13:35:43 +03:00
Yuri Kuznetsov 4cb3c4918b campaign log search by email address 2020-10-19 12:59:36 +03:00
Yuri Kuznetsov 26fa0d65e1 fix campaign log search 2020-10-19 12:57:17 +03:00
Yuri Kuznetsov 958baed3df fix email invalid on bounced 2020-10-19 12:35:48 +03:00
Yuri Kuznetsov 40d6fb7565 fix export 2020-10-15 19:40:37 +03:00
Yuri Kuznetsov a7551b7dbf fix request getContentType 2020-10-15 17:35:58 +03:00
Yuri Kuznetsov 9ed33c4dc4 remove self assign in portal 2020-10-14 16:23:02 +03:00
Yuri Kuznetsov 6b18fbe8f7 select manager fix 2020-10-14 15:48:52 +03:00
Yuri Kuznetsov 53b9880ea8 entry point changes 2020-10-14 15:15:09 +03:00
Yuri Kuznetsov 62538664df Merge branch 'master' of github.com:espocrm/espocrm 2020-10-14 13:17:56 +03:00
Yuri Kuznetsov f678fb16cc ouauth callback changes 2020-10-14 13:17:46 +03:00
Yuri Kuznetsov fe09aa1cd9 cs fix 2020-10-13 11:01:52 +03:00
Yuri Kuznetsov 710decc610 fix htmlizer and email template no id 2020-10-13 10:47:37 +03:00
Yuri Kuznetsov 5d481d3528 email auto reply error log 2020-10-13 10:47:27 +03:00
Yuri Kuznetsov 8bcd3b72c5 hook fix 2020-10-13 09:56:13 +03:00
Yuri Kuznetsov 0a0da810e9 application state changes 2020-10-13 09:56:04 +03:00
Yuri Kuznetsov e630c046fe Merge branch 'hotfix/5.9.5' 2020-10-12 14:01:49 +03:00
Yuri Kuznetsov 2857b5c53d fix import button 2020-10-12 14:00:25 +03:00
Yuri Kuznetsov 0965c61f55 fix reminder submition 2020-10-12 10:14:17 +03:00
Yuri Kuznetsov 9dd318b176 fix label 2020-10-10 16:08:58 +03:00
Yuri Kuznetsov f8beca720c label fixes 2020-10-10 16:06:57 +03:00
114 changed files with 3215 additions and 1581 deletions
+3 -3
View File
@@ -7,7 +7,7 @@
/data/tmp/*
/build
/node_modules
npm-debug.log
/npm-debug.log
/test.php
/main.html
@@ -15,7 +15,7 @@ npm-debug.log
!/tests/unit/testData/cache/.data
/tests/integration/config.php
.phpunit.result.cache
composer.phar
vendor/
/composer.phar
/vendor/
/custom/Espo/Custom/*
/install/config.php
@@ -148,14 +148,12 @@ class GoogleMaps
$GLOBALS['log']->debug("Template helper _googleMapsImage: URL: {$url}.");
$image = \Espo\Core\TemplateHelpers\GoogleMaps::getImage($url);
$image = \Espo\Classes\TemplateHelpers\GoogleMaps::getImage($url);
if (!$image) {
return null;
}
$filePath = tempnam(sys_get_temp_dir(), 'google_maps_image');
file_put_contents($filePath, $image);
+2
View File
@@ -178,6 +178,8 @@ class Auth
}
if ($hasAuthData) {
$authResult = null;
try {
$authResult = $this->authentication->login($username, $password, $request, $authenticationMethod);
}
+8 -2
View File
@@ -138,7 +138,13 @@ class RequestWrapper implements ApiRequest
public function getContentType() : ?string
{
return $this->getHeader('Content-Type');
if (!$this->hasHeader('Content-Type')) {
return null;
}
$contentType = $this->request->getHeader('Content-Type')[0];
return strtolower($contentType);
}
public function getBodyContents() : ?string
@@ -163,7 +169,7 @@ class RequestWrapper implements ApiRequest
{
$contents = $this->getBodyContents();
if (strtolower($this->getContentType()) === 'application/json' && $contents) {
if ($this->getContentType() === 'application/json' && $contents) {
$this->parsedBody = json_decode($contents);
if (is_array($this->parsedBody)) {
@@ -39,6 +39,7 @@ use Espo\Core\{
Api\ResponseWrapper,
Api\RouteProcessor,
Utils\Route,
Utils\Log,
};
use Slim\{
@@ -63,12 +64,15 @@ class Api implements ApplicationRunner
protected $injectableFactory;
protected $applicationUser;
protected $routeUtil;
protected $log;
public function __construct(InjectableFactory $injectableFactory, ApplicationUser $applicationUser, Route $routeUtil)
{
public function __construct(
InjectableFactory $injectableFactory, ApplicationUser $applicationUser, Route $routeUtil, Log $log
) {
$this->injectableFactory = $injectableFactory;
$this->applicationUser = $applicationUser;
$this->routeUtil = $routeUtil;
$this->log = $log;
}
public function run()
@@ -85,7 +89,7 @@ class Api implements ApplicationRunner
$this->addRoute($slim, $item);
}
$slim->addErrorMiddleware(false, true, true);
$slim->addErrorMiddleware(false, true, true, $this->log);
$slim->run();
}
@@ -29,9 +29,8 @@
namespace Espo\Core\ApplicationRunners;
use Espo\Core\Exceptions\Error;
use Espo\Core\{
Exceptions\Error,
InjectableFactory,
EntryPointManager,
ApplicationUser,
@@ -39,7 +38,6 @@ use Espo\Core\{
Portal\Application as PortalApplication,
Utils\Route,
Utils\ClientManager,
Authentication\Authentication,
Api\Auth as ApiAuth,
Api\ErrorOutput as ApiErrorOutput,
@@ -48,18 +46,12 @@ use Espo\Core\{
};
use Slim\{
App as SlimApp,
Factory\AppFactory as SlimAppFactory,
};
use Psr\Http\{
Message\ResponseInterface as Psr7Response,
Message\ServerRequestInterface as Psr7Request,
Server\RequestHandlerInterface as Psr7RequestHandler,
ResponseEmitter,
Factory\ServerRequestCreatorFactory,
Psr7\Response,
};
use StdClass;
use Exception;
/**
@@ -113,30 +105,19 @@ class EntryPoint implements ApplicationRunner
}
}
$slim = SlimAppFactory::create();
$request = (ServerRequestCreatorFactory::create())->createServerRequestFromGlobals();
$slim->setBasePath(Route::detectBasePath());
if ($request->getMethod() !== 'GET') {
throw new Error("Only GET request allowed for entry points.");
}
$slim->add(
function (Psr7Request $request, Psr7RequestHandler $handler) use (
$entryPoint, $data, $authRequired, $authNotStrict, $slim
) : Psr7Response {
$requestWrapped = new RequestWrapper($request, $slim->getBasePath());
$responseWrapped = new ResponseWrapper($handler->handle($request));
$requestWrapped = new RequestWrapper($request, Route::detectBasePath());
$this->processRequest($entryPoint, $requestWrapped, $responseWrapped, $data, $authRequired, $authNotStrict);
$responseWrapped = new ResponseWrapper(new Response());
return $responseWrapped->getResponse();
}
);
$this->processRequest($entryPoint, $requestWrapped, $responseWrapped, $data, $authRequired, $authNotStrict);
$route = Route::detectEntryPointRoute();
$slim->get($route, function (Psr7Request $request, Psr7Response $response) : Psr7Response {
return $response;
});
$slim->run();
(new ResponseEmitter())->emit($responseWrapped->getResponse());
}
protected function processRequest(
+36 -9
View File
@@ -55,11 +55,13 @@ class ApplicationState
}
/**
* Get portal ID (if an applicaition is portal).
* Get a portal ID (if an applicaition is portal).
*/
public function getPortalId() : string
{
if (!$this->isPortal()) throw new Error("Can't get portal ID for non-portal application.");
if (!$this->isPortal()) {
throw new Error("Can't get portal ID for non-portal application.");
}
return $this->getPortal()->id;
}
@@ -69,7 +71,9 @@ class ApplicationState
*/
public function getPortal() : PortalEntity
{
if (!$this->isPortal()) throw new Error("Can't get portal for non-portal application.");
if (!$this->isPortal()) {
throw new Error("Can't get portal for non-portal application.");
}
return $this->container->get('portal');
}
@@ -83,21 +87,38 @@ class ApplicationState
}
/**
* Get current logged user. If no auth is applied, then system user will be returned.
* Get a current logged user. If no auth is applied, then the system user will be returned.
*/
public function getUser() : UserEntity
{
if (!$this->hasUser()) throw new Error("User is not yet available.");
if (!$this->hasUser()) {
throw new Error("User is not yet available.");
}
return $this->container->get('user');
}
/**
* Get an ID of a current logged user. If no auth is applied, then the system user will be returned.
*/
public function getUserId() : string
{
return $this->getUser()->id;
}
/**
* Whether a user is logged.
*/
public function isLogged() : bool
{
if (!$this->container->has('user')) return false;
if ($this->getUser()->isSystem()) return false;
if (!$this->container->has('user')) {
return false;
}
if ($this->getUser()->isSystem()) {
return false;
}
return true;
}
@@ -106,7 +127,10 @@ class ApplicationState
*/
public function isAdmin() : bool
{
if (!$this->isLogged()) return false;
if (!$this->isLogged()) {
return false;
}
return $this->getUser()->isAdmin();
}
@@ -116,7 +140,10 @@ class ApplicationState
*/
public function isApi() : bool
{
if (!$this->isLogged()) return false;
if (!$this->isLogged()) {
return false;
}
return $this->getUser()->isApi();
}
}
@@ -368,7 +368,7 @@ class Authentication
if ($code) {
if (!$impl->verifyCode($loggedUser, $code)) {
Result::fail('Code not verified');
return Result::fail('Code not verified');
}
return $result;
@@ -103,7 +103,7 @@ class CommandManager
$skipIndex = 1;
if (isset($argv[0]) && $argv[0] === 'command.php') {
if (isset($argv[0]) && preg_match('/command\.php$/', $argv[0])) {
$skipIndex = 2;
}
@@ -48,37 +48,57 @@ class AclCheck implements Command
$id = $options['id'] ?? null;
$action = $options['action'] ?? null;
if (empty($userId)) return null;
if (empty($scope)) return null;
if (empty($id)) return null;
if (empty($userId)) {
return null;
}
if (empty($scope)) {
return null;
}
if (empty($id)) {
return null;
}
$container = $this->container;
$entityManager = $container->get('entityManager');
$user = $entityManager->getEntity('User', $userId);
if (!$user) return null;
if (!$user) {
return null;
}
if ($user->isPortal()) {
$portalIdList = $user->getLinkMultipleIdList('portals');
foreach ($portalIdList as $portalId) {
$application = new PortalApplication($portalId);
$containerPortal = $application->getContainer();
$entityManager = $containerPortal->get('entityManager');
$user = $entityManager->getEntity('User', $userId);
if (!$user) return null;
if (!$user) {
return null;
}
$result = $this->check($user, $scope, $id, $action, $containerPortal);
if ($result) {
return 'true';
}
}
return null;
}
if ($this->check($user, $scope, $id, $action, $container)) {
return 'true';
}
return null;
}
protected function check($user, $scope, $id, $action, $container)
@@ -86,7 +106,10 @@ class AclCheck implements Command
$entityManager = $container->get('entityManager');
$entity = $entityManager->getEntity($scope, $id);
if (!$entity) return false;
if (!$entity) {
return false;
}
$aclManager = $container->get('aclManager');
@@ -41,6 +41,7 @@ use Espo\Core\{
use Symfony\Component\Process\PhpExecutableFinder;
use Exception;
use Throwable;
class Upgrade implements Command
{
@@ -78,7 +79,7 @@ class Upgrade implements Command
$versionInfo = $this->getVersionInfo($toVersion);
$nextVersion = $versionInfo->nextVersion ?? null;
$lastVersion = $infoData->lastVersion ?? null;
$lastVersion = $versionInfo->lastVersion ?? null;
$packageFile = $this->getPackageFile($params, $versionInfo);
@@ -127,7 +128,7 @@ class Upgrade implements Command
try {
$this->runUpgradeProcess($upgradeId, $params);
}
catch (Exception $e) {
catch (Throwable $e) {
$errorMessage = $e->getMessage();
}
@@ -137,7 +138,8 @@ class Upgrade implements Command
$this->fileManager->unlink($packageFile);
}
if (!empty($errorMessage)) {
if (isset($errorMessage)) {
$errorMessage = !empty($errorMessage) ? $errorMessage : "Error: An unexpected error occurred.";
fwrite(\STDOUT, $errorMessage . "\n");
return;
@@ -287,8 +289,11 @@ class Upgrade implements Command
$upgradeManager = $this->getUpgradeManager(true);
$upgradeManager->runInstallStep($stepName, ['id' => $upgradeId]);
}
} catch (Exception $e) {
$GLOBALS['log']->error('Upgrade Error: ' . $e->getMessage());
} catch (Throwable $e) {
try {
$GLOBALS['log']->error('Upgrade Error: ' . $e->getMessage());
}
catch (Throwable $t) {}
throw new Error($e->getMessage());
}
@@ -308,7 +313,10 @@ class Upgrade implements Command
$shellResult = shell_exec($command);
if ($shellResult !== 'true') {
$GLOBALS['log']->error('Upgrade Error: ' . $shellResult);
try {
$GLOBALS['log']->error('Upgrade Error: ' . $shellResult);
}
catch (Throwable $t) {}
throw new Error($shellResult);
}
@@ -73,7 +73,7 @@ class UpgradeStep implements Command
try {
$result = $upgradeManager->runInstallStep($stepName, $params); // throw Exception on error
} catch (\Exception $e) {
die("Error: " . $e->getMessage() . "\n");
die("Error: " . $e->getMessage());
}
if (is_bool($result)) {
@@ -38,6 +38,6 @@ class NotType extends BaseFunction
{
public function process(ArgumentList $args)
{
return !$this->evaluate($args);
return !$this->evaluate($args[0]);
}
}
+2 -1
View File
@@ -40,7 +40,8 @@ class Parser
{
protected $priorityList = [
['='],
['||', '&&'],
['||'],
['&&'],
['==', '!=', '>', '<', '>=', '<='],
['+', '-'],
['*', '/', '%'],
+1 -1
View File
@@ -120,7 +120,7 @@ class Htmlizer
$relationList = $entity->getRelationList();
if (!$skipLinks && $level === 0 && $this->entityManager) {
if (!$skipLinks && $level === 0 && $this->entityManager && $entity->id) {
foreach ($relationList as $relation) {
$collection = null;
+17 -4
View File
@@ -155,9 +155,11 @@ class InjectableFactory
$dependencyClass = null;
if ($param->getType()) {
$type = $param->getType();
if ($type && !$type->isBuiltin()) {
try {
$dependencyClass = $param->getClass();
$dependencyClass = new ReflectionClass($type->getName());
}
catch (Throwable $e) {
$badClassName = $param->getType()->getName();
@@ -241,15 +243,26 @@ class InjectableFactory
}
$params = $class->getMethod($methodName)->getParameters();
if (!$params || !count($params)) {
return false;
}
if ($skipInstanceCheck) {
return true;
}
$injection = $this->container->get($name);
$paramClass = $params[0]->getClass();
$paramClass = null;
if ($skipInstanceCheck || $paramClass && $paramClass->isInstance($injection)) {
$type = $params[0]->getType();
if ($type && !$type->isBuiltin()) {
$paramClass = new ReflectionClass($type->getName());
}
if ($paramClass && $paramClass->isInstance($injection)) {
return true;
}
@@ -59,7 +59,7 @@ class BaseLanguage implements Loader
$this->fileManager,
$this->metadata,
$this->dataCache,
$this->config->get('useCache')
$this->config->get('useCache') ?? false
);
}
}
@@ -42,7 +42,7 @@ class DefaultLanguage extends BaseLanguage
$this->fileManager,
$this->metadata,
$this->dataCache,
$this->config->get('useCache')
$this->config->get('useCache') ?? false
);
}
}
+1 -1
View File
@@ -64,7 +64,7 @@ class Language implements Loader
$this->fileManager,
$this->metadata,
$this->dataCache,
$this->config->get('useCache')
$this->config->get('useCache') ?? false
);
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ class Metadata implements Loader
public function load() : MetadataService
{
$useCache = $this->config->get('useCache');
$useCache = $this->config->get('useCache') ?? false;
return new MetadataService($this->fileManager, $this->dataCache, $useCache);
}
@@ -123,4 +123,22 @@ class DefaultNotificator implements Notificator
return $this->userIdEnabledMap[$userId];
}
/**
* For backward compatibility.
* @todo Remove.
*/
protected function getEntityManager()
{
return $this->entityManager;
}
/**
* For backward compatibility.
* @todo Remove.
*/
protected function getUser()
{
return $this->user;
}
}
+8 -8
View File
@@ -120,11 +120,11 @@ class Tcpdf extends \TCPDF
$html = $this->headerHtml;
if ($this->useGroupNumbers) {
$html = str_replace('{pageNumber}', '{:png:}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{:pnp:}', $html);
$html = str_replace('{pageNumber}', '{{:png:}}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
} else {
$html = str_replace('{pageNumber}', '{:pnp:}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{:pnp:}', $html);
$html = str_replace('{pageNumber}', '{{:pnp:}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
}
if ($this->isUnicodeFont()) {
@@ -148,11 +148,11 @@ class Tcpdf extends \TCPDF
$html = $this->footerHtml;
if ($this->useGroupNumbers) {
$html = str_replace('{pageNumber}', '{:png:}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{:pnp:}', $html);
$html = str_replace('{pageNumber}', '{{:png:}}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
} else {
$html = str_replace('{pageNumber}', '{:pnp:}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{:pnp:}', $html);
$html = str_replace('{pageNumber}', '{{:pnp:}}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
}
if ($this->isUnicodeFont()) {
@@ -34,12 +34,13 @@ use Espo\Core\{
InjectableFactory,
ApplicationUser,
Portal\Utils\Route,
Utils\Log,
};
class Api extends ApiBase
{
public function __construct(InjectableFactory $injectableFactory, ApplicationUser $applicationUser, Route $routeUtil)
public function __construct(InjectableFactory $injectableFactory, ApplicationUser $applicationUser, Route $routeUtil, Log $log)
{
parent::__construct($injectableFactory, $applicationUser, $routeUtil);
parent::__construct($injectableFactory, $applicationUser, $routeUtil, $log);
}
}
@@ -49,6 +49,9 @@ class CategoryTree extends Database
->select()
->from($pathEntityType)
->select(['ascendorId', "'" . $entity->id . "'"])
->where([
'descendorId' => $parentId,
])
->build();
$subSelect2 = $em->getQueryBuilder()
+43 -10
View File
@@ -2368,7 +2368,9 @@ class SelectManager
protected function textFilter($textFilter, array &$result, $noFullText = false)
{
$fieldDefs = $this->getSeed()->getAttributes();
$fieldList = $this->getTextFilterFieldList();
$group = [];
$textFilterContainsMinLength = $this->getConfig()->get('textFilterContainsMinLength', self::MIN_LENGTH_FOR_CONTENT_SEARCH);
@@ -2401,6 +2403,7 @@ class SelectManager
$textFilterForFullTextSearch = str_replace('%', '*', $textFilterForFullTextSearch);
$skipFullTextSearch = false;
if (!$forceFullTextSearch) {
if (mb_strpos($textFilterForFullTextSearch, '*') === 0) {
$skipFullTextSearch = true;
@@ -2409,9 +2412,12 @@ class SelectManager
}
}
if ($noFullText) $skipFullTextSearch = true;
if ($noFullText) {
$skipFullTextSearch = true;
}
$fullTextSearchData = null;
if (!$skipFullTextSearch) {
$fullTextSearchData = $this->getFullTextSearchDataForTextFilter($textFilterForFullTextSearch, !$useFullTextSearch);
}
@@ -2419,6 +2425,7 @@ class SelectManager
$fullTextGroup = [];
$fullTextSearchFieldList = [];
if ($fullTextSearchData) {
if ($this->fullTextRelevanceThreshold) {
$fullTextGroup[] = [$fullTextSearchData['where'] . '>=' => $this->fullTextRelevanceThreshold];
@@ -2434,12 +2441,15 @@ class SelectManager
$orderTypeMap = [
'combined' => self::FT_ORDER_COMBINTED,
'relavance' => self::FT_ORDER_RELEVANCE,
'relevance' => self::FT_ORDER_RELEVANCE,
'original' => self::FT_ORDER_ORIGINAL,
];
$mOrderType = $this->getMetadata()->get(['entityDefs', $this->entityType, 'collection', 'fullTextSearchOrderType']);
if ($mOrderType) $fullTextOrderType = $orderTypeMap[$mOrderType];
if ($mOrderType) {
$fullTextOrderType = $orderTypeMap[$mOrderType];
}
if (!isset($result['orderBy']) || $fullTextOrderType === self::FT_ORDER_RELEVANCE) {
$result['orderBy'] = [[$relevanceExpression, 'desc']];
@@ -2468,9 +2478,14 @@ class SelectManager
foreach ($fieldList as $field) {
if ($useFullTextSearch) {
if (in_array($field, $fullTextSearchFieldList)) continue;
if (in_array($field, $fullTextSearchFieldList)) {
continue;
}
}
if ($forceFullTextSearch) {
continue;
}
if ($forceFullTextSearch) continue;
$seed = $this->getSeed();
@@ -2478,21 +2493,34 @@ class SelectManager
if (strpos($field, '.') !== false) {
list($link, $foreignField) = explode('.', $field);
$foreignEntityType = $seed->getRelationParam($link, 'entity');
$seed = $this->getEntityManager()->getEntity($foreignEntityType);
$this->addLeftJoin($link, $result);
if ($seed->getRelationParam($link, 'type') === $seed::HAS_MANY) {
$this->setDistinct(true, $result);
}
$attributeType = $seed->getAttributeType($foreignField);
} else {
$attributeType = $seed->getAttributeType($field);
if ($attributeType === 'foreign') {
$link = $seed->getAttributeParam($field, 'relation');
if ($link) {
$this->addLeftJoin($link, $result);
}
}
}
if ($attributeType === 'int') {
if (is_numeric($textFilter)) {
$group[$field] = intval($textFilter);
}
continue;
}
@@ -2666,11 +2694,6 @@ class SelectManager
$selectParams1['havingClause'][] = $selectParams2['havingClause'];
}
if (!empty($selectParams2['leftJoins'])) {
foreach ($selectParams2['leftJoins'] as $item) {
$this->addLeftJoin($item, $selectParams1);
}
}
if (!empty($selectParams2['joins'])) {
foreach ($selectParams2['joins'] as $item) {
@@ -2678,6 +2701,16 @@ class SelectManager
}
}
if (!empty($selectParams2['leftJoins'])) {
foreach ($selectParams2['leftJoins'] as $item) {
if ($this->hasJoin($item, $selectParams1)) {
continue;
}
$this->addLeftJoin($item, $selectParams1);
}
}
if (isset($selectParams2['select'])) {
$selectParams1['select'] = $selectParams2['select'];
}
@@ -462,7 +462,7 @@ abstract class Base
{
if (!isset($this->data['restoreFileList'])) {
$backupPath = $this->getPath('backupPath');
$this->data['restoreFileList'] = $this->getFileList($backupPath);
$this->data['restoreFileList'] = $this->getFileList($backupPath, true);
}
return $this->data['restoreFileList'];
@@ -496,7 +496,7 @@ abstract class Base
*
* @return array
*/
protected function getFileList($dirPath)
protected function getFileList($dirPath, $skipVendorFileList = false)
{
$fileList = array();
@@ -508,10 +508,11 @@ abstract class Base
}
}
//vendor file list
$vendorFileList = $this->getVendorFileList('copy');
if (!empty($vendorFileList)) {
$fileList = array_merge($fileList, $vendorFileList);
if (!$skipVendorFileList) {
$vendorFileList = $this->getVendorFileList('copy');
if (!empty($vendorFileList)) {
$fileList = array_merge($fileList, $vendorFileList);
}
}
return $fileList;
@@ -738,7 +739,11 @@ abstract class Base
return true;
}
catch (Throwable $e) {
$GLOBALS['log']->error('Database rebuild failure, details: '. $e->getMessage() .'.');
try {
$GLOBALS['log']->error('Database rebuild failure, details: '. $e->getMessage() .'.');
}
catch (Throwable $e) {}
}
return false;
@@ -77,8 +77,6 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
protected function initPackage(array $data)
{
$GLOBALS['log']->setLevel('info');
$processId = $data['id'];
if (empty($processId)) {
@@ -150,19 +148,23 @@ class Install extends \Espo\Core\Upgrades\Actions\Base
$GLOBALS['log']->info('Installation process ['. $this->getProcessId() .']: Start "copy" step.');
/* remove files defined in a manifest "deleteBeforeCopy" */
$this->deleteFiles('deleteBeforeCopy', true);
/* remove files defined in a manifest */
if (!$this->deleteFiles('delete', true)) {
$this->throwErrorAndRemovePackage('Cannot delete files.');
}
/* copy files from directory "Files" to EspoCRM files */
if (!$this->copyFiles()) {
$this->throwErrorAndRemovePackage('Cannot copy files.');
}
/* remove files defined in a manifest */
$this->deleteFiles('delete', true);
if (!$this->deleteFiles('vendor')) {
$this->throwErrorAndRemovePackage('Cannot delete vendor files.');
}
$this->deleteFiles('vendor');
$this->copyFiles('vendor');
if (!$this->copyFiles('vendor')) {
$this->throwErrorAndRemovePackage('Cannot copy vendor files.');
}
$GLOBALS['log']->info('Installation process ['. $this->getProcessId() .']: End "copy" step.');
}
@@ -169,6 +169,7 @@ class AdminNotificationManager
$extension = $this->getEntityManager()->getRepository('Extension')
->select(['version'])
->where([
'name' => $extensionName,
'isInstalled' => true,
])
->order('createdAt', true)
+28 -33
View File
@@ -66,22 +66,13 @@ class Autoload
$this->loader = $loader;
}
public function get($key = null, $returns = null)
protected function getData() : array
{
if (!isset($this->data)) {
$this->init();
}
if (!isset($key)) {
return $this->data;
}
return Util::getValueByKey($this->data, $key, $returns);
}
public function getAll()
{
return $this->get();
return $this->data;
}
protected function init()
@@ -94,46 +85,48 @@ class Autoload
return;
}
$this->data = $this->unify();
$this->data = $this->loadData();
if ($useCache) {
$result = $this->dataCache->store($this->cacheKey, $this->data);
}
}
protected function unify()
protected function loadData() : array
{
$data = $this->loadData($this->paths['corePath']);
$data = $this->loadDataFromFile($this->paths['corePath']);
foreach ($this->metadata->getModuleList() as $moduleName) {
$modulePath = str_replace('{*}', $moduleName, $this->paths['modulePath']);
$data = array_merge($data, $this->loadData($modulePath));
$data = array_merge_recursive($data, $this->loadDataFromFile($modulePath));
}
$data = array_merge($data, $this->loadData($this->paths['customPath']));
$data = array_merge_recursive($data, $this->loadDataFromFile($this->paths['customPath']));
return $data;
}
protected function loadData($autoloadFile, $returns = [])
protected function loadDataFromFile(string $filePath) : array
{
if (file_exists($autoloadFile)) {
$content = $this->fileManager->getContents($autoloadFile);
$arrayContent = Json::getArrayData($content);
if (!empty($arrayContent)) {
return $this->normalizeData($arrayContent);
}
$GLOBALS['log']->error('Autoload: Empty file or syntax error ['.$autoloadFile.'].');
if (!$this->fileManager->isFile($filePath)) {
return [];
}
return $returns;
$content = $this->fileManager->getContents($filePath);
$arrayContent = Json::getArrayData($content);
if (empty($arrayContent)) {
$GLOBALS['log']->error("Autoload: Empty file or syntax error in '{$filePath}'.");
return [];
}
return $this->normalizeData($arrayContent);
}
protected function normalizeData(array $data)
protected function normalizeData(array $data) : array
{
$normalizedData = [];
@@ -161,12 +154,14 @@ class Autoload
public function register()
{
try {
$autoloadList = $this->getAll();
$data = $this->getData();
}
catch (Exception $e) {} //bad permissions
catch (Exception $e) {} // bad permissions
if (!empty($autoloadList)) {
$this->loader->register($autoloadList);
if (empty($data)) {
return;
}
$this->loader->register($data);
}
}
+10 -10
View File
@@ -38,42 +38,42 @@ class Loader
$this->namespaceLoader = $namespaceLoader;
}
public function register(array $autoloadList)
public function register(array $data)
{
/* load "psr-4", "psr-0", "classmap" */
$this->namespaceLoader->register($autoloadList);
$this->namespaceLoader->register($data);
/* load "autoloadFileList" */
$this->registerAutoloadFileList($autoloadList);
$this->registerAutoloadFileList($data);
/* load "files" */
$this->registerFiles($autoloadList);
$this->registerFiles($data);
}
protected function registerAutoloadFileList(array $autoloadList)
protected function registerAutoloadFileList(array $data)
{
$keyName = 'autoloadFileList';
if (!isset($autoloadList[$keyName])) {
if (!isset($data[$keyName])) {
return;
}
foreach ($autoloadList[$keyName] as $filePath) {
foreach ($data[$keyName] as $filePath) {
if (file_exists($filePath)) {
require_once($filePath);
}
}
}
protected function registerFiles(array $autoloadList)
protected function registerFiles(array $data)
{
$keyName = 'files';
if (!isset($autoloadList[$keyName])) {
if (!isset($data[$keyName])) {
return;
}
foreach ($autoloadList[$keyName] as $id => $filePath) {
foreach ($data[$keyName] as $id => $filePath) {
if (file_exists($filePath)) {
require_once($filePath);
}
@@ -74,9 +74,9 @@ class NamespaceLoader
$this->classLoader = new ClassLoader();
}
public function register(array $autoloadList)
public function register(array $data)
{
$this->addListToClassLoader($this->classLoader, $autoloadList);
$this->addListToClassLoader($this->classLoader, $data);
$this->classLoader->register(true);
}
@@ -147,7 +147,7 @@ class NamespaceLoader
return false;
}
protected function addListToClassLoader($classLoader, array $list, $skipVendorNamespaces = false)
protected function addListToClassLoader(ClassLoader $classLoader, array $list, bool $skipVendorNamespaces = false)
{
foreach ($this->methodNameMap as $type => $methodName) {
if (!isset($list[$type])) {
@@ -96,7 +96,7 @@ class PersonName extends Base
$whereItems[] = "CONCAT:({$firstName}, ' ', {$middleName}, ' ', {$lastName})";
} else
if ($format === 'lastFirstMiddle') {
$whereItems[] = "CONCAT:({$lastName}, ' ', {$firstColumn}, ' ', {$middleName})";
$whereItems[] = "CONCAT:({$lastName}, ' ', {$firstName}, ' ', {$middleName})";
}
$selectExpression = $this->getSelect($fullList);
+23 -30
View File
@@ -262,7 +262,7 @@ class Manager
}
if ($result && function_exists('opcache_invalidate')) {
@opcache_invalidate($fullPath);
opcache_invalidate($fullPath);
}
return $result;
@@ -516,6 +516,12 @@ class Manager
return true;
}
$parentDirPath = dirname($fullPath);
if (!file_exists($parentDirPath)) {
$this->mkdir($parentDirPath, $permission);
}
$defaultPermissions = $this->getPermissionUtils()->getRequiredPermissions($fullPath);
if (!isset($permission)) {
@@ -524,11 +530,11 @@ class Manager
}
try {
$umask = @umask(0);
$result = mkdir($fullPath, $permission, true);
$umask = umask(0);
$result = mkdir($fullPath, $permission);
if ($umask) {
@umask($umask);
umask($umask);
}
if (!empty($defaultPermissions['user'])) {
@@ -615,7 +621,7 @@ class Manager
$this->getPermissionUtils()->setDefaultPermissions($destFile);
if (function_exists('opcache_invalidate')) {
@opcache_invalidate($destFile);
opcache_invalidate($destFile);
}
}
}
@@ -719,7 +725,7 @@ class Manager
if (file_exists($filePath) && is_file($filePath)) {
if (function_exists('opcache_invalidate')) {
@opcache_invalidate($filePath, true);
opcache_invalidate($filePath, true);
}
$result &= unlink($filePath);
@@ -821,7 +827,7 @@ class Manager
/**
* Remove empty parent directories if they are empty.
* @param string $path
* @param string $path
* @return bool
*/
protected function removeEmptyDirs($path)
@@ -838,29 +844,19 @@ class Manager
}
/**
* Check if $dirname is directory.
*
* @param string $dirname
* @param string $basePath
*
* @return boolean
* Check whether a path is a directory.
*/
public function isDir($dirname, $basePath = null)
public function isDir(string $dirPath) : bool
{
if (!empty($basePath)) {
$dirname = $this->concatPaths([$basePath, $dirname]);
}
return is_dir($dirname);
return is_dir($dirPath);
}
public function isFile($filename, $basePath = null)
/**
* Check whether a path is a file.
*/
public function isFile(string $filePath) : bool
{
if (!empty($basePath)) {
$filename = $this->concatPaths([$basePath, $filename]);
}
return is_file($filename);
return is_file($filePath);
}
/**
@@ -891,12 +887,9 @@ class Manager
}
/**
* Check if directory is empty.
*
* @param string $path
* @return boolean
* Check whether a directory is empty.
*/
public function isDirEmpty($path)
public function isDirEmpty(string $path) : bool
{
if (is_dir($path)) {
$fileList = $this->getFileList($path, true);
@@ -390,7 +390,7 @@ class Permission
protected function chmodReal($filename, $mode)
{
try {
$result = @chmod($filename, $mode);
$result = chmod($filename, $mode);
} catch (\Exception $e) {
$result = false;
}
@@ -400,7 +400,7 @@ class Permission
$this->chgrp($filename, $this->getDefaultGroup(true));
try {
$result = @chmod($filename, $mode);
$result = chmod($filename, $mode);
} catch (\Exception $e) {
throw new Error($e->getMessage());
}
@@ -412,7 +412,7 @@ class Permission
protected function chownReal($path, $user)
{
try {
$result = @chown($path, $user);
$result = chown($path, $user);
} catch (\Exception $e) {
throw new Error($e->getMessage());
}
@@ -423,7 +423,7 @@ class Permission
protected function chgrpReal($path, $group)
{
try {
$result = @chgrp($path, $group);
$result = chgrp($path, $group);
} catch (\Exception $e) {
throw new Error($e->getMessage());
}
+3 -4
View File
@@ -53,10 +53,10 @@ class ScheduledJob
protected $checkingCronPeriod = '25 hours';
protected $cronSetup = [
'linux' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BIN-DIR} -f {CRON-FILE} > /dev/null 2>&1',
'linux' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BINARY} -f {CRON-FILE} > /dev/null 2>&1',
'windows' => '{PHP-BINARY} -f {FULL-CRON-PATH}',
'mac' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BIN-DIR} -f {CRON-FILE} > /dev/null 2>&1',
'default' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BIN-DIR} -f {CRON-FILE} > /dev/null 2>&1',
'mac' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BINARY} -f {CRON-FILE} > /dev/null 2>&1',
'default' => '* * * * * cd {DOCUMENT_ROOT}; {PHP-BINARY} -f {CRON-FILE} > /dev/null 2>&1',
];
protected $classFinder;
@@ -105,7 +105,6 @@ class ScheduledJob
$desc = $language->translate('cronSetup', 'options', 'ScheduledJob');
$data = array(
'PHP-BIN-DIR' => $this->getSystemUtil()->getPhpBin(),
'PHP-BINARY' => $this->getSystemUtil()->getPhpBinary(),
'CRON-FILE' => $this->cronFile,
'DOCUMENT_ROOT' => $this->getSystemUtil()->getRootDir(),
+5 -14
View File
@@ -29,6 +29,8 @@
namespace Espo\Core\Utils;
use Symfony\Component\Process\PhpExecutableFinder;
class System
{
/**
@@ -100,22 +102,11 @@ class System
}
/**
* Get path to PHP
*
* @return string
* Deprecated. Use getPhpBinary()
*/
public function getPhpBin()
{
if (isset($_SERVER['PHP_PATH']) && !empty($_SERVER['PHP_PATH'])) {
return $_SERVER['PHP_PATH'];
}
$phpBin = @exec('which php');
if (!empty($phpBin)) {
return $phpBin;
}
return defined("PHP_BINDIR") ? PHP_BINDIR . DIRECTORY_SEPARATOR . 'php' : 'php';
return $this->getPhpBinary();
}
/**
@@ -125,7 +116,7 @@ class System
*/
public function getPhpBinary()
{
return defined("PHP_BINARY") ? PHP_BINARY : 'php';
return (new PhpExecutableFinder)->find();
}
/**
@@ -34,13 +34,15 @@ use Espo\Core\EntryPoints\{
NoAuth,
};
use Espo\Core\Api\Request;
class OauthCallback implements EntryPoint
{
use NoAuth;
public function run()
public function run(Request $request)
{
echo "EspoCRM rocks! If this window is not closed automatically, it's probable that URL you use to access ".
echo "If this window is not closed automatically, it's probable that URL you use to access ".
"EspoCRM doesn't match URL specified at Administration > Settings > Site URL.";
}
}
@@ -34,17 +34,20 @@ use Espo\ORM\Entity;
use Espo\Core\{
Utils\Config,
ORM\EntityManager,
ApplicationState,
};
class AssignmentEmailNotification
{
protected $config;
protected $entityManager;
protected $applicationState;
public function __construct(Config $config, EntityManager $entityManager)
public function __construct(Config $config, EntityManager $entityManager, ApplicationState $applicationState)
{
$this->config = $config;
$this->entityManager = $entityManager;
$this->applicationState = $applicationState;
}
public function afterSave(Entity $entity, array $options = [])
@@ -67,17 +70,25 @@ class AssignmentEmailNotification
if ($entity->has('assignedUsersIds')) {
$userIdList = $entity->getLinkMultipleIdList('assignedUsers');
$fetchedAssignedUserIdList = $entity->getFetched('assignedUsersIds');
if (!is_array($fetchedAssignedUserIdList)) {
$fetchedAssignedUserIdList = [];
}
foreach ($userIdList as $userId) {
if (in_array($userId, $fetchedAssignedUserIdList)) continue;
if (!$this->isNotSelfAssignment($entity, $userId)) continue;
if (in_array($userId, $fetchedAssignedUserIdList)) {
continue;
}
if (!$this->isNotSelfAssignment($entity, $userId)) {
continue;
}
$this->createJob($entity, $userId);
}
} else {
$userId = $entity->get('assignedUserId');
if (!empty($userId) &&
$entity->isAttributeChanged('assignedUserId') && $this->isNotSelfAssignment($entity, $userId)
) {
@@ -96,26 +107,29 @@ class AssignmentEmailNotification
$isNotSelfAssignment = $assignedUserId !== $entity->get('modifiedById');
}
} else {
$isNotSelfAssignment = $assignedUserId !== $this->getUser()->id;
$isNotSelfAssignment = $assignedUserId !== $this->applicationState->getUserId();
}
return $isNotSelfAssignment;
}
protected function createJob(Entity $entity, $userId)
{
$job = $this->entityManager->getEntity('Job');
$job->set([
'serviceName' => 'EmailNotification',
'methodName' => 'notifyAboutAssignmentJob',
'data' => [
'userId' => $userId,
'assignerUserId' => $this->getUser()->id,
'assignerUserId' => $this->applicationState->getUserId(),
'entityId' => $entity->id,
'entityType' => $entity->getEntityType(),
],
'executeTime' => date('Y-m-d H:i:s'),
'queue' => 'e0',
]);
$this->entityManager->saveEntity($job);
}
}
@@ -36,6 +36,9 @@ use Espo\Core\{
Jobs\Job,
};
use Throwable;
use DateTime;
class SubmitPopupReminders implements Job
{
const REMINDER_PAST_HOURS = 24;
@@ -53,21 +56,27 @@ class SubmitPopupReminders implements Job
public function run()
{
if (!$this->config->get('useWebSocket')) return;
if (!$this->config->get('useWebSocket')) {
return;
}
$dt = new \DateTime();
$dt = new DateTime();
$now = $dt->format('Y-m-d H:i:s');
$pastHours = $this->config->get('reminderPastHours', self::REMINDER_PAST_HOURS);
$nowShifted = $dt->modify('-' . $pastHours . ' hours')->format('Y-m-d H:i:s');
$reminderList = $this->entityManager->getRepository('Reminder')->where([
'type' => 'Popup',
'remindAt<=' => $now,
'startAt>' => $nowShifted,
'isSubmitted' => false,
])->find();
$reminderList = $this->entityManager
->getRepository('Reminder')
->where([
'type' => 'Popup',
'remindAt<=' => $now,
'startAt>' => $nowShifted,
'isSubmitted' => false,
])
->find();
$submitData = [];
@@ -78,6 +87,7 @@ class SubmitPopupReminders implements Job
if (!$userId || !$entityType || !$entityId) {
$this->deleteReminder($reminder);
continue;
}
@@ -85,19 +95,24 @@ class SubmitPopupReminders implements Job
if (!$entity) {
$this->deleteReminder($reminder);
continue;
}
if ($entity->hasLinkMultipleField('users')) {
$entity->loadLinkMultipleField('users', ['status' => 'acceptanceStatus']);
$status = $entity->getLinkMultipleColumn('users', 'status', $userId);
if ($status === 'Declined') {
$this->deleteReminder($reminder);
continue;
}
}
$dateAttribute = 'dateStart';
if ($entityType === 'Task') {
$dateAttribute = 'dateEnd';
}
@@ -125,10 +140,10 @@ class SubmitPopupReminders implements Job
foreach ($submitData as $userId => $list) {
try {
$this->getContainer()->get('webSocketSubmission')->submit('popupNotifications.event', $userId, (object) [
$this->webSocketSubmission->submit('popupNotifications.event', $userId, (object) [
'list' => $list
]);
} catch (\Throwable $e) {
} catch (Throwable $e) {
$GLOBALS['log']->error('Job SubmitPopupReminders: [' . $e->getCode() . '] ' .$e->getMessage());
}
}
@@ -38,7 +38,8 @@
},
"labels": {
"Create Task": "Create Task",
"Complete": "Complete"
"Complete": "Complete",
"overdue": "overdue"
},
"presetFilters": {
"actual": "Actual",
@@ -2,5 +2,6 @@
"massEmail",
"status",
"target",
"sentAt"
"sentAt",
"emailAddress"
]
@@ -78,6 +78,7 @@
}
},
"collection": {
"textFilterFields": ["queueItem.id", "queueItem.emailAddress"],
"orderBy": "createdAt",
"order": "desc"
},
@@ -45,6 +45,7 @@
}
},
"collection": {
"textFilterFields": ["id", "emailAddress"],
"orderBy": "createdAt",
"order": "desc"
}
@@ -873,7 +873,7 @@ class Activities implements
$service->handleListParams($params);
$selectParams = $selectManager->getSelectParams($params, false, true);
$selectParams = $selectManager->getSelectParams($params, false, true, true);
$offset = $selectParams['offset'];
$limit = $selectParams['limit'];
+10 -3
View File
@@ -98,7 +98,7 @@ class Lead extends PersonService implements
$ignoreAttributeList = ['createdAt', 'modifiedAt', 'modifiedById', 'modifiedByName', 'createdById', 'createdByName'];
$convertFieldsDefs = $this->getMetadata()->get('entityDefs.Lead.convertFields', array());
$convertFieldsDefs = $this->getMetadata()->get('entityDefs.Lead.convertFields', []);
foreach ($entityList as $entityType) {
if (!$this->getAcl()->checkScope($entityType, 'edit')) {
@@ -141,7 +141,10 @@ class Lead extends PersonService implements
$attachment = $lead->get($field);
if ($attachment) {
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
$attachment = $this->getEntityManager()
->getRepository('Attachment')
->getCopiedAttachment($attachment);
$idAttribute = $field . 'Id';
$nameAttribute = $field . 'Name';
@@ -189,7 +192,11 @@ class Lead extends PersonService implements
continue;
}
$leadAttribute = $leadAttributeList[$i];
$leadAttribute = $leadAttributeList[$i] ?? null;
if (!$leadAttribute) {
throw new Error("Not compatible fields in 'convertFields' map.");
}
if (!$lead->has($leadAttribute)) {
continue;
+1 -1
View File
@@ -175,7 +175,7 @@ class BaseEntity implements Entity
// @todo Remove this.
if ($this->hasRelation($name) && $this->id && $this->entityManager) {
trigger_error(
"Accessing related records with Entity::get is deprecated. Use \$entityManager->getRelation(...)->find()",
"Accessing related records with Entity::get is deprecated. Use \$repository->getRelation(...)->find()",
E_USER_DEPRECATED
);
+1 -7
View File
@@ -37,13 +37,7 @@ interface Builder
{
/**
* Build a query instance.
* @todo Uncomment when 7.4 is a min supported PHP version. Need the support of contravariant method parameters.
* @todo Uncomment when 7.4 is a min supported PHP version.
*/
//public function build() : Query;
/**
* Clone an existing query for a further modification and building.
* @todo Uncomment when 7.4 is a min supported PHP version. Need the support of contravariant method parameters.
*/
//public function clone(Query $query) : self;
}
@@ -297,12 +297,6 @@ class RDBRepository extends Repository
$additionalColumnsConditions = $params['additionalColumnsConditions'] ?? [];
unset($params['additionalColumnsConditions']);
if ($type === Entity::MANY_MANY && count($additionalColumnsConditions)) {
$select = $this->applyRelationAdditionalColumnsConditions(
$entity, $relationName, $additionalColumnsConditions, $select
);
}
$select = null;
if ($entityType) {
@@ -310,6 +304,12 @@ class RDBRepository extends Repository
$select = Select::fromRaw($params);
}
if ($type === Entity::MANY_MANY && count($additionalColumnsConditions)) {
$select = $this->applyRelationAdditionalColumnsConditions(
$entity, $relationName, $additionalColumnsConditions, $select
);
}
return (int) $this->getMapper()->countRelated($entity, $relationName, $select);
}
@@ -20,7 +20,7 @@
},
"tooltips": {
"name": "Give the filter a descriptive name.",
"subject": "Use a wildcard *:\n\ntext* - starts with text,\n*text* - contains text,\n*text - ends with text.",
"subject": "Use a wildcard *: \n\n * `text*` starts with text,\n * `*text*` contains text,\n * `*text` ends with text.",
"bodyContains": "Body of the email contains any of the specified words or phrases.",
"from": "Emails being sent from the specified address. Leave empty if not needed. You can use wildcard *.",
"to": "Emails being sent to the specified address. Leave empty if not needed. You can use wildcard *.",
@@ -270,6 +270,7 @@
"notModified": "You have not modified the record",
"duplicate": "The record you are creating might already exist",
"dropToAttach": "Drop to attach",
"fieldInvalid": "{field} is invalid",
"fieldIsRequired": "{field} is required",
"fieldShouldBeEmail": "{field} should be a valid email",
"fieldShouldBeFloat": "{field} should be a valid float",
@@ -144,7 +144,8 @@
},
"currencyFormat": {
"1": "10 USD",
"2": "$10"
"2": "$10",
"3": "10 $"
},
"personNameFormat": {
"firstLast": "First Last",
@@ -169,7 +169,7 @@
"Cancel": "Отменить",
"Apply": "Применить",
"Unlink": "Убрать ссылку",
"Mass Update": "Обновить все",
"Mass Update": "Массовое обновление",
"Export": "Экспортировать",
"No Data": "Нет данных",
"No Access": "Нет доступа",
@@ -185,7 +185,7 @@
"uninstallConfirmation": "Ви впевнені, що хочете видалити розширення?",
"cronIsNotConfigured": "Заплановані завдання не працюють. Отже, вхідні електронні листи, сповіщення та нагадування не працюють. Будь ласка, дотримуйтесь інструкцій (https://www.espocrm.com/documentation/administration/server-configuration/#user-content-setup-a-crontab), щоб налаштувати cron job.",
"newExtensionVersionIsAvailable": "Нова {extensionName} версія {latestVersion} доступна.",
"upgradeVersion": "EspoCRM буде оновлено до версії **{версія}**. Будьте терплячі, оскільки це може зайняти деякий час.",
"upgradeVersion": "EspoCRM буде оновлено до версії **{version}**. Будьте терплячі, оскільки це може зайняти деякий час.",
"upgradeDone": "EspoCRM оновлено до версії **{version}**.",
"downloadUpgradePackage": "Завантажте апгрейд [звідси]({url})",
"upgradeInfo": "Перегляньте [документацію]({url}) про те, як оновити EspoCRM.\n",
@@ -257,4 +257,4 @@
"noteStatus": "Повідомлення про оновлення статусу",
"passwordChangeLink": "Посилання на зміну паролю"
}
}
}
@@ -65,7 +65,10 @@
"parent": false
},
"EmailFolder": {
"assignedUser": false
"assignedUser": {
"read": "yes",
"edit": "no"
}
},
"Email": {
"inboundEmails": false,
@@ -53,11 +53,21 @@
{
"label":"Folders",
"link":"#EmailFolder",
"configCheck": "!emailFoldersDisabled"
"configCheck": "!emailFoldersDisabled",
"accessDataList": [
{
"inPortalDisabled": true
}
]
},
{
"label":"Filters",
"link":"#EmailFilter"
"link":"#EmailFilter",
"accessDataList": [
{
"inPortalDisabled": true
}
]
}
]
},
@@ -29,6 +29,9 @@
"isActive": {
"nonAdminReadOnly": true
},
"emailAddress": {
"nonAdminReadOnly": true
},
"teams": {
"nonAdminReadOnly": true
},
@@ -12,7 +12,7 @@
},
"userId": {
"type": "varchar",
"maxLength": 11,
"maxLength": 24,
"index": true
}
}
@@ -2,7 +2,7 @@
"fields": {
"timeZone": {
"type": "enum",
"detault": "",
"default": "",
"view": "views/preferences/fields/time-zone"
},
"dateFormat": {
@@ -124,7 +124,8 @@
"assignmentNotificationsIgnoreEntityTypeList": {
"type": "checklist",
"translation": "Global.scopeNamesPlural",
"view": "views/preferences/fields/assignment-notifications-ignore-entity-type-list"
"view": "views/preferences/fields/assignment-notifications-ignore-entity-type-list",
"default": []
},
"assignmentEmailNotificationsIgnoreEntityTypeList": {
"type": "checklist",
@@ -140,14 +141,17 @@
},
"signature": {
"type": "wysiwyg",
"view": "views/preferences/fields/signature"
"view": "views/preferences/fields/signature",
"default": ""
},
"defaultReminders": {
"type": "jsonArray",
"view": "crm:views/meeting/fields/reminders"
"view": "crm:views/meeting/fields/reminders",
"default": []
},
"theme": {
"type": "enum",
"default": "",
"view": "views/preferences/fields/theme",
"translation": "Global.themes"
},
@@ -173,7 +177,8 @@
},
"doNotFillAssignedUserIfNotRequired": {
"type": "bool",
"tooltip": true
"tooltip": true,
"default": false
},
"followEntityOnStreamPost": {
"type": "bool",
@@ -187,16 +192,20 @@
"type": "multiEnum",
"view": "views/preferences/fields/auto-follow-entity-type-list",
"translation": "Global.scopeNamesPlural",
"default": [],
"tooltip": true
},
"emailUseExternalClient": {
"type": "bool"
"type": "bool",
"default": false
},
"scopeColorsDisabled": {
"type": "bool"
"type": "bool",
"default": false
},
"tabColorsDisabled": {
"type": "bool"
"type": "bool",
"default": false
}
},
"noDeletedAttribute": true
@@ -496,7 +496,7 @@
},
"currencyFormat": {
"type": "enumInt",
"options": [1, 2]
"options": [1, 2, 3]
},
"currencyDecimalPlaces": {
"type": "int",
@@ -162,8 +162,6 @@ class Email extends \Espo\Core\Select\SelectManager
$result['whereClause'][] = [
'usersMiddle.userId' => $this->getUser()->id
];
$this->addUsersColumns($result);
}
protected function boolFilterOnlyMy(&$result)
+60 -13
View File
@@ -46,6 +46,7 @@ use Espo\Core\{
Utils\Config,
Utils\Util,
Utils\Language,
Utils\FieldUtil,
};
use Espo\Entities\User;
@@ -57,8 +58,24 @@ use Espo\ORM\{
Entity,
};
use StdClass;
use Throwable;
class App
{
protected $config;
protected $entityManager;
protected $metadata;
protected $acl;
protected $aclManager;
protected $dataManager;
protected $selectManagerFactory;
protected $injectableFactory;
protected $serviceFactory;
protected $user;
protected $preferences;
protected $fieldUtil;
public function __construct(
Config $config,
EntityManager $entityManager,
@@ -70,7 +87,8 @@ class App
InjectableFactory $injectableFactory,
ServiceFactory $serviceFactory,
User $user,
Preferences $preferences
Preferences $preferences,
FieldUtil $fieldUtil
) {
$this->config = $config;
$this->entityManager = $entityManager;
@@ -83,12 +101,14 @@ class App
$this->serviceFactory = $serviceFactory;
$this->user = $user;
$this->preferences = $preferences;
$this->fieldUtil = $fieldUtil;
}
public function getUserData()
{
$preferencesData = $this->preferences->getValueMap();
unset($preferencesData->smtpPassword);
$this->filterPreferencesData($preferencesData);
$settingsService = $this->serviceFactory->create('Settings');
@@ -134,11 +154,16 @@ class App
foreach (($this->metadata->get(['app', 'appParams']) ?? []) as $paramKey => $item) {
$className = $item['className'] ?? null;
if (!$className) continue;
if (!$className) {
continue;
}
try {
$itemParams = $this->injectableFactory->create($className)->get();
} catch (\Throwable $e) {
} catch (Throwable $e) {
$GLOBALS['log']->error("appParam {$paramKey}: " . $e->getMessage());
continue;
}
$appParams[$paramKey] = $itemParams;
@@ -452,19 +477,41 @@ class App
$entityTypeList = ['Contact', 'Lead'];
foreach ($entityTypeList as $entityType) {
$entityList = $this->entityManager->getRepository($entityType)->where([
'doNotCall' => true,
'phoneNumber!=' => null,
])->select(['id', 'phoneNumber'])->find();
$entityList = $this->entityManager
->getRepository($entityType)
->where([
'doNotCall' => true,
'phoneNumber!=' => null,
])
->select(['id', 'phoneNumber'])
->find();
foreach ($entityList as $entity) {
$phoneNumber = $entity->get('phoneNumber');
if (!$phoneNumber) continue;
$phoneNumberEntity = $this->entityManager->getRepository('PhoneNumber')->getByNumber($phoneNumber);
if (!$phoneNumberEntity) continue;
$phoneNumberEntity->set('optOut', true);
$this->entityManager->saveEntity($phoneNumberEntity);
if (!$phoneNumber) {
continue;
}
$phoneNumberEntity = $this->entityManager->getRepository('PhoneNumber')->getByNumber($phoneNumber);
if (!$phoneNumberEntity) {
continue;
}
$phoneNumberEntity->set('optOut', true);
$this->entityManager->saveEntity($phoneNumberEntity);
}
}
}
protected function filterPreferencesData(StdClass $data)
{
$passwordFieldList = $this->fieldUtil->getFieldByTypeList('Preferences', 'password');
foreach ($passwordFieldList as $field) {
unset($data->$field);
}
}
}
+16
View File
@@ -44,6 +44,10 @@ class Attachment extends Record
protected $inlineAttachmentFieldTypeList = ['wysiwyg'];
protected $adminOnlyHavingInlineAttachmentsEntityTypeList = [
'TemplateManager',
];
protected $imageTypeList = [
'image/png',
'image/jpeg',
@@ -160,12 +164,24 @@ class Attachment extends Record
protected function checkAttachmentField($relatedEntityType, $field, $role = 'Attachment')
{
if (
$this->getUser()->isAdmin()
&&
$role === 'Inline Attachment'
&&
in_array($relatedEntityType, $this->adminOnlyHavingInlineAttachmentsEntityTypeList)
) {
return;
}
$fieldType = $this->getMetadata()->get(['entityDefs', $relatedEntityType, 'fields', $field, 'type']);
if (!$fieldType) {
throw new Error("Field '{$field}' does not exist.");
}
$attachmentFieldTypeListParam = lcfirst(str_replace(' ', '', $role)) . 'FieldTypeList';
if (!in_array($fieldType, $this->$attachmentFieldTypeListParam)) {
throw new Error("Field type '{$fieldType}' is not allowed for {$role}.");
}
+31 -6
View File
@@ -133,6 +133,7 @@ class EmailTemplate extends Record implements
if (!empty($params['relatedId']) && !empty($params['relatedType'])) {
$related = $this->getEntityManager()->getEntity($params['relatedType'], $params['relatedId']);
if ($related) {
$entityHash[$related->getEntityType()] = $related;
}
@@ -155,6 +156,7 @@ class EmailTemplate extends Record implements
if ($handlebarsInSubject) {
$subject = $htmlizer->render($parent, $subject);
}
if ($handlebarsInBody) {
$body = $htmlizer->render($parent, $body);
}
@@ -244,7 +246,9 @@ class EmailTemplate extends Record implements
}
foreach ($attributeList as $attribute) {
if (in_array($attribute, $forbiddenAttributeList)) continue;
if (in_array($attribute, $forbiddenAttributeList)) {
continue;
}
$value = $entity->get($attribute);
@@ -252,13 +256,18 @@ class EmailTemplate extends Record implements
continue;
}
if (!$entity->getAttributeType($attribute)) continue;
if (!$entity->getAttributeType($attribute)) {
continue;
}
$value = $this->formatAttributeValue($entity, $attribute);
if (is_null($value)) continue;
if (is_null($value)) {
continue;
}
$variableName = $attribute;
if (!is_null($prefixLink)) {
$variableName = $prefixLink . '.' . $attribute;
}
@@ -266,7 +275,7 @@ class EmailTemplate extends Record implements
$text = str_replace('{' . $type . '.' . $variableName . '}', $value, $text);
}
if (!$skipLinks) {
if (!$skipLinks && $entity->id) {
$relationDefs = $entity->getRelations();
foreach ($entity->getRelationList() as $relation) {
@@ -301,10 +310,12 @@ class EmailTemplate extends Record implements
}
$replaceData = [];
$replaceData['today'] = $this->getDateTime()->getTodayString();
$replaceData['now'] = $this->getDateTime()->getNowString();
$timeZone = $this->getConfig()->get('timeZone');
$now = new DateTime('now', new DateTimezone($timeZone));
$replaceData['currentYear'] = $now->format('Y');
@@ -328,11 +339,13 @@ class EmailTemplate extends Record implements
$value = $this->getLanguage()->translateOption($value, $attribute, $entity->getEntityType());
} else if ($fieldType === 'array' || $fieldType === 'multiEnum' || $fieldType === 'checklist') {
$valueList = [];
if (is_array($value)) {
foreach ($value as $v) {
$valueList[] = $this->getLanguage()->translateOption($v, $attribute, $entity->getEntityType());
}
}
$value = implode(', ', $valueList);
$value = $this->getLanguage()->translateOption($value, $attribute, $entity->getEntityType());
} else {
@@ -348,13 +361,16 @@ class EmailTemplate extends Record implements
if (!is_string($value)) {
$value = '';
}
$value = nl2br($value);
} else if ($attributeType == 'float') {
if (is_float($value)) {
$decimalPlaces = 2;
if ($fieldType === 'currency') {
$decimalPlaces = $this->getConfig()->get('currencyDecimalPlaces');
}
$value = $this->getNumber()->format($value, $decimalPlaces);
}
} else if ($attributeType == 'int') {
@@ -370,7 +386,9 @@ class EmailTemplate extends Record implements
$value = '';
}
if (!is_string($value)) return null;
if (!is_string($value)) {
return null;
}
return $value;
}
@@ -389,6 +407,7 @@ class EmailTemplate extends Record implements
if ($parentId && $parentType) {
$e = $this->getEntityManager()->getEntity($parentType, $parentId);
if ($e && $this->getAcl()->check($e)) {
$dataList[] = [
'type' => 'parent',
@@ -426,6 +445,7 @@ class EmailTemplate extends Record implements
foreach ($fm->getEntityTypeFieldList($entityType) as $field) {
$fieldType = $fm->getEntityTypeFieldParam($entityType, $field, 'type');
$fieldAttributeList = $fm->getAttributeList($entityType, $field);
if (
$fm->getEntityTypeFieldParam($entityType, $field, 'disabled') ||
$fm->getEntityTypeFieldParam($entityType, $field, 'directAccessDisabled') ||
@@ -441,9 +461,14 @@ class EmailTemplate extends Record implements
$attributeList = $fm->getEntityTypeAttributeList($entityType);
$values = (object) [];
foreach ($attributeList as $a) {
if (!$e->has($a)) continue;
if (!$e->has($a)) {
continue;
}
$value = $emailTemplateService->formatAttributeValue($e, $a);
if ($value != '') {
$values->$a = $value;
}
+11 -3
View File
@@ -798,7 +798,9 @@ class InboundEmail extends RecordService implements
}
$d = new DateTime();
$d->modify('-3 hours');
$threshold = $d->format('Y-m-d H:i:s');
$emailAddress = $this->getEntityManager()->getRepository('EmailAddress')->getByAddress($email->get('from'));
@@ -819,6 +821,7 @@ class InboundEmail extends RecordService implements
try {
$replyEmailTemplateId = $inboundEmail->get('replyEmailTemplateId');
if ($replyEmailTemplateId) {
$entityHash = [];
@@ -912,7 +915,9 @@ class InboundEmail extends RecordService implements
return true;
}
} catch (Exception $e) {}
} catch (Exception $e) {
$GLOBALS['log']->error("Inbound Email: Auto-reply error: " . $e->getMessage());
}
}
protected function getSmtpParamsFromInboundEmail(InboundEmailEntity $emailAccount)
@@ -988,9 +993,12 @@ class InboundEmail extends RecordService implements
if ($isHard && $emailAddress) {
$emailAddressEntity = $this->getEntityManager()->getRepository('EmailAddress')->getByAddress($emailAddress);
$emailAddressEntity->set('invalid', true);
$this->getEntityManager()->saveEntity($emailAddressEntity);
if ($emailAddressEntity) {
$emailAddressEntity->set('invalid', true);
$this->getEntityManager()->saveEntity($emailAddressEntity);
}
}
if ($campaignId && $target && $target->id) {
+5 -2
View File
@@ -81,7 +81,7 @@ class Notification extends \Espo\Services\Record implements
$collection = $this->entityManager->createCollection();
$userList = $this->getEntityManager()->getRepository('User')
->select(['id'])
->select(['id', 'type'])
->where([
'isActive' => true,
'id' => $userIdList,
@@ -90,7 +90,10 @@ class Notification extends \Espo\Services\Record implements
foreach ($userList as $user) {
$userId = $user->id;
if (!$this->checkUserNoteAccess($user, $note)) continue;
if (!$this->checkUserNoteAccess($user, $note)) {
continue;
}
if ($note->get('createdById') === $user->id) continue;
if ($related && $related->getEntityType() == 'Email' && $related->get('sentById') == $user->id) continue;
if ($related && $related->get('createdById') == $user->id) continue;
+184 -44
View File
@@ -59,6 +59,7 @@ use Espo\Core\{
use Espo\Tools\Export\Export as ExportTool;
use StdClass;
use Exception;
use Espo\Core\Di;
@@ -705,7 +706,7 @@ class Record implements Crud,
}
}
protected function processValidationField(Entity $entity, $field, $data)
protected function processValidationField(Entity $entity, string $field, $data)
{
$fieldType = $this->fieldUtil->getEntityTypeFieldParam($this->entityType, $field, 'type');
@@ -723,10 +724,11 @@ class Record implements Crud,
}
$skipPropertyName = 'validate' . ucfirst($type) . 'SkipFieldList';
if (property_exists($this, $skipPropertyName)) {
$skipList = $this->$skipPropertyName;
if (in_array($type, $skipList)) {
if (in_array($field, $skipList)) {
continue;
}
}
@@ -1245,13 +1247,19 @@ class Record implements Crud,
public function delete(string $id)
{
if (empty($id)) throw new BadRequest("ID is empty.");
if (empty($id)) {
throw new BadRequest("ID is empty.");
}
$entity = $this->getRepository()->get($id);
if (!$entity) throw new NotFound("Record {$id} not found.");
if (!$entity) {
throw new NotFound("Record {$id} not found.");
}
if (!$this->getAcl()->check($entity, 'delete')) throw new ForbiddenSilent("No delete access.");
if (!$this->getAcl()->check($entity, 'delete')) {
throw new ForbiddenSilent("No delete access.");
}
$this->beforeDeleteEntity($entity);
@@ -1292,6 +1300,7 @@ class Record implements Crud,
}
$maxSize = 0;
if ($disableCount) {
if (!empty($params['maxSize'])) {
$maxSize = $params['maxSize'];
@@ -1376,10 +1385,16 @@ class Record implements Crud,
];
foreach ($statusList as $status) {
if (in_array($status, $statusIgnoreList)) continue;
if (!$status) continue;
if (in_array($status, $statusIgnoreList)) {
continue;
}
if (!$status) {
continue;
}
$selectParamsSub = $selectParams;
$selectParamsSub['whereClause'][] = [
$statusField => $status
];
@@ -1454,12 +1469,19 @@ class Record implements Crud,
public function restoreDeleted(string $id)
{
if (!$this->getUser()->isAdmin()) throw new Forbidden();
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
$entity = $this->getEntityEvenDeleted($id);
if (!$entity) throw new NotFound();
if (!$entity->get('deleted')) throw new Forbidden();
if (!$entity) {
throw new NotFound();
}
if (!$entity->get('deleted')) {
throw new Forbidden();
}
$this->getRepository()->restoreDeleted($entity->id);
@@ -1475,6 +1497,7 @@ class Record implements Crud,
return $this->getConfig()->get('maxSelectTextAttributeLengthForList', self::MAX_SELECT_TEXT_ATTRIBUTE_LENGTH);
}
}
return null;
}
@@ -1487,12 +1510,15 @@ class Record implements Crud,
public function findLinked(string $id, string $link, array $params) : RecordCollection
{
$entity = $this->getRepository()->get($id);
if (!$entity) {
throw new NotFound();
}
if (!$this->getAcl()->check($entity, 'read')) {
throw new Forbidden();
}
if (empty($link)) {
throw new Error();
}
@@ -1515,6 +1541,7 @@ class Record implements Crud,
}
$methodName = 'findLinkedEntities' . ucfirst($link);
if (method_exists($this, $methodName)) {
return $this->$methodName($id, $params);
}
@@ -1638,35 +1665,45 @@ class Record implements Crud,
}
$entity = $this->getRepository()->get($id);
if (!$entity) {
throw new NotFound();
}
if ($this->noEditAccessRequiredForLink) {
if (!$this->getAcl()->check($entity, 'read')) throw new Forbidden();
if (!$this->getAcl()->check($entity, 'read')) {
throw new Forbidden();
}
} else {
if (!$this->getAcl()->check($entity, 'edit')) throw new Forbidden();
if (!$this->getAcl()->check($entity, 'edit')) {
throw new Forbidden();
}
}
$methodName = 'link' . ucfirst($link);
if ($link !== 'entity' && $link !== 'entityMass' && method_exists($this, $methodName)) {
return $this->$methodName($id, $foreignId);
}
$foreignEntityType = $entity->getRelationParam($link, 'entity');
if (!$foreignEntityType) {
throw new Error("Entity '{$this->entityType}' has not relation '{$link}'.");
}
$foreignEntity = $this->getEntityManager()->getEntity($foreignEntityType, $foreignId);
if (!$foreignEntity) {
throw new NotFound();
}
$accessActionRequired = 'edit';
if (in_array($link, $this->noEditAccessRequiredLinkList)) {
$accessActionRequired = 'read';
}
if (!$this->getAcl()->check($foreignEntity, $accessActionRequired)) {
throw new Forbidden();
}
@@ -1712,30 +1749,39 @@ class Record implements Crud,
}
if ($this->noEditAccessRequiredForLink) {
if (!$this->getAcl()->check($entity, 'read')) throw new Forbidden();
if (!$this->getAcl()->check($entity, 'read')) {
throw new Forbidden();
}
} else {
if (!$this->getAcl()->check($entity, 'edit')) throw new Forbidden();
if (!$this->getAcl()->check($entity, 'edit')) {
throw new Forbidden();
}
}
$methodName = 'unlink' . ucfirst($link);
if ($link !== 'entity' && method_exists($this, $methodName)) {
return $this->$methodName($id, $foreignId);
}
$foreignEntityType = $entity->getRelationParam($link, 'entity');
if (!$foreignEntityType) {
throw new Error("Entity '{$this->entityType}' has not relation '{$link}'.");
}
$foreignEntity = $this->getEntityManager()->getEntity($foreignEntityType, $foreignId);
if (!$foreignEntity) {
throw new NotFound();
}
$accessActionRequired = 'edit';
if (in_array($link, $this->noEditAccessRequiredLinkList)) {
$accessActionRequired = 'read';
}
if (!$this->getAcl()->check($foreignEntity, $accessActionRequired)) {
throw new Forbidden();
}
@@ -1756,6 +1802,7 @@ class Record implements Crud,
if (!$this->getMetadata()->get(['scopes', $this->entityType, 'stream'])) throw new NotFound();
$entity = $this->getRepository()->get($id);
if (!$entity) throw new NotFound();
if (!$this->getAcl()->check($entity, 'edit')) throw new Forbidden();
if (!$this->getAcl()->check($entity, 'stream')) throw new Forbidden();
@@ -1776,6 +1823,7 @@ class Record implements Crud,
if (!$this->getMetadata()->get(['scopes', $this->entityType, 'stream'])) throw new NotFound();
$entity = $this->getRepository()->get($id);
if (!$entity) throw new NotFound();
if (!$this->getAcl()->check($entity, 'edit')) throw new Forbidden();
if (!$this->getAcl()->check($entity, 'stream')) throw new Forbidden();
@@ -1810,14 +1858,17 @@ class Record implements Crud,
}
$entity = $this->getRepository()->get($id);
if (!$entity) {
throw new NotFound();
}
if (!$this->getAcl()->check($entity, 'edit')) {
throw new Forbidden();
}
$methodName = 'massLink' . ucfirst($link);
if (method_exists($this, $methodName)) {
return $this->$methodName($id, $where, $selectData);
}
@@ -1874,7 +1925,9 @@ class Record implements Crud,
public function massUpdate(array $params, StdClass $data)
{
if ($this->getAcl()->get('massUpdatePermission') !== 'yes') throw new Forbidden();
if ($this->getAcl()->get('massUpdatePermission') !== 'yes') {
throw new Forbidden();
}
$resultIdList = [];
$repository = $this->getRepository();
@@ -1891,11 +1944,13 @@ class Record implements Crud,
foreach ($collection as $entity) {
if ($this->getAcl()->check($entity, 'edit') && $this->checkEntityForMassUpdate($entity, $data)) {
$entity->set($data);
try {
$this->processValidation($entity, $data);
} catch (\Exception $e) {
} catch (Exception $e) {
continue;
}
if ($this->checkAssignment($entity)) {
$repository->save($entity, ['massUpdate' => true, 'skipStreamNotesAcl' => true]);
$resultIdList[] = $entity->id;
@@ -1944,8 +1999,11 @@ class Record implements Crud,
foreach ($collection as $entity) {
if ($this->getAcl()->check($entity, 'delete') && $this->checkEntityForMassRemove($entity)) {
$repository->remove($entity);
$resultIdList[] = $entity->id;
$count++;
$this->processActionHistoryRecord('delete', $entity);
}
}
@@ -1960,7 +2018,9 @@ class Record implements Crud,
public function massRecalculateFormula(array $params)
{
if (!$this->getUser()->isAdmin()) throw new Forbidden();
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
$count = 0;
@@ -1981,7 +2041,10 @@ class Record implements Crud,
public function follow(string $id, ?string $userId = null)
{
$entity = $this->getRepository()->get($id);
if (!$entity) throw new NotFoundSilent();
if (!$entity) {
throw new NotFoundSilent();
}
if (!$this->getAcl()->check($entity, 'stream')) {
throw new Forbidden();
@@ -1997,7 +2060,10 @@ class Record implements Crud,
public function unfollow(string $id, ?string $userId = null)
{
$entity = $this->getRepository()->get($id);
if (!$entity) throw new NotFoundSilent();
if (!$entity) {
throw new NotFoundSilent();
}
if (empty($userId)) {
$userId = $this->getUser()->id;
@@ -2021,7 +2087,10 @@ class Record implements Crud,
$collection = $this->getRepository()->sth()->find($selectParams);
foreach ($collection as $entity) {
if (!$this->getAcl()->check($entity, 'stream') || !$this->getAcl()->check($entity, 'read')) continue;
if (!$this->getAcl()->check($entity, 'stream') || !$this->getAcl()->check($entity, 'read')) {
continue;
}
if ($streamService->followEntity($entity, $userId)) {
$resultIdList[] = $entity->id;
}
@@ -2031,7 +2100,9 @@ class Record implements Crud,
'count' => count($resultIdList),
];
if (isset($params['ids'])) $result['ids'] = $resultIdList;
if (isset($params['ids'])) {
$result['ids'] = $resultIdList;
}
return $result;
}
@@ -2042,7 +2113,9 @@ class Record implements Crud,
$streamService = $this->getStreamService();
if (empty($userId)) $userId = $this->getUser()->id;
if (empty($userId)) {
$userId = $this->getUser()->id;
}
$selectParams = $this->convertMassActionSelectParams($params);
@@ -2066,24 +2139,36 @@ class Record implements Crud,
protected function convertMassActionSelectParams($params)
{
if (array_key_exists('ids', $params)) {
if (!is_array($params['ids'])) throw new BadRequest();
$selectParams = $this->getSelectParams([]);
$selectParams['whereClause'][] = [
'id' => $params['ids']
];
} else if (array_key_exists('where', $params)) {
$p = ['where' => $params['where']];
if (!empty($params['selectData']) && is_array($params['selectData'])) {
foreach ($params['selectData'] as $k => $v) {
$p[$k] = $v;
}
if (!is_array($params['ids'])) {
throw new BadRequest();
}
$selectParams = $this->getSelectParams($p);
} else {
throw new BadRequest();
$selectParams = $this->getSelectParams([]);
$selectParams['whereClause'][] = [
'id' => $params['ids'],
];
return $selectParams;
}
return $selectParams;
if (array_key_exists('where', $params)) {
$searchParams = [
'where' => $params['where'],
];
if (!empty($params['selectData']) && is_array($params['selectData'])) {
foreach ($params['selectData'] as $k => $v) {
$searchParams[$k] = $v;
}
}
unset($searchParams['select']);
return $this->getSelectParams($searchParams);
}
throw new BadRequest();
}
protected function getDuplicateWhereClause(Entity $entity, $data)
@@ -2099,7 +2184,12 @@ class Record implements Crud,
if ($entity->id) {
$where['id!='] = $entity->id;
}
$duplicate = $this->getRepository()->select(['id'])->where($where)->findOne();
$duplicate = $this->getRepository()
->select(['id'])
->where($where)
->findOne();
if ($duplicate) {
return true;
}
@@ -2133,7 +2223,9 @@ class Record implements Crud,
$limit = self::FIND_DUPLICATES_LIMIT;
$duplicateList = $this->getRepository()->limit(0, $limit)->find($selectParams);
$duplicateList = $this->getRepository()
->limit(0, $limit)
->find($selectParams);
if (count($duplicateList)) {
return $duplicateList;
@@ -2168,14 +2260,17 @@ class Record implements Crud,
foreach ($this->internalAttributeList as $attribute) {
$entity->clear($attribute);
}
foreach ($this->forbiddenAttributeList as $attribute) {
$entity->clear($attribute);
}
if (!$this->getUser()->isAdmin()) {
foreach ($this->onlyAdminAttributeList as $attribute) {
$entity->clear($attribute);
}
}
foreach ($this->getAcl()->getScopeForbiddenAttributeList($entity->getEntityType(), 'read') as $attribute) {
$entity->clear($attribute);
}
@@ -2216,14 +2311,16 @@ class Record implements Crud,
$this->beforeMerge($entity, $sourceList, $attributes);
$fieldDefs = $this->getMetadata()->get('entityDefs.' . $entity->getEntityType() . '.fields', array());
$fieldDefs = $this->getMetadata()->get('entityDefs.' . $entity->getEntityType() . '.fields', []);
$hasPhoneNumber = false;
if (!empty($fieldDefs['phoneNumber']) && $fieldDefs['phoneNumber']['type'] == 'phone') {
$hasPhoneNumber = true;
}
$hasEmailAddress = false;
if (!empty($fieldDefs['emailAddress']) && $fieldDefs['emailAddress']['type'] == 'email') {
$hasEmailAddress = true;
}
@@ -2239,6 +2336,7 @@ class Record implements Crud,
if ($hasEmailAddress) {
$emailAddressToRelateList = [];
$emailAddressList = $repository->findRelated($entity, 'emailAddresses');
foreach ($emailAddressList as $emailAddress) {
$emailAddressToRelateList[] = $emailAddress;
}
@@ -2266,12 +2364,14 @@ class Record implements Crud,
if ($hasPhoneNumber) {
$phoneNumberList = $repository->findRelated($source, 'phoneNumbers');
foreach ($phoneNumberList as $phoneNumber) {
$phoneNumberToRelateList[] = $phoneNumber;
}
}
if ($hasEmailAddress) {
$emailAddressList = $repository->findRelated($source, 'emailAddresses');
foreach ($emailAddressList as $emailAddress) {
$emailAddressToRelateList[] = $emailAddress;
}
@@ -2280,10 +2380,12 @@ class Record implements Crud,
$mergeLinkList = [];
$linksDefs = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'links']);
foreach ($linksDefs as $link => $d) {
if (!empty($d['notMergeable'])) {
continue;
}
if (!empty($d['type']) && in_array($d['type'], ['hasMany', 'hasChildren'])) {
$mergeLinkList[] = $link;
}
@@ -2292,6 +2394,7 @@ class Record implements Crud,
foreach ($sourceList as $source) {
foreach ($mergeLinkList as $link) {
$linkedList = $repository->findRelated($source, $link);
foreach ($linkedList as $linked) {
$repository->relate($entity, $link, $linked);
}
@@ -2306,10 +2409,13 @@ class Record implements Crud,
if ($hasEmailAddress) {
$emailAddressData = [];
foreach ($emailAddressToRelateList as $i => $emailAddress) {
$o = (object) [];
$o->emailAddress = $emailAddress->get('name');
$o->primary = false;
if (empty($attributes->emailAddress)) {
if ($i === 0) {
$o->primary = true;
@@ -2317,8 +2423,10 @@ class Record implements Crud,
} else {
$o->primary = $o->emailAddress === $attributes->emailAddress;
}
$o->optOut = $emailAddress->get('optOut');
$o->invalid = $emailAddress->get('invalid');
$emailAddressData[] = $o;
}
$attributes->emailAddressData = $emailAddressData;
@@ -2326,10 +2434,12 @@ class Record implements Crud,
if ($hasPhoneNumber) {
$phoneNumberData = [];
foreach ($phoneNumberToRelateList as $i => $phoneNumber) {
$o = (object) [];
$o->phoneNumber = $phoneNumber->get('name');
$o->primary = false;
if (empty($attributes->phoneNumber)) {
if ($i === 0) {
$o->primary = true;
@@ -2337,7 +2447,9 @@ class Record implements Crud,
} else {
$o->primary = $o->phoneNumber === $attributes->phoneNumber;
}
$o->type = $phoneNumber->get('type');
$phoneNumberData[] = $o;
}
$attributes->phoneNumberData = $phoneNumberData;
@@ -2364,9 +2476,11 @@ class Record implements Crud,
protected function findLinkedFollowers($id, $params)
{
$entity = $this->getRepository()->get($id);
if (!$entity) {
throw new NotFound();
}
if (!$this->getAcl()->check($entity, 'read')) {
throw new Forbidden();
}
@@ -2396,32 +2510,51 @@ class Record implements Crud,
foreach ($fields as $field => $item) {
if (!empty($item['duplicateIgnore']) || in_array($field, $this->duplicateIgnoreFieldList)) {
$attributeToIgnoreList = $fieldManager->getAttributeList($this->entityType, $field);
foreach ($attributeToIgnoreList as $attribute) {
unset($attributes->$attribute);
}
continue;
}
if (empty($item['type'])) {
continue;
}
if (empty($item['type'])) continue;
$type = $item['type'];
if (in_array($type, ['file', 'image'])) {
$attachment = $entity->get($field);
if ($attachment) {
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
$attachment = $this->getEntityManager()
->getRepository('Attachment')
->getCopiedAttachment($attachment);
$idAttribute = $field . 'Id';
if ($attachment) {
$attributes->$idAttribute = $attachment->id;
}
}
} else if (in_array($type, ['attachmentMultiple'])) {
$attachmentList = $entity->get($field);
if (count($attachmentList)) {
$idList = [];
$nameHash = (object) [];
$typeHash = (object) [];
foreach ($attachmentList as $attachment) {
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
$attachment = $this->getEntityManager()
->getRepository('Attachment')
->getCopiedAttachment($attachment);
$attachment->set('field', $field);
$this->getEntityManager()->saveEntity($attachment);
if ($attachment) {
$idList[] = $attachment->id;
$nameHash->{$attachment->id} = $attachment->get('name');
@@ -2435,8 +2568,12 @@ class Record implements Crud,
} else if ($type === 'linkMultiple') {
$foreignLink = $entity->getRelationParam($field, 'foreign');
$foreignEntityType = $entity->getRelationParam($field, 'entity');
if ($foreignEntityType && $foreignLink) {
$foreignRelationType = $this->getMetadata()->get(['entityDefs', $foreignEntityType, 'links', $foreignLink, 'type']);
$foreignRelationType = $this->getMetadata()->get(
['entityDefs', $foreignEntityType, 'links', $foreignLink, 'type']
);
if ($foreignRelationType !== 'hasMany') {
unset($attributes->{$field . 'Ids'});
unset($attributes->{$field . 'Names'});
@@ -2461,7 +2598,9 @@ class Record implements Crud,
$duplicatingEntityId = $data->_duplicatingEntityId;
if (!$duplicatingEntityId) return;
$duplicatingEntity = $this->getEntityManager()->getEntity($entity->getEntityType(), $duplicatingEntityId);
if (!$duplicatingEntity) return;
if (!$this->getAcl()->check($duplicatingEntity, 'read')) return;
@@ -2474,6 +2613,7 @@ class Record implements Crud,
foreach ($this->duplicatingLinkList as $link) {
$linkedList = $repository->findRelated($duplicatingEntity, $link);
foreach ($linkedList as $linked) {
$repository->relate($entity, $link, $linked);
}
+20 -12
View File
@@ -131,6 +131,8 @@ class Export
$collection = null;
$exportAllFields = !array_key_exists('fieldList', $params);
if (array_key_exists('collection', $params)) {
$collection = $params['collection'];
} else {
@@ -138,26 +140,33 @@ class Export
if (array_key_exists('ids', $params)) {
$ids = $params['ids'];
$where = [
[
'type' => 'in',
'field' => 'id',
'value' => $ids
'value' => $ids,
]
];
$selectParams = $selectManager->getSelectParams(['where' => $where], true, true);
$selectParams = $selectManager->getSelectParams(['where' => $where], true, true, true);
}
else if (array_key_exists('where', $params)) {
$where = $params['where'];
$p = [];
$p['where'] = $where;
$searchParams = [];
$searchParams['where'] = $where;
if (!empty($params['selectData']) && is_array($params['selectData'])) {
foreach ($params['selectData'] as $k => $v) {
$p[$k] = $v;
$searchParams[$k] = $v;
}
}
$selectParams = $this->getSelectParams($p);
unset($searchParams['select']);
$selectParams = $selectManager->getSelectParams($searchParams, true, true, true);
}
else {
throw new BadRequest();
@@ -172,14 +181,15 @@ class Export
$select = Select::fromRaw($selectParams);
$collection = $this->entityManager->getRepository($this->entityType)
$collection = $this->entityManager
->getRepository($this->entityType)
->clone($select)
->sth()
->find();
}
$attributeListToSkip = [
'deleted'
'deleted',
];
foreach ($this->skipAttributeList as $attribute) {
@@ -208,15 +218,12 @@ class Export
}
}
if (!array_key_exists('fieldList', $params)) {
$exportAllFields = true;
if ($exportAllFields) {
$fieldDefs = $this->metadata->get(['entityDefs', $this->entityType, 'fields'], []);
$fieldList = array_keys($fieldDefs);
array_unshift($fieldList, 'id');
} else {
$exportAllFields = false;
$fieldList = $params['fieldList'];
}
@@ -225,6 +232,7 @@ class Export
unset($fieldList[$i]);
}
}
$fieldList = array_values($fieldList);
if (method_exists($exportObj, 'filterFieldList')) {
@@ -56,7 +56,11 @@ class Csv
public function loadAdditionalFields(Entity $entity, $fieldList)
{
foreach ($fieldList as $field) {
if ($this->metadata->get(['entityDefs', $entity->getEntityType(), 'fields', $field, 'type']) === 'linkMultiple') {
$fieldType = $this->metadata->get(['entityDefs', $entity->getEntityType(), 'fields', $field, 'type']);
if (
$fieldType === 'linkMultiple' || $fieldType === 'attachmentMultiple'
) {
if (!$entity->has($field . 'Ids')) {
$entity->loadLinkMultipleField($field);
}
+235 -77
View File
@@ -36,12 +36,23 @@ use Espo\Core\{
Utils\Config,
Utils\Metadata,
Utils\Language,
Utils\DateTime,
Utils\DateTime as DateTimeUtil,
FileStorage\Manager as FileStorageManager,
Utils\File\Manager as FileManager,
ORM\EntityManager,
};
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use DateTime;
use DateTimeZone;
use Exception;
class Xlsx
{
protected $config;
@@ -56,7 +67,7 @@ class Xlsx
Config $config,
Metadata $metadata,
Language $language,
DateTime $dateTime,
DateTimeUtil $dateTime,
EntityManager $entityManager,
FileStorageManager $fileStorageManager,
FileManager $fileManager
@@ -113,7 +124,9 @@ class Xlsx
}
}
foreach ($fieldList as $field) {
if ($this->getMetadata()->get(['entityDefs', $entity->getEntityType(), 'fields', $field, 'type']) === 'linkMultiple') {
$fieldType = $this->getMetadata()->get(['entityDefs', $entity->getEntityType(), 'fields', $field, 'type']);
if ($fieldType === 'linkMultiple' || $fieldType === 'attachmentMultiple') {
if (!$entity->has($field . 'Ids')) {
$entity->loadLinkMultipleField($field);
}
@@ -144,18 +157,24 @@ class Xlsx
}
$linkDefs = $this->getMetadata()->get(['entityDefs', $entityType, 'links']);
if (is_array($linkDefs)) {
foreach ($linkDefs as $link => $defs) {
if (empty($defs['type'])) continue;
if (empty($defs['type'])) {
continue;
}
if ($defs['type'] === 'belongsToParent') {
$linkList[] = $link;
} else if ($defs['type'] === 'belongsTo' && !empty($defs['noJoin'])) {
}
else if ($defs['type'] === 'belongsTo' && !empty($defs['noJoin'])) {
if ($this->getMetadata()->get(['entityDefs', $entityType, 'fields', $link])) {
$linkList[] = $link;
}
}
}
}
foreach ($linkList as $item) {
if (in_array($item, $fieldList) && !in_array($item . 'Name', $attributeList)) {
$attributeList[] = $item . 'Name';
@@ -164,6 +183,7 @@ class Xlsx
foreach ($fieldList as $field) {
$type = $this->getMetadata()->get(['entityDefs', $entityType, 'fields', $field, 'type']);
if ($type === 'currencyConverted') {
if (!in_array($field, $attributeList)) {
$attributeList[] = $field;
@@ -178,43 +198,49 @@ class Xlsx
throw new Error();
}
$phpExcel = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$phpExcel = new Spreadsheet();
$sheet = $phpExcel->setActiveSheetIndex(0);
if (isset($params['exportName'])) {
$exportName = $params['exportName'];
} else {
}
else {
$exportName = $this->language->translate($entityType, 'scopeNamesPlural');
}
$sheetName = mb_substr($exportName, 0, 30, 'utf-8');
$badCharList = ['*', ':', '/', '\\', '?', '[', ']'];
foreach ($badCharList as $badChar) {
$sheetName = str_replace($badCharList, ' ', $sheetName);
}
$sheetName = str_replace('\'', '', $sheetName);
$sheet->setTitle($sheetName);
$fieldList = $params['fieldList'];
$titleStyle = array(
'font' => array(
$titleStyle = [
'font' => [
'bold' => true,
'size' => 12
)
);
$dateStyle = array(
'font' => array(
'size' => 12
)
);
'size' => 12,
],
];
$now = new \DateTime();
$now->setTimezone(new \DateTimeZone($this->config->get('timeZone', 'UTC')));
$dateStyle = [
'font' => [
'size' => 12,
],
];
$now = new DateTime();
$now->setTimezone(new DateTimeZone($this->config->get('timeZone', 'UTC')));
$sheet->setCellValue('A1', $exportName);
$sheet->setCellValue('B1', \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($now->format('Y-m-d H:i:s'))));
$sheet->setCellValue('B1', SharedDate::PHPToExcel(strtotime($now->format('Y-m-d H:i:s'))));
$sheet->getStyle('A1')->applyFromArray($titleStyle);
$sheet->getStyle('B1')->applyFromArray($dateStyle);
@@ -227,6 +253,7 @@ class Xlsx
foreach ($azRangeCopied as $i => $char1) {
foreach ($azRangeCopied as $j => $char2) {
$azRange[] = $char1 . $char2;
if ($i * count($azRangeCopied) + $j === count($fieldList)) {
break 2;
}
@@ -249,39 +276,45 @@ class Xlsx
}
$label = $name;
if (strpos($name, '_') !== false) {
list($linkName, $foreignField) = explode('_', $name);
$foreignScope = $this->metadata->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
if ($foreignScope) {
$label = $this->language->translate($linkName, 'links', $entityType) . '.' . $this->language->translate($foreignField, 'fields', $foreignScope);
}
} else {
}
else {
$label = $this->language->translate($name, 'fields', $entityType);
}
$sheet->setCellValue($col . $rowNumber, $label);
$sheet->getColumnDimension($col)->setAutoSize(true);
if (in_array($defs['type'], ['phone', 'email', 'url', 'link', 'linkParent'])) {
$linkColList[] = $col;
} else if ($name == 'name') {
}
else if ($name == 'name') {
$linkColList[] = $col;
}
$lastIndex = $i;
}
$col = $azRange[$i];
$headerStyle = array(
'font' => array(
$headerStyle = [
'font' => [
'bold' => true,
'size' => 12
)
);
'size' => 12,
]
];
$sheet->getStyle("A$rowNumber:$col$rowNumber")->applyFromArray($headerStyle);
$sheet->setAutoFilter("A$rowNumber:$col$rowNumber");
$typesCache = array();
$typesCache = [];
$rowNumber++;
@@ -295,18 +328,28 @@ class Xlsx
if ($dataFp) {
$line = fgets($dataFp);
if ($line === false) break;
if ($line === false) {
break;
}
$row = unserialize(base64_decode($line));
} else {
if ($lineIndex >= $lineCount) break;
}
else {
if ($lineIndex >= $lineCount) {
break;
}
$row = $dataList[$lineIndex];
}
$i = 0;
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
$defs = $this->metadata->get(['entityDefs', $entityType, 'fields', $name]);
if (!$defs) {
$defs = array();
$defs['type'] = 'base';
@@ -315,17 +358,22 @@ class Xlsx
$type = $defs['type'];
$foreignField = $name;
$linkName = null;
if (strpos($name, '_') !== false) {
list($linkName, $foreignField) = explode('_', $name);
$foreignScope = $this->metadata->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
if ($foreignScope) {
$type = $this->metadata->get(['entityDefs', $foreignScope, 'fields', $foreignField, 'type'], $type);
}
}
if ($type === 'foreign') {
$linkName = $this->metadata->get(['entityDefs', $entityType, 'fields', $name, 'link']);
$foreignField = $this->metadata->get(['entityDefs', $entityType, 'fields', $name, 'field']);
$foreignScope = $this->metadata->get(['entityDefs', $entityType, 'links', $linkName, 'entity']);
if ($foreignScope) {
$type = $this->metadata->get(['entityDefs', $foreignScope, 'fields', $foreignField, 'type'], $type);
}
@@ -333,40 +381,50 @@ class Xlsx
$typesCache[$name] = $type;
$link = null;
if ($type == 'link') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($type == 'linkParent') {
}
else if ($type == 'linkParent') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($type == 'int') {
}
else if ($type == 'int') {
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
} else if ($type == 'float') {
}
else if ($type == 'float') {
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
} else if ($type == 'currency') {
}
else if ($type == 'currency') {
if (array_key_exists($name.'Currency', $row) && array_key_exists($name, $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
$currency = $row[$name . 'Currency'];
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
$currency = $row[$name . 'Currency'] ?? $this->getConfig()->get('defaultCurrency');
$sheet->getStyle("$col$rowNumber")
->getNumberFormat()
->setFormatCode('[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00');
->setFormatCode(
$this->getCurrencyFormatCode($currency)
);
}
} else if ($type == 'currencyConverted') {
}
else if ($type == 'currencyConverted') {
if (array_key_exists($name, $row)) {
$currency = $this->getConfig()->get('defaultCurrency');
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
$sheet->getStyle("$col$rowNumber")
->getNumberFormat()
->setFormatCode('[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00');
->setFormatCode(
$this->getCurrencyFormatCode($currency)
);
$sheet->setCellValue("$col$rowNumber", $row[$name] ? $row[$name] : '');
}
} else if ($type == 'personName') {
}
else if ($type == 'personName') {
if (!empty($row['name'])) {
$sheet->setCellValue("$col$rowNumber", $row['name']);
} else {
@@ -382,41 +440,51 @@ class Xlsx
}
$sheet->setCellValue("$col$rowNumber", $personName);
}
} else if ($type == 'date') {
}
else if ($type == 'date') {
if (isset($row[$name])) {
$sheet->setCellValue("$col$rowNumber", \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($row[$name])));
$sheet->setCellValue("$col$rowNumber", SharedDate::PHPToExcel(strtotime($row[$name])));
}
} else if ($type == 'datetime' || $type == 'datetimeOptional') {
}
else if ($type == 'datetime' || $type == 'datetimeOptional') {
$value = null;
if ($type == 'datetimeOptional') {
if (isset($row[$name . 'Date']) && $row[$name . 'Date']) {
$value = $row[$name . 'Date'];
}
}
if (!$value) {
if (isset($row[$name])) {
$value = $row[$name];
}
}
if ($value && strlen($value) > 11) {
try {
$timeZone = $this->config->get('timeZone');
$dt = new \DateTime($value);
$dt->setTimezone(new \DateTimeZone($timeZone));
$dt = new DateTime($value);
$dt->setTimezone(new DateTimeZone($timeZone));
$value = $dt->format($this->dateTime->getInternalDateTimeFormat());
} catch (\Exception $e) {
}
catch (Exception $e) {
$value = '';
}
}
if ($value) {
$sheet->setCellValue("$col$rowNumber", \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($value)));
$sheet->setCellValue("$col$rowNumber", SharedDate::PHPToExcel(strtotime($value)));
}
} else if ($type == 'image') {
}
else if ($type == 'image') {
if (isset($row[$name . 'Id']) && $row[$name . 'Id']) {
$attachment = $this->getEntityManager()->getEntity('Attachment', $row[$name . 'Id']);
if ($attachment) {
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$objDrawing = new Drawing();
$filePath = $this->fileStorageManager->getLocalFilePath($attachment);
if ($filePath && file_exists($filePath)) {
@@ -429,66 +497,87 @@ class Xlsx
}
}
} else if ($type == 'file') {
}
else if ($type == 'file') {
if (array_key_exists($name.'Name', $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
}
} else if ($type == 'enum') {
}
else if ($type == 'enum') {
if (array_key_exists($name, $row)) {
if ($linkName) {
$value = $this->language->translateOption($row[$name], $foreignField, $foreignScope);
} else {
}
else {
$value = $this->language->translateOption($row[$name], $name, $entityType);
}
$sheet->setCellValue("$col$rowNumber", $value);
}
} else if ($type == 'linkMultiple') {
}
else if ($type == 'linkMultiple' || $type == 'attachmentMultiple') {
if (array_key_exists($name . 'Ids', $row) && array_key_exists($name . 'Names', $row)) {
$nameList = [];
foreach ($row[$name . 'Ids'] as $relatedId) {
$relatedName = $relatedId;
if (property_exists($row[$name . 'Names'], $relatedId)) {
$relatedName = $row[$name . 'Names']->$relatedId;
}
$nameList[] = $relatedName;
}
$sheet->setCellValue("$col$rowNumber", implode(', ', $nameList));
}
} else if ($type == 'address') {
}
else if ($type == 'address') {
$value = '';
if (!empty($row[$name . 'Street'])) {
$value = $value .= $row[$name.'Street'];
}
if (!empty($row[$name.'City']) || !empty($row[$name.'State']) || !empty($row[$name.'PostalCode'])) {
if ($value) {
$value .= "\n";
}
if (!empty($row[$name.'City'])) {
$value .= $row[$name.'City'];
if (
!empty($row[$name.'State']) || !empty($row[$name.'PostalCode'])
) {
$value .= ', ';
}
}
if (!empty($row[$name.'State'])) {
$value .= $row[$name.'State'];
if (!empty($row[$name.'PostalCode'])) {
$value .= ' ';
}
}
if (!empty($row[$name.'PostalCode'])) {
$value .= $row[$name.'PostalCode'];
}
}
if (!empty($row[$name.'Country'])) {
if ($value) {
$value .= "\n";
}
$value .= $row[$name.'Country'];
}
$sheet->setCellValue("$col$rowNumber", $value);
} else if ($type == 'duration') {
}
else if ($type == 'duration') {
if (!empty($row[$name])) {
$seconds = intval($row[$name]);
@@ -499,35 +588,58 @@ class Xlsx
$minutes = intval(floor($seconds / 60));
$value = '';
if ($days) {
$value .= (string) $days . $this->language->translate('d', 'durationUnits');
if ($minutes || $hours) {
$value .= ' ';
}
}
if ($hours) {
$value .= (string) $hours . $this->language->translate('h', 'durationUnits');
if ($minutes) {
$value .= ' ';
}
}
if ($minutes) {
$value .= (string) $minutes . $this->language->translate('m', 'durationUnits');
}
$sheet->setCellValue("$col$rowNumber", $value);
}
} else if ($type == 'multiEnum' || $type == 'array') {
}
else if ($type == 'multiEnum' || $type == 'array') {
if (!empty($row[$name])) {
$array = json_decode($row[$name]);
if (is_array($array)) {
$value = implode(', ', $array);
$sheet->setCellValue("$col$rowNumber", $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
if (!is_array($array)) {
$array = [];
}
foreach ($array as $i => $item) {
if ($linkName) {
$itemValue = $this->language
->translateOption($item, $foreignField, $foreignScope);
}
else {
$itemValue = $this->language
->translateOption($item, $name, $entityType);
}
$array[$i] = $itemValue;
}
$value = implode(', ', $array);
$sheet->setCellValue("$col$rowNumber", $value, DataType::TYPE_STRING);
}
} else {
}
else {
if (array_key_exists($name, $row)) {
$sheet->setCellValueExplicit("$col$rowNumber", $row[$name], \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
$sheet->setCellValueExplicit("$col$rowNumber", $row[$name], DataType::TYPE_STRING);
}
}
@@ -535,8 +647,10 @@ class Xlsx
$foreignLink = null;
$isForeign = false;
if (strpos($name, '_')) {
$isForeign = true;
list($foreignLink, $foreignField) = explode('_', $name);
}
@@ -544,59 +658,80 @@ class Xlsx
if (array_key_exists('id', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/#".$entityType . "/view/" . $row['id'];
}
} else if ($type == 'url') {
}
else if ($type == 'url') {
if (array_key_exists($name, $row) && filter_var($row[$name], FILTER_VALIDATE_URL)) {
$link = $row[$name];
}
} else if ($type == 'link') {
}
else if ($type == 'link') {
if (array_key_exists($name.'Id', $row)) {
$foreignEntity = null;
if (!$isForeign) {
$foreignEntity = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $name, 'entity']);
} else {
$foreignEntity1 = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $foreignLink, 'entity']);
$foreignEntity = $this->getMetadata()->get(['entityDefs', $foreignEntity1, 'links', $foreignField, 'entity']);
$foreignEntity = $this->getMetadata()->get(
['entityDefs', $entityType, 'links', $name, 'entity']
);
}
else {
$foreignEntity1 = $this->getMetadata()->get(
['entityDefs', $entityType, 'links', $foreignLink, 'entity']
);
$foreignEntity = $this->getMetadata()->get(
['entityDefs', $foreignEntity1, 'links', $foreignField, 'entity']
);
}
if ($foreignEntity) {
$link = $this->getConfig()->getSiteUrl() . "/#" . $foreignEntity. "/view/". $row[$name.'Id'];
}
}
} else if ($type == 'file') {
}
else if ($type == 'file') {
if (array_key_exists($name.'Id', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/?entryPoint=download&id=" . $row[$name.'Id'];
}
} else if ($type == 'linkParent') {
}
else if ($type == 'linkParent') {
if (array_key_exists($name.'Id', $row) && array_key_exists($name.'Type', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/#".$row[$name.'Type']."/view/". $row[$name.'Id'];
}
} else if ($type == 'phone') {
}
else if ($type == 'phone') {
if (array_key_exists($name, $row)) {
$link = "tel:".$row[$name];
}
} else if ($type == 'email' && array_key_exists($name, $row)) {
}
else if ($type == 'email' && array_key_exists($name, $row)) {
if (array_key_exists($name, $row)) {
$link = "mailto:".$row[$name];
}
}
if ($link) {
$sheet->getCell("$col$rowNumber")->getHyperlink()->setUrl($link);
$sheet->getCell("$col$rowNumber")->getHyperlink()->setTooltip($link);
}
}
$rowNumber++;
}
$sheet->getStyle("A2:A$rowNumber")
->getNumberFormat()
->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
->setFormatCode(NumberFormat::FORMAT_TEXT);
$startingRowNumber = 4;
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
if (!array_key_exists($name, $typesCache)) {
break;
}
$type = $typesCache[$name];
switch ($type) {
@@ -604,31 +739,37 @@ class Xlsx
case 'currencyConverted': {
} break;
case 'int': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('0');
} break;
case 'float': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1);
->setFormatCode(NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1);
} break;
case 'date': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->dateTime->getDateFormat());
} break;
case 'datetime': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->dateTime->getDateTimeFormat());
} break;
case 'datetimeOptional': {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode($this->dateTime->getDateTimeFormat());
} break;
default: {
$sheet->getStyle($col.$startingRowNumber.':'.$col.$rowNumber)
->getNumberFormat()
@@ -640,25 +781,42 @@ class Xlsx
$linkStyle = [
'font' => [
'color' => ['rgb' => '345b7c'],
'underline' => 'single'
'underline' => 'single',
]
];
foreach ($linkColList as $linkColumn) {
$sheet->getStyle($linkColumn.$startingRowNumber.':'.$linkColumn.$rowNumber)->applyFromArray($linkStyle);
}
$objWriter = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($phpExcel, 'Xlsx');
$objWriter = IOFactory::createWriter($phpExcel, 'Xlsx');
if (!$this->fileManager->isDir('data/cache/')) {
$this->fileManager->mkdir('data/cache/');
}
$tempFileName = 'data/cache/' . 'export_' . substr(md5(rand()), 0, 7);
$objWriter->save($tempFileName);
$fp = fopen($tempFileName, 'r');
$xlsx = stream_get_contents($fp);
$this->fileManager->unlink($tempFileName);
return $xlsx;
}
protected function getCurrencyFormatCode(string $currency) : string
{
$currencySymbol = $this->getMetadata()->get(['app', 'currency', 'symbolMap', $currency], '');
$currencyFormat = $this->getConfig()->get('currencyFormat') ?? 2;
if ($currencyFormat == 3) {
return '#,##0.00_-"' . $currencySymbol . '"';
}
return '[$'.$currencySymbol.'-409]#,##0.00;-[$'.$currencySymbol.'-409]#,##0.00';
}
}
@@ -37,11 +37,12 @@ use Espo\Core\{
Utils\Language,
};
class LabelManager implements Di\DefaultLanguageAware, Di\MetadataAware, Di\FileManagerAware
class LabelManager implements Di\DefaultLanguageAware, Di\MetadataAware, Di\FileManagerAware, Di\DataCacheAware
{
use Di\DefaultLanguageSetter;
use Di\MetadataSetter;
use Di\FileManagerSetter;
use Di\DataCacheSetter;
protected $ignoreList = [
'Global.sets',
@@ -71,7 +72,7 @@ class LabelManager implements Di\DefaultLanguageAware, Di\MetadataAware, Di\File
public function getScopeData($language, $scope)
{
$languageObj = new Language($language, $this->fileManager, $this->metadata);
$languageObj = new Language($language, $this->fileManager, $this->metadata, $this->dataCache);
$data = $languageObj->get($scope);
@@ -179,8 +180,8 @@ class LabelManager implements Di\DefaultLanguageAware, Di\MetadataAware, Di\File
public function saveLabels($language, $scope, $labels)
{
$languageObj = new Language($language, $this->fileManager, $this->metadata);
$languageOriginalObj = new Language($language, $this->fileManager, $this->metadata, false, true);
$languageObj = new Language($language, $this->fileManager, $this->metadata, $this->dataCache);
$languageOriginalObj = new Language($language, $this->fileManager, $this->metadata, $this->dataCache, false, true);
$returnDataHash = [];
@@ -201,6 +201,8 @@ class LeadCapture
$lead = $this->getLeadWithPopulatedData($leadCapture, $data);
}
$campaign = null;
$campaingService = $this->serviceFactory->create('Campaign');
if ($leadCapture->get('campaignId')) {
+242 -228
View File
File diff suppressed because it is too large Load Diff
@@ -34,19 +34,30 @@ define('crm:views/case/record/detail', 'views/record/detail', function (Dep) {
setupActionItems: function () {
Dep.prototype.setupActionItems.call(this);
if (
this.getAcl().checkModel(this.model, 'edit') &&
!~['Closed', 'Rejected', 'Duplicate'].indexOf(this.model.get('status')) &&
this.getAcl().checkField(this.entityType, 'status', 'edit')
) {
this.dropdownItemList.push({
'label': 'Close',
'name': 'close',
});
this.dropdownItemList.push({
'label': 'Reject',
'name': 'reject',
});
var statusList = this.getMetadata().get(
['entityDefs', 'Case', 'fields', 'status', 'options']
) || [];
if (~statusList.indexOf('Closed')) {
this.dropdownItemList.push({
'label': 'Close',
'name': 'close',
});
}
if (~statusList.indexOf('Rejected')) {
this.dropdownItemList.push({
'label': 'Reject',
'name': 'reject',
});
}
}
},
@@ -68,14 +68,14 @@ define('crm:views/record/panels/tasks', 'views/record/panels/relationship', func
name: 'name',
link: true,
},
{
name: 'isOverdue'
}
],
[
{
name: 'isOverdue'
},
{name: 'assignedUser'},
{name: 'status'},
{name: 'dateEnd'},
{name: 'status'},
]
]
},
@@ -32,7 +32,8 @@ Espo.define('crm:views/task/fields/is-overdue', 'views/fields/base', function (D
readOnly: true,
_template: '{{#if isOverdue}}<span class="label label-danger">{{translate "overdue"}}</span>{{/if}}',
_template: '{{#if isOverdue}}<span class="label label-danger">' +
'{{translate "overdue" scope="Task"}}</span>{{/if}}',
data: function () {
var isOverdue = false;
@@ -0,0 +1,5 @@
{{#if isNotEmpty}}
{{value}} {{currencySymbol}}
{{else}}
{{#if valueIsSet}}{{{translate 'None'}}}{{else}}...{{/if}}
{{/if}}
@@ -0,0 +1,3 @@
{{#if isNotEmpty}}
<span title="{{currencySymbol}}{{value}}">{{value}} {{currencySymbol}}</span>
{{/if}}
+1 -1
View File
@@ -4,7 +4,7 @@
<div class="button-container">
<button class="btn btn-default pull-right hidden" data-action="reset">{{translate 'Reset'}}</button>
<div class="btn-group">
<button class="btn btn-default dropdown-toggle select-field" data-toggle="dropdown" tabindex="-1">{{translate 'Select Field'}} <span class="caret"></span></button>
<button class="btn btn-default dropdown-toggle select-field" data-toggle="dropdown" tabindex="-1">{{translate 'Add Field'}} <span class="caret"></span></button>
<ul class="dropdown-menu pull-left filter-list">
{{#each ../fieldList}}
<li data-name="{{./this}}"><a href="javascript:" data-name="{{./this}}" data-action="add-field">{{translate this scope=../../entityType category='fields'}}</a></li>
+15 -5
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/integrations/index', 'view', function (Dep) {
define('views/admin/integrations/index', 'view', function (Dep) {
return Dep.extend({
@@ -51,12 +51,13 @@ Espo.define('views/admin/integrations/index', 'view', function (Dep) {
},
setup: function () {
this.integrationList = Object.keys(this.getMetadata().get('integrations') || {});;
this.integrationList = Object.keys(this.getMetadata().get('integrations') || {});
this.integration = this.options.integration || null;
this.on('after:render', function () {
this.renderHeader();
if (!this.integration) {
this.renderDefaultPage();
} else {
@@ -70,34 +71,45 @@ Espo.define('views/admin/integrations/index', 'view', function (Dep) {
this.getRouter().navigate('#Admin/integrations/name=' + integration, {trigger: false});
var viewName = this.getMetadata().get('integrations.' + integration + '.view') || 'views/admin/integrations/' + Espo.Utils.camelCaseToHyphen(this.getMetadata().get('integrations.' + integration + '.authMethod'));
var viewName = this.getMetadata().get('integrations.' + integration + '.view') ||
'views/admin/integrations/' +
Espo.Utils.camelCaseToHyphen(this.getMetadata().get('integrations.' + integration + '.authMethod'));
this.notify('Loading...');
this.createView('content', viewName, {
el: '#integration-content',
integration: integration,
}, function (view) {
this.renderHeader();
view.render();
this.notify(false);
$(window).scrollTop(0);
}.bind(this));
},
renderDefaultPage: function () {
$('#integration-header').html('').hide();
if (this.integrationList.length) {
var msg = this.translate('selectIntegration', 'messages', 'Integration');
} else {
var msg = '<p class="lead">' + this.translate('noIntegrations', 'messages', 'Integration') + '</p>';
}
$('#integration-content').html(msg);
},
renderHeader: function () {
if (!this.integration) {
$('#integration-header').html('');
return;
}
$('#integration-header').show().html(this.translate(this.integration, 'titles', 'Integration'));
},
@@ -106,5 +118,3 @@ Espo.define('views/admin/integrations/index', 'view', function (Dep) {
},
});
});
+7 -1
View File
@@ -219,7 +219,13 @@ define('views/admin/layouts/index', 'view', function (Dep) {
},
navigate: function (scope, type) {
this.getRouter().navigate('#Admin/layouts/scope=' + scope + '&type=' + type, {trigger: false});
var url = '#Admin/layouts/scope=' + scope + '&type=' + type;
if (this.em) {
url += '&em=true';
}
this.getRouter().navigate(url, {trigger: false});
},
renderDefaultPage: function () {
+17 -8
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/export/modals/export', ['views/modal', 'model'], function (Dep, Model) {
define('views/export/modals/export', ['views/modal', 'model'], function (Dep, Model) {
return Dep.extend({
@@ -44,11 +44,11 @@ Espo.define('views/export/modals/export', ['views/modal', 'model'], function (De
{
name: 'export',
label: 'Export',
style: 'danger'
style: 'danger',
},
{
name: 'cancel',
label: 'Cancel'
label: 'Cancel',
}
];
@@ -73,44 +73,53 @@ Espo.define('views/export/modals/export', ['views/modal', 'model'], function (De
this.createView('record', 'views/export/record/record', {
scope: this.scope,
model: this.model,
el: this.getSelector() + ' .record'
el: this.getSelector() + ' .record',
});
},
actionExport: function () {
var data = this.getView('record').fetch();
this.model.set(data);
if (this.getView('record').validate()) return;
if (this.getView('record').validate()) {
return;
}
var returnData = {
exportAllFields: data.exportAllFields,
format: data.format
format: data.format,
};
if (!data.exportAllFields) {
var attributeList = [];
data.fieldList.forEach(function (item) {
if (item === 'id') {
attributeList.push('id');
return;
}
var type = this.getMetadata().get(['entityDefs', this.scope, 'fields', item, 'type']);
if (type) {;
if (type) {
this.getFieldManager().getAttributeList(type, item).forEach(function (attribute) {
attributeList.push(attribute);
}, this);
}
if (~item.indexOf('_')) {
attributeList.push(item);
}
}, this);
returnData.attributeList = attributeList;
returnData.fieldList = data.fieldList;
}
this.trigger('proceed', returnData);
this.close();
}
},
});
});
+22 -10
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
define('views/export/record/record', 'views/record/base', function (Dep) {
return Dep.extend({
@@ -48,10 +48,10 @@ Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
fieldList = fieldList.filter(function (item) {
var defs = this.getMetadata().get(['entityDefs', this.scope, 'fields', item]) || {};
if (defs.disabled) return;
if (defs.exportDisabled) return;
if (defs.type === 'map') return;
if (defs.type === 'attachmentMultiple') return;
return true;
}, this);
@@ -61,32 +61,43 @@ Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
fieldList.unshift('id');
var translatedOptions = {};
fieldList.forEach(function (item) {
translatedOptions[item] = this.getLanguage().translate(item, 'fields', this.scope);
}, this);
this.createField('exportAllFields', 'views/fields/bool', {
});
this.createField('exportAllFields', 'views/fields/bool', {});
var setFieldList = this.model.get('fieldList') || [];
setFieldList.forEach(function (item) {
if (~fieldList.indexOf(item)) return;
if (!~item.indexOf('_')) return;
if (~fieldList.indexOf(item)) {
return;
}
if (!~item.indexOf('_')) {
return;
}
var arr = item.split('_');
fieldList.push(item);
var foreignScope = this.getMetadata().get(['entityDefs', this.scope, 'links', arr[0], 'entity']);
if (!foreignScope) return;
translatedOptions[item] = this.getLanguage().translate(arr[0], 'links', this.scope) + '.' + this.getLanguage().translate(arr[1], 'fields', foreignScope);
if (!foreignScope) {
return;
}
translatedOptions[item] = this.getLanguage().translate(arr[0], 'links', this.scope) + '.' +
this.getLanguage().translate(arr[1], 'fields', foreignScope);
}, this);
this.createField('fieldList', 'views/fields/multi-enum', {
required: true,
translatedOptions: translatedOptions,
options: fieldList
options: fieldList,
});
var formatList =
@@ -98,6 +109,7 @@ Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
});
this.controlAllFields();
this.listenTo(this.model, 'change:exportAllFields', function () {
this.controlAllFields();
}, this);
@@ -109,7 +121,7 @@ Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
} else {
this.hideField('fieldList');
}
}
},
});
});
-10
View File
@@ -356,11 +356,6 @@ define('views/fields/address', 'views/fields/base', function (Dep) {
this.$country.attr('autocomplete', 'espo-country');
}
this.controlStreetTextareaHeight();
this.$street.on('input', function (e) {
this.controlStreetTextareaHeight();
}.bind(this));
var cityList = this.getConfig().get('addressCityList') || [];
if (cityList.length) {
this.$city.autocomplete({
@@ -394,11 +389,6 @@ define('views/fields/address', 'views/fields/base', function (Dep) {
this.$city.attr('autocomplete', 'espo-city');
}
this.controlStreetTextareaHeight();
this.$street.on('input', function (e) {
this.controlStreetTextareaHeight();
}.bind(this));
var stateList = this.getConfig().get('addressStateList') || [];
if (stateList.length) {
this.$state.autocomplete({
+2
View File
@@ -257,6 +257,8 @@ define('views/fields/base', 'view', function (Dep) {
this.events = {};
}
this.validations = Espo.Utils.clone(this.validations);
this.defs = this.options.defs || {};
this.name = this.options.name || this.defs.name;
this.params = this.options.params || this.defs.params || {};
+5 -1
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/fields/currency', 'views/fields/float', function (Dep) {
define('views/fields/currency', 'views/fields/float', function (Dep) {
return Dep.extend({
@@ -40,12 +40,16 @@ Espo.define('views/fields/currency', 'views/fields/float', function (Dep) {
detailTemplate2: 'fields/currency/detail-2',
detailTemplate3: 'fields/currency/detail-3',
listTemplate: 'fields/currency/list',
listTemplate1: 'fields/currency/list-1',
listTemplate2: 'fields/currency/list-2',
listTemplate3: 'fields/currency/list-3',
detailTemplateNoCurrency: 'fields/currency/detail-no-currency',
maxDecimalPlaces: 3,
+2
View File
@@ -44,6 +44,8 @@ define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], function (De
translatedOptions: null,
fetchEmptyValueAsNull: false,
searchTypeList: ['anyOf', 'noneOf', 'isEmpty', 'isNotEmpty'],
data: function () {
-4
View File
@@ -253,10 +253,6 @@ define('views/fields/link-parent', 'views/fields/base', function (Dep) {
e.currentTarget.value = this.model.get(this.nameName);
}
}.bind(this));
} else if (this.mode == 'search') {
this.$elementName.on('blur', function (e) {
e.currentTarget.value = '';
}.bind(this));
}
if (!this.autocompleteDisabled) {
-4
View File
@@ -260,10 +260,6 @@ define('views/fields/link', 'views/fields/base', function (Dep) {
e.currentTarget.value = this.model.get(this.nameName);
}
}.bind(this));
} else if (this.mode == 'search') {
this.$elementName.on('blur', function (e) {
e.currentTarget.value = '';
}.bind(this));
}
var $elementName = this.$elementName;
+8
View File
@@ -44,6 +44,8 @@ define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], function
seeMoreDisabled: true,
fetchEmptyValueAsNull: false,
setup: function () {
Dep.prototype.setup.call(this);
@@ -532,6 +534,12 @@ define('views/fields/wysiwyg', ['views/fields/text', 'lib!Summernote'], function
data[this.name] = code;
} else {
data[this.name] = this.$element.val();
if (this.fetchEmptyValueAsNull) {
if (!data[this.name]) {
data[this.name] = null;
}
}
}
if (this.model.has('isHtml') && this.hasBodyPlainField) {
+4 -4
View File
@@ -388,13 +388,13 @@ define('views/import/step2', 'view', function (Dep) {
},
disableButtons: function () {
this.$el.find('button[data-action="next"]').addClass('disabled');
this.$el.find('button[data-action="back"]').addClass('disabled');
this.$el.find('button[data-action="next"]').addClass('disabled').attr('disabled', 'disabled');
this.$el.find('button[data-action="back"]').addClass('disabled').attr('disabled', 'disabled');
},
enableButtons: function () {
this.$el.find('button[data-action="next"]').removeClass('disabled');
this.$el.find('button[data-action="back"]').removeClass('disabled');
this.$el.find('button[data-action="next"]').removeClass('disabled').removeAttr('disabled');
this.$el.find('button[data-action="back"]').removeClass('disabled').removeAttr('disabled');
},
fetch: function (skipValidation) {
+33 -9
View File
@@ -58,9 +58,11 @@ define('views/list-with-categories', 'views/list', function (Dep) {
data: function () {
var data = {};
data.categoriesDisabled = this.categoriesDisabled;
data.isExpanded = this.isExpanded;
data.hasExpandedToggler = this.hasExpandedToggler;
return data;
},
@@ -101,7 +103,10 @@ define('views/list-with-categories', 'views/list', function (Dep) {
this.applyCategoryToCollection();
this.listenTo(this.collection, 'sync', function (c, d, o) {
if (o && o.openCategory) return;
if (o && o.openCategory) {
return;
}
this.controlListVisibility();
}, this);
},
@@ -120,6 +125,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
this.openCategory(params.categoryId, params.categoryName);
}
}
this.selectCurrentCategory();
}
},
@@ -129,13 +135,14 @@ define('views/list-with-categories', 'views/list', function (Dep) {
for (var i = 0; i < this.collection.where.length; i++) {
if (this.collection.where[i].type === 'textFilter') {
return true;
break;
}
}
}
if (this.collection.data && this.collection.data.textFilter) {
return true;
}
return false;
},
@@ -161,9 +168,11 @@ define('views/list-with-categories', 'views/list', function (Dep) {
} else {
this.controlListVisibility();
}
if (!this.categoriesDisabled && !this.hasView('categories')) {
this.loadCategories();
}
if (!this.isExpanded && !this.hasView('nestedCategories')) {
this.loadNestedCategories();
}
@@ -171,6 +180,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
actionExpand: function () {
this.isExpanded = true;
this.setIsExpandedStoredValue(true);
this.applyCategoryToCollection();
@@ -307,12 +317,15 @@ define('views/list-with-categories', 'views/list', function (Dep) {
this.listenToOnce(collection, 'sync', function () {
callback.call(this, collection);
}, this);
collection.fetch();
}, this);
},
applyCategoryToNestedCategoriesCollection: function () {
if (!this.nestedCategoriesCollection) return;
if (!this.nestedCategoriesCollection) {
return;
}
this.nestedCategoriesCollection.where = null;
@@ -335,6 +348,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
collection.maxDepth = 1;
collection.data.checkIfEmpty = true;
if (!this.getAcl().checkScope(this.scope, 'create')) {
collection.data.onlyNotEmpty = true;
}
@@ -344,6 +358,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
collection.fetch().then(function () {
this.controlListVisibility();
this.controlNestedCategoriesVisibility();
callback.call(this, collection);
}.bind(this));
}, this);
@@ -353,7 +368,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
this.getNestedCategoriesCollection(function (collection) {
this.createView('nestedCategories', 'views/record/list-nested-categories', {
collection: collection,
el: this.options.el + ' .nested-categories-container'
el: this.options.el + ' .nested-categories-container',
}, function (view) {
view.render();
});
@@ -378,18 +393,22 @@ define('views/list-with-categories', 'views/list', function (Dep) {
if (this.currentCategoryId) {
view.setSelected(this.currentCategoryId);
}
view.render();
this.listenTo(view, 'select', function (model) {
if (!this.isExpanded) {
var id = null;
var name = null;
if (model && model.id) {
id = model.id;
name = model.get('name');
}
this.openCategory(id, name);
this.navigateToCurrentCategory();
return;
}
this.currentCategoryId = null;
@@ -409,6 +428,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
this.listenToOnce(this.collection, 'sync', function () {
this.notify(false);
}, this);
this.collection.fetch();
}, this);
@@ -417,9 +437,7 @@ define('views/list-with-categories', 'views/list', function (Dep) {
}, this);
},
applyCategoryToCollection: function () {
this.collection.whereFunction = function () {
var filter;
var isExpanded = this.isExpanded;
@@ -457,14 +475,14 @@ define('views/list-with-categories', 'views/list', function (Dep) {
filter = {
field: this.categoryField,
type: this.categoryFilterType,
value: this.currentCategoryId
value: this.currentCategoryId,
};
}
}
if (filter) {
return [filter];
}
}.bind(this);
},
@@ -479,19 +497,26 @@ define('views/list-with-categories', 'views/list', function (Dep) {
if (this.currentCategoryId) {
var names = {};
names[this.currentCategoryId] = this.currentCategoryName;
var data = {};
var idsAttribute = this.categoryField + 'Ids';
var namesAttribute = this.categoryField + 'Names';
data[idsAttribute] = [this.currentCategoryId],
data[namesAttribute] = names;
return data;
}
} else {
var idAttribute = this.categoryField + 'Id';
var nameAttribute = this.categoryField + 'Name';
var data = {};
data[idAttribute] = this.currentCategoryId;
data[nameAttribute] = this.currentCategoryName;
return data;
}
},
@@ -500,6 +525,5 @@ define('views/list-with-categories', 'views/list', function (Dep) {
this.clearView('categories');
this.getRouter().navigate('#' + this.categoryScope, {trigger: true});
},
});
});
+8 -1
View File
@@ -62,8 +62,12 @@ define('views/modals/compose-email', 'views/modals/edit', function (Dep) {
var attributes = this.options.attributes || {};
require('email-helper', function (EmailHelper) {
this.getRouter().confirmLeaveOut = false;
var emailHelper = new EmailHelper();
var link = emailHelper.composeMailToLink(attributes, this.getConfig().get('outboundEmailBccAddress'));
document.location.href = link;
}.bind(this));
@@ -80,7 +84,9 @@ define('views/modals/compose-email', 'views/modals/edit', function (Dep) {
},
createRecordView: function (model, callback) {
var viewName = this.getMetadata().get('clientDefs.' + model.name + '.recordViews.compose') || 'views/email/record/compose';
var viewName = this.getMetadata().get('clientDefs.' + model.name + '.recordViews.compose') ||
'views/email/record/compose';
var options = {
model: model,
el: this.containerSelector + ' .edit-container',
@@ -93,6 +99,7 @@ define('views/modals/compose-email', 'views/modals/edit', function (Dep) {
appendSignature: this.options.appendSignature,
exit: function () {}
};
this.createView('edit', viewName, options, callback);
},
@@ -30,6 +30,8 @@ define('views/preferences/fields/signature', 'views/fields/wysiwyg', function (D
return Dep.extend({
fetchEmptyValueAsNull: true,
toolbar: [
["style", ["bold", "italic", "underline", "clear"]],
["color", ["color"]],
+2
View File
@@ -246,6 +246,8 @@ define('views/record/detail', ['views/record/base', 'view-record-helper'], funct
this.getAcl().check(this.entityType, 'edit')
&&
!~this.getAcl().getScopeForbiddenFieldList(this.entityType).indexOf('assignedUser')
&&
!this.getUser().isPortal()
) {
if (this.model.has('assignedUserId')) {
this.dropdownItemList.push({
+1 -1
View File
@@ -998,7 +998,7 @@ define('views/record/list', 'view', function (Dep) {
}, this);
if (
this.getConfig().get('exportDisabled') && !this.getUser().get('isAdmin')
this.getConfig().get('exportDisabled') && !this.getUser().isAdmin()
||
this.getAcl().get('exportPermission') === 'no'
||
@@ -25,14 +25,40 @@
* 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.
************************************************************************/
Espo.define('views/settings/fields/default-currency', 'views/fields/enum', function (Dep) {
define('views/settings/fields/default-currency', 'views/fields/enum', function (Dep) {
return Dep.extend({
setup: function () {
Dep.prototype.setup.call(this);
this.validations.push('existing');
},
setupOptions: function () {
this.params.options = Espo.Utils.clone(this.getConfig().get('currencyList') || []);
}
},
validateExisting: function () {
var currencyList = this.model.get('currencyList');
if (!currencyList) {
return;
}
var value = this.model.get(this.name);
if (~currencyList.indexOf(value)) {
return;
}
var msg = this.translate('fieldInvalid', 'messages').replace('{field}', this.getLabelText());
this.showValidationMessage(msg);
return true;
},
});
});
+25 -2
View File
@@ -126,7 +126,9 @@ define('views/user/record/detail', 'views/record/detail', function (Dep) {
},
setupNonAdminFieldsAccess: function () {
if (this.getUser().isAdmin()) return;
if (this.getUser().isAdmin()) {
return;
}
var nonAdminReadOnlyFieldList = [
'userName',
@@ -138,9 +140,30 @@ define('views/user/record/detail', 'views/record/detail', function (Dep) {
'portalRoles',
'contact',
'accounts',
'type'
'type',
'emailAddress',
];
nonAdminReadOnlyFieldList = nonAdminReadOnlyFieldList.filter(
function (item) {
if (!this.model.hasField(item)) {
return true;
}
var aclDefs = this.getMetadata().get(['entityAcl', 'User', 'fields', item]);
if (!aclDefs) {
return true;
}
if (aclDefs.nonAdminReadOnly) {
return true;
}
return false;
}.bind(this),
);
nonAdminReadOnlyFieldList.forEach(function (field) {
this.setFieldReadOnly(field, true);
}, this);
+7 -3
View File
@@ -31,6 +31,11 @@ if (substr(php_sapi_name(), 0, 3) != 'cli') exit;
include "bootstrap.php";
use Espo\Core\{
Application,
ApplicationRunners\Rebuild,
};
$arg = isset($_SERVER['argv'][1]) ? trim($_SERVER['argv'][1]) : '';
if (empty($arg)) {
@@ -46,7 +51,7 @@ if (!isset($pathInfo['extension']) || $pathInfo['extension'] !== 'zip' || !is_fi
die("Unsupported package.\n");
}
$app = new \Espo\Core\Application();
$app = new Application();
$app->setupSystemUser();
$config = $app->getContainer()->get('config');
@@ -67,8 +72,7 @@ try {
}
try {
$app = new \Espo\Core\Application();
$app->runRebuild();
(new Application())->run(Rebuild::class);
} catch (\Exception $e) {}
echo "Extension installation is complete.\n";
+16 -4
View File
@@ -82,12 +82,24 @@ class SystemHelper extends \Espo\Core\Utils\System
public function getBaseUrl()
{
$pageUrl = ($_SERVER["HTTPS"] == 'on') ? 'https://' : 'http://';
$pageUrl = 'http://';
if ($_SERVER["SERVER_PORT"] != "80") {
$pageUrl .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
} else {
if (isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] == 'https') {
$pageUrl = 'https://';
}
if (isset($_SERVER['HTTPS']) && $_SERVER["HTTPS"] == 'on') {
$pageUrl = 'https://';
}
if ($_SERVER["SERVER_PORT"] == '443') {
$pageUrl = 'https://';
}
if (in_array($_SERVER["SERVER_PORT"], ['80', '443'])) {
$pageUrl .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
} else {
$pageUrl .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
}
$baseUrl = str_ireplace('/install/index.php', '', $pageUrl);
+9 -9
View File
@@ -13,10 +13,10 @@
{foreach from=$phpRequirementList key=name item=value}
<tr class="list-row">
<td class="cell col-md-5">
{if $langs['systemRequirements'][$name] eq ''}
{$name}
{if isset($langs['systemRequirements'][$name])}
{$langs['systemRequirements'][{$name}]}
{else}
{$langs['systemRequirements'][{$name}]}
{$name}
{/if}
</td>
<td class="cell col-md-3">{$value['actual']}</td>
@@ -41,10 +41,10 @@
{foreach from=$mysqlRequirementList key=name item=value}
<tr class="list-row">
<td class="cell col-md-5">
{if $langs['systemRequirements'][$name] eq ''}
{$name}
{if isset($langs['systemRequirements'][$name])}
{$langs['systemRequirements'][{$name}]}
{else}
{$langs['systemRequirements'][{$name}]}
{$name}
{/if}
</td>
<td class="cell col-md-3">{$value['actual']}</td>
@@ -68,10 +68,10 @@
{foreach from=$permissionRequirementList key=name item=value}
<tr class="list-row">
<td class="cell col-md-5">
{if $langs['systemRequirements'][$name] eq ''}
{$name}
{if isset($langs['systemRequirements'][$name])}
{$langs['systemRequirements'][{$name}]}
{else}
{$langs['systemRequirements'][{$name}]}
{$name}
{/if}
</td>
<td class="cell col-md-3">{$langs['systemRequirements'][{$value['type']}]}</td>

Some files were not shown because too many files have changed in this diff Show More