Compare commits

...

84 Commits
6.1.2 ... 6.1.8

Author SHA1 Message Date
Yuri Kuznetsov
3e6bc02256 v 2021-07-20 15:17:28 +03:00
Taras Machyshyn
2a52d3c52e NamespaceLoader bug fixes 2021-06-24 17:47:57 +03:00
Yuri Kuznetsov
a9623ff698 hotfix cache clear on login 2021-06-17 13:19:59 +03:00
Yuri Kuznetsov
f774b4bbcf queue optimization 2021-05-24 12:20:40 +03:00
Yuri Kuznetsov
da2e2dbf20 fix typo 2021-05-19 15:55:38 +03:00
Eymen Elkum
c054712828 config param emailAutoReplaySuppressPeriod (#1998) 2021-05-19 10:14:23 +03:00
Yuri Kuznetsov
44d772a32f fix typo 2021-05-09 15:23:59 +03:00
Yuri Kuznetsov
a90971b762 not held event strikethrough 2021-05-07 10:34:38 +03:00
Yuri Kuznetsov
09d7e8abde search apply button fixes 2021-05-05 11:54:24 +03:00
Yuri Kuznetsov
1ddfcff441 version 2021-05-05 11:17:35 +03:00
Yuri Kuznetsov
1a672a8aa3 escaping 2021-05-04 16:08:35 +03:00
Eymen Elkum
a6fb23302e typo fix (#1974) 2021-04-24 16:59:32 +03:00
Yuri Kuznetsov
85ed99a1dc email get size error fix 2021-04-15 11:40:06 +03:00
Yuri Kuznetsov
6e9f368a0a version 2021-04-12 10:42:26 +03:00
Yuri Kuznetsov
84a3688525 scheduler honor eventAssignedUserIsAttendeeDisabled 2021-04-12 10:40:06 +03:00
Yuri Kuznetsov
3c08986889 cs fix 2021-04-12 10:37:07 +03:00
Yuri Kuznetsov
477828beb9 fix get entity followers 2021-04-12 10:31:47 +03:00
Eymen Elkum
fadc6c46ba fix (#1969) 2021-04-12 10:31:29 +03:00
Yuri Kuznetsov
c1b7e06308 remove noJoin 2021-04-01 13:27:42 +03:00
Taras Machyshyn
e86ec32dd7 Hide permission warnings 2021-03-31 15:12:46 +03:00
Taras Machyshyn
c8bd2ec1a1 Time format fixes 2021-03-31 14:39:47 +03:00
Taras Machyshyn
3cf94bb728 Notification fixes 2021-03-31 12:17:18 +03:00
Yuri Kuznetsov
0f8621d923 spatie async update 2021-03-25 16:11:30 +02:00
Yuri Kuznetsov
2d9e9a5e93 async downgrade 2021-03-25 15:38:50 +02:00
Yuri Kuznetsov
abeb5fc0e3 v 2021-03-25 15:14:57 +02:00
Taras Machyshyn
47e61cecdd Upgrade scripts 2021-03-25 15:14:22 +02:00
Taras Machyshyn
17aaf08717 Installation: code changes 2021-03-25 13:23:23 +02:00
Taras Machyshyn
89e849d851 Installation: bug fixes 2021-03-25 13:06:19 +02:00
Yuri Kuznetsov
42098f477a fix oauth client 2021-03-25 12:14:22 +02:00
Taras Machyshyn
e44ee9ca03 Installation: corrected languages 2021-03-25 11:10:18 +02:00
Yuri Kuznetsov
c4861c5efa fix exceptions 2021-03-22 09:16:45 +02:00
Taras Machyshyn
4380175c8b Upgrade scripts 2021-03-18 17:39:32 +02:00
Yuri Kuznetsov
8ff46bba74 diff: removing git folder in vendor 2021-03-18 15:47:40 +02:00
Yuri Kuznetsov
06f1305f23 removing git folder in vendor on build 2021-03-18 15:29:21 +02:00
Eymen Elkum
43723697f7 fix (#1944) 2021-03-15 14:20:42 +02:00
Yuri Kuznetsov
99d6a0ab32 local storage cache exceeded no error 2021-03-15 13:52:01 +02:00
Yuri Kuznetsov
c2809ca091 no lib cache 2021-03-15 13:44:31 +02:00
Yuri Kuznetsov
108432c268 cs fix 2021-03-15 13:43:49 +02:00
Yuri Kuznetsov
970242e860 cs fixes 2021-03-10 16:05:38 +02:00
Yuri Kuznetsov
dff2e344a4 fix formula modulo 2021-03-06 16:25:13 +02:00
Eymen Elkum
24617f4a41 variable name typo (#1937) 2021-03-04 09:25:31 +02:00
Taras Machyshyn
2ae0f36241 LDAP bug fixes 2021-03-03 10:57:01 +02:00
Yuri Kuznetsov
eec372c6df fix css 2021-03-02 11:39:26 +02:00
Yuri Kuznetsov
011e83fac6 update selectize 2021-03-02 11:37:07 +02:00
Yuri Kuznetsov
dd8e55c2ac restpore tcpdf 2021-03-01 14:34:38 +02:00
Yuri Kuznetsov
00fcd5a9b6 fix tcpdf page number 2021-02-26 12:32:48 +02:00
Yuri Kuznetsov
3dda43c70a update tcpdf 2021-02-26 10:38:46 +02:00
Yuri Kuznetsov
34029c6f6f update composer deps 2021-02-26 09:26:40 +02:00
Yuri Kuznetsov
cc307e3217 cleanup 2021-02-25 16:40:42 +02:00
Yuri Kuznetsov
e3c45a5039 hide reset to default field for custom entities 2021-02-25 16:39:33 +02:00
Yuri Kuznetsov
7cbc24feb8 v 2021-02-22 10:52:07 +02:00
Yuri Kuznetsov
2ff248a4c4 use forked lighrncandy 2021-02-18 15:35:01 +02:00
Yuri Kuznetsov
0010976803 attachment download calculating actual filesize 2021-02-18 08:54:03 +02:00
Eymen Elkum
bd422323ee fix typo (#1924) 2021-02-18 08:44:40 +02:00
Eymen Elkum
6a66d99017 add missing import (#1923) 2021-02-18 08:34:24 +02:00
Yuri Kuznetsov
1f7d92b209 return type fix 2021-02-17 16:36:15 +02:00
Yuri Kuznetsov
71c72136f6 attachment proper size calculation 2021-02-17 09:09:52 +02:00
Yuri Kuznetsov
7f57e64292 Merge branch 'hotfix/6.1.3' of github.com:espocrm/espocrm into hotfix/6.1.3 2021-02-15 10:51:31 +02:00
Yuri Kuznetsov
300a0ece75 save and continue action 2 2021-02-13 19:21:47 +02:00
Yuri Kuznetsov
3172ba213e save and continue action 2021-02-13 19:16:45 +02:00
Yuri Kuznetsov
5685ccbffd deprecated fix 2021-02-13 11:03:44 +02:00
Yuri Kuznetsov
3e372f753d fix 2021-02-12 16:53:54 +02:00
Yuri Kuznetsov
51ea8a5e1c fromula array join function 2021-02-12 14:11:02 +02:00
Yuri Kuznetsov
d3c634b89f fix lead convert 2021-02-12 12:52:35 +02:00
Yuri Kuznetsov
eca341d7ec update phpspreadsheet 2021-02-11 14:18:49 +02:00
Taras Machyshyn
454ca3b136 Merge branch 'hotfix/6.1.3' of https://github.com/espocrm/espocrm into hotfix/6.1.3 2021-02-11 13:09:38 +02:00
Taras Machyshyn
e3b8b450a4 DBAL: bug fixing 2021-02-11 13:09:20 +02:00
Taras Machyshyn
c15c486603 Fix changing default values in MariaDB 2021-02-10 12:42:46 +02:00
Yuri Kuznetsov
c6554afaae Merge branch 'hotfix/6.1.3' of https://github.com/espocrm/espocrm into hotfix/6.1.3 2021-02-10 10:44:11 +02:00
Yuri Kuznetsov
0d6b5ce68c fix 2021-02-10 10:43:54 +02:00
Taras Machyshyn
2830400da5 DBAL: do not change collation when changing a field 2021-02-10 10:22:01 +02:00
Yuri Kuznetsov
aa8edf1ee9 v 2021-02-09 11:05:49 +02:00
Yuri Kuznetsov
f1bf439f7b wysiwyg modal backdrop 2021-02-08 15:37:17 +02:00
Yuri Kuznetsov
220992fc37 formula generate id 2021-02-08 15:18:14 +02:00
Yuri Kuznetsov
de52af79c5 cs fix 2021-02-07 12:28:36 +02:00
Yuri Kuznetsov
c555102b5f refactoring 2021-02-07 12:28:00 +02:00
Yuri Kuznetsov
4c05175dbe tracking url uid 2021-02-07 12:12:30 +02:00
Yuri Kuznetsov
de543fa2db fix typo 2021-02-07 08:50:02 +02:00
Yuri Kuznetsov
a0f85bd55a attachment source list improvements 2021-02-05 13:13:05 +02:00
Yuri Kuznetsov
7273caac36 cs fix 2021-02-05 12:37:57 +02:00
Yuri Kuznetsov
d8383dc38a opcache invalidate error fix 2021-02-04 16:46:33 +02:00
Yuri Kuznetsov
7824ab8dc9 fix logger handler 2021-02-04 16:32:37 +02:00
Yuri Kuznetsov
7320bf68e5 fix id attribute reading 2021-02-02 14:42:48 +02:00
Yuri Kuznetsov
d7b9492f15 login exception fix 2021-02-01 19:11:13 +02:00
136 changed files with 2601 additions and 856 deletions

View File

@@ -149,6 +149,7 @@ module.exports = function (grunt) {
'build/tmp/custom/Espo/Custom/*',
'!build/tmp/custom/Espo/Custom/.htaccess',
'build/tmp/install/config.php',
'build/tmp/vendor/*/*/.git',
]
}
},

View File

@@ -69,7 +69,7 @@ class Attachment extends \Espo\Core\Controllers\Record
$response->setHeader('Content-Type', $fileData->type);
$response->setHeader('Content-Disposition', 'Content-Disposition: attachment; filename="'.$fileData->name.'"');
if ($fileData->size) {
$response->setHeader('Content-Length', $fileData->size);
$response->setHeader('Content-Length', strlen($fileData->contents));
}
return $fileData->contents;

View File

@@ -185,6 +185,8 @@ class Auth
}
catch (Exception $e) {
$this->processException($response, $e);
return;
}
if ($authResult && ($authResult->isSuccess() || $authResult->isSecondStepRequired())) {

View File

@@ -50,10 +50,19 @@ class ErrorOutput
404 => 'Page Not Found',
409 => 'Conflict',
500 => 'Internal Server Error',
503 => 'Service Unavailable',
];
protected $allowedStatusCodeList = [
200, 201, 400, 401, 403, 404, 409, 500,
200,
201,
400,
401,
403,
404,
409,
500,
503,
];
protected $ignorePrintXStatusReasonExceptionClassNameList = [

View File

@@ -305,11 +305,11 @@ class LDAP extends Espo
$data[$fieldName] = $fieldValue;
}
$this->useSystemUser();
$user = $this->entityManager->getEntity('User');
$user->set($data);
$this->applicationUser->setUser($user);
$this->entityManager->saveEntity($user);
return $this->entityManager->getEntity('User', $user->id);

View File

@@ -57,6 +57,7 @@ class EntryPointManager
public function checkAuthRequired(string $name) : bool
{
$className = $this->getClassName($name);
if (!$className) {
throw new NotFound("EntryPoint {$name} not found.");
}
@@ -72,6 +73,7 @@ class EntryPointManager
public function checkNotStrictAuth(string $name) : bool
{
$className = $this->getClassName($name);
if (!$className) {
throw new NotFound("EntryPoint {$name} not found.");
}
@@ -82,6 +84,7 @@ class EntryPointManager
public function run(string $name, Request $request, Response $response, ?StdClass $data = null)
{
$className = $this->getClassName($name);
if (!$className) {
throw new NotFound("EntryPoint {$name} not found.");
}
@@ -94,6 +97,7 @@ class EntryPointManager
protected function getClassName(string $name) : ?string
{
$name = ucfirst($name);
return $this->classFinder->find('EntryPoints', $name);
}
}

View File

@@ -29,6 +29,8 @@
namespace Espo\Core\ExternalAccount\OAuth2;
use Exception;
class Client
{
const AUTH_TYPE_URI = 0;
@@ -79,7 +81,7 @@ class Client
public function __construct(array $params = [])
{
if (!extension_loaded('curl')) {
throw new \Exception('CURL extension not found.');
throw new Exception('CURL extension not found.');
}
}
@@ -139,16 +141,21 @@ class Client
switch ($this->tokenType) {
case self::TOKEN_TYPE_URI:
$params[$this->accessTokenParamName] = $this->accessToken;
break;
case self::TOKEN_TYPE_BEARER:
$httpHeaders['Authorization'] = 'Bearer ' . $this->accessToken;
break;
case self::TOKEN_TYPE_OAUTH:
$httpHeaders['Authorization'] = 'OAuth ' . $this->accessToken;
break;
default:
throw new \Exception('Unknown access token type.');
break;
default:
throw new Exception('Unknown access token type.');
}
}
@@ -157,15 +164,16 @@ class Client
private function execute($url, $params, $httpMethod, array $httpHeaders = [])
{
$curlOptions = array(
$curlOptions = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CUSTOMREQUEST => $httpMethod
);
CURLOPT_CUSTOMREQUEST => $httpMethod,
];
switch ($httpMethod) {
case self::HTTP_METHOD_POST:
$curlOptions[CURLOPT_POST] = true;
case self::HTTP_METHOD_PUT:
case self::HTTP_METHOD_PATCH:
if (is_array($params)) {
@@ -173,36 +181,49 @@ class Client
} else {
$postFields = $params;
}
$curlOptions[CURLOPT_POSTFIELDS] = $postFields;
break;
case self::HTTP_METHOD_HEAD:
$curlOptions[CURLOPT_NOBODY] = true;
case self::HTTP_METHOD_DELETE:
case self::HTTP_METHOD_GET:
if (strpos($url, '?') === false) {
$url .= '?';
}
if (is_array($params)) {
$url .= http_build_query($params, null, '&');
}
break;
default:
break;
}
$curlOptions[CURLOPT_URL] = $url;
$curlOptHttpHeader = array();
$curlOptHttpHeader = [];
foreach ($httpHeaders as $key => $value) {
if (is_int($key) && !is_string($key)) {
$curlOptHttpHeader[] = $value;
continue;
}
$curlOptHttpHeader[] = "{$key}: {$value}";
}
$curlOptions[CURLOPT_HTTPHEADER] = $curlOptHttpHeader;
$ch = curl_init();
curl_setopt_array($ch, $curlOptions);
curl_setopt($ch, CURLOPT_HEADER, 1);
@@ -230,18 +251,20 @@ class Client
$resultArray = null;
if ($curlError = curl_error($ch)) {
throw new \Exception($curlError);
} else {
throw new Exception($curlError);
}
else {
$resultArray = json_decode($responceBody, true);
}
curl_close($ch);
return array(
return [
'result' => (null !== $resultArray) ? $resultArray: $responceBody,
'code' => intval($httpCode),
'contentType' => $contentType,
'header' => $responceHeader,
);
];
}
public function getAccessToken($url, $grantType, array $params)
@@ -249,18 +272,24 @@ class Client
$params['grant_type'] = $grantType;
$httpHeaders = [];
switch ($this->tokenType) {
switch ($this->authType) {
case self::AUTH_TYPE_URI:
case self::AUTH_TYPE_FORM:
$params['client_id'] = $this->clientId;
$params['client_secret'] = $this->clientSecret;
break;
case self::AUTH_TYPE_AUTHORIZATION_BASIC:
$params['client_id'] = $this->clientId;
$httpHeaders['Authorization'] = 'Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret);
break;
default:
throw new \Exception();
throw new Exception("Bad auth type.");
}
return $this->execute($url, $params, self::HTTP_METHOD_POST, $httpHeaders);

View File

@@ -42,7 +42,7 @@ class AddressBuilder
protected $state;
protected $portalCode;
protected $postalCode;
public function clone(AddressValue $address) : self
{

View File

@@ -46,7 +46,7 @@ class AddressValue
protected $state = null;
protected $portalCode = null;
protected $postalCode = null;
public function getStreet() : ?string
{

View File

@@ -0,0 +1,59 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class JoinType extends BaseFunction
{
public function process(ArgumentList $args)
{
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$list = $this->evaluate($args[0]);
$separator = $this->evaluate($args[1]);
if (!is_string($separator)) {
$this->throwBadArgumentValue(2, 'string');
}
if (is_null($list)) {
return '';
}
return implode($separator, $list);
}
}

View File

@@ -38,18 +38,19 @@ class ModuloType extends BaseFunction
{
public function process(ArgumentList $args)
{
$result = 1;
foreach ($args as $subItem) {
$part = $this->evaluate($subItem);
if (!is_float($part) && !is_int($part)) {
$part = floatval($part);
}
$result %= $part;
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$result = $this->evaluate($args[0]);
$part = $this->evaluate($args[1]);
if (!is_float($part) && !is_int($part)) {
$part = floatval($part);
}
$result %= $part;
return $result;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Functions\UtilGroup;
use Espo\Core\Formula\{
ArgumentList,
};
use Espo\Core\Utils\Util;
class GenerateIdType
{
public function process(ArgumentList $args)
{
return Util::generateId();
}
}

View File

@@ -39,8 +39,8 @@ use Espo\Core\{
Utils\Config,
};
use LogicException;
use UnexpectedValueException;
use RuntimeException;
use Throwable;
class EspoFileHandler extends MonologStreamHandler
{
@@ -60,35 +60,34 @@ class EspoFileHandler extends MonologStreamHandler
protected function write(array $record): void
{
if (!$this->url) {
throw new LogicException(
'Missing logger path. Check logger params in the data/config.php.'
throw new RuntimeException(
"Missing a logger file path. Check logger params in `data/config.php`."
);
}
$this->errorMessage = null;
try {
if (!is_writable($this->url)) {
$this->fileManager->checkCreateFile($this->url);
}
if (!is_writable($this->url)) {
$this->fileManager->checkCreateFile($this->url);
}
if (!is_writable($this->url)) {
return;
}
if (is_writable($this->url)) {
set_error_handler([$this, 'customErrorHandler']);
$this->fileManager->appendContents($this->url, $this->pruneMessage($record));
restore_error_handler();
}
if (isset($this->errorMessage)) {
throw new UnexpectedValueException(
sprintf('File "%s" could not be opened: ' . $this->errorMessage, $this->url)
$this->fileManager->appendContents(
$this->url,
$this->pruneMessage($record)
);
}
}
catch (Throwable $e) {
$msg = "Could not write file `" . $this->url . "`.";
private function customErrorHandler($code, $msg)
{
$this->errorMessage = $msg;
if ($e->getMessage()) {
$msg .= " Error message: " . $e->getMessage();
}
throw new RuntimeException($msg);
}
}
protected function pruneMessage(array $record)
@@ -97,6 +96,7 @@ class EspoFileHandler extends MonologStreamHandler
if (strlen($message) > $this->maxErrorMessageLength) {
$record['message'] = substr($message, 0, $this->maxErrorMessageLength) . '...';
$record['formatted'] = $this->getFormatter()->format($record);
}

View File

@@ -123,7 +123,7 @@ class Tcpdf extends \TCPDF
$html = str_replace('{pageNumber}', '{{:png:}}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
} else {
$html = str_replace('{pageNumber}', '{{:pnp:}', $html);
$html = str_replace('{pageNumber}', '{{:pnp:}}', $html);
$html = str_replace('{pageAbsoluteNumber}', '{{:pnp:}}', $html);
}

View File

@@ -160,6 +160,12 @@ class NamespaceLoader
continue;
}
if ($type == 'classmap') {
$classLoader->$methodName($list[$type]);
continue;
}
foreach ($list[$type] as $prefix => $path) {
if (!$skipVendorNamespaces) {
$vendorNamespaces = $this->getVendorNamespaces($path);

View File

@@ -165,13 +165,22 @@ class Job
public function hasScheduledJobOnMinute(string $scheduledJobId, string $time) : bool
{
$dateObj = new DateTime($time);
$timeWithoutSeconds = $dateObj->format('Y-m-d H:i:');
$fromString = $dateObj->format('Y-m-d H:i:00');
$toString = $dateObj->format('Y-m-d H:i:59');
$job = $this->getEntityManager()->getRepository('Job')
->select(['id'])
->where([
'scheduledJobId' => $scheduledJobId,
'executeTime*' => $timeWithoutSeconds . '%',
'status' => [ // This forces usage of an appropriate index.
CronManager::PENDING,
CronManager::READY,
CronManager::RUNNING,
CronManager::SUCCESS,
],
'executeTime>=' => $fromString,
'executeTime<=' => $toString,
])
->findOne();

View File

@@ -29,8 +29,11 @@
namespace Espo\Core\Utils\Database;
use Espo\Core\Utils\Util;
use Espo\ORM\Entity;
use Espo\Core\{
Utils\Metadata,
Utils\File\Manager as FileManager,
Utils\Config,
};
class Converter
{
@@ -40,11 +43,7 @@ class Converter
private $config;
private $schemaConverter;
private $schemaFromMetadata = null;
public function __construct(\Espo\Core\Utils\Metadata $metadata, \Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Config $config = null)
public function __construct(Metadata $metadata, FileManager $fileManager, Config $config = null)
{
$this->metadata = $metadata;
$this->fileManager = $fileManager;

View File

@@ -0,0 +1,77 @@
<?php
namespace Espo\Core\Utils\Database\DBAL\Driver\PDO\MySQL;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use PDO;
// Espo: requires for the issue https://github.com/doctrine/dbal/issues/4496
use Espo\Core\Utils\Database\DBAL\Schema\MySQLSchemaManager;
use Doctrine\DBAL\Connection as MySQLDriverConnection;
// End: espo
final class Driver extends AbstractMySQLDriver
{
/**
* {@inheritdoc}
*
* @return Connection
*/
public function connect(array $params)
{
$driverOptions = $params['driverOptions'] ?? [];
if (! empty($params['persistent'])) {
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
return new Connection(
$this->constructPdoDsn($params),
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions
);
}
/**
* Constructs the MySQL PDO DSN.
*
* @param mixed[] $params
*
* @return string The DSN.
*/
protected function constructPdoDsn(array $params)
{
$dsn = 'mysql:';
if (isset($params['host']) && $params['host'] !== '') {
$dsn .= 'host=' . $params['host'] . ';';
}
if (isset($params['port'])) {
$dsn .= 'port=' . $params['port'] . ';';
}
if (isset($params['dbname'])) {
$dsn .= 'dbname=' . $params['dbname'] . ';';
}
if (isset($params['unix_socket'])) {
$dsn .= 'unix_socket=' . $params['unix_socket'] . ';';
}
if (isset($params['charset'])) {
$dsn .= 'charset=' . $params['charset'] . ';';
}
return $dsn;
}
// Espo: requires for the issue https://github.com/doctrine/dbal/issues/4496
public function getSchemaManager(MySQLDriverConnection $conn, AbstractPlatform $platform)
{
return new MySQLSchemaManager($conn, $platform);
}
// End: espo
}

View File

@@ -92,6 +92,9 @@ class Comparator extends OriginalComparator
if ($length2 > $length1) {
$changedProperties[] = 'length';
}
if ($length2 < $length1) {
$column2->setLength($length1);
}
// Espo: end
if ($properties1['fixed'] !== $properties2['fixed']) {
@@ -136,7 +139,10 @@ class Comparator extends OriginalComparator
}
// Espo: skip collation changes
if ($key == 'collation') continue;
if ($key == 'collation') {
$column2->setPlatformOption('collation', $platformOptions1['collation']);
continue;
}
// Espo: end
$changedProperties[] = $key;

View File

@@ -0,0 +1,205 @@
<?php
namespace Espo\Core\Utils\Database\DBAL\Schema;
use Doctrine\DBAL\Schema\MySQLSchemaManager as OriginalComparator;
// Espo: requires for the issue https://github.com/doctrine/dbal/issues/4496
//use Doctrine\DBAL\Platforms\MariaDb1027Platform;
//use Doctrine\DBAL\Platforms\MySQLPlatform;
use Espo\Core\Utils\Database\DBAL\Platforms\{
MariaDb1027Platform,
MySQLPlatform,
};
use Doctrine\DBAL\Schema\Column;
// Espo: end
use Doctrine\DBAL\Types\Type;
use function array_change_key_case;
use function array_shift;
use function array_values;
use function assert;
use function explode;
use function is_string;
use function preg_match;
use function strpos;
use function strtok;
use function strtolower;
use function strtr;
use const CASE_LOWER;
class MySQLSchemaManager extends OriginalComparator
{
private const MARIADB_ESCAPE_SEQUENCES = [
'\\0' => "\0",
"\\'" => "'",
'\\"' => '"',
'\\b' => "\b",
'\\n' => "\n",
'\\r' => "\r",
'\\t' => "\t",
'\\Z' => "\x1a",
'\\\\' => '\\',
'\\%' => '%',
'\\_' => '_',
// Internally, MariaDB escapes single quotes using the standard syntax
"''" => "'",
];
/**
* {@inheritdoc}
*/
protected function _getPortableTableColumnDefinition($tableColumn)
{
$tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
$dbType = strtolower($tableColumn['type']);
$dbType = strtok($dbType, '(), ');
assert(is_string($dbType));
$length = $tableColumn['length'] ?? strtok('(), ');
$fixed = null;
if (! isset($tableColumn['name'])) {
$tableColumn['name'] = '';
}
$scale = null;
$precision = null;
$type = $this->_platform->getDoctrineTypeMapping($dbType);
// In cases where not connected to a database DESCRIBE $table does not return 'Comment'
if (isset($tableColumn['comment'])) {
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
$tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
}
switch ($dbType) {
case 'char':
case 'binary':
$fixed = true;
break;
case 'float':
case 'double':
case 'real':
case 'numeric':
case 'decimal':
if (
preg_match(
'([A-Za-z]+\(([0-9]+),([0-9]+)\))',
$tableColumn['type'],
$match
) === 1
) {
$precision = $match[1];
$scale = $match[2];
$length = null;
}
break;
case 'tinytext':
$length = MySQLPlatform::LENGTH_LIMIT_TINYTEXT;
break;
case 'text':
$length = MySQLPlatform::LENGTH_LIMIT_TEXT;
break;
case 'mediumtext':
$length = MySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT;
break;
case 'tinyblob':
$length = MySQLPlatform::LENGTH_LIMIT_TINYBLOB;
break;
case 'blob':
$length = MySQLPlatform::LENGTH_LIMIT_BLOB;
break;
case 'mediumblob':
$length = MySQLPlatform::LENGTH_LIMIT_MEDIUMBLOB;
break;
case 'tinyint':
case 'smallint':
case 'mediumint':
case 'int':
case 'integer':
case 'bigint':
case 'year':
$length = null;
break;
}
if ($this->_platform instanceof MariaDb1027Platform) {
$columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']);
} else {
$columnDefault = $tableColumn['default'];
}
$options = [
'length' => $length !== null ? (int) $length : null,
'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false,
'fixed' => (bool) $fixed,
'default' => $columnDefault,
'notnull' => $tableColumn['null'] !== 'YES',
'scale' => null,
'precision' => null,
'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false,
'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
? $tableColumn['comment']
: null,
];
if ($scale !== null && $precision !== null) {
$options['scale'] = (int) $scale;
$options['precision'] = (int) $precision;
}
$column = new Column($tableColumn['field'], Type::getType($type), $options);
if (isset($tableColumn['characterset'])) {
$column->setPlatformOption('charset', $tableColumn['characterset']);
}
if (isset($tableColumn['collation'])) {
$column->setPlatformOption('collation', $tableColumn['collation']);
}
return $column;
}
private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string
{
if ($columnDefault === 'NULL' || $columnDefault === null) {
return null;
}
if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches) === 1) {
return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES);
}
switch ($columnDefault) {
case 'current_timestamp()':
return $platform->getCurrentTimestampSQL();
case 'curdate()':
return $platform->getCurrentDateSQL();
case 'curtime()':
return $platform->getCurrentTimeSQL();
}
return $columnDefault;
}
}

View File

@@ -29,13 +29,8 @@
namespace Espo\Core\Utils\Database;
use PDO;
use ReflectionClass;
use Espo\ORM\Entity;
use Espo\Core\{
Exceptions\Error,
Utils\Util,
Utils\Config,
};
@@ -44,6 +39,9 @@ use Doctrine\DBAL\{
Platforms\AbstractPlatform as DbalPlatform,
};
use PDO;
use ReflectionClass;
class Helper
{
private $config;
@@ -53,15 +51,15 @@ class Helper
private $pdoConnection;
protected $dbalDrivers = [
'mysqli' => '\\Doctrine\\DBAL\\Driver\\Mysqli\\Driver',
'pdo_mysql' => '\\Doctrine\\DBAL\\Driver\\PDO\\MySQL\\Driver',
'mysqli' => 'Doctrine\\DBAL\\Driver\\Mysqli\\Driver',
'pdo_mysql' => 'Espo\\Core\\Utils\\Database\\DBAL\\Driver\\PDO\\MySQL\\Driver',
];
protected $dbalPlatforms = [
'MariaDb1027Platform' => '\\Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MariaDb1027Platform',
'MySQL57Platform' => '\\Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MySQL57Platform',
'MySQL80Platform' => '\\Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MySQL80Platform',
'MySQLPlatform' => '\\Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MySQLPlatform',
'MariaDb1027Platform' => 'Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MariaDb1027Platform',
'MySQL57Platform' => 'Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MySQL57Platform',
'MySQL80Platform' => 'Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MySQL80Platform',
'MySQLPlatform' => 'Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MySQLPlatform',
];
public function __construct(Config $config = null)
@@ -106,6 +104,7 @@ class Helper
{
if (!isset($params)) {
$config = $this->getConfig();
if ($config) {
$params = $config->get('database');
}
@@ -116,6 +115,7 @@ class Helper
}
$driverName = isset($params['driver']) ? $params['driver'] : 'pdo_mysql';
unset($params['driver']);
if (!isset($this->dbalDrivers[$driverName])) {
@@ -131,23 +131,23 @@ class Helper
$driver = new $driverClass();
$version = $this->getFullDatabaseVersion();
$platform = $driver->createDatabasePlatformForVersion($version);
$params['platform'] = $this->createDbalPlatform($platform);
return new DbalConnection(
$params,
$driver
);
return new DbalConnection($params, $driver);
}
protected function createDbalPlatform(DbalPlatform $platform)
{
$reflect = new ReflectionClass($platform);
$platformClass = $reflect->getShortName();
if (isset($this->dbalPlatforms[$platformClass])) {
$class = $this->dbalPlatforms[$platformClass];
return new $class();
}
@@ -155,14 +155,16 @@ class Helper
}
/**
* Create PDO connection
* @param array $params
* @return \Pdo| \PDOException
* Create PDO connection.
*
* @param array $params
* @return PDO|\PDOException
*/
public function createPdoConnection(array $params = null)
{
if (!isset($params)) {
$config = $this->getConfig();
if ($config) {
$params = $config->get('database');
}
@@ -176,40 +178,45 @@ class Helper
$port = empty($params['port']) ? '' : ';port=' . $params['port'];
$dbname = empty($params['dbname']) ? '' : ';dbname=' . $params['dbname'];
$options = array();
$options = [];
if (isset($params['sslCA'])) {
$options[PDO::MYSQL_ATTR_SSL_CA] = $params['sslCA'];
}
if (isset($params['sslCert'])) {
$options[PDO::MYSQL_ATTR_SSL_CERT] = $params['sslCert'];
}
if (isset($params['sslKey'])) {
$options[PDO::MYSQL_ATTR_SSL_KEY] = $params['sslKey'];
}
if (isset($params['sslCAPath'])) {
$options[PDO::MYSQL_ATTR_SSL_CAPATH] = $params['sslCAPath'];
}
if (isset($params['sslCipher'])) {
$options[PDO::MYSQL_ATTR_SSL_CIPHER] = $params['sslCipher'];
}
$dsn = $platform . ':host='.$params['host'].$port.$dbname;
$dbh = new \PDO($dsn, $params['user'], $params['password'], $options);
$dbh = new PDO($dsn, $params['user'], $params['password'], $options);
return $dbh;
}
/**
* Get maximum index length. If $tableName is empty get a value for all database tables
*
* @param string|null $tableName
* Get maximum index length. If $tableName is empty get a value for all database tables.
*
* @param ?string $tableName
* @return int
*/
public function getMaxIndexLength($tableName = null, $default = 1000)
{
$tableEngine = $this->getTableEngine($tableName);
if (!$tableEngine) {
return $default;
}
@@ -224,17 +231,18 @@ class Helper
if (version_compare($version, '10.2.2') >= 0) {
return 3072; //InnoDB, MariaDB 10.2.2+
}
break;
case 'MySQL':
if (version_compare($version, '5.7.0') >= 0) {
return 3072; //InnoDB, MySQL 5.7+
}
break;
}
return 767; //InnoDB
break;
}
return 1000; //MyISAM
@@ -263,19 +271,22 @@ class Helper
protected function getFullDatabaseVersion()
{
$connection = $this->getPdoConnection();
if (!$connection) {
return null;
}
$sth = $connection->prepare("select version()");
$sth->execute();
return $sth->fetchColumn();
}
/**
* Get Database version
* @return string|null
* Get Database version.
*
* @return ?string
*/
public function getDatabaseVersion()
{
@@ -287,7 +298,7 @@ class Helper
}
/**
* Get table/database tables engine. If $tableName is empty get a value for all database tables
* Get table/database tables engine. If $tableName is empty get a value for all database tables.
*
* @param string|null $tableName
*
@@ -318,15 +329,16 @@ class Helper
}
/**
* Check if full text supports. If $tableName is empty get a value for all database tables
* Check if full text is supported. If $tableName is empty get a value for all database tables.
*
* @param string $tableName
* @param string $tableName
*
* @return boolean
*/
public function isSupportsFulltext($tableName = null, $default = false)
public function doesSupportFulltext($tableName = null, $default = false)
{
$tableEngine = $this->getTableEngine($tableName);
if (!$tableEngine) {
return $default;
}
@@ -340,33 +352,34 @@ class Helper
}
return false; //InnoDB
break;
}
return true; //MyISAM
}
public function isTableSupportsFulltext($tableName, $default = false)
public function doesTableSupportFulltext($tableName, $default = false)
{
return $this->isSupportsFulltext($tableName, $default);
return $this->doesSupportFulltext($tableName, $default);
}
public function getPdoDatabaseParam($name, \PDO $pdoConnection)
public function getPdoDatabaseParam($name, PDO $pdoConnection)
{
if (!method_exists($pdoConnection, 'prepare')) {
return null;
}
$sth = $pdoConnection->prepare("SHOW VARIABLES LIKE '" . $name . "'");
$sth->execute();
$res = $sth->fetch(\PDO::FETCH_NUM);
$res = $sth->fetch(PDO::FETCH_NUM);
$version = empty($res[1]) ? null : $res[1];
return $version;
}
public function getPdoDatabaseVersion(\PDO $pdoConnection)
public function getPdoDatabaseVersion(PDO $pdoConnection)
{
return $this->getPdoDatabaseParam('version', $pdoConnection);
}

View File

@@ -548,7 +548,7 @@ class Converter
protected function applyFullTextSearch(array &$ormMetadata, string $entityType)
{
if (!$this->getDatabaseHelper()->isTableSupportsFulltext(Util::toUnderScore($entityType))) return;
if (!$this->getDatabaseHelper()->doesTableSupportFulltext(Util::toUnderScore($entityType))) return;
if (!$this->getMetadata()->get(['entityDefs', $entityType, 'collection', 'fullTextSearch'])) return;
$fieldList = $this->getMetadata()->get(['entityDefs', $entityType, 'collection', 'textFilterFields'], ['name']);

View File

@@ -31,13 +31,13 @@ namespace Espo\Core\Utils\File;
use Espo\Core\{
Exceptions\Error,
Utils\Config,
Utils\Util,
Utils\Json,
};
use Exception;
use StdClass;
use Throwable;
class Manager
{
@@ -261,8 +261,8 @@ class Manager
$result = (file_put_contents($fullPath, $data, $flags) !== FALSE);
}
if ($result && function_exists('opcache_invalidate')) {
opcache_invalidate($fullPath);
if ($result) {
$this->opcacheInvalidate($fullPath);
}
return $result;
@@ -620,9 +620,7 @@ class Manager
$this->getPermissionUtils()->setDefaultPermissions($destFile);
if (function_exists('opcache_invalidate')) {
opcache_invalidate($destFile);
}
$this->opcacheInvalidate($destFile);
}
}
@@ -724,9 +722,7 @@ class Manager
}
if (file_exists($filePath) && is_file($filePath)) {
if (function_exists('opcache_invalidate')) {
opcache_invalidate($filePath, true);
}
$this->opcacheInvalidate($filePath, true);
$result &= unlink($filePath);
}
@@ -1112,4 +1108,16 @@ class Manager
return preg_replace('/^'. preg_quote($basePath, $dirSeparator) . '/', '', $path);
}
protected function opcacheInvalidate(string $filepath, bool $force = false)
{
if (!function_exists('opcache_invalidate')) {
return;
}
try {
opcache_invalidate($filepath, $force);
}
catch (Throwable $e) {}
}
}

View File

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

View File

@@ -37,6 +37,7 @@ use Espo\Core\EntryPoints\EntryPoint;
use Espo\Core\Di;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
class Attachment implements EntryPoint,
Di\EntityManagerAware,
@@ -52,7 +53,7 @@ class Attachment implements EntryPoint,
'image/webp',
];
public function run(Request $request)
public function run(Request $request, Response $response) : void
{
$id = $request->get('id');
@@ -88,7 +89,9 @@ class Attachment implements EntryPoint,
header('Pragma: public');
header('Content-Length: ' . filesize($fileName));
readfile($fileName);
exit;
}
}

View File

@@ -29,15 +29,13 @@
namespace Espo\EntryPoints;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
use Espo\Core\EntryPoints\NotStrictAuth;
use Espo\Core\Di;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
class Avatar extends Image implements Di\MetadataAware
{
@@ -73,7 +71,7 @@ class Avatar extends Image implements Di\MetadataAware
return $colorList[$index];
}
public function run(Request $request)
public function run(Request $request, Response $response) : void
{
$userId = $request->get('id');
$size = $request->get('size') ?? null;

View File

@@ -29,8 +29,6 @@
namespace Espo\EntryPoints;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
@@ -43,6 +41,7 @@ use Espo\Core\{
Utils\ClientManager,
ServiceFactory,
Api\Request,
Api\Response,
};
class ConfirmOptIn implements EntryPoint
@@ -58,19 +57,23 @@ class ConfirmOptIn implements EntryPoint
$this->serviceFactory = $serviceFactory;
}
public function run(Request $request)
public function run(Request $request, Response $response) : void
{
$id = $request->get('id');
if (!$id) throw new BadRequest();
if (!$id) {
throw new BadRequest();
}
$data = $this->serviceFactory->create('LeadCapture')->confirmOptIn($id);
if ($data->status === 'success') {
$action = 'optInConfirmationSuccess';
} else if ($data->status === 'expired') {
}
else if ($data->status === 'expired') {
$action = 'optInConfirmationExpired';
} else {
}
else {
throw new Error();
}

View File

@@ -40,10 +40,10 @@ use Espo\Core\EntryPoints\{
use Espo\Core\{
Acl,
ORM\EntityManager,
Api\Request,
Api\Response,
};
use Espo\Core\Api\Request;
class Download implements EntryPoint
{
protected $fileTypesToShowInline = [
@@ -66,11 +66,13 @@ class Download implements EntryPoint
$this->entityManager = $entityManager;
}
public function run(Request $request)
public function run(Request $request, Response $response) : void
{
$id = $request->get('id');
if (!$id) throw new BadRequest();
if (!$id) {
throw new BadRequest();
}
$attachment = $this->entityManager->getEntity('Attachment', $id);
@@ -82,34 +84,42 @@ class Download implements EntryPoint
throw new Forbidden();
}
$sourceId = $attachment->getSourceId();
if ($this->entityManager->getRepository('Attachment')->hasDownloadUrl($attachment)) {
$downloadUrl = $this->entityManager->getRepository('Attachment')->getDownloadUrl($attachment);
$downloadUrl = $this->entityManager
->getRepository('Attachment')
->getDownloadUrl($attachment);
header('Location: ' . $downloadUrl);
exit;
}
$fileName = $this->entityManager->getRepository('Attachment')->getFilePath($attachment);
$fileName = $this->entityManager
->getRepository('Attachment')
->getFilePath($attachment);
if (!file_exists($fileName)) {
throw new NotFound();
}
$outputFileName = $attachment->get('name');
$outputFileName = str_replace("\"", "\\\"", $outputFileName);
$type = $attachment->get('type');
$disposition = 'attachment';
if (in_array($type, $this->fileTypesToShowInline)) {
$disposition = 'inline';
}
header('Content-Description: File Transfer');
if ($type) {
header('Content-Type: ' . $type);
}
header("Content-Disposition: " . $disposition . ";filename=\"" . $outputFileName . "\"");
header('Expires: 0');
header('Cache-Control: must-revalidate');
@@ -117,6 +127,7 @@ class Download implements EntryPoint
header('Content-Length: ' . filesize($fileName));
readfile($fileName);
exit;
}
}

View File

@@ -38,6 +38,7 @@ use Espo\Core\Exceptions\Error;
use Espo\Core\EntryPoints\EntryPoint;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use Espo\Core\Di;
@@ -76,7 +77,7 @@ class Image implements EntryPoint,
protected $allowedFieldList = null;
public function run(Request $request)
public function run(Request $request, Response $response) : void
{
$id = $request->get('id');
$size = $request->get('size') ?? null;

View File

@@ -30,15 +30,15 @@
namespace Espo\EntryPoints;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
use Espo\Core\EntryPoints\{
NoAuth,
};
use Espo\Core\Api\Request;
use Espo\Core\{
Api\Request,
Api\Response,
};
use Espo\Core\Di;
@@ -51,7 +51,7 @@ class LogoImage extends Image implements Di\ConfigAware
protected $allowedFieldList = ['companyLogo'];
public function run(Request $request)
public function run(Request $request, Response $response) : void
{
$id = $request->get('id');
$size = $request->get('size') ?? null;

View File

@@ -40,6 +40,7 @@ use Espo\Core\{
ORM\EntityManager,
ServiceFactory,
Api\Request,
Api\Response,
};
class Pdf implements EntryPoint
@@ -53,7 +54,7 @@ class Pdf implements EntryPoint
$this->serviceFactory = $serviceFactory;
}
public function run(Request $request)
public function run(Request $request, Response $response) : void
{
$entityId = $request->get('entityId');
$entityType = $request->get('entityType');

View File

@@ -63,7 +63,7 @@ class Cleanup implements Job
protected $config;
protected $entityManager;
protected $metedata;
protected $metadata;
protected $fileManager;
protected $injectableFactory;
protected $selectManagerFactory;

View File

@@ -17,7 +17,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.phpppph
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
@@ -29,12 +29,8 @@
namespace Espo\Modules\Crm\EntryPoints;
use Espo\Core\Utils\Util;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
use Espo\Core\EntryPoints\{
EntryPoint,
@@ -42,6 +38,8 @@ use Espo\Core\EntryPoints\{
};
use Espo\Core\{
Api\Request,
Api\Response,
ORM\EntityManager,
ServiceFactory,
};
@@ -59,10 +57,13 @@ class CampaignTrackOpened implements EntryPoint
$this->serviceFactory = $serviceFactory;
}
public function run($request)
public function run(Request $request, Response $response) : void
{
$id = $request->get('id');
if (!$id) throw new BadRequest();
if (!$id) {
throw new BadRequest();
}
$queueItemId = $id;
@@ -83,28 +84,45 @@ class CampaignTrackOpened implements EntryPoint
}
$massEmailId = $queueItem->get('massEmailId');
if (!$massEmailId) return;
if (!$massEmailId) {
return;
}
$massEmail = $this->entityManager->getEntity('MassEmail', $massEmailId);
if (!$massEmail) return;
if (!$massEmail) {
return;
}
$campaignId = $massEmail->get('campaignId');
if (!$campaignId) return;
if (!$campaignId) {
return;
}
$campaign = $this->entityManager->getEntity('Campaign', $campaignId);
if (!$campaign) return;
if (!$campaign) {
return;
}
if (!$target) {
return;
}
$campaignService = $this->serviceFactory->create('Campaign');
$campaignService->logOpened($campaignId, $queueItemId, $target, null, $queueItem->get('isTest'));
header('Content-Type: image/png');
$img = imagecreatetruecolor(1, 1);
imagesavealpha($img, true);
$color = imagecolorallocatealpha($img, 127, 127, 127, 127);
imagefill($img, 0, 0, $color);
imagepng($img);

View File

@@ -17,7 +17,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.phpppph
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
@@ -29,10 +29,10 @@
namespace Espo\Modules\Crm\EntryPoints;
use Espo\Core\Exceptions;
use Espo\Modules\Crm\Entities\EmailQueueItem;
use Espo\Modules\Crm\Entities\CampaignTrackingUrl;
use Espo\{
Modules\Crm\Entities\EmailQueueItem,
Modules\Crm\Entities\CampaignTrackingUrl,
};
use Espo\Core\EntryPoints\{
EntryPoint,
@@ -40,6 +40,10 @@ use Espo\Core\EntryPoints\{
};
use Espo\Core\{
Exceptions\NotFoundSilent,
Exceptions\BadRequest,
Api\Request,
Api\Response,
ORM\EntityManager,
ServiceFactory,
Utils\Hasher,
@@ -75,40 +79,60 @@ class CampaignUrl implements EntryPoint
$this->metadata = $metadata;
}
public function run($request)
public function run(Request $request, Response $response) : void
{
$queueItemId = $request->get('queueItemId') ?? null;
$trackingUrlId = $request->get('id') ?? null;
$emailAddress = $request->get('emailAddress') ?? null;
$hash = $request->get('hash') ?? null;
$uid = $request->get('uid') ?? null;
if (!$trackingUrlId) {
throw new BadRequest();
}
if (!$trackingUrlId) throw new Exceptions\BadRequest();
$trackingUrl = $this->entityManager->getEntity('CampaignTrackingUrl', $trackingUrlId);
if (!$trackingUrl) throw new Exceptions\NotFound();
if (!$trackingUrl) {
throw new NotFoundSilent("Tracking URL '{$trackingUrlId}' not found.");
}
if ($emailAddress && $hash) {
$this->processWithHash($trackingUrl, $emailAddress, $hash);
} else {
if (!$queueItemId) throw new Exceptions\BadRequest();
}
else if ($uid && $hash) {
$this->processWithUniqueId($trackingUrl, $uid, $hash);
}
else {
if (!$queueItemId) {
throw new BadRequest();
}
$queueItem = $this->entityManager->getEntity('EmailQueueItem', $queueItemId);
if (!$queueItem) throw new Exceptions\NotFound();
if (!$queueItem) {
throw new NotFoundSilent();
}
$this->processWithQueueItem($trackingUrl, $queueItem);
}
if ($trackingUrl->get('action') === 'Show Message') {
$this->displayMessage($trackingUrl->get('message'));
return;
}
if ($trackingUrl->get('url')) {
ob_clean();
header('Location: ' . $trackingUrl->get('url') . '');
die;
}
}
protected function processWithQueueItem(CampaignTrackingUrl $trackingUrl, EmailQueueItem $queueItem)
protected function processWithQueueItem(CampaignTrackingUrl $trackingUrl, EmailQueueItem $queueItem) : void
{
$target = null;
$campaign = null;
@@ -121,6 +145,7 @@ class CampaignUrl implements EntryPoint
}
$campaignId = $trackingUrl->get('campaignId');
if ($campaignId) {
$campaign = $this->entityManager->getEntity('Campaign', $campaignId);
}
@@ -134,23 +159,27 @@ class CampaignUrl implements EntryPoint
if ($campaign && $target) {
$campaignService = $this->serviceFactory->create('Campaign');
$campaignService->logClicked($campaignId, $queueItem->id, $target, $trackingUrl, null, $queueItem->get('isTest'));
$campaignService->logClicked(
$campaignId, $queueItem->id, $target, $trackingUrl, null, $queueItem->get('isTest')
);
}
}
protected function processWithHash(CampaignTrackingUrl $trackingUrl, string $emailAddress, string $hash)
protected function processWithHash(CampaignTrackingUrl $trackingUrl, string $emailAddress, string $hash) : void
{
$hash2 = $this->hasher->hash($emailAddress);
$hashActual = $this->hasher->hash($emailAddress);
if ($hash2 !== $hash) {
throw new Exceptions\NotFound();
if ($hashActual !== $hash) {
throw new NotFoundSilent();
}
$eaRepository = $this->entityManager->getRepository('EmailAddress');
$ea = $eaRepository->getByAddress($emailAddress);
if (!$ea) {
throw new Exceptions\NotFound();
throw new NotFoundSilent();
}
$entityList = $eaRepository->getEntityListByAddressId($ea->id);
@@ -163,12 +192,23 @@ class CampaignUrl implements EntryPoint
}
}
protected function displayMessage(?string $message)
protected function processWithUniqueId(CampaignTrackingUrl $trackingUrl, string $uid, string $hash) : void
{
$message = $message ?? '';
$hashActual = $this->hasher->hash($uid);
if ($hashActual !== $hash) {
throw new NotFoundSilent();
}
$this->hookManager->process('CampaignTrackingUrl', 'afterClick', $trackingUrl, [], [
'uid' => $uid,
]);
}
protected function displayMessage(?string $message) : void
{
$data = [
'message' => $message,
'message' => $message ?? '',
'view' => $this->metadata->get(['clientDefs', 'Campaign', 'trackinkUrlMessageView']),
'template' => $this->metadata->get(['clientDefs', 'Campaign', 'trackinkUrlMessageTemplate']),
];
@@ -177,9 +217,10 @@ class CampaignUrl implements EntryPoint
Espo.require('crm:controllers/tracking-url', function (Controller) {
var controller = new Controller(app.baseController.params, app.getControllerInjection());
controller.masterView = app.masterView;
controller.doAction('displayMessage', ".json_encode($data).");
controller.doAction('displayMessage', " . json_encode($data) . ");
});
";
$this->clientManager->display($runScript);
}
}

View File

@@ -29,10 +29,7 @@
namespace Espo\Modules\Crm\EntryPoints;
use Espo\Core\Utils\Util;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
@@ -42,6 +39,8 @@ use Espo\Core\EntryPoints\{
};
use Espo\Core\{
Api\Request,
Api\Response,
ORM\EntityManager,
Utils\ClientManager,
HookManager,
@@ -61,7 +60,7 @@ class EventConfirmation implements EntryPoint
$this->hookManager = $hookManager;
}
public function run($request)
public function run(Request $request, Response $response) : void
{
$uid = $request->get('uid') ?? null;
$action = $request->get('action') ?? null;
@@ -102,6 +101,7 @@ class EventConfirmation implements EntryPoint
$status = 'None';
$hookMethodName = 'afterConfirmation';
if ($action == 'accept') {
$status = 'Accepted';
} else if ($action == 'decline') {

View File

@@ -17,7 +17,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.phpppph
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
@@ -29,12 +29,8 @@
namespace Espo\Modules\Crm\EntryPoints;
use Espo\Core\Utils\Util;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
use Espo\Core\EntryPoints\{
EntryPoint,
@@ -42,6 +38,8 @@ use Espo\Core\EntryPoints\{
};
use Espo\Core\{
Api\Request,
Api\Response,
ORM\EntityManager,
Utils\ClientManager,
HookManager,
@@ -77,7 +75,7 @@ class SubscribeAgain implements EntryPoint
$this->hasher = $hasher;
}
public function run($request)
public function run(Request $request, Response $response) : void
{
$id = $request->get('id') ?? null;
$emailAddress = $request->get('emailAddress') ?? null;
@@ -92,6 +90,7 @@ class SubscribeAgain implements EntryPoint
if (!$id) {
throw new BadRequest();
}
$queueItemId = $id;
$queueItem = $this->entityManager->getEntity('EmailQueueItem', $queueItemId);
@@ -104,10 +103,13 @@ class SubscribeAgain implements EntryPoint
$target = null;
$massEmailId = $queueItem->get('massEmailId');
if ($massEmailId) {
$massEmail = $this->entityManager->getEntity('MassEmail', $massEmailId);
if ($massEmail) {
$campaignId = $massEmail->get('campaignId');
if ($campaignId) {
$campaign = $this->entityManager->getEntity('Campaign', $campaignId);
}
@@ -124,6 +126,7 @@ class SubscribeAgain implements EntryPoint
if ($massEmail->get('optOutEntirely')) {
$emailAddress = $target->get('emailAddress');
if ($emailAddress) {
$ea = $this->entityManager->getRepository('EmailAddress')->getByAddress($emailAddress);
@@ -190,7 +193,6 @@ class SubscribeAgain implements EntryPoint
$this->entityManager->removeEntity($logRecord);
}
}
}
protected function display(array $actionData)
@@ -214,8 +216,6 @@ class SubscribeAgain implements EntryPoint
protected function processWithHash(string $emailAddress, string $hash)
{
$secretKey = $this->config->get('hashSecretKey');
$hash2 = $this->hasher->hash($emailAddress);
if ($hash2 !== $hash) {
@@ -242,7 +242,8 @@ class SubscribeAgain implements EntryPoint
'emailAddress' => $emailAddress,
'hash' => $hash,
]);
} else {
}
else {
throw new NotFound();
}
}

View File

@@ -29,12 +29,8 @@
namespace Espo\Modules\Crm\EntryPoints;
use Espo\Core\Utils\Util;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
use Espo\Core\EntryPoints\{
EntryPoint,
@@ -42,6 +38,8 @@ use Espo\Core\EntryPoints\{
};
use Espo\Core\{
Api\Request,
Api\Response,
ORM\EntityManager,
Utils\ClientManager,
HookManager,
@@ -81,7 +79,7 @@ class Unsubscribe implements EntryPoint
$this->serviceFactory = $serviceFactory;
}
public function run($request)
public function run(Request $request, Response $response) : void
{
$id = $request->get('id') ?? null;
$emailAddress = $request->get('emailAddress') ?? null;
@@ -89,12 +87,14 @@ class Unsubscribe implements EntryPoint
if ($emailAddress && $hash) {
$this->processWithHash($emailAddress, $hash);
return;
}
if (!$id) {
throw new BadRequest();
}
$queueItemId = $id;
$queueItem = $this->entityManager->getEntity('EmailQueueItem', $queueItemId);
@@ -107,10 +107,13 @@ class Unsubscribe implements EntryPoint
$target = null;
$massEmailId = $queueItem->get('massEmailId');
if ($massEmailId) {
$massEmail = $this->entityManager->getEntity('MassEmail', $massEmailId);
if ($massEmail) {
$campaignId = $massEmail->get('campaignId');
if ($campaignId) {
$campaign = $this->entityManager->getEntity('Campaign', $campaignId);
}
@@ -127,8 +130,10 @@ class Unsubscribe implements EntryPoint
if ($massEmail->get('optOutEntirely')) {
$emailAddress = $target->get('emailAddress');
if ($emailAddress) {
$ea = $this->entityManager->getRepository('EmailAddress')->getByAddress($emailAddress);
if ($ea) {
$ea->set('optOut', true);
$this->entityManager->saveEntity($ea);
@@ -181,6 +186,7 @@ class Unsubscribe implements EntryPoint
if ($campaign && $target) {
$campaignService = $this->serviceFactory->create('Campaign');
$campaignService->logOptedOut(
$campaignId, $queueItemId, $target, $queueItem->get('emailAddress'), null, $queueItem->get('isTest')
);
@@ -208,8 +214,6 @@ class Unsubscribe implements EntryPoint
protected function processWithHash(string $emailAddress, string $hash)
{
$secretKey = $this->config->get('hashSecretKey');
$hash2 = $this->hasher->hash($emailAddress);
if ($hash2 !== $hash) {
@@ -236,8 +240,8 @@ class Unsubscribe implements EntryPoint
'emailAddress' => $emailAddress,
'hash' => $hash,
]);
} else {
}
else {
throw new NotFound();
}
}

View File

@@ -7,6 +7,7 @@
"editQuick":"crm:views/knowledge-base-article/record/edit-quick",
"detailQuick":"crm:views/knowledge-base-article/record/detail-quick",
"detail":"crm:views/knowledge-base-article/record/detail",
"edit":"crm:views/knowledge-base-article/record/edit",
"list":"crm:views/knowledge-base-article/record/list"
},
"modalViews": {

View File

@@ -645,8 +645,7 @@
"campaign": {
"type": "belongsTo",
"entity": "Campaign",
"foreign": "contacts",
"noJoin": true
"foreign": "contacts"
},
"campaignLogRecords": {
"type": "hasChildren",

View File

@@ -84,7 +84,7 @@ class Lead extends PersonService implements
}
}
public function getConvertAttributes(string $id)
public function getConvertAttributes(string $id) : array
{
$lead = $this->getEntity($id);
@@ -107,8 +107,6 @@ class Lead extends PersonService implements
$attributes = [];
$target = $this->getEntityManager()->getEntity($entityType);
$fieldMap = [];
$fieldList = array_keys($this->getMetadata()->get('entityDefs.Lead.fields', []));
@@ -165,7 +163,9 @@ class Lead extends PersonService implements
$typeHash = (object) [];
foreach ($attachmentList as $attachment) {
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
$attachment = $this->getEntityManager()
->getRepository('Attachment')
->getCopiedAttachment($attachment);
if ($attachment) {
$idList[] = $attachment->id;
@@ -182,11 +182,22 @@ class Lead extends PersonService implements
continue;
}
else if ($type === 'linkMultiple') {
$attributes[$field . 'Ids'] = $lead->get($leadField . 'Ids');
$attributes[$field . 'Names'] = $lead->get($leadField . 'Names');
$attributes[$field . 'Columns'] = $lead->get($leadField . 'Columns');
continue;
}
$leadAttributeList = $this->fieldUtil->getAttributeList('Lead', $leadField);
$attributeList = $this->fieldUtil->getAttributeList($entityType, $field);
if (count($attributeList) !== count($leadAttributeList)) {
continue;
}
foreach ($attributeList as $i => $attribute) {
if (in_array($attribute, $ignoreAttributeList)) {
continue;
@@ -194,10 +205,6 @@ class Lead extends PersonService implements
$leadAttribute = $leadAttributeList[$i] ?? null;
if (!$leadAttribute) {
throw new Error("Not compatible fields in 'convertFields' map.");
}
if (!$lead->has($leadAttribute)) {
continue;
}

View File

@@ -641,7 +641,7 @@ class BaseMapper implements Mapper
foreach ($conditions as $left => $value) {
$columns[] = $left;
$valueList[] = $v;
$valueList[] = $value;
}
$columns[] = $distantKey;

View File

@@ -1085,7 +1085,7 @@ abstract class BaseQueryComposer implements QueryComposer
}
protected function convertComplexExpression(
?Entity $entity = null, string $attribute, bool $distinct, array &$params
?Entity $entity, string $attribute, bool $distinct, array &$params
) : string {
$function = null;
@@ -1501,7 +1501,7 @@ abstract class BaseQueryComposer implements QueryComposer
return $list;
}
protected function getSelectPart(?Entity $entity = null, array &$params) : string
protected function getSelectPart(?Entity $entity, array &$params) : string
{
$itemList = $params['select'] ?? [];
@@ -1579,7 +1579,7 @@ abstract class BaseQueryComposer implements QueryComposer
return $selectPart;
}
protected function getSelectPartItemPair(?Entity $entity = null, array &$params, $attribute) : ?array
protected function getSelectPartItemPair(?Entity $entity, array &$params, $attribute) : ?array
{
$maxTextColumnsLength = $params['maxTextColumnsLength'] ?? null;
$skipTextColumns = $params['skipTextColumns'] ?? false;
@@ -2842,7 +2842,7 @@ abstract class BaseQueryComposer implements QueryComposer
}
protected function composeSelectQuery(
?string $from = null,
?string $from,
string $select,
?string $alias = null,
?string $joins = null,

View File

@@ -292,7 +292,7 @@ class RDBRelation
protected function processCheckForeignEntity(Entity $entity)
{
if ($this->foreignEntityType && $this->foreignEntityType !== $entity->getEntityType()) {
throw new InvalidArgumentException("Entity type doesn't match an entity type of the relation.");
throw new RuntimeException("Entity type doesn't match an entity type of the relation.");
}
if (!$entity->id) {
@@ -370,7 +370,7 @@ class RDBRelation
}
if ($id === '') {
throw new InvalidArgumentException();
throw new RuntimeException();
}
$seed = $this->entityManager->getEntityFactory()->create($this->foreignEntityType);
@@ -389,7 +389,7 @@ class RDBRelation
}
if ($id === '') {
throw new InvalidArgumentException();
throw new RuntimeException();
}
$seed = $this->entityManager->getEntityFactory()->create($this->foreignEntityType);
@@ -408,7 +408,7 @@ class RDBRelation
}
if ($id === '') {
throw new InvalidArgumentException();
throw new RuntimeException();
}
$seed = $this->entityManager->getEntityFactory()->create($this->foreignEntityType);

View File

@@ -40,7 +40,6 @@ use Espo\ORM\{
};
use RuntimeException;
use BadMethodCallException;
/**
* Builds select parameters for related records for RDB repository.

View File

@@ -41,7 +41,6 @@ use Espo\ORM\{
use StdClass;
use RuntimeException;
use InvalidArgumentException;
class RDBRepository extends Repository
{
@@ -232,7 +231,7 @@ class RDBRepository extends Repository
$params = $params ?? [];
if ($entity->getEntityType() !== $this->entityType) {
throw new InvalidArgumentException("Not supported entity type.");
throw new RuntimeException("Not supported entity type.");
}
if (!$entity->id) {
@@ -283,7 +282,7 @@ class RDBRepository extends Repository
$params = $params ?? [];
if ($entity->getEntityType() !== $this->entityType) {
throw new InvalidArgumentException("Not supported entity type.");
throw new RuntimeException("Not supported entity type.");
}
if (!$entity->id) {
@@ -373,7 +372,7 @@ class RDBRepository extends Repository
}
if ($entity->getEntityType() !== $this->entityType) {
throw new InvalidArgumentException("Not supported entity type.");
throw new RuntimeException("Not supported entity type.");
}
if ($foreign instanceof Entity) {
@@ -421,7 +420,7 @@ class RDBRepository extends Repository
}
if ($entity->getEntityType() !== $this->entityType) {
throw new InvalidArgumentException("Not supported entity type.");
throw new RuntimeException("Not supported entity type.");
}
$this->beforeRelate($entity, $relationName, $foreign, $columnData, $options);
@@ -478,7 +477,7 @@ class RDBRepository extends Repository
}
if ($entity->getEntityType() !== $this->entityType) {
throw new InvalidArgumentException("Not supported entity type.");
throw new RuntimeException("Not supported entity type.");
}
$this->beforeUnrelate($entity, $relationName, $foreign, $options);
@@ -628,7 +627,7 @@ class RDBRepository extends Repository
*
* @return int|float
*/
public function sum(string $attributel)
public function sum(string $attribute)
{
return $this->createSelectBuilder()->sum($attribute);
}

View File

@@ -69,13 +69,19 @@ class Attachment extends \Espo\Core\Repositories\Database implements
parent::beforeSave($entity, $options);
$storage = $entity->get('storage');
if (!$storage) {
$entity->set('storage', $this->config->get('defaultFileStorage', null));
}
if ($entity->isNew()) {
if (!$entity->has('size') && $entity->has('contents')) {
$entity->set('size', mb_strlen($entity->get('contents')));
$contents = $entity->get('contents');
$entity->set(
'size',
mb_strlen($contents, '8bit')
);
}
}
}

View File

@@ -44,7 +44,7 @@ return [
'version' => '@@version',
'timeZone' => 'UTC',
'dateFormat' => 'DD.MM.YYYY',
'timeFormat' => 'hh:mm',
'timeFormat' => 'HH:mm',
'weekStart' => 0,
'thousandSeparator' => ',',
'decimalMark' => '.',
@@ -134,6 +134,7 @@ return [
'activitiesEntityList' => ['Meeting', 'Call'],
'historyEntityList' => ['Meeting', 'Call', 'Email'],
'busyRangesEntityList' => ['Meeting', 'Call'],
'emailAutoReplySuppressPeriod' => '3 hours',
'cleanupJobPeriod' => '1 month',
'cleanupActionHistoryPeriod' => '15 days',
'cleanupAuthTokenPeriod' => '1 month',

View File

@@ -170,6 +170,7 @@ return [
'ldapPortalUserRolesIds',
'ldapPortalUserRolesNames',
'cleanupJobPeriod',
'emailAutoReplySuppressPeriod',
'cleanupActionHistoryPeriod',
'adminNotifications',
'adminNotificationsNewVersion',

View File

@@ -179,6 +179,10 @@
{
"name": "env\\userAttribute",
"insertText": "env\\userAttribute(ATTRIBUTE)"
},
{
"name": "util\\generateId",
"insertText": "util\\generateId()"
}
]
}

View File

@@ -293,8 +293,6 @@ class EmailAccount extends Record implements
$importer = new Importer($this->getEntityManager(), $this->getConfig(), $notificator, $this->parserClassName);
$maxSize = $this->getConfig()->get('emailMessageMaxSize');
$user = $this->getEntityManager()->getEntity('User', $emailAccount->get('assignedUserId'));
if (!$user) {
throw new Error();
@@ -419,12 +417,7 @@ class EmailAccount extends Record implements
}
}
$fetchOnlyHeader = false;
if ($maxSize) {
if ($storage->getSize($id) > $maxSize * 1024 * 1024) {
$fetchOnlyHeader = true;
}
}
$fetchOnlyHeader = $this->checkFetchOnlyHeader($storage, $id);
$folderData = null;
if ($emailAccount->get('emailFolderId')) {
@@ -627,4 +620,26 @@ class EmailAccount extends Record implements
$handler->applyParams($emailAccount->id, $params);
}
}
protected function checkFetchOnlyHeader(Imap $storage, string $id): bool
{
$maxSize = $this->getConfig()->get('emailMessageMaxSize');
if (!$maxSize) {
return false;
}
try {
$size = $storage->getSize($id);
}
catch (Throwable $e) {
return false;
}
if ($size > $maxSize * 1024 * 1024) {
return true;
}
return false;
}
}

View File

@@ -79,6 +79,8 @@ class InboundEmail extends RecordService implements
protected $parserClassName = MailMimeParser::class;
protected $emailAutoReplySuppressPeriod = '3 hours';
const PORTION_LIMIT = 20;
protected function getCrypt()
@@ -163,8 +165,6 @@ class InboundEmail extends RecordService implements
$importer = new Importer($this->getEntityManager(), $this->getConfig(), $notificator, $this->parserClassName);
$maxSize = $this->getConfig()->get('emailMessageMaxSize');
$teamId = $emailAccount->get('teamId');
$userId = null;
@@ -327,13 +327,7 @@ class InboundEmail extends RecordService implements
}
}
$fetchOnlyHeader = false;
if ($maxSize) {
if ($storage->getSize($id) > $maxSize * 1024 * 1024) {
$fetchOnlyHeader = true;
}
}
$fetchOnlyHeader = $this->checkFetchOnlyHeader($storage, $id);
$message = null;
$email = null;
@@ -813,7 +807,7 @@ class InboundEmail extends RecordService implements
$d = new DateTime();
$d->modify('-3 hours');
$d->modify('-' . $this->config->get('emailAutoReplySuppressPeriod', $this->emailAutoReplySuppressPeriod));
$threshold = $d->format('Y-m-d H:i:s');
@@ -1222,4 +1216,26 @@ class InboundEmail extends RecordService implements
$handler->applyParams($emailAccount->id, $params);
}
}
protected function checkFetchOnlyHeader(Imap $storage, string $id): bool
{
$maxSize = $this->getConfig()->get('emailMessageMaxSize');
if (!$maxSize) {
return false;
}
try {
$size = $storage->getSize($id);
}
catch (Throwable $e) {
return false;
}
if ($size > $maxSize * 1024 * 1024) {
return true;
}
return false;
}
}

View File

@@ -42,7 +42,7 @@ use Espo\Core\Exceptions\{
use Espo\ORM\{
Entity,
EntityCollection,
Collection,
EntityManager,
};
@@ -2128,7 +2128,7 @@ class Record implements Crud,
return false;
}
public function findDuplicates(Entity $entity, $data = null) : ?EntityCollection
public function findDuplicates(Entity $entity, $data = null) : ?Collection
{
if (!$data) {
$data = (object) [];

View File

@@ -1626,6 +1626,12 @@ class Stream
$collection = $this->entityManager->getRepository('User')->find($selectParams);
$total = $this->entityManager->getRepository('User')->count($selectParams);
$userService = $this->serviceFactory->create('User');
foreach ($collection as $e) {
$userService->prepareEntityForOutput($e);
}
return new RecordCollection($collection, $total);
}

View File

@@ -570,7 +570,7 @@ class User extends Record implements
$sender = $this->emailSender->create();
if (!$this->emailSender->hasSystemSmtp()) {
$sender->withStmpParams([
$sender->withSmtpParams([
'server' => $this->getConfig()->get('internalSmtpServer'),
'port' => $this->getConfig()->get('internalSmtpPort'),
'auth' => $this->getConfig()->get('internalSmtpAuth'),

View File

@@ -65,9 +65,9 @@ class Webhook extends Record implements
}
}
protected function filtetInput($data)
protected function filterInput($data)
{
parent::filtetInput($data);
parent::filterInput($data);
unset($data->entityType);
unset($data->field);

View File

@@ -32,10 +32,10 @@ namespace Espo\Tools\LeadCapture;
use Espo\Core\{
Exceptions\Error,
Exceptions\NotFound,
Exceptions\BadRequest,
ORM\EntityManager,
Utils\FieldUtil,
Utils\Language,
ServicaFactory,
HookManager,
Mail\EmailSender,
Utils\Config,
@@ -729,7 +729,7 @@ class LeadCapture
]);
if (!empty($data->description)) {
$logRecord->set('description', $description);
$logRecord->set('description', $data->description);
}
$this->entityManager->saveEntity($logRecord);

File diff suppressed because one or more lines are too long

View File

@@ -26,10 +26,12 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('crm:views/knowledge-base-article/record/detail', 'views/record/detail', function (Dep) {
define('crm:views/knowledge-base-article/record/detail', 'views/record/detail', function (Dep) {
return Dep.extend({
saveAndContinueEditingAction: true,
setup: function () {
Dep.prototype.setup.call(this);

View File

@@ -0,0 +1,36 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('crm:views/knowledge-base-article/record/edit', 'views/record/edit', function (Dep) {
return Dep.extend({
saveAndContinueEditingAction: true,
});
});

View File

@@ -57,7 +57,7 @@ define('crm:views/record/panels/activities', ['views/record/panels/relationship'
rows: [
[
{name: 'ico', view: 'crm:views/fields/ico'},
{name: 'name', link: true}
{name: 'name', link: true, view: 'views/event/fields/name-for-history'}
],
[
{name: 'assignedUser'},

View File

@@ -49,10 +49,14 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
this.assignedUserField = this.options.assignedUserField || 'assignedUser';
var usersFieldDefault = 'users';
if (!this.model.hasLink('users') && this.model.hasLink('assignedUsers')) {
usersFieldDefault = 'assignedUsers';
}
this.eventAssignedUserIsAttendeeDisabled =
this.getConfig().get('eventAssignedUserIsAttendeeDisabled') || false;
this.usersField = this.options.usersField || usersFieldDefault;
this.userIdList = [];
@@ -65,16 +69,24 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
m.hasChanged(this.startField) ||
m.hasChanged(this.endField) ||
m.hasChanged(this.usersField + 'Ids') ||
m.hasChanged(this.assignedUserField + 'Id');
if (!isChanged) return;
!this.eventAssignedUserIsAttendeeDisabled && m.hasChanged(this.assignedUserField + 'Id');
if (!isChanged) {
return;
}
if (!m.hasChanged(this.assignedUserField + 'Id') && !m.hasChanged(this.usersField + 'Ids')) {
this.initDates(true);
if (!this.start || !this.end || !this.userIdList.length) {
if (!this.timeline) return;
if (!this.timeline) {
return;
}
this.showNoData();
this.trigger('no-data');
return;
}
@@ -93,8 +105,12 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
this.reRender();
}
} else {
if (this.isRemoved()) return;
if (this.isRemoved()) {
return;
}
this.trigger('has-data');
this.reRender();
}
}, this);
@@ -132,13 +148,17 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
this.initGroupsDataSet();
this.initDates();
if (!$timeline.get(0)) return;
if (!$timeline.get(0)) {
return;
}
$timeline.get(0).innerHTML = '';
if (!this.start || !this.end || !this.userIdList.length) {
this.showNoData();
this.trigger('no-data');
return;
}
@@ -151,15 +171,17 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
this.fetch(this.start, this.end, function (eventList) {
var itemsDataSet = new Vis.DataSet(eventList);
var timeline = this.timeline = new Vis.Timeline($timeline.get(0), itemsDataSet, this.groupsDataSet, {
var timeline = this.timeline =new Vis.Timeline($timeline.get(0), itemsDataSet, this.groupsDataSet, {
dataAttributes: 'all',
start: this.start.toDate(),
end: this.end.toDate(),
moment: function (date) {
var m = moment(date);
if (date && date.noTimeZone) {
return m;
}
return m.tz(this.getDateTime().getTimeZone());
}.bind(this),
format: this.getFormatObject(),
@@ -195,6 +217,7 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
e.skipClick = true;
this.blockClick = true;
setTimeout(function () {this.blockClick = false}.bind(this), 100);
this.start = moment(e.start);
@@ -217,6 +240,7 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
this.addEvent(convertedEventList);
var itemsDataSet = new this.Vis.DataSet(convertedEventList);
this.timeline.setItems(itemsDataSet);
},
@@ -242,13 +266,15 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
endS = this.model.get(this.endField + 'Date');
}
if (!startS || !endS) return;
if (!startS || !endS) {
return;
}
if (this.model.get('isAllDay')) {
this.eventStart = moment.tz(startS, this.getDateTime().getTimeZone());
this.eventEnd = moment.tz(endS, this.getDateTime().getTimeZone());
this.eventEnd.add(1, 'day');
} else {
}else {
this.eventStart = moment.utc(startS).tz(this.getDateTime().getTimeZone());
this.eventEnd = moment.utc(endS).tz(this.getDateTime().getTimeZone());
}
@@ -281,6 +307,7 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
runFetch: function () {
this.fetch(this.start, this.end, function (eventList) {
var itemsDataSet = new this.Vis.DataSet(eventList);
this.timeline.setItems(itemsDataSet);
}.bind(this));
},
@@ -304,6 +331,7 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
this.ajaxGetRequest(url).then(function (data) {
this.fetchedStart = from.clone();
this.fetchedEnd = to.clone();
var eventList = [];
for (var userId in data) {
@@ -344,6 +372,7 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
};
var color = this.getColorFromScopeName(this.model.entityType);
if (color) {
o.style += '; border-color: ' + color;
var rgb = this.hexToRgb(color);
@@ -362,11 +391,17 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
convertEventList: function (list) {
var resultList = [];
list.forEach(function (iten) {
var event = this.convertEvent(iten);
if (!event) return;
if (!event) {
return;
}
resultList.push(event);
}, this);
return resultList;
},
@@ -410,8 +445,12 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
var assignedUserId = this.model.get(this.assignedUserField + 'Id');
var names = this.model.get(this.usersField + 'Names') || {};
if (assignedUserId) {
if (!~userIdList.indexOf(assignedUserId)) userIdList.unshift(assignedUserId);
if (!this.eventAssignedUserIsAttendeeDisabled && assignedUserId) {
if (!~userIdList.indexOf(assignedUserId)) {
userIdList.unshift(assignedUserId);
}
names[assignedUserId] = this.model.get(this.assignedUserField + 'Name');
}
@@ -435,8 +474,13 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
if (this.calendarType === 'single') {
return name;
}
var avatarHtml = this.getAvatarHtml(id);
if (avatarHtml) avatarHtml += ' ';
if (avatarHtml) {
avatarHtml += ' ';
}
var html = avatarHtml + '<span data-id="'+id+'" class="group-title">' + name + '</span>';
return html;
@@ -446,8 +490,11 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
if (this.getConfig().get('avatarsDisabled')) {
return '';
}
var t;
var cache = this.getCache();
if (cache) {
t = cache.get('app', 'timestamp');
} else {
@@ -481,6 +528,7 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
year: ''
}
};
return format;
},
@@ -493,10 +541,11 @@ define('crm:views/scheduler/scheduler', ['view', 'lib!vis'], function (Dep, Vis)
hexToRgb: function (hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
} : null;
},

View File

@@ -2,7 +2,12 @@
<div class="btn-group">
<button class="btn btn-primary" data-action="save">{{translate 'Save'}}</button>
<button class="btn btn-default" data-action="close">{{translate 'Close'}}</button>
{{#unless isCustom}}{{#unless isNew}}<button class="btn btn-default" data-action="resetToDefault">{{translate 'Reset to Default' scope='Admin'}}</button>{{/unless}}{{/unless}}
{{#if hasResetToDefault}}
<button
class="btn btn-default"
data-action="resetToDefault"
>{{translate 'Reset to Default' scope='Admin'}}</button>
{{/if}}
</div>
</div>

View File

@@ -0,0 +1,7 @@
<a
href="#{{model.name}}/view/{{model.id}}"
class="link"
data-id="{{model.id}}"
title="{{value}}"
{{#if strikethrough}}style="text-decoration: line-through;"{{/if}}
>{{#if value}}{{value}}{{else}}{{translate 'None'}}{{/if}}</a>

View File

@@ -501,6 +501,19 @@ define(
this.trigger('auth');
}.bind(this));
var userId = data.user.id;
var lastUserId = this.storage.get('user', 'lastUserId');
if (lastUserId !== userId) {
if (this.cache) {
this.cache.clear('app', 'metadata');
}
this.language.clearCache();
}
this.storage.set('user', 'lastUserId', userId);
}.bind(this));
this.baseController.on('logout', function () {

View File

@@ -30,9 +30,11 @@ define('cache', [], function () {
var Cache = function (cacheTimestamp) {
this.basePrefix = this.prefix;
if (cacheTimestamp) {
this.prefix = this.basePrefix + '-' + cacheTimestamp;
}
if (!this.get('app', 'timestamp')) {
this.storeTimestamp();
}
@@ -44,15 +46,20 @@ define('cache', [], function () {
handleActuality: function (cacheTimestamp) {
var stored = parseInt(this.get('app', 'cacheTimestamp'));
if (stored) {
if (stored !== cacheTimestamp) {
this.clear();
this.set('app', 'cacheTimestamp', cacheTimestamp);
this.storeTimestamp();
}
} else {
this.clear();
this.set('app', 'cacheTimestamp', cacheTimestamp);
this.storeTimestamp();
}
},
@@ -83,8 +90,10 @@ define('cache', [], function () {
try {
var stored = localStorage.getItem(key);
} catch (error) {
}
catch (error) {
console.error(error);
return null;
}
@@ -93,42 +102,55 @@ define('cache', [], function () {
if (stored.length > 9 && stored.substr(0, 9) === '__JSON__:') {
var jsonString = stored.substr(9);
try {
result = JSON.parse(jsonString);
} catch (error) {
}
catch (error) {
result = stored;
}
}
return result;
}
return null;
},
set: function (type, name, value) {
this.checkType(type);
var key = this.composeKey(type, name);
if (value instanceof Object || Array.isArray(value)) {
value = '__JSON__:' + JSON.stringify(value);
}
try {
localStorage.setItem(key, value);
} catch (error) {
console.error(error);
}
catch (error) {
console.log('Local storage limit exceeded.');
}
},
clear: function (type, name) {
var reText;
if (typeof type !== 'undefined') {
if (typeof name === 'undefined') {
reText = '^' + this.composeFullPrefix(type);
} else {
}
else {
reText = '^' + this.composeKey(type, name);
}
} else {
}
else {
reText = '^' + this.basePrefix + '-';
}
var re = new RegExp(reText);
for (var i in localStorage) {
if (re.test(i)) {
delete localStorage[i];

View File

@@ -243,12 +243,15 @@ var Espo = Espo || {classMap:{}};
if (realName in this.libsConfig) {
var libData = this.libsConfig[realName] || {};
path = libData.path || path;
exportsTo = libData.exportsTo || exportsTo;
exportsAs = libData.exportsAs || exportsAs;
noAppCache = libData.noAppCache || noAppCache;
//noAppCache = libData.noAppCache || noAppCache;
}
noAppCache = true;
fetchObject = function () {
var from = root;

View File

@@ -46,7 +46,8 @@ define('views/admin/field-manager/edit', ['view', 'model'], function (Dep, Model
fieldList: this.fieldList,
isCustom: this.defs.isCustom,
isNew: this.isNew,
hasDynamicLogicPanel: this.hasDynamicLogicPanel
hasDynamicLogicPanel: this.hasDynamicLogicPanel,
hasResetToDefault: !this.defs.isCustom && !this.entityTypeIsCustom && !this.isNew,
};
},
@@ -74,13 +75,16 @@ define('views/admin/field-manager/edit', ['view', 'model'], function (Dep, Model
fields: {
name: {required: true, maxLength: 100},
label: {required: true},
tooltipText: {}
tooltipText: {},
}
};
this.entityTypeIsCustom = !!this.getMetadata().get(['scopes', this.scope, 'isCustom']);
if (!this.isNew) {
this.model.id = this.field;
this.model.scope = this.scope;
this.model.set('name', this.field);
this.model.set('label', this.getLanguage().translate(this.field, 'fields', this.scope));

View File

@@ -126,7 +126,7 @@ define('views/admin/layouts/detail', 'views/admin/layouts/grid', function (Dep)
promiseList.push(
new Promise(
function (resolve) {
if (this.getMetadata().get(['clientDefs', scope, 'layoutDefaultSidePanelDisabled'])) resolve();
if (this.getMetadata().get(['clientDefs', this.scope, 'layoutDefaultSidePanelDisabled'])) resolve();
this.getHelper().layoutManager.getOriginal(this.scope, 'defaultSidePanel', this.setId,
function (layoutLoaded) {

View File

@@ -32,6 +32,8 @@ define('views/email-template/record/detail', 'views/record/detail', function (De
duplicateAction: true,
saveAndContinueEditingAction: true,
setup: function () {
Dep.prototype.setup.call(this);
this.listenToInsertField();

View File

@@ -26,15 +26,16 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/email-template/record/edit', ['views/record/edit', 'views/email-template/record/detail'], function (Dep, Detail) {
define('views/email-template/record/edit', ['views/record/edit', 'views/email-template/record/detail'], function (Dep, Detail) {
return Dep.extend({
saveAndContinueEditingAction: true,
setup: function () {
Dep.prototype.setup.call(this);
Detail.prototype.listenToInsertField.call(this);
},
});
});

View File

@@ -0,0 +1,48 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/event/fields/name-for-history', 'views/fields/varchar', function (Dep) {
return Dep.extend({
listLinkTemplate: 'event/fields/name-for-history/list-link',
data: function () {
var data = Dep.prototype.data.call(this);
var status = this.model.get('status');
var canceledStatusList = this.getMetadata().get('app.calendar.canceledStatusList') || [];
data.strikethrough = !!~canceledStatusList.indexOf(status);
return data;
},
});
});

View File

@@ -283,19 +283,27 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
var $element = this.$element = this.$el.find('.main-element');
var valueList = this.getSearchParamsData().valueList || this.searchParams.valueFront || [];
this.$element.val(valueList.join(':,:'));
var data = [];
(this.params.options || []).forEach(function (value) {
var label = this.getLanguage().translateOption(value, this.name, this.scope);
if (this.translatedOptions) {
if (value in this.translatedOptions) {
label = this.translatedOptions[value];
}
}
if (label === '') {
return;
};
data.push({
value: value,
label: label
label: label,
});
}, this);
@@ -312,11 +320,14 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
if (!this.matchAnyWord) {
selectizeOptions.score = function (search) {
var score = this.getScoreFunction(search);
search = search.toLowerCase();
return function (item) {
if (item.label.toLowerCase().indexOf(search) === 0) {
return score(item);
}
return 0;
};
};
@@ -324,16 +335,17 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
if (this.allowCustomOptions) {
selectizeOptions.persist = false;
selectizeOptions.create = function (input) {
return {
value: input,
label: input
label: input,
}
};
selectizeOptions.render = {
option_create: function (data, escape) {
return '<div class="create"><strong>' + escape(data.input) + '</strong>&hellip;</div>';
}
},
};
}
@@ -342,6 +354,7 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
this.$el.find('.selectize-dropdown-content').addClass('small');
var type = this.$el.find('select.search-type').val();
this.handleSearchType(type);
this.$el.find('select.search-type').on('change', function () {
@@ -355,10 +368,13 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
fetchFromDom: function () {
var selected = [];
this.$el.find('.list-group .list-group-item').each(function (i, el) {
var value = $(el).data('value').toString();
selected.push(value);
});
this.selected = selected;
},

View File

@@ -68,16 +68,21 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
events: {
'click a.remove-attachment': function (e) {
var $div = $(e.currentTarget).parent();
var id = $div.attr('data-id');
if (id) {
this.deleteAttachment(id);
}
$div.parent().remove();
this.$el.find('input.file').val(null);
},
'change input.file': function (e) {
var $file = $(e.currentTarget);
var files = e.currentTarget.files;
this.uploadFiles(files);
$file.replaceWith($file.clone(true));
@@ -90,6 +95,7 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
e.preventDefault();
var id = $(e.currentTarget).data('id');
var attachmentIdList = this.model.get(this.idsName) || [];
var typeHash = this.model.get(this.typeHashName) || {};
@@ -100,14 +106,18 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
imageIdListLeft.push(id);
var met = false;
attachmentIdList.forEach(function (cId) {
if (cId === id) {
met = true;
return;
}
if (!this.isTypeIsImage(typeHash[cId])) {
return;
}
if (met) {
imageIdListLeft.push(cId);
} else {
@@ -118,6 +128,7 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
var imageIdList = imageIdListLeft.concat(imageIdListRight);
var imageList = [];
imageIdList.forEach(function (cId) {
imageList.push({
id: cId,
@@ -182,19 +193,43 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
var sourceDefs = this.getMetadata().get(['clientDefs', 'Attachment', 'sourceDefs']) || {};
this.sourceList = Espo.Utils.clone(this.params.sourceList || []).filter(function (item) {
if (!(item in sourceDefs)) return true;
var defs = sourceDefs[item];
if (defs.configCheck) {
var configCheck = defs.configCheck;
if (configCheck) {
var arr = configCheck.split('.');
if (this.getConfig().getByPath(arr)) {
return true;
}
this.sourceList = Espo.Utils.clone(this.params.sourceList || []);
this.sourceList = this.sourceList
.concat(
this.getMetadata().get(['clientDefs', 'Attachment', 'generalSourceList']) || []
)
.filter(
function (item, i, self) {
return self.indexOf(item) === i;
}
}
}, this);
)
.filter(
function (item) {
var defs = sourceDefs[item] || {};
if (defs.accessDataList) {
if (
!Espo.Utils.checkAccessDataList(
defs.accessDataList, this.getAcl(), this.getUser()
)
) {
return false;
}
}
if (defs.configCheck) {
var arr = defs.configCheck.split('.');
if (!this.getConfig().getByPath(arr)) {
return false;
}
}
return true;
},
this
);
this.listenTo(this.model, 'change:' + this.nameHashName, function () {
this.nameHash = _.clone(this.model.get(this.nameHashName)) || {};
@@ -222,16 +257,19 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
empty: function () {
this.clearIds();
this.$attachments.empty();
},
handleResize: function () {
var width = this.$el.width();
this.$el.find('img.image-preview').css('maxWidth', width + 'px');
},
deleteAttachment: function (id) {
this.removeId(id);
if (this.model.isNew()) {
this.getModelFactory().create('Attachment', function (attachment) {
attachment.id = id;
@@ -242,40 +280,55 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
getImageUrl: function (id, size) {
var url = this.getBasePath() + '?entryPoint=image&id=' + id;
if (size) {
url += '&size=' + size;
}
if (this.getUser().get('portalId')) {
url += '&portalId=' + this.getUser().get('portalId');
}
return url;
},
getDownloadUrl: function (id) {
id = Handlebars.Utils.escapeExpression(id);
var url = this.getBasePath() + '?entryPoint=download&id=' + id;
if (this.getUser().get('portalId')) {
url += '&portalId=' + this.getUser().get('portalId');
}
return url;
},
removeId: function (id) {
var arr = _.clone(this.model.get(this.idsName) || []);
var i = arr.indexOf(id);
arr.splice(i, 1);
this.model.set(this.idsName, arr);
var nameHash = _.clone(this.model.get(this.nameHashName) || {});
delete nameHash[id];
this.model.set(this.nameHashName, nameHash);
var typeHash = _.clone(this.model.get(this.typeHashName) || {});
delete typeHash[id];
this.model.set(this.typeHashName, typeHash);
},
clearIds: function (silent) {
var silent = silent || false;
this.model.set(this.idsName, [], {silent: silent});
this.model.set(this.nameHashName, {}, {silent: silent});
this.model.set(this.typeHashName, {}, {silent: silent})
@@ -285,19 +338,25 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
var arr = _.clone(this.model.get(this.idsName) || []);
arr.push(attachment.id);
this.model.set(this.idsName, arr);
var typeHash = _.clone(this.model.get(this.typeHashName) || {});
typeHash[attachment.id] = attachment.get('type');
this.model.set(this.typeHashName, typeHash);
var nameHash = _.clone(this.model.get(this.nameHashName) || {});
nameHash[attachment.id] = attachment.get('name');
this.model.set(this.nameHashName, nameHash);
},
getEditPreview: function (name, type, id) {
name = Handlebars.Utils.escapeExpression(name);
id = Handlebars.Utils.escapeExpression(id);
var preview = name;
@@ -309,11 +368,15 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
},
addAttachmentBox: function (name, type, id, link) {
id = Handlebars.Utils.escapeExpression(id);
var $attachments = this.$attachments;
var removeLink = '<a href="javascript:" class="remove-attachment pull-right"><span class="fas fa-times"></span></a>';
var removeLink = '<a href="javascript:" class="remove-attachment pull-right">'+
'<span class="fas fa-times"></span></a>';
var preview = name;
if (this.showPreviews && id) {
preview = this.getEditPreview(name, type, id);
} else {
@@ -321,19 +384,27 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
}
if (link && preview === name) {
preview = '<a href="'+this.getBasePath()+'?entryPoint=download&id='+id+'" target="_BLANK">' + preview + '</a>';
preview = '<a href="' + this.getBasePath() + '?entryPoint=download&id=' + id + '" target="_BLANK">' +
preview + '</a>';
}
var $att = $('<div>').addClass('gray-box')
.append(removeLink)
.append($('<span class="preview">' + preview + '</span>').css('width', 'cacl(100% - 30px)'));
.append(
$('<span class="preview">' + preview + '</span>')
.css('width', 'cacl(100% - 30px)')
);
var $container = $('<div>').append($att);
$attachments.append($container);
if (!id) {
var $loading = $('<span class="small uploading-message">' + this.translate('Uploading...') + '</span>');
var $loading = $('<span class="small uploading-message">' +
this.translate('Uploading...') + '</span>');
$container.append($loading);
$att.on('ready', function () {
$loading.html(this.translate('Ready'));
}.bind(this));
@@ -359,7 +430,9 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
var exceedsMaxFileSize = false;
var maxFileSize = this.params.maxFileSize || 0;
var appMaxUploadSize = this.getHelper().getAppParam('maxUploadSize') || 0;
if (!maxFileSize || maxFileSize > appMaxUploadSize) {
maxFileSize = appMaxUploadSize;
}
@@ -374,10 +447,11 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
}
if (exceedsMaxFileSize) {
var msg = this.translate('fieldMaxFileSizeError', 'messages')
.replace('{field}', this.getLabelText())
.replace('{max}', maxFileSize);
.replace('{field}', this.getLabelText())
.replace('{max}', maxFileSize);
this.showValidationMessage(msg, 'label');
return;
}
@@ -397,7 +471,9 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
$attachmentBox.find('.remove-attachment').on('click.uploading', function () {
canceledList.push(attachment.cid);
totalCount--;
if (uploadedCount == totalCount) {
this.isUploading = false;
if (totalCount) {
@@ -409,6 +485,7 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
var attachment = model.clone();
var fileReader = new FileReader();
fileReader.onload = function (e) {
attachment.set('name', file.name);
attachment.set('type', file.type || 'text/plain');
@@ -424,9 +501,12 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
function () {
if (canceledList.indexOf(attachment.cid) === -1) {
$attachmentBox.trigger('ready');
this.pushAttachment(attachment);
$attachmentBox.attr('data-id', attachment.id);
uploadedCount++;
if (uploadedCount == totalCount && this.isUploading) {
this.model.trigger('attachment-uploaded:' + this.name);
this.afterAttachmentsUploaded.call(this);
@@ -438,11 +518,14 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
.fail(
function () {
$attachmentBox.remove();
totalCount--;
if (!totalCount) {
this.isUploading = false;
this.$el.find('.uploading-message').remove();
}
if (uploadedCount == totalCount && this.isUploading) {
this.isUploading = false;
this.afterAttachmentsUploaded.call(this);
@@ -450,7 +533,9 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
}.bind(this)
);
}.bind(this);
fileReader.readAsDataURL(file);
}, this);
}.bind(this));
},
@@ -460,7 +545,7 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
},
afterRender: function () {
if (this.mode == 'edit') {
if (this.mode === 'edit') {
this.$attachments = this.$el.find('div.attachments');
var ids = this.model.get(this.idsName) || [];
@@ -482,8 +567,11 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
this.$el.on('drop', function (e) {
e.preventDefault();
e.stopPropagation();
var e = e.originalEvent;
if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length) {
this.uploadFiles(e.dataTransfer.files);
}
@@ -492,13 +580,15 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
this.$el.get(0).addEventListener('dragover', function (e) {
e.preventDefault();
}.bind(this));
this.$el.get(0).addEventListener('dragleave', function (e) {
e.preventDefault();
}.bind(this));
}
if (this.mode == 'search') {
if (this.mode === 'search') {
var type = this.$el.find('select.search-type').val();
this.handleSearchType(type);
}
@@ -506,6 +596,7 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
if (this.previewSize === 'large') {
this.handleResize();
this.resizeIsBeingListened = true;
$(window).on('resize.' + this.cid, function () {
this.handleResize();
}.bind(this));
@@ -517,29 +608,38 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
if (~this.previewTypeList.indexOf(type)) {
return true;
}
return false
},
getDetailPreview: function (name, type, id) {
name = Handlebars.Utils.escapeExpression(name);
id = Handlebars.Utils.escapeExpression(id);
var preview = name;
if (this.isTypeIsImage(type)) {
preview = '<a data-action="showImagePreview" data-id="' + id + '" href="' + this.getImageUrl(id) + '"><img src="'+this.getImageUrl(id, this.previewSize)+'" class="image-preview"></a>';
preview = '<a data-action="showImagePreview" data-id="' + id + '" href="' +
this.getImageUrl(id) + '"><img src="'+
this.getImageUrl(id, this.previewSize)+'" class="image-preview"></a>';
}
return preview;
},
getValueForDisplay: function () {
if (this.mode == 'detail' || this.mode == 'list') {
if (this.mode === 'detail' || this.mode === 'list') {
var nameHash = this.nameHash;
var typeHash = this.model.get(this.typeHashName) || {};
var previews = [];
var names = [];
for (var id in nameHash) {
var type = typeHash[id] || false;
var name = nameHash[id];
if (
this.showPreviews
&&
@@ -547,12 +647,22 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
&&
(this.mode === 'detail' || this.mode === 'list' && this.showPreviewsInListMode)
) {
previews.push('<div class="attachment-preview">' + this.getDetailPreview(name, type, id) + '</div>');
previews.push(
'<div class="attachment-preview">' +
this.getDetailPreview(name, type, id) + '</div>'
);
continue;
}
var line = '<div class="attachment-block"><span class="fas fa-paperclip text-soft small"></span> <a href="' + this.getDownloadUrl(id) + '" target="_BLANK">' + Handlebars.Utils.escapeExpression(name) + '</a></div>';
var line = '<div class="attachment-block">' +
'<span class="fas fa-paperclip text-soft small"></span> ' +
'<a href="' + this.getDownloadUrl(id) + '" target="_BLANK">' +
Handlebars.Utils.escapeExpression(name) + '</a></div>';
names.push(line);
}
var string = previews.join('') + names.join('');
return string;
@@ -569,6 +679,7 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
this.notify('Loading...');
var filters = null;
if (('getSelectFilters' + source) in this) {
filters = this['getSelectFilters' + source]();
@@ -585,38 +696,51 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
}
}
}
var boolFilterList = this.getMetadata().get(['clientDefs', 'Attachment', 'sourceDefs', source, 'boolFilterList']);
var boolFilterList = this.getMetadata().get(
['clientDefs', 'Attachment', 'sourceDefs', source, 'boolFilterList']
);
if (('getSelectBoolFilterList' + source) in this) {
boolFilterList = this['getSelectBoolFilterList' + source]();
}
var primaryFilterName = this.getMetadata().get(['clientDefs', 'Attachment', 'sourceDefs', source, 'primaryFilter']);
var primaryFilterName = this.getMetadata().get(
['clientDefs', 'Attachment', 'sourceDefs', source, 'primaryFilter']
);
if (('getSelectPrimaryFilterName' + source) in this) {
primaryFilterName = this['getSelectPrimaryFilterName' + source]();
}
this.createView('insertFromSource', viewName, {
scope: source,
createButton: false,
filters: filters,
boolFilterList: boolFilterList,
primaryFilterName: primaryFilterName,
multiple: true
multiple: true,
}, function (view) {
view.render();
this.notify(false);
this.listenToOnce(view, 'select', function (modelList) {
if (Object.prototype.toString.call(modelList) !== '[object Array]') {
modelList = [modelList];
}
modelList.forEach(function (model) {
if (model.name === 'Attachment') {
this.pushAttachment(model);
} else {
}
else {
this.ajaxPostRequest(source + '/action/getAttachmentList', {
id: model.id
id: model.id,
}).done(function (attachmentList) {
attachmentList.forEach(function (item) {
this.getModelFactory().create('Attachment', function (attachment) {
attachment.set(item);
this.pushAttachment(attachment, true);
}, this);
}, this);
@@ -625,6 +749,7 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
}, this);
});
}, this);
return;
}
},
@@ -633,7 +758,9 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
if (this.isRequired()) {
if ((this.model.get(this.idsName) || []).length == 0) {
var msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
this.showValidationMessage(msg, 'label');
return true;
}
}
@@ -642,14 +769,18 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
validateReady: function () {
if (this.isUploading) {
var msg = this.translate('fieldIsUploading', 'messages').replace('{field}', this.getLabelText());
this.showValidationMessage(msg, 'label');
return true;
}
},
fetch: function () {
var data = {};
data[this.idsName] = this.model.get(this.idsName) || [];
return data;
},
@@ -664,20 +795,23 @@ define('views/fields/attachment-multiple', 'views/fields/base', function (Dep) {
var data = {
type: 'isNotLinked',
data: {
type: type
}
type: type,
},
};
return data;
} else if (type === 'isNotEmpty') {
}
else if (type === 'isNotEmpty') {
var data = {
type: 'isLinked',
data: {
type: type
}
type: type,
},
};
return data;
}
}
},
});
});

View File

@@ -220,22 +220,31 @@ define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], function (De
var $element = this.$element = this.$el.find('.main-element');
var type = this.$el.find('select.search-type').val();
this.handleSearchType(type);
var valueList = this.getSearchParamsData().valueList || this.searchParams.value || [];
this.$element.val(valueList.join(':,:'));
var data = [];
(this.params.options || []).forEach(function (value) {
var label = this.getLanguage().translateOption(value, this.name, this.scope);
if (this.translatedOptions) {
if (value in this.translatedOptions) {
label = this.translatedOptions[value];
}
}
if (label === '') {
return;
}
data.push({
value: value,
label: label
label: label,
});
}, this);
@@ -249,14 +258,17 @@ define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], function (De
plugins: ['remove_button'],
score: function (search) {
var score = this.getScoreFunction(search);
search = search.toLowerCase();
return function (item) {
if (item.label.toLowerCase().indexOf(search) === 0) {
return score(item);
}
return 0;
};
}
},
});
this.$el.find('.selectize-dropdown-content').addClass('small');

View File

@@ -62,15 +62,21 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
events: {
'click a.remove-attachment': function (e) {
var $div = $(e.currentTarget).parent();
this.deleteAttachment();
$div.parent().remove();
this.$el.find('input.file').val(null);
},
'change input.file': function (e) {
var $file = $(e.currentTarget);
var files = e.currentTarget.files;
if (files.length) {
this.uploadFile(files[0]);
$file.replaceWith($file.clone(true));
}
},
@@ -78,16 +84,18 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
e.preventDefault();
var id = this.model.get(this.idName);
this.createView('preview', 'views/modals/image-preview', {
id: id,
model: this.model,
name: this.model.get(this.nameName)
name: this.model.get(this.nameName),
}, function (view) {
view.render();
});
},
'click a.action[data-action="insertFromSource"]': function (e) {
var name = $(e.currentTarget).data('name');
this.insertFromSource(name);
}
},
@@ -109,9 +117,13 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
showValidationMessage: function (msg, selector) {
var $label = this.$el.find('label');
var title = $label.attr('title');
$label.attr('title', '');
Dep.prototype.showValidationMessage.call(this, msg, selector);
$label.attr('title', title);
},
@@ -119,7 +131,9 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
if (this.isRequired()) {
if (this.model.get(this.idName) == null) {
var msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
var $target;
if (this.isUploading) {
$target = this.$el.find('.gray-box');
} else {
@@ -127,6 +141,7 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
}
this.showValidationMessage(msg, $target);
return true;
}
}
@@ -135,7 +150,9 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
validateReady: function () {
if (this.isUploading) {
var $target = this.$el.find('.gray-box');
var msg = this.translate('fieldIsUploading', 'messages').replace('{field}', this.getLabelText());
var msg = this.translate('fieldIsUploading', 'messages')
.replace('{field}', this.getLabelText());
this.showValidationMessage(msg, $target);
@@ -153,25 +170,43 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
var sourceDefs = this.getMetadata().get(['clientDefs', 'Attachment', 'sourceDefs']) || {};
this.sourceList = Espo.Utils.clone(this.params.sourceList || []).filter(function (item) {
if (!(item in sourceDefs)) {
return true;
}
this.sourceList = Espo.Utils.clone(this.params.sourceList || []);
var defs = sourceDefs[item];
if (defs.configCheck) {
var configCheck = defs.configCheck;
if (configCheck) {
var arr = configCheck.split('.');
if (this.getConfig().getByPath(arr)) {
return true;
}
this.sourceList = this.sourceList
.concat(
this.getMetadata().get(['clientDefs', 'Attachment', 'generalSourceList']) || []
)
.filter(
function (item, i, self) {
return self.indexOf(item) === i;
}
}
}, this);
)
.filter(
function (item) {
var defs = sourceDefs[item] || {};
if (defs.accessDataList) {
if (
!Espo.Utils.checkAccessDataList(
defs.accessDataList, this.getAcl(), this.getUser()
)
) {
return false;
}
}
if (defs.configCheck) {
var arr = defs.configCheck.split('.');
if (!this.getConfig().getByPath(arr)) {
return false;
}
}
return true;
},
this
);
if ('showPreview' in this.params) {
this.showPreview = this.params.showPreview;
@@ -215,7 +250,9 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
this.$el.on('drop', function (e) {
e.preventDefault();
e.stopPropagation();
var e = e.originalEvent;
if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length) {
this.uploadFile(e.dataTransfer.files[0]);
}
@@ -224,6 +261,7 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
this.$el.on('dragover', function (e) {
e.preventDefault();
}.bind(this));
this.$el.on('dragleave', function (e) {
e.preventDefault();
}.bind(this));
@@ -231,6 +269,7 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
if (this.mode == 'search') {
var type = this.$el.find('select.search-type').val();
this.handleSearchType(type);
}
@@ -248,6 +287,7 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
handleResize: function () {
var width = this.$el.width();
this.$el.find('img.image-preview').css('maxWidth', width + 'px');
},
@@ -268,6 +308,8 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
}
}
id = Handlebars.Utils.escapeExpression(id);
var src = this.getBasePath() + '?entryPoint=image&size=' + previewSize + '&id=' + id;
var img = '<img src="' + src + '" class="image-preview">';
@@ -288,6 +330,8 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
getEditPreview: function (name, type, id) {
name = Handlebars.Utils.escapeExpression(name);
id = Handlebars.Utils.escapeExpression(id);
var preview = name;
if (~this.previewTypeList.indexOf(type)) {
@@ -329,6 +373,8 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
},
getImageUrl: function (id, size) {
id = Handlebars.Utils.escapeExpression(id);
var url = this.getBasePath() + '?entryPoint=image&id=' + id;
if (size) {
@@ -375,7 +421,6 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
},
setAttachment: function (attachment) {
var arr = _.clone(this.model.get(this.idsName));
var o = {};
o[this.idName] = attachment.id;
@@ -391,6 +436,7 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
var maxFileSize = this.params.maxFileSize || 0;
var appMaxUploadSize = this.getHelper().getAppParam('maxUploadSize') || 0;
if (!maxFileSize || maxFileSize > appMaxUploadSize) {
maxFileSize = appMaxUploadSize;
}
@@ -405,6 +451,7 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
var msg = this.translate('fieldMaxFileSizeError', 'messages')
.replace('{field}', this.getLabelText())
.replace('{max}', maxFileSize);
this.showValidationMessage(msg, '.attachment-button label');
return;
@@ -419,12 +466,16 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
$attachmentBox.find('.remove-attachment').on('click.uploading', function () {
isCanceled = true;
this.$el.find('.attachment-button').removeClass('hidden');
this.isUploading = false;
this.$el.find('input.file').val(null);
}.bind(this));
var fileReader = new FileReader();
fileReader.onload = function (e) {
this.handleFileUpload(file, e.target.result, function (result, fileParams) {
attachment.set('name', fileParams.name);
@@ -456,6 +507,7 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
);
}.bind(this));
}.bind(this);
fileReader.readAsDataURL(file);
}, this);
},
@@ -464,8 +516,9 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
var params = {
name: file.name,
type: file.type,
size: file.size
size: file.size,
};
callback(contents, params);
},
@@ -474,7 +527,8 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
var self = this;
var removeLink = '<a href="javascript:" class="remove-attachment pull-right"><span class="fas fa-times"></span></a>';
var removeLink = '<a href="javascript:" class="remove-attachment pull-right">' +
'<span class="fas fa-times"></span></a>';
var preview = name;
if (this.showPreview && id) {
@@ -484,15 +538,20 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
}
var $att = $('<div>').append(removeLink)
.append($('<span class="preview">' + preview + '</span>').css('width', 'cacl(100% - 30px)'))
.append($('<span class="preview">' + preview + '</span>')
.css('width', 'cacl(100% - 30px)'))
.addClass('gray-box');
var $container = $('<div>').append($att);
this.$attachment.append($container);
if (!id) {
var $loading = $('<span class="small uploading-message">' + this.translate('Uploading...') + '</span>');
var $loading = $('<span class="small uploading-message">' +
this.translate('Uploading...') + '</span>');
$container.append($loading);
$att.on('ready', function () {
$loading.html(self.translate('Ready'));
});
@@ -511,6 +570,7 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
this.notify('Loading...');
var filters = null;
if (('getSelectFilters' + source) in this) {
filters = this['getSelectFilters' + source]();
@@ -521,44 +581,57 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
type: 'equals',
field: 'accountId',
value: this.model.get('parentId'),
valueName: this.model.get('parentName')
valueName: this.model.get('parentName'),
}
};
}
}
}
var boolFilterList = this.getMetadata().get(['clientDefs', 'Attachment', 'sourceDefs', source, 'boolFilterList']);
var boolFilterList = this.getMetadata().get(
['clientDefs', 'Attachment', 'sourceDefs', source, 'boolFilterList']
);
if (('getSelectBoolFilterList' + source) in this) {
boolFilterList = this['getSelectBoolFilterList' + source]();
}
var primaryFilterName = this.getMetadata().get(['clientDefs', 'Attachment', 'sourceDefs', source, 'primaryFilter']);
var primaryFilterName = this.getMetadata().get(
['clientDefs', 'Attachment', 'sourceDefs', source, 'primaryFilter']
);
if (('getSelectPrimaryFilterName' + source) in this) {
primaryFilterName = this['getSelectPrimaryFilterName' + source]();
}
this.createView('insertFromSource', viewName, {
scope: source,
createButton: false,
filters: filters,
boolFilterList: boolFilterList,
primaryFilterName: primaryFilterName,
multiple: false
multiple: false,
}, function (view) {
view.render();
this.notify(false);
this.listenToOnce(view, 'select', function (modelList) {
if (Object.prototype.toString.call(modelList) !== '[object Array]') {
modelList = [modelList];
}
modelList.forEach(function (model) {
if (model.name === 'Attachment') {
this.setAttachment(model);
} else {
}
else {
this.ajaxPostRequest(source + '/action/getAttachmentList', {
id: model.id
id: model.id,
}).done(function (attachmentList) {
attachmentList.forEach(function (item) {
this.getModelFactory().create('Attachment', function (attachment) {
attachment.set(item);
this.setAttachment(attachment, true);
}, this);
}, this);
@@ -567,15 +640,18 @@ define('views/fields/file', 'views/fields/link', function (Dep) {
}, this);
});
}, this);
return;
}
},
fetch: function () {
var data = {};
data[this.idName] = this.model.get(this.idName);
return data;
}
},
});
});

View File

@@ -125,6 +125,9 @@ Espo.define('views/fields/link-multiple-with-primary', 'views/fields/link-multip
addLinkHtml: function (id, name) {
name = name || id;
id = Handlebars.Utils.escapeExpression(id);
name = Handlebars.Utils.escapeExpression(name);
if (this.mode == 'search') {
return Dep.prototype.addLinkHtml.call(this, id, name);
}
@@ -132,7 +135,7 @@ Espo.define('views/fields/link-multiple-with-primary', 'views/fields/link-multip
var $container = this.$el.find('.link-container');
var $el = $('<div class="form-inline list-group-item link-with-role clearfix link-group-item-with-primary">').addClass('link-' + id).attr('data-id', id);
var nameHtml = '<div>' + this.getHelper().escapeString(name) || id + '&nbsp;' + '</div>';
var nameHtml = '<div>' + name + '&nbsp;' + '</div>';
var removeHtml = '<a href="javascript:" class="pull-right" data-id="' + id + '" data-action="clearLink"><span class="fas fa-times"></a>';
$left = $('<div>');

View File

@@ -79,6 +79,9 @@ define('views/fields/link-multiple-with-role', 'views/fields/link-multiple', fun
name = this.translate(this.foreignScope, 'scopeNames');
}
id = Handlebars.Utils.escapeExpression(id);
name = Handlebars.Utils.escapeExpression(name);
var role = (this.columns[id] || {})[this.columnName] || '';
var roleHtml = '';
@@ -107,7 +110,7 @@ define('views/fields/link-multiple-with-role', 'views/fields/link-multiple', fun
if (this.mode == 'detail') {
iconHtml = this.getIconHtml(id);
}
var lineHtml = '<div>' + iconHtml + '<a href="#' + this.foreignScope + '/view/' + id + '">' + this.getHelper().escapeString(name) + '</a> ' + roleHtml + '</div>';
var lineHtml = '<div>' + iconHtml + '<a href="#' + this.foreignScope + '/view/' + id + '">' + name + '</a> ' + roleHtml + '</div>';
return lineHtml;
},
@@ -161,6 +164,8 @@ define('views/fields/link-multiple-with-role', 'views/fields/link-multiple', fun
},
getJQSelect: function (id, roleValue) {
id = Handlebars.Utils.escapeExpression(id);
var $role = $('<select class="role form-control input-sm pull-right" data-id="'+id+'">');
this.roleList.forEach(function (role) {
var selectedHtml = (role == roleValue) ? 'selected': '';
@@ -178,10 +183,13 @@ define('views/fields/link-multiple-with-role', 'views/fields/link-multiple', fun
return Dep.prototype.addLinkHtml.call(this, id, name);
}
id = Handlebars.Utils.escapeExpression(id);
name = Handlebars.Utils.escapeExpression(name);
var $container = this.$el.find('.link-container');
var $el = $('<div class="form-inline list-group-item link-with-role link-group-item-with-columns clearfix">').addClass('link-' + id);
var nameHtml = '<div>' + this.getHelper().escapeString(name) + '&nbsp;' + '</div>';
var nameHtml = '<div>' + name + '&nbsp;' + '</div>';
var removeHtml = '<a href="javascript:" class="pull-right" data-id="' + id + '" data-action="clearLink"><span class="fas fa-times"></a>';

View File

@@ -328,9 +328,12 @@ define('views/fields/link-multiple', 'views/fields/base', function (Dep) {
addLinkHtml: function (id, name) {
name = name || id;
id = Handlebars.Utils.escapeExpression(id);
name = Handlebars.Utils.escapeExpression(name);
var $container = this.$el.find('.link-container');
var $el = $('<div />').addClass('link-' + id).addClass('list-group-item').attr('data-id', id);
$el.html(this.getHelper().escapeString(name || id) + '&nbsp');
$el.html(name + '&nbsp');
$el.prepend('<a href="javascript:" class="pull-right" data-id="' + id + '" data-action="clearLink"><span class="fas fa-times"></a>');
$container.append($el);
@@ -343,6 +346,10 @@ define('views/fields/link-multiple', 'views/fields/base', function (Dep) {
getDetailLinkHtml: function (id) {
var name = this.nameHash[id] || id;
id = Handlebars.Utils.escapeExpression(id);
name = Handlebars.Utils.escapeExpression(name);
if (!name && id) {
name = this.translate(this.foreignScope, 'scopeNames');
}
@@ -350,7 +357,7 @@ define('views/fields/link-multiple', 'views/fields/base', function (Dep) {
if (this.mode == 'detail') {
iconHtml = this.getIconHtml(id);
}
return '<a href="#' + this.foreignScope + '/view/' + id + '">' + iconHtml + this.getHelper().escapeString(name) + '</a>';
return '<a href="#' + this.foreignScope + '/view/' + id + '">' + iconHtml + name + '</a>';
},
getValueForDisplay: function () {

View File

@@ -56,6 +56,8 @@ define('views/fields/link-parent', 'views/fields/base', function (Dep) {
searchTypeList: ['is', 'isEmpty', 'isNotEmpty'],
initialSearchIsNotIdle: true,
data: function () {
var nameValue = this.model.get(this.nameName);
if (!nameValue && this.model.get(this.idName) && this.model.get(this.typeName)) {

View File

@@ -406,6 +406,8 @@ define('views/fields/link', 'views/fields/base', function (Dep) {
this.searchData.oneOfIdList.splice(index, 1);
}
delete this.searchData.oneOfNameHash[id];
this.trigger('change');
},
addLinkOneOf: function (id, name) {
@@ -413,6 +415,8 @@ define('views/fields/link', 'views/fields/base', function (Dep) {
this.searchData.oneOfIdList.push(id);
this.searchData.oneOfNameHash[id] = name;
this.addLinkOneOfHtml(id, name);
this.trigger('change');
}
},
@@ -421,10 +425,17 @@ define('views/fields/link', 'views/fields/base', function (Dep) {
},
addLinkOneOfHtml: function (id, name) {
id = Handlebars.Utils.escapeExpression(id);
name = this.getHelper().escapeString(name);
var $container = this.$el.find('.link-one-of-container');
var $el = $('<div />').addClass('link-' + id).addClass('list-group-item');
$el.html(this.getHelper().escapeString(name) + '&nbsp');
$el.html(name + '&nbsp');
$el.prepend('<a href="javascript:" class="pull-right" data-id="' + id + '" data-action="clearLinkOneOf"><span class="fas fa-times"></a>');
$container.append($el);
return $el;

View File

@@ -150,6 +150,8 @@ Espo.define('views/fields/user', 'views/fields/link', function (Dep) {
this.searchData.teamIdList.splice(index, 1);
}
delete this.searchData.teamNameHash[id];
this.trigger('change');
},
addLinkTeams: function (id, name) {
@@ -159,6 +161,8 @@ Espo.define('views/fields/user', 'views/fields/link', function (Dep) {
this.searchData.teamIdList.push(id);
this.searchData.teamNameHash[id] = name;
this.addLinkTeamsHtml(id, name);
this.trigger('change');
}
},
@@ -167,6 +171,9 @@ Espo.define('views/fields/user', 'views/fields/link', function (Dep) {
},
addLinkTeamsHtml: function (id, name) {
id = Handlebars.Utils.escapeExpression(id);
name = Handlebars.Utils.escapeExpression(name);
var $container = this.$el.find('.link-teams-container');
var $el = $('<div />').addClass('link-' + id).addClass('list-group-item');
$el.html(name + '&nbsp');

View File

@@ -152,7 +152,11 @@ define('views/notification/badge', 'view', function (Dep) {
checkBypass: function () {
var last = this.getRouter().getLast() || {};
if (last.controller == 'Admin' && last.action == 'upgrade') {
if (
last.controller == 'Admin'
&&
~['upgrade', 'extensions'].indexOf(last.action)
) {
return true;
}
},

View File

@@ -86,7 +86,9 @@ define('views/record/list', 'view', function (Dep) {
return;
}
e.preventDefault();
var id = $(e.currentTarget).data('id');
var id = $(e.currentTarget).attr('data-id');
var model = this.collection.get(id);
var scope = this.getModelScope(id);
@@ -130,7 +132,9 @@ define('views/record/list', 'view', function (Dep) {
'click .record-checkbox': function (e) {
var $target = $(e.currentTarget);
var id = $target.data('id');
var id = $target.attr('data-id');
if (e.currentTarget.checked) {
this.checkRecord(id, $target);
} else {
@@ -1080,7 +1084,9 @@ define('views/record/list', 'view', function (Dep) {
if (this.selectable) {
this.events['click .list a.link'] = function (e) {
e.preventDefault();
var id = $(e.target).data('id');
var id = $(e.target).attr('data-id');
if (id) {
var model = this.collection.get(id);
if (this.checkboxes) {
@@ -1407,7 +1413,9 @@ define('views/record/list', 'view', function (Dep) {
getSelected: function () {
var list = [];
this.$el.find('input.record-checkbox:checked').each(function (i, el) {
var id = $(el).data('id');
var id = $(el).attr('data-id');
var model = this.collection.get(id);
list.push(model);
}.bind(this));

View File

@@ -30,6 +30,8 @@ define('views/template/record/detail', 'views/record/detail', function (Dep) {
return Dep.extend({
saveAndContinueEditingAction: true,
setup: function () {
Dep.prototype.setup.call(this);

View File

@@ -30,6 +30,8 @@ define('views/template/record/edit', 'views/record/edit', function (Dep) {
return Dep.extend({
saveAndContinueEditingAction: true,
setup: function () {
Dep.prototype.setup.call(this);

View File

@@ -30,8 +30,6 @@ define('views/wysiwyg/modals/insert-image', 'views/modal', function (Dep) {
return Dep.extend({
backdrop: true,
template: 'wysiwyg/modals/insert-image',
events: {

View File

@@ -30,8 +30,6 @@ define('views/wysiwyg/modals/insert-link', 'views/modal', function (Dep) {
return Dep.extend({
backdrop: true,
template: 'wysiwyg/modals/insert-link',
events: {

View File

@@ -25,11 +25,10 @@
"laminas/laminas-servicemanager": "^3.3.1",
"monolog/monolog": "2.*",
"yzalis/identicon": "*",
"zordius/lightncandy": "1.*",
"zordius/lightncandy": "dev-espo#v1.2.5e",
"composer/semver": "^3",
"tecnickcom/tcpdf": "^6.3",
"phpoffice/phpspreadsheet": "^1.10",
"spatie/async": "dev-for-espocrm",
"tecnickcom/tcpdf": "6.3.5",
"spatie/async": "0.0.4",
"symfony/process": "4.1.7",
"symfony/http-foundation": "4.4.7",
"cboden/ratchet": "^0.4.1",
@@ -39,6 +38,7 @@
"robthree/twofactorauth": "^1.6",
"nesbot/carbon": "^2.26",
"zbateson/mail-mime-parser": "1.3.*",
"phpoffice/phpspreadsheet": "^1.16",
"doctrine/dbal": "^3.0"
},
"require-dev": {
@@ -54,7 +54,7 @@
"repositories": [
{
"type": "git",
"url": "https://github.com/yurikuzn/async.git"
"url": "https://github.com/yurikuzn/lightncandy.git"
}
]
}

531
composer.lock generated
View File

@@ -1,10 +1,10 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "80dd458b8718443e013ae4d448e4a732",
"content-hash": "a32e7a77ab0ae1e795186b26455beb14",
"packages": [
{
"name": "cboden/ratchet",
@@ -112,20 +112,6 @@
}
],
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2020-11-11T10:22:58+00:00"
},
{
@@ -188,20 +174,6 @@
"validation",
"versioning"
],
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2020-05-26T18:22:04+00:00"
},
{
@@ -316,20 +288,6 @@
"redis",
"xcache"
],
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache",
"type": "tidelift"
}
],
"time": "2020-07-07T18:54:01+00:00"
},
{
@@ -423,20 +381,6 @@
"sqlserver",
"sqlsrv"
],
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal",
"type": "tidelift"
}
],
"time": "2020-11-15T18:20:41+00:00"
},
{
@@ -513,20 +457,6 @@
"event system",
"events"
],
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager",
"type": "tidelift"
}
],
"time": "2020-05-29T18:28:51+00:00"
},
{
@@ -575,12 +505,6 @@
"cron",
"schedule"
],
"funding": [
{
"url": "https://github.com/dragonmantank",
"type": "github"
}
],
"time": "2020-08-21T02:30:13+00:00"
},
{
@@ -626,6 +550,56 @@
],
"time": "2017-07-23T21:35:13+00:00"
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.13.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"require-dev": {
"simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
},
"type": "library",
"autoload": {
"psr-0": {
"HTMLPurifier": "library/"
},
"files": [
"library/HTMLPurifier.composer.php"
],
"exclude-from-classmap": [
"/library/HTMLPurifier/Language/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "Edward Z. Yang",
"email": "admin@htmlpurifier.org",
"homepage": "http://ezyang.com"
}
],
"description": "Standards compliant HTML filter written in PHP",
"homepage": "http://htmlpurifier.org/",
"keywords": [
"html"
],
"time": "2020-06-29T00:56:53+00:00"
},
{
"name": "fig/http-message-util",
"version": "1.1.4",
@@ -1045,12 +1019,6 @@
"service-manager",
"servicemanager"
],
"funding": [
{
"url": "https://funding.communitybridge.org/projects/laminas-project",
"type": "community_bridge"
}
],
"time": "2020-05-11T14:43:22+00:00"
},
{
@@ -1231,31 +1199,92 @@
"time": "2020-09-14T14:23:00+00:00"
},
{
"name": "markbaker/complex",
"version": "1.4.7",
"name": "maennchen/zipstream-php",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/MarkBaker/PHPComplex.git",
"reference": "1ea674a8308baf547cbcbd30c5fcd6d301b7c000"
"url": "https://github.com/maennchen/ZipStream-PHP.git",
"reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/1ea674a8308baf547cbcbd30c5fcd6d301b7c000",
"reference": "1ea674a8308baf547cbcbd30c5fcd6d301b7c000",
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58",
"reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58",
"shasum": ""
},
"require": {
"php": "^5.6.0|^7.0.0"
"myclabs/php-enum": "^1.5",
"php": ">= 7.1",
"psr/http-message": "^1.0",
"symfony/polyfill-mbstring": "^1.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.3",
"phpcompatibility/php-compatibility": "^8.0",
"ext-zip": "*",
"guzzlehttp/guzzle": ">= 6.3",
"mikey179/vfsstream": "^1.6",
"phpunit/phpunit": ">= 7.5"
},
"type": "library",
"autoload": {
"psr-4": {
"ZipStream\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paul Duncan",
"email": "pabs@pablotron.org"
},
{
"name": "Jonatan Männchen",
"email": "jonatan@maennchen.ch"
},
{
"name": "Jesse Donat",
"email": "donatj@gmail.com"
},
{
"name": "András Kolesár",
"email": "kolesar@kolesar.hu"
}
],
"description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
"keywords": [
"stream",
"zip"
],
"time": "2020-05-30T13:11:16+00:00"
},
{
"name": "markbaker/complex",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/MarkBaker/PHPComplex.git",
"reference": "9999f1432fae467bc93c53f357105b4c31bb994c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/9999f1432fae467bc93c53f357105b4c31bb994c",
"reference": "9999f1432fae467bc93c53f357105b4c31bb994c",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"phpcompatibility/php-compatibility": "^9.0",
"phpdocumentor/phpdocumentor": "2.*",
"phploc/phploc": "2.*",
"phploc/phploc": "^4.0",
"phpmd/phpmd": "2.*",
"phpunit/phpunit": "^4.8.35|^5.4.0",
"sebastian/phpcpd": "2.*",
"squizlabs/php_codesniffer": "^3.3.0"
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
"sebastian/phpcpd": "^4.0",
"squizlabs/php_codesniffer": "^3.4"
},
"type": "library",
"autoload": {
@@ -1323,33 +1352,34 @@
"complex",
"mathematics"
],
"time": "2018-10-13T23:28:42+00:00"
"time": "2020-08-26T10:42:07+00:00"
},
{
"name": "markbaker/matrix",
"version": "1.2.0",
"version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/MarkBaker/PHPMatrix.git",
"reference": "5348c5a67e3b75cd209d70103f916a93b1f1ed21"
"reference": "361c0f545c3172ee26c3d596a0aa03f0cef65e6a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/5348c5a67e3b75cd209d70103f916a93b1f1ed21",
"reference": "5348c5a67e3b75cd209d70103f916a93b1f1ed21",
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/361c0f545c3172ee26c3d596a0aa03f0cef65e6a",
"reference": "361c0f545c3172ee26c3d596a0aa03f0cef65e6a",
"shasum": ""
},
"require": {
"php": "^5.6.0|^7.0.0"
"php": "^7.1 || ^8.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
"phpcompatibility/php-compatibility": "dev-master",
"phploc/phploc": "^4",
"phpmd/phpmd": "dev-master",
"phpunit/phpunit": "^5.7",
"sebastian/phpcpd": "^3.0",
"squizlabs/php_codesniffer": "^3.0@dev"
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"phpcompatibility/php-compatibility": "^9.0",
"phpdocumentor/phpdocumentor": "2.*",
"phploc/phploc": "^4.0",
"phpmd/phpmd": "2.*",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
"sebastian/phpcpd": "^4.0",
"squizlabs/php_codesniffer": "^3.4"
},
"type": "library",
"autoload": {
@@ -1357,22 +1387,22 @@
"Matrix\\": "classes/src/"
},
"files": [
"classes/src/functions/adjoint.php",
"classes/src/functions/antidiagonal.php",
"classes/src/functions/cofactors.php",
"classes/src/functions/determinant.php",
"classes/src/functions/diagonal.php",
"classes/src/functions/identity.php",
"classes/src/functions/inverse.php",
"classes/src/functions/minors.php",
"classes/src/functions/trace.php",
"classes/src/functions/transpose.php",
"classes/src/operations/add.php",
"classes/src/operations/directsum.php",
"classes/src/operations/subtract.php",
"classes/src/operations/multiply.php",
"classes/src/operations/divideby.php",
"classes/src/operations/divideinto.php"
"classes/src/Functions/adjoint.php",
"classes/src/Functions/antidiagonal.php",
"classes/src/Functions/cofactors.php",
"classes/src/Functions/determinant.php",
"classes/src/Functions/diagonal.php",
"classes/src/Functions/identity.php",
"classes/src/Functions/inverse.php",
"classes/src/Functions/minors.php",
"classes/src/Functions/trace.php",
"classes/src/Functions/transpose.php",
"classes/src/Operations/add.php",
"classes/src/Operations/directsum.php",
"classes/src/Operations/subtract.php",
"classes/src/Operations/multiply.php",
"classes/src/Operations/divideby.php",
"classes/src/Operations/divideinto.php"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -1382,7 +1412,7 @@
"authors": [
{
"name": "Mark Baker",
"email": "mark@lange.demon.co.uk"
"email": "mark@demon-angel.eu"
}
],
"description": "PHP Class for working with matrices",
@@ -1392,7 +1422,7 @@
"matrix",
"vector"
],
"time": "2019-10-06T11:29:25+00:00"
"time": "2021-01-23T16:37:31+00:00"
},
{
"name": "michelf/php-markdown",
@@ -1519,17 +1549,53 @@
"logging",
"psr-3"
],
"funding": [
"time": "2020-07-23T08:41:23+00:00"
},
{
"name": "myclabs/php-enum",
"version": "1.7.7",
"source": {
"type": "git",
"url": "https://github.com/myclabs/php-enum.git",
"reference": "d178027d1e679832db9f38248fcc7200647dc2b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/d178027d1e679832db9f38248fcc7200647dc2b7",
"reference": "d178027d1e679832db9f38248fcc7200647dc2b7",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7",
"squizlabs/php_codesniffer": "1.*",
"vimeo/psalm": "^3.8"
},
"type": "library",
"autoload": {
"psr-4": {
"MyCLabs\\Enum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"url": "https://github.com/Seldaek",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
"type": "tidelift"
"name": "PHP Enum contributors",
"homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
}
],
"time": "2020-07-23T08:41:23+00:00"
"description": "PHP Enum implementation",
"homepage": "http://github.com/myclabs/php-enum",
"keywords": [
"enum"
],
"time": "2020-11-14T18:14:52+00:00"
},
{
"name": "nesbot/carbon",
@@ -1707,16 +1773,16 @@
},
{
"name": "phpoffice/phpspreadsheet",
"version": "1.10.1",
"version": "1.16.0",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
"reference": "1648dc9ebef6ebe0c5a172e16cf66732918416e0"
"reference": "76d4323b85129d0c368149c831a07a3e258b2b50"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/1648dc9ebef6ebe0c5a172e16cf66732918416e0",
"reference": "1648dc9ebef6ebe0c5a172e16cf66732918416e0",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/76d4323b85129d0c368149c831a07a3e258b2b50",
"reference": "76d4323b85129d0c368149c831a07a3e258b2b50",
"shasum": ""
},
"require": {
@@ -1733,26 +1799,30 @@
"ext-xmlwriter": "*",
"ext-zip": "*",
"ext-zlib": "*",
"markbaker/complex": "^1.4",
"markbaker/matrix": "^1.2",
"php": "^7.1",
"ezyang/htmlpurifier": "^4.13",
"maennchen/zipstream-php": "^2.1",
"markbaker/complex": "^1.5||^2.0",
"markbaker/matrix": "^1.2||^2.0",
"php": "^7.2||^8.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"psr/simple-cache": "^1.0"
},
"require-dev": {
"dompdf/dompdf": "^0.8.3",
"dompdf/dompdf": "^0.8.5",
"friendsofphp/php-cs-fixer": "^2.16",
"jpgraph/jpgraph": "^4.0",
"mpdf/mpdf": "^8.0",
"phpcompatibility/php-compatibility": "^9.3",
"phpunit/phpunit": "^7.5",
"phpunit/phpunit": "^8.5||^9.3",
"squizlabs/php_codesniffer": "^3.5",
"tecnickcom/tcpdf": "^6.3"
},
"suggest": {
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
"dompdf/dompdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)",
"jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)"
},
"type": "library",
"autoload": {
@@ -1796,7 +1866,7 @@
"xls",
"xlsx"
],
"time": "2019-12-01T23:13:51+00:00"
"time": "2020-12-31T18:03:49+00:00"
},
{
"name": "psr/container",
@@ -1847,6 +1917,55 @@
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/http-client",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
"homepage": "https://github.com/php-fig/http-client",
"keywords": [
"http",
"http-client",
"psr",
"psr-18"
],
"time": "2020-06-29T06:28:15+00:00"
},
{
"name": "psr/http-factory",
"version": "1.0.1",
@@ -2104,16 +2223,16 @@
},
{
"name": "psr/simple-cache",
"version": "1.0.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
"reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24"
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24",
"reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24",
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"shasum": ""
},
"require": {
@@ -2148,7 +2267,7 @@
"psr-16",
"simple-cache"
],
"time": "2017-01-02T13:31:39+00:00"
"time": "2017-10-23T01:57:42+00:00"
},
{
"name": "ralouphie/getallheaders",
@@ -2815,25 +2934,21 @@
"micro",
"router"
],
"funding": [
{
"url": "https://opencollective.com/slimphp",
"type": "open_collective"
},
{
"url": "https://tidelift.com/funding/github/packagist/slim/slim",
"type": "tidelift"
}
],
"time": "2020-04-14T20:49:48+00:00"
},
{
"name": "spatie/async",
"version": "dev-for-espocrm",
"version": "0.0.4",
"source": {
"type": "git",
"url": "https://github.com/yurikuzn/async.git",
"reference": "9e0f665e4cf0cb4f4d3d78c78ab567b2b539415f"
"url": "https://github.com/spatie/async.git",
"reference": "8b76df4ab77dcf7680eee4b83353d038e28f92f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/async/zipball/8b76df4ab77dcf7680eee4b83353d038e28f92f7",
"reference": "8b76df4ab77dcf7680eee4b83353d038e28f92f7",
"shasum": ""
},
"require": {
"opis/closure": "^3.0",
@@ -2847,21 +2962,14 @@
},
"type": "library",
"autoload": {
"files": [],
"files": [
"src/helpers.php"
],
"psr-4": {
"Spatie\\Async\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Spatie\\Async\\Tests\\": "tests"
}
},
"scripts": {
"test": [
"vendor/bin/phpunit"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@@ -2879,7 +2987,7 @@
"async",
"spatie"
],
"time": "2018-12-11T08:49:14+00:00"
"time": "2018-01-29T15:09:29+00:00"
},
{
"name": "symfony/http-foundation",
@@ -2934,20 +3042,6 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-30T14:07:33+00:00"
},
{
@@ -3668,6 +3762,7 @@
"identicon",
"image"
],
"abandoned": true,
"time": "2014-07-13T09:19:12+00:00"
},
{
@@ -3844,20 +3939,14 @@
},
{
"name": "zordius/lightncandy",
"version": "v1.2.4",
"version": "dev-espo",
"source": {
"type": "git",
"url": "https://github.com/zordius/lightncandy.git",
"reference": "dfdb910ae7b59e274f1ff97d29b724871f01b4cc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zordius/lightncandy/zipball/dfdb910ae7b59e274f1ff97d29b724871f01b4cc",
"reference": "dfdb910ae7b59e274f1ff97d29b724871f01b4cc",
"shasum": ""
"url": "https://github.com/yurikuzn/lightncandy.git",
"reference": "207d424c4ced34644663fdc0758c19d5b6974adc"
},
"require": {
"php": ">=5.4.0"
"php": ">=7.1.0"
},
"require-dev": {
"phpunit/phpunit": "^7"
@@ -3873,7 +3962,6 @@
"LightnCandy\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@@ -3886,13 +3974,13 @@
"description": "An extremely fast PHP implementation of handlebars ( http://handlebarsjs.com/ ) and mustache ( http://mustache.github.io/ ).",
"homepage": "https://github.com/zordius/lightncandy",
"keywords": [
"PHP",
"handlebars",
"logicless",
"mustache",
"php",
"template"
],
"time": "2019-06-09T04:10:55+00:00"
"time": "2021-02-18T13:06:28+00:00"
}
],
"packages-dev": [
@@ -4650,16 +4738,6 @@
"testing",
"xunit"
],
"funding": [
{
"url": "https://phpunit.de/donate.html",
"type": "custom"
},
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-06-22T07:06:58+00:00"
},
{
@@ -5333,20 +5411,6 @@
"polyfill",
"portable"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-02-27T09:26:54+00:00"
},
{
@@ -5394,12 +5458,12 @@
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "aed98a490f9a8f78468232db345ab9cf606cf598"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598",
"reference": "aed98a490f9a8f78468232db345ab9cf606cf598",
"shasum": ""
},
@@ -5441,7 +5505,7 @@
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"spatie/async": 20
"zordius/lightncandy": 20
},
"prefer-stable": false,
"prefer-lowest": false,
@@ -5457,6 +5521,5 @@
"ext-curl": "*",
"ext-exif": "*"
},
"platform-dev": [],
"plugin-api-version": "1.1.0"
"platform-dev": []
}

View File

@@ -999,6 +999,10 @@ optgroup {
}
}
.input-group .input-group-btn .btn-sm {
height: 30px;
}
.panel-body,
section {
> p:first-child,

View File

@@ -1,4 +1,5 @@
@import "misc/selectize/selectize.less";
@import "misc/selectize/custom.less";
@import "misc/selectize/selectize.bootstrap3.less";
@import "misc/summernote/summernote.less";
@import "misc/summernote/icons.less";

View File

@@ -0,0 +1,8 @@
.filter > .form-group .field {
.selectize-control {
.selectize-input {
font-size: @font-size-small;
padding: 4px 5px 0px;
}
}
}

View File

@@ -34,4 +34,10 @@
.disabled [data-value] .remove {
border-left-color: lighten(desaturate(@selectize-color-item-border, 100%), @selectize-lighten-disabled-item-border);
}
}
.remove-single {
position: absolute;
right: 0;
top: 0;
font-size: 23px;
}
}

View File

@@ -1,6 +1,7 @@
/**
* selectize.bootstrap3.css (v0.12.1) - Bootstrap 3 Theme
* selectize.css (v0.13.3)
* Copyright (c) 20132015 Brian Reavis & contributors
* Copyright (c) 2020 Selectize Team & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
@@ -12,11 +13,12 @@
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
* @author Ris Adams <selectize@risadams.com>
*/
@import "selectize";
@selectize-fonts-family: inherit;
@selectize-font-family: inherit;
@selectize-font-size: inherit;
@selectize-line-height: @line-height-computed;
@@ -147,4 +149,4 @@
background: none;
.selectize-box-shadow(none);
.selectize-border-radius(0);
}
}

View File

@@ -187,6 +187,9 @@
-webkit-user-select: auto !important;
.selectize-box-shadow(none) !important;
&:focus { outline: none !important; }
&[placeholder] {
box-sizing: initial;
}
}
}
@@ -226,9 +229,16 @@
.selectize-border-radius(1px);
}
}
[data-selectable], .optgroup-header {
.option, .optgroup-header {
padding: @selectize-padding-dropdown-item-y @selectize-padding-dropdown-item-x;
}
.option, [data-disabled], [data-disabled] [data-selectable].option {
cursor: inherit;
opacity: 0.5;
}
[data-selectable].option {
opacity: 1;
}
.optgroup:first-child .optgroup-header {
border-top: 0 none;
}
@@ -253,6 +263,7 @@
overflow-y: auto;
overflow-x: hidden;
max-height: @selectize-max-height-dropdown;
-webkit-overflow-scrolling: touch;
}
.selectize-control.single .selectize-input {
@@ -293,12 +304,3 @@
opacity: @selectize-opacity-disabled;
background-color: @selectize-color-disabled;
}
.filter > .form-group .field {
.selectize-control {
.selectize-input {
font-size: @font-size-small;
padding: 4px 5px 0px;
}
}
}

View File

@@ -34,6 +34,12 @@ class Language{
private $data = array();
protected $defaultLabels = [
'nginx' => 'linux',
'apache' => 'linux',
'microsoft-iis' => 'windows',
];
public function __construct()
{
require_once 'SystemHelper.php';
@@ -118,19 +124,35 @@ class Language{
$serverOs = $this->getSystemHelper()->getOs();
$rewriteRules = $this->getSystemHelper()->getRewriteRules();
if (isset($i18n['options']['modRewriteInstruction'][$serverType][$serverOs])) {
$modRewriteInstruction = $i18n['options']['modRewriteInstruction'][$serverType][$serverOs];
preg_match_all('/\{(.*?)\}/', $modRewriteInstruction, $match);
if (isset($match[1])) {
foreach ($match[1] as $varName) {
if (isset($rewriteRules[$varName])) {
$modRewriteInstruction = str_replace('{'.$varName.'}', $rewriteRules[$varName], $modRewriteInstruction);
}
}
$label = $i18n['options']['modRewriteInstruction'][$serverType][$serverOs] ?? null;
if (!isset($label) && isset($this->defaultLabels[$serverType])) {
$defaultLabel = $this->defaultLabels[$serverType];
if (!isset($i18n['options']['modRewriteInstruction'][$serverType][$defaultLabel])) {
$defaultLangFile = 'install/core/i18n/' . $this->defaultLanguage . '/install.json';
$defaultData = $this->getLangData($defaultLangFile);
$i18n['options']['modRewriteInstruction'][$serverType][$defaultLabel] = $defaultData['options']['modRewriteInstruction'][$serverType][$defaultLabel];
}
$i18n['options']['modRewriteInstruction'][$serverType][$serverOs] = $modRewriteInstruction;
$label = $i18n['options']['modRewriteInstruction'][$serverType][$defaultLabel];
}
if (!$label) {
return;
}
preg_match_all('/\{(.*?)\}/', $label, $match);
if (isset($match[1])) {
foreach ($match[1] as $varName) {
if (isset($rewriteRules[$varName])) {
$label = str_replace('{'.$varName.'}', $rewriteRules[$varName], $label);
}
}
}
$i18n['options']['modRewriteInstruction'][$serverType][$serverOs] = $label;
}
}

View File

@@ -35,7 +35,7 @@ class SystemHelper extends \Espo\Core\Utils\System
protected $apiPath;
protected $modRewriteUrl = '/Metadata';
protected $modRewriteUrl = '/';
protected $writableDir = 'data';

View File

@@ -27,6 +27,20 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
$clearedCookieList = [
'auth-token-secret',
'auth-username',
'auth-token',
];
foreach ($clearedCookieList as $cookieName) {
if (!isset($_COOKIE[$cookieName])) {
continue;
}
setcookie($cookieName, null, -1, '/');
}
$config = $installer->getConfig();
$fields = array(

View File

@@ -30,14 +30,19 @@
return [
'apiPath' => '/api/v1',
'rewriteRules' => [
'APACHE1' => 'a2enmod rewrite
service apache2 restart',
'APACHE1' => 'sudo a2enmod rewrite
sudo service apache2 restart',
'APACHE2' => '&#60;Directory /PATH_TO_ESPO/&#62;
AllowOverride <b>All</b>
&#60;/Directory&#62;',
'APACHE3' => 'service apache2 restart',
'APACHE2_PATH1' => '/etc/apache2/sites-available/ESPO_VIRTUAL_HOST.conf',
'APACHE2_PATH2' => '/etc/apache2/apache2.conf',
'APACHE2_PATH3' => '/etc/httpd/conf/httpd.conf',
'APACHE3' => 'sudo service apache2 restart',
'APACHE4' => '# RewriteBase /',
'APACHE5' => 'RewriteBase {ESPO_PATH}{API_PATH}',
'WINDOWS_APACHE1' => 'LoadModule rewrite_module modules/mod_rewrite.so',
'NGINX_PATH' => '/etc/nginx/sites-available/YOUR_SITE',
'NGINX' => 'server {
# ...
@@ -88,5 +93,7 @@ service apache2 restart',
deny all;
}
}',
'APACHE_LINK' => 'https://www.espocrm.com/documentation/administration/apache-server-configuration/',
'NGINX_LINK' => 'https://www.espocrm.com/documentation/administration/nginx-server-configuration/',
],
];
];

View File

@@ -108,8 +108,8 @@
"mysqlSettingError": "EspoCRM изисква настройка MySQL \"{NAME}\", за да се настрои да {VALUE}",
"requiredMariadbVersion": "Вашият MariaDB версия не се поддържа от EspoCRM, моля обновете до MariaDB {} minVersion най-малко",
"Ajax failed": "Възникна неочаквана грешка",
"Bad init Permission": "Разрешението е отказано за \"{*}\" директория. Моля, избран 775 за \"{*}\" или просто изпълни тази команда в терминала <предварително> <б> {C} </ B> </ предварително> операция не е позволено? Предложения това: {ХСС}",
"permissionInstruction": "<br> Run тази команда в терминал: <предварително> <б> \"{C}\" </ B> </ предварително>",
"Bad init Permission": "Разрешението е отказано за \"{*}\" директория. Моля, избран 775 за \"{*}\" или просто изпълни тази команда в терминала <pre> <b> {C} </b> </pre> операция не е позволено? Предложения това: {ХСС}",
"permissionInstruction": "<br> Run тази команда в терминал: <pre> <b>\"{C}\"</b> </pre>",
"operationNotPermitted": "Операция не е позволено? Предложения това: <br> <br> {ХСС}"
},
"options": {
@@ -118,19 +118,18 @@
},
"modRewriteInstruction": {
"apache": {
"windows": "<br> <предварително> 1. Намерете httpd.conf файл (обикновено вие ще го намерите в папка, наречена конф, довереник или нещо в този дух) <br> 2. Вътре в httpd.conf файла разкоментирайте модули линия LoadModule rewrite_module / mod_rewrite.so (премахване на лирата \"#\" знак от в предната част на линията) <br> 3. Също така да намерите линията ClearModuleList се некоментирана след това намерете и се уверете, че mod_rewrite.c линията AddModule не е коментиран. </ Предварително>",
"linux": "<br> <br> 1. Активиране на \"mod_rewrite\". За това тече тези команди в терминал: <предварително> {APACHE1} </ предварително> <br> 2. Ако предишната стъпка не помогне, опитайте се да се даде възможност на .htaccess подкрепа. Добавяне / редактиране на настройките за конфигурация сървър (/etc/apache/apache2.conf, /etc/httpd/conf/httpd.conf): <предварително> {} apache2 </ предварително> След това стартирате тази команда в терминал: <предварително> {APACHE3} </ предварително> <br> 3. Ако предишната стъпка не помогне, опитайте да добавите пътя на RewriteBase. Отваряне на файл {} API_PATH .htaccess и да се замени следния ред: <предварително> {} APACHE4 </ предварително> Да <предварително> {} APACHE5 </ предварително> <br> За повече информация, моля посетете насоките <един HREF = \" https://www.espocrm.com/documentation/administration/apache-server-configuration/ \"целева =\" _ празно \">"
"windows": "<br> <pre> 1. Намерете httpd.conf файл (обикновено вие ще го намерите в папка, наречена конф, довереник или нещо в този дух) <br> 2. Вътре в httpd.conf файла разкоментирайте модули линия LoadModule rewrite_module / mod_rewrite.so (премахване на лирата \"#\" знак от в предната част на линията) <br> 3. Също така да намерите линията ClearModuleList се некоментирана след това намерете и се уверете, че mod_rewrite.c линията AddModule не е коментиран. </pre>",
"linux": "<br> <br> 1. Активиране на \"mod_rewrite\". За това тече тези команди в терминал: <pre>{APACHE1}</pre> <br> 2. Ако предишната стъпка не помогне, опитайте се да се даде възможност на .htaccess подкрепа. Добавяне / редактиране на настройките за конфигурация сървър (<code>{APACHE2_PATH1}</code>, <code>{APACHE2_PATH2}</code>, <code>{APACHE2_PATH3}</code>): <pre>{APACHE2}</pre> След това стартирате тази команда в терминал: <pre>{APACHE3}</pre> <br> 3. Ако предишната стъпка не помогне, опитайте да добавите пътя на RewriteBase. Отваряне на файл {API_PATH} .htaccess и да се замени следния ред: <pre>{APACHE4}</pre> Да <pre>{APACHE5}</pre> <br> За повече информация, моля посетете насоките <a href=\"{APACHE_LINK}\" target=\"_blank\">Apache server configuration for EspoCRM</a>.<br><br>"
},
"nginx": {
"linux": "<br> <предварително> {Nginx} </ предварително> <br> За повече информация посети насоките <един HREF = \"https://www.espocrm.com/documentation/administration/nginx-server-configuration/\" целева = \"_ празно\"> конфигурацията на сървъра Nginx за EspoCRM </a>. <br> <br>",
"windows": "<br> <предварително> {Nginx} </ предварително> <br> За повече информация посети насоките <един HREF = \"https://www.espocrm.com/documentation/administration/nginx-server-configuration/\" целева = \"_ празно\"> конфигурацията на сървъра Nginx за EspoCRM </a>. <br> <br>"
"linux": "<br> Добавете този код в Nginx сървър блок конфигурационния файл <code>{NGINX_PATH}</code> вътре в раздел \"сървър\": <pre>{NGINX}</pre> <br> За повече информация посети насоките <a href=\"{NGINX_LINK}\" target=\"_blank\">конфигурацията на сървъра Nginx за EspoCRM</a>. <br> <br>"
}
},
"modRewriteTitle": {
"apache": "API. Грешка: EspoCRM API е недостъпна <br> Възможни проблеми:. Увреждания \"mod_rewrite\" в Apache сървъра, инвалиди .htaccess подкрепа или RewriteBase въпрос <br> ли само необходимите стъпки. След всяка проверка стъпка, ако проблемът не бъде решен.",
"nginx": "API Грешка: EspoCRM API е недостъпна <br> Добавете този код в Nginx сървър блок конфигурационния файл (/ и т.н. / Nginx / сайтове-достъпни / YOUR_SITE) вътре в раздел \"сървър\".:",
"microsoft-iis": "API. Грешка: EspoCRM API е недостъпна <br> Възможен проблем: увреждания \"URL Rewrite\". Моля, проверете и се даде възможност на \"URL Rewrite\" Модул в IIS сървър",
"default": "API. Грешка: EspoCRM API е недостъпна <br> Възможен проблем: инвалиди Rewrite модул. Моля, проверете и се даде възможност на Rewrite модул в сървъра си (напр mod_rewrite в Apache) и .htaccess подкрепа."
"apache": "<h3>API. Грешка: EspoCRM API е недостъпна</h3> <br> Ли само необходимите стъпки. След всяка проверка стъпка, ако проблемът не бъде решен.",
"nginx": "<h3>API Грешка: EspoCRM API е недостъпна</h3>",
"microsoft-iis": "<h3>API. Грешка: EspoCRM API е недостъпна</h3> <br> Възможен проблем: увреждания \"URL Rewrite\". Моля, проверете и се даде възможност на \"URL Rewrite\" Модул в IIS сървър",
"default": "<h3>API. Грешка: EspoCRM API е недостъпна</h3> <br> Възможен проблем: инвалиди Rewrite модул. Моля, проверете и се даде възможност на Rewrite модул в сървъра си (напр mod_rewrite в Apache) и .htaccess подкрепа."
}
},
"systemRequirements": {
@@ -141,4 +140,4 @@
"readable": "четлив",
"requiredMariadbVersion": "MariaDB версия"
}
}
}

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