mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-04 20:07:02 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb6f9b602f | ||
|
|
cf508a540e | ||
|
|
5278e3bf06 | ||
|
|
5763f5b58e | ||
|
|
0666880786 | ||
|
|
e0113388d2 | ||
|
|
384f28ecae | ||
|
|
d7596c208c | ||
|
|
6d1ab5870f | ||
|
|
73dbfa38ec | ||
|
|
4adb068699 | ||
|
|
3e4c738ab1 | ||
|
|
cd92e4fcd8 | ||
|
|
fba191f22c | ||
|
|
8874c8827a | ||
|
|
34529a8ed9 | ||
|
|
8fd44acae2 | ||
|
|
4dd540ffc7 | ||
|
|
9c20116c9b | ||
|
|
926410d58f | ||
|
|
c64a107ad9 | ||
|
|
5dcd25946b | ||
|
|
d12865bbcb | ||
|
|
3fed415437 | ||
|
|
a03a13d3b9 | ||
|
|
e625951831 | ||
|
|
f533c68c9b | ||
|
|
2420746f1b | ||
|
|
2dfd00dd2e | ||
|
|
e043bb48e9 | ||
|
|
9aaef9d957 | ||
|
|
87449aae67 | ||
|
|
191d064fe1 | ||
|
|
dcb3e2feaf | ||
|
|
da9a423e59 | ||
|
|
3be4510e63 | ||
|
|
a6bb5a239b | ||
|
|
a5fb42609b | ||
|
|
40c2c1718e | ||
|
|
aeecfd63da | ||
|
|
1da1e6da9b | ||
|
|
bf9f23ebdd | ||
|
|
7beb4f8d83 | ||
|
|
991b859643 | ||
|
|
9bd74e08db | ||
|
|
cfdf65025d | ||
|
|
bf471e654c | ||
|
|
5feee1cf55 | ||
|
|
6af6fc017b | ||
|
|
8ee9a792fc | ||
|
|
9ef1c5928f | ||
|
|
64c933e365 | ||
|
|
32055f3d6e | ||
|
|
804acae44b | ||
|
|
6b3f37c00e | ||
|
|
4bdc4878cd | ||
|
|
625d2bc128 | ||
|
|
0af5bb1b4b | ||
|
|
7a0d59357c | ||
|
|
adbb46d02c | ||
|
|
ac3884179e | ||
|
|
7cf1af188d | ||
|
|
21b695e4ef | ||
|
|
fbda66defc |
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -8,4 +8,9 @@
|
||||
*.tpl text eol=crlf
|
||||
*.html text eol=crlf
|
||||
|
||||
bin/command text eol=lf
|
||||
|
||||
.gitattributes text eol=crlf
|
||||
.gitignore text eol=crlf
|
||||
|
||||
*.png binary
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2022 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\Classes\FieldValidators\Attachment;
|
||||
|
||||
use Espo\Classes\FieldValidators\LinkParentType;
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Related extends LinkParentType
|
||||
{
|
||||
public function checkValid(Entity $entity, string $field): bool
|
||||
{
|
||||
$typeValue = $entity->get($field . 'Type');
|
||||
|
||||
if ($typeValue === 'TemplateManager') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::checkValid($entity, $field);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ use Espo\Core\Exceptions\HasBody;
|
||||
use Espo\Core\{
|
||||
Api\Request,
|
||||
Api\Response,
|
||||
Exceptions\HasLogMessage,
|
||||
Utils\Log,
|
||||
Utils\Config,
|
||||
};
|
||||
@@ -121,14 +122,16 @@ class ErrorOutput
|
||||
$message = $exception->getMessage();
|
||||
$statusCode = $exception->getCode();
|
||||
|
||||
if ($exception instanceof HasLogMessage) {
|
||||
$message = $exception->getLogMessage();
|
||||
}
|
||||
|
||||
if ($route) {
|
||||
$this->processRoute($route, $request, $exception);
|
||||
}
|
||||
|
||||
$logLevel = 'error';
|
||||
|
||||
$messageLineFile = null;
|
||||
|
||||
$messageLineFile =
|
||||
'line: ' . $exception->getLine() . ', ' .
|
||||
'file: ' . $exception->getFile();
|
||||
@@ -176,10 +179,10 @@ class ErrorOutput
|
||||
}
|
||||
|
||||
if ($toPrintBody) {
|
||||
$codeDesription = $this->getCodeDescription($statusCode);
|
||||
$codeDescription = $this->getCodeDescription($statusCode);
|
||||
|
||||
$statusText = isset($codeDesription) ?
|
||||
$statusCode . ' '. $codeDesription :
|
||||
$statusText = isset($codeDescription) ?
|
||||
$statusCode . ' '. $codeDescription :
|
||||
'HTTP ' . $statusCode;
|
||||
|
||||
if ($message) {
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Espo\Core\EntryPoint;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
|
||||
use Espo\Core\{
|
||||
Exceptions\NotFoundSilent,
|
||||
InjectableFactory,
|
||||
Utils\ClassFinder,
|
||||
Api\Request,
|
||||
@@ -44,7 +45,6 @@ use Espo\Core\{
|
||||
class EntryPointManager
|
||||
{
|
||||
private InjectableFactory $injectableFactory;
|
||||
|
||||
private ClassFinder $classFinder;
|
||||
|
||||
public function __construct(InjectableFactory $injectableFactory, ClassFinder $classFinder)
|
||||
@@ -53,12 +53,15 @@ class EntryPointManager
|
||||
$this->classFinder = $classFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function checkAuthRequired(string $name): bool
|
||||
{
|
||||
$className = $this->getClassName($name);
|
||||
|
||||
if (!$className) {
|
||||
throw new NotFound("Entry point '{$name}' not found.");
|
||||
throw new NotFoundSilent("Entry point '{$name}' not found.");
|
||||
}
|
||||
|
||||
$noAuth = false;
|
||||
@@ -75,23 +78,29 @@ class EntryPointManager
|
||||
return $className::$authRequired ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function checkNotStrictAuth(string $name): bool
|
||||
{
|
||||
$className = $this->getClassName($name);
|
||||
|
||||
if (!$className) {
|
||||
throw new NotFound("Entry point '{$name}' not found.");
|
||||
throw new NotFoundSilent("Entry point '{$name}' not found.");
|
||||
}
|
||||
|
||||
return $className::$notStrictAuth ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function run(string $name, Request $request, Response $response): void
|
||||
{
|
||||
$className = $this->getClassName($name);
|
||||
|
||||
if (!$className) {
|
||||
throw new NotFound("Entry point '{$name}' not found.");
|
||||
throw new NotFoundSilent("Entry point '{$name}' not found.");
|
||||
}
|
||||
|
||||
$entryPoint = $this->injectableFactory->create($className);
|
||||
|
||||
@@ -33,6 +33,7 @@ use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Application\Runner\Params as RunnerParams;
|
||||
use Espo\Core\EntryPoint\EntryPointManager;
|
||||
use Espo\Core\ApplicationUser;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Portal\Application as PortalApplication;
|
||||
use Espo\Core\Authentication\AuthenticationFactory;
|
||||
use Espo\Core\Authentication\AuthToken\Manager as AuthTokenManager;
|
||||
@@ -89,7 +90,7 @@ class Starter
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws \Espo\Core\Exceptions\NotFound
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function start(?string $entryPoint = null, bool $final = false): void
|
||||
{
|
||||
@@ -110,8 +111,19 @@ class Starter
|
||||
throw new BadRequest("No 'entryPoint' param.");
|
||||
}
|
||||
|
||||
$authRequired = $this->entryPointManager->checkAuthRequired($entryPoint);
|
||||
$authNotStrict = $this->entryPointManager->checkNotStrictAuth($entryPoint);
|
||||
$responseWrapped = new ResponseWrapper(new Response());
|
||||
|
||||
try {
|
||||
$authRequired = $this->entryPointManager->checkAuthRequired($entryPoint);
|
||||
$authNotStrict = $this->entryPointManager->checkNotStrictAuth($entryPoint);
|
||||
}
|
||||
catch (NotFound $exception) {
|
||||
$this->errorOutput->processWithBodyPrinting($requestWrapped, $responseWrapped, $exception);
|
||||
|
||||
(new ResponseEmitter())->emit($responseWrapped->getResponse());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($authRequired && !$authNotStrict && !$final) {
|
||||
$portalId = $this->detectPortalId($requestWrapped);
|
||||
@@ -123,8 +135,6 @@ class Starter
|
||||
}
|
||||
}
|
||||
|
||||
$responseWrapped = new ResponseWrapper(new Response());
|
||||
|
||||
$this->processRequest(
|
||||
$entryPoint,
|
||||
$requestWrapped,
|
||||
|
||||
35
application/Espo/Core/Exceptions/HasLogMessage.php
Normal file
35
application/Espo/Core/Exceptions/HasLogMessage.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2022 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\Exceptions;
|
||||
|
||||
interface HasLogMessage
|
||||
{
|
||||
public function getLogMessage(): string;
|
||||
}
|
||||
@@ -32,10 +32,11 @@ namespace Espo\Core\FieldValidation\Exceptions;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error\Body;
|
||||
|
||||
use Espo\Core\Exceptions\HasLogMessage;
|
||||
use Espo\Core\FieldValidation\Failure;
|
||||
use LogicException;
|
||||
|
||||
class ValidationError extends BadRequest
|
||||
class ValidationError extends BadRequest implements HasLogMessage
|
||||
{
|
||||
private ?Failure $failure = null;
|
||||
|
||||
@@ -75,4 +76,18 @@ class ValidationError extends BadRequest
|
||||
|
||||
return $this->failure;
|
||||
}
|
||||
|
||||
public function getLogMessage(): string
|
||||
{
|
||||
if (!$this->failure) {
|
||||
return "Field validation failure.";
|
||||
}
|
||||
|
||||
$entityType = $this->failure->getEntityType();
|
||||
$field = $this->failure->getField();
|
||||
$type = $this->failure->getType();
|
||||
|
||||
return "Field validation failure; " .
|
||||
"entityType: {$entityType}, field: {$field}, type: {$type}.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,8 @@ class Htmlizer
|
||||
string $template,
|
||||
?string $cacheId = null,
|
||||
?array $additionalData = null,
|
||||
bool $skipLinks = false
|
||||
bool $skipLinks = false,
|
||||
bool $skipInlineAttachmentHandling = false
|
||||
): string {
|
||||
|
||||
$template = str_replace('<tcpdf ', '', $template);
|
||||
@@ -188,9 +189,11 @@ class Htmlizer
|
||||
|
||||
$html = $renderer($data);
|
||||
|
||||
$html = str_replace('?entryPoint=attachment&', '?entryPoint=attachment&', $html);
|
||||
if (!$skipInlineAttachmentHandling) {
|
||||
$html = str_replace('?entryPoint=attachment&', '?entryPoint=attachment&', $html);
|
||||
}
|
||||
|
||||
if ($this->entityManager) {
|
||||
if (!$skipInlineAttachmentHandling && $this->entityManager) {
|
||||
/** @var string $html */
|
||||
$html = preg_replace_callback(
|
||||
'/\?entryPoint=attachment\&id=([A-Za-z0-9]*)/',
|
||||
|
||||
@@ -39,25 +39,16 @@ use LogicException;
|
||||
|
||||
class TemplateRenderer
|
||||
{
|
||||
/**
|
||||
* @var ?array<string,mixed>
|
||||
*/
|
||||
/** @var ?array<string,mixed> */
|
||||
private $data = null;
|
||||
|
||||
private ?User $user = null;
|
||||
|
||||
private ?Entity $entity = null;
|
||||
|
||||
private bool $skipRelations = false;
|
||||
|
||||
private bool $skipInlineAttachmentHandling = false;
|
||||
private bool $applyAcl = false;
|
||||
|
||||
private bool $useUserTimezone = false;
|
||||
|
||||
private HtmlizerFactory $htmlizerFactory;
|
||||
|
||||
private ApplicationState $applicationState;
|
||||
|
||||
private ?string $template = null;
|
||||
|
||||
public function __construct(HtmlizerFactory $htmlizerFactory, ApplicationState $applicationState)
|
||||
@@ -107,6 +98,12 @@ class TemplateRenderer
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSkipInlineAttachmentHandling(bool $skipInlineAttachmentHandling = true): self
|
||||
{
|
||||
$this->skipInlineAttachmentHandling = $skipInlineAttachmentHandling;
|
||||
|
||||
return $this;
|
||||
}
|
||||
public function setApplyAcl(bool $applyAcl = true): self
|
||||
{
|
||||
$this->applyAcl = $applyAcl;
|
||||
@@ -149,7 +146,8 @@ class TemplateRenderer
|
||||
$template,
|
||||
null,
|
||||
$this->data,
|
||||
$this->skipRelations
|
||||
$this->skipRelations,
|
||||
$this->skipInlineAttachmentHandling
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -305,8 +305,17 @@ class Util
|
||||
*/
|
||||
private static function arrayToObjectInternal($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return (object) array_map(fn($v) => self::arrayToObjectInternal($v), $value);
|
||||
if (!is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// @todo Change to `array_is_list` when PHP 8.1 is the min supported.
|
||||
$isList = $value === array_values($value);
|
||||
|
||||
$value = array_map(fn($v) => self::arrayToObjectInternal($v), $value);
|
||||
|
||||
if (!$isList) {
|
||||
$value = (object) $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
@@ -75,10 +75,14 @@ class Avatar extends Image implements Di\MetadataAware
|
||||
$sum += ord($hash[$i]);
|
||||
}
|
||||
|
||||
$x = intval($sum % 128) + 1;
|
||||
$x = $sum % 128 + 1;
|
||||
|
||||
$colorList = $this->metadata->get(['app', 'avatars', 'colorList']) ?? $this->colorList;
|
||||
|
||||
if ($x === 128) {
|
||||
$x--;
|
||||
}
|
||||
|
||||
$index = intval($x * count($colorList) / 128);
|
||||
|
||||
return $colorList[$index];
|
||||
|
||||
@@ -174,6 +174,7 @@ class Invitations
|
||||
$subjectTpl,
|
||||
'invitation-email-subject-' . $entity->getEntityType(),
|
||||
$data,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
@@ -182,7 +183,8 @@ class Invitations
|
||||
$bodyTpl,
|
||||
'invitation-email-body-' . $entity->getEntityType(),
|
||||
$data,
|
||||
false
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
$email->set('subject', $subject);
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
"scope": "Opportunity",
|
||||
"field": "leadSource"
|
||||
},
|
||||
"Account.options.industry": {
|
||||
"scope": "Lead",
|
||||
"field": "industry"
|
||||
},
|
||||
"Meeting": {
|
||||
"scope": "Call"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"entityDefs.Lead.fields.source.options": {
|
||||
"scope": "Opportunity",
|
||||
"field": "leadSource"
|
||||
},
|
||||
"entityDefs.Account.fields.industry.options": {
|
||||
"scope": "Lead",
|
||||
"field": "industry"
|
||||
}
|
||||
},
|
||||
"frontendHiddenPathList": [
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
"type": "enum",
|
||||
"view": "crm:views/lead/fields/industry",
|
||||
"customizationOptionsDisabled": true,
|
||||
"optionsPath": "entityDefs.Account.fields.industry.options",
|
||||
"translation": "Account.options.industry",
|
||||
"default": "",
|
||||
"isSorted": true
|
||||
},
|
||||
|
||||
@@ -147,7 +147,7 @@ class RelationDefs
|
||||
{
|
||||
if (!$this->hasForeignEntityType()) {
|
||||
throw new RuntimeException(
|
||||
"No 'entity' paramater defined in the relation '{$this->name}'."
|
||||
"No 'entity' parameter defined in the relation '{$this->name}'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ class RelationDefs
|
||||
{
|
||||
if (!$this->hasForeignRelationName()) {
|
||||
throw new RuntimeException(
|
||||
"No 'foreign' paramater defined in the relation '{$this->name}'."
|
||||
"No 'foreign' parameter defined in the relation '{$this->name}'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ class RelationDefs
|
||||
{
|
||||
if (!$this->hasForeignKey()) {
|
||||
throw new RuntimeException(
|
||||
"No 'foreignKey' paramater defined in the relation '{$this->name}'."
|
||||
"No 'foreignKey' parameter defined in the relation '{$this->name}'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ class RelationDefs
|
||||
{
|
||||
if (!$this->hasKey()) {
|
||||
throw new RuntimeException(
|
||||
"No 'key' paramater defined in the relation '{$this->name}'."
|
||||
"No 'key' parameter defined in the relation '{$this->name}'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ class RelationDefs
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a mid key is defined. For Many-to-Many relationships only.
|
||||
* Whether a mid-key is defined. For Many-to-Many relationships only.
|
||||
*/
|
||||
public function hasMidKey(): bool
|
||||
{
|
||||
@@ -232,14 +232,14 @@ class RelationDefs
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a mid key. For Many-to-Many relationships only.
|
||||
* Get a mid-key. For Many-to-Many relationships only.
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getMidKey(): string
|
||||
{
|
||||
if (!$this->hasMidKey()) {
|
||||
throw new RuntimeException(
|
||||
"No 'midKey' paramater defined in the relation '{$this->name}'."
|
||||
"No 'midKey' parameter defined in the relation '{$this->name}'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ class RelationDefs
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a foreign mid key is defined. For Many-to-Many relationships only.
|
||||
* Whether a foreign mid-key is defined. For Many-to-Many relationships only.
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function hasForeignMidKey(): bool
|
||||
@@ -256,14 +256,14 @@ class RelationDefs
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a foreign mid key. For Many-to-Many relationships only.
|
||||
* Get a foreign mid-key. For Many-to-Many relationships only.
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getForeignMidKey(): string
|
||||
{
|
||||
if (!$this->hasForeignMidKey()) {
|
||||
throw new RuntimeException(
|
||||
"No 'foreignMidKey' paramater defined in the relation '{$this->name}'."
|
||||
"No 'foreignMidKey' parameter defined in the relation '{$this->name}'."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ class RelationDefs
|
||||
{
|
||||
if (!$this->hasRelationshipName()) {
|
||||
throw new RuntimeException(
|
||||
"No 'relationName' paramater defined in the relation '{$this->name}'."
|
||||
"No 'relationName' parameter defined in the relation '{$this->name}'."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -271,7 +271,8 @@
|
||||
"Field": "Field",
|
||||
"Resolution": "Resolution",
|
||||
"Resolve Conflict": "Resolve Conflict",
|
||||
"Download": "Download"
|
||||
"Download": "Download",
|
||||
"Global Search": "Global Search"
|
||||
},
|
||||
"messages": {
|
||||
"pleaseWait": "Please wait...",
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"related": {
|
||||
"type": "linkParent",
|
||||
"noLoad": true,
|
||||
"view": "views/attachment/fields/parent"
|
||||
"view": "views/attachment/fields/parent",
|
||||
"validatorClassName": "Espo\\Classes\\FieldValidators\\Attachment\\Related"
|
||||
},
|
||||
"sourceId": {
|
||||
"type": "varchar",
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"name": {
|
||||
"type": "varchar",
|
||||
"required": true,
|
||||
"trim": true
|
||||
"trim": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"subject": {
|
||||
"type": "varchar",
|
||||
@@ -19,7 +20,8 @@
|
||||
"notStorable": true,
|
||||
"textFilterDisabled": true,
|
||||
"layoutFiltersDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"fromAddress": {
|
||||
"type": "varchar",
|
||||
@@ -27,15 +29,18 @@
|
||||
"notStorable": true,
|
||||
"textFilterDisabled": true,
|
||||
"layoutFiltersDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"fromString": {
|
||||
"type": "varchar",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"replyToString": {
|
||||
"type": "varchar",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"replyToName": {
|
||||
"type": "varchar",
|
||||
@@ -43,7 +48,8 @@
|
||||
"notStorable": true,
|
||||
"textFilterDisabled": true,
|
||||
"layoutFiltersDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"replyToAddress": {
|
||||
"type": "varchar",
|
||||
@@ -51,7 +57,8 @@
|
||||
"notStorable": true,
|
||||
"textFilterDisabled": true,
|
||||
"layoutFiltersDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"addressNameMap": {
|
||||
"type": "jsonObject",
|
||||
@@ -65,7 +72,8 @@
|
||||
"required": true,
|
||||
"view": "views/email/fields/from-address-varchar",
|
||||
"textFilterDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"to": {
|
||||
"type": "varchar",
|
||||
@@ -74,28 +82,32 @@
|
||||
"view": "views/email/fields/email-address-varchar",
|
||||
"textFilterDisabled": true,
|
||||
"validatorClassName": "Espo\\Classes\\FieldValidators\\Email\\EmailAddresses",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"cc": {
|
||||
"type": "varchar",
|
||||
"notStorable": true,
|
||||
"view": "views/email/fields/email-address-varchar",
|
||||
"customizationDisabled": true,
|
||||
"textFilterDisabled": true
|
||||
"textFilterDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"bcc": {
|
||||
"type": "varchar",
|
||||
"notStorable": true,
|
||||
"view": "views/email/fields/email-address-varchar",
|
||||
"customizationDisabled": true,
|
||||
"textFilterDisabled": true
|
||||
"textFilterDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"replyTo": {
|
||||
"type": "varchar",
|
||||
"notStorable": true,
|
||||
"view": "views/email/fields/email-address-varchar",
|
||||
"textFilterDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"personStringData": {
|
||||
"type": "varchar",
|
||||
@@ -151,7 +163,8 @@
|
||||
"notStorable": true,
|
||||
"default": null,
|
||||
"textFilterDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"isUsers": {
|
||||
"type": "bool",
|
||||
@@ -200,44 +213,52 @@
|
||||
"maxLength": 300,
|
||||
"readOnly": true,
|
||||
"textFilterDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "base",
|
||||
"notStorable": true,
|
||||
"view": "views/email/fields/email-address",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"fromEmailAddress": {
|
||||
"type": "link",
|
||||
"view": "views/email/fields/from-email-address",
|
||||
"textFilterDisabled": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"toEmailAddresses": {
|
||||
"type": "linkMultiple",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"ccEmailAddresses": {
|
||||
"type": "linkMultiple",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"replyToEmailAddresses": {
|
||||
"type": "linkMultiple",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"bodyPlain": {
|
||||
"type": "text",
|
||||
"seeMoreDisabled": true,
|
||||
"clientReadOnly": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"body": {
|
||||
"type": "wysiwyg",
|
||||
"view": "views/email/fields/body",
|
||||
"attachmentField": "attachments",
|
||||
"useIframe": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"isHtml": {
|
||||
"type": "bool",
|
||||
@@ -262,12 +283,14 @@
|
||||
},
|
||||
"attachments": {
|
||||
"type": "attachmentMultiple",
|
||||
"sourceList": ["Document"]
|
||||
"sourceList": ["Document"],
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"hasAttachment": {
|
||||
"type": "bool",
|
||||
"readOnly": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"parent": {
|
||||
"type": "linkParent",
|
||||
@@ -280,7 +303,8 @@
|
||||
},
|
||||
"dateSent": {
|
||||
"type": "datetime",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"deliveryDate": {
|
||||
"type": "datetime",
|
||||
@@ -412,33 +436,38 @@
|
||||
"layoutListDisabled": true,
|
||||
"layoutMassUpdateDisabled": true,
|
||||
"dbType": "text",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"icsEventData": {
|
||||
"type": "jsonObject",
|
||||
"readOnly": true,
|
||||
"directAccessDisabled": true,
|
||||
"notStorable": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"icsEventUid": {
|
||||
"type": "varchar",
|
||||
"maxLength": 255,
|
||||
"index": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"icsEventDateStart": {
|
||||
"type": "datetimeOptional",
|
||||
"readOnly": true,
|
||||
"notStorable": true,
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"createEvent": {
|
||||
"type": "base",
|
||||
"disabled": true,
|
||||
"notStorable": true,
|
||||
"view": "views/email/fields/create-event",
|
||||
"customizationDisabled": true
|
||||
"customizationDisabled": true,
|
||||
"layoutAvailabilityList": []
|
||||
},
|
||||
"createdEvent": {
|
||||
"type": "linkParent",
|
||||
@@ -446,7 +475,8 @@
|
||||
"view": "views/email/fields/created-event",
|
||||
"fieldManagerParamList": [
|
||||
"tooltipText"
|
||||
]
|
||||
],
|
||||
"layoutAvailabilityList": []
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
@@ -551,7 +581,8 @@
|
||||
"type": "varchar",
|
||||
"len": "4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"bccEmailAddresses": {
|
||||
"type": "hasMany",
|
||||
@@ -565,7 +596,8 @@
|
||||
"type": "varchar",
|
||||
"len": "4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layoutDefaultSidePanelDisabled": true
|
||||
},
|
||||
"replyToEmailAddresses": {
|
||||
"type": "hasMany",
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
"monitoredFolders": {
|
||||
"type": "array",
|
||||
"default": ["INBOX"],
|
||||
"options": ["INBOX"],
|
||||
"view": "views/email-account/fields/folders",
|
||||
"displayAsList": true,
|
||||
"noEmptyString": true,
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
"monitoredFolders": {
|
||||
"type": "array",
|
||||
"default": ["INBOX"],
|
||||
"options": ["INBOX"],
|
||||
"view": "views/inbound-email/fields/folders",
|
||||
"displayAsList": true,
|
||||
"noEmptyString": true,
|
||||
|
||||
@@ -68,8 +68,9 @@
|
||||
},
|
||||
"leadSource": {
|
||||
"type": "enum",
|
||||
"view": "crm:views/opportunity/fields/lead-source",
|
||||
"customizationOptionsDisabled": true,
|
||||
"optionsPath": "entityDefs.Lead.fields.source.options",
|
||||
"translation": "Lead.options.source",
|
||||
"default": "Web Site"
|
||||
},
|
||||
"apiKey": {
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"assignmentNotificatorClassName": "Espo\\Classes\\AssignmentNotificators\\Email",
|
||||
"readLoaderClassNameList": [
|
||||
"Espo\\Classes\\FieldProcessing\\Email\\AddressDataLoader",
|
||||
"Espo\\Classes\\FieldProcessing\\Email\\UserColumnsLoader"
|
||||
],
|
||||
"listLoaderClassNameList": [
|
||||
"Espo\\Classes\\FieldProcessing\\Email\\StringDataLoader"
|
||||
],
|
||||
"massActions": {
|
||||
"update": {
|
||||
"allowed": true
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"chartColorAlternativeList": ["#7492cc", "#c29c4a", "#a1404a", "#6a5f96", "#b07e53"],
|
||||
"calendarColors": {
|
||||
"": "#a58dc7a0",
|
||||
"Meeting": "#6680b3",
|
||||
"Meeting": "#697da5",
|
||||
"Call": "#a1404a",
|
||||
"Task": "#5d8a55"
|
||||
},
|
||||
|
||||
@@ -158,7 +158,7 @@ class Attachment extends Record
|
||||
unset($data->parentId);
|
||||
unset($data->relatedId);
|
||||
|
||||
$isBeingUploaded = (bool) $data->isBeingUploaded;
|
||||
$isBeingUploaded = (bool) ($data->isBeingUploaded ?? false);
|
||||
|
||||
$contents = '';
|
||||
|
||||
@@ -283,6 +283,18 @@ class Attachment extends Record
|
||||
}
|
||||
}
|
||||
|
||||
private function getFieldType(AttachmentEntity $attachment): ?string
|
||||
{
|
||||
$field = $attachment->getTargetField();
|
||||
$entityType = $attachment->getParentType() ?? $attachment->getRelatedType();
|
||||
|
||||
if (!$field || !$entityType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
@@ -295,10 +307,8 @@ class Attachment extends Record
|
||||
return;
|
||||
}
|
||||
|
||||
$fieldType = $this->metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
|
||||
if (
|
||||
$fieldType === self::FIELD_TYPE_IMAGE ||
|
||||
$this->getFieldType($attachment) === self::FIELD_TYPE_IMAGE ||
|
||||
$attachment->getRole() === AttachmentEntity::ROLE_INLINE_ATTACHMENT
|
||||
) {
|
||||
$this->checkAttachmentTypeImage($attachment);
|
||||
@@ -306,7 +316,7 @@ class Attachment extends Record
|
||||
return;
|
||||
}
|
||||
|
||||
$extension = self::getFileExtension($attachment) ?? '';
|
||||
$extension = strtolower(self::getFileExtension($attachment) ?? '');
|
||||
|
||||
$mimeType = $this->getMimeTypeUtil()->getMimeTypeByExtension($extension) ??
|
||||
$attachment->getType();
|
||||
@@ -759,13 +769,15 @@ class Attachment extends Record
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->checkAttachmentTypeImage($attachment, $filePath);
|
||||
}
|
||||
catch (Forbidden $e) {
|
||||
$this->entityManager->removeEntity($attachment);
|
||||
if ($this->getFieldType($attachment) === self::FIELD_TYPE_IMAGE) {
|
||||
try {
|
||||
$this->checkAttachmentTypeImage($attachment, $filePath);
|
||||
}
|
||||
catch (Forbidden $e) {
|
||||
$this->entityManager->removeEntity($attachment);
|
||||
|
||||
throw new ForbiddenSilent($e->getMessage());
|
||||
throw new ForbiddenSilent($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$attachment->set('isBeingUploaded', false);
|
||||
|
||||
@@ -730,6 +730,12 @@ class LeadCapture
|
||||
$lead->set('campaignId', $leadCapture->getCampaignId());
|
||||
}
|
||||
|
||||
$teamId = $leadCapture->getTargetTeamId();
|
||||
|
||||
if ($teamId) {
|
||||
$lead->addLinkMultipleId('teams', $teamId);
|
||||
}
|
||||
|
||||
// Skipping the 'required' validation.
|
||||
$validationParams = FieldValidationParams::create()->withTypeSkipFieldList('required', $fieldList);
|
||||
|
||||
|
||||
@@ -591,6 +591,7 @@ define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], function (D
|
||||
timezone: this.getDateTime().timeZone,
|
||||
longPressDelay: 300,
|
||||
eventColor: this.colors[''],
|
||||
nowIndicator: true,
|
||||
windowResize: () => {
|
||||
this.adjustSize();
|
||||
},
|
||||
|
||||
@@ -56,19 +56,24 @@ define('crm:views/campaign/record/detail-bottom', 'views/record/detail-bottom',
|
||||
index: -1,
|
||||
});
|
||||
|
||||
this.listenTo(this.model, 'change', function () {
|
||||
this.listenTo(this.model, 'change', () => {
|
||||
this.manageMassEmails();
|
||||
}, this);
|
||||
});
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
Dep.prototype.setupPanels.call(this);
|
||||
Dep.prototype.afterRender.call(this);
|
||||
|
||||
this.manageMassEmails();
|
||||
},
|
||||
|
||||
manageMassEmails: function () {
|
||||
var parentView = this.getParentView();
|
||||
if (!parentView) return;
|
||||
|
||||
if (!parentView) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (~['Email', 'Newsletter'].indexOf(this.model.get('type'))) {
|
||||
parentView.showPanel('massEmails');
|
||||
parentView.showPanel('trackingUrls');
|
||||
|
||||
@@ -26,17 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('crm:views/lead/fields/industry', 'views/fields/enum', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setup: function () {
|
||||
this.params.options = this.getMetadata().get('entityDefs.Account.fields.industry.options');
|
||||
this.params.translation = 'Account.options.industry';
|
||||
|
||||
Dep.prototype.setup.call(this);
|
||||
}
|
||||
|
||||
});
|
||||
define('crm:views/lead/fields/industry', ['views/fields/enum'], function (Dep) {
|
||||
|
||||
return Dep.extend({});
|
||||
});
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
<h4>{{{title}}}</h4>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="button-container">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" data-action="save">{{translate 'Save'}}</button>
|
||||
<button class="btn btn-default" data-action="cancel">{{translate 'Cancel'}}</button>
|
||||
<button class="btn btn-default" data-action="resetToDefault">{{translate 'Reset to Default' scope='Admin'}}</button>
|
||||
<button class="btn btn-primary btn-xs-wide" data-action="save">{{translate 'Save'}}</button>
|
||||
<button class="btn btn-default btn-xs-wide" data-action="cancel">{{translate 'Cancel'}}</button>
|
||||
<button class="btn btn-default btn-xs-wide" data-action="resetToDefault"
|
||||
>{{translate 'Reset to Default' scope='Admin'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if hasSubject}}
|
||||
<div class="subject-field">{{{subjectField}}}</div>
|
||||
{{/if}}
|
||||
<div class="body-field">{{{bodyField}}}</div>
|
||||
<div class="body-field">{{{bodyField}}}</div>
|
||||
|
||||
6
client/res/templates/fields/array/list-link.tpl
Normal file
6
client/res/templates/fields/array/list-link.tpl
Normal file
@@ -0,0 +1,6 @@
|
||||
<a
|
||||
href="#{{scope}}/view/{{model.id}}"
|
||||
class="link"
|
||||
data-id="{{model.id}}"
|
||||
title="{{value}}"
|
||||
>{{#if value}}{{{value}}}{{else}}{{translate 'None'}}{{/if}}</a>
|
||||
@@ -970,7 +970,7 @@ function (
|
||||
resolve(options);
|
||||
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
this.requestUserData(data => {
|
||||
options = data;
|
||||
|
||||
@@ -766,7 +766,7 @@
|
||||
return new Promise((resolve, reject) => {
|
||||
this.require(
|
||||
subject,
|
||||
() => resolve(),
|
||||
(...args) => resolve(...args),
|
||||
() => reject()
|
||||
);
|
||||
});
|
||||
|
||||
@@ -599,6 +599,8 @@ define('utils', [], function () {
|
||||
getKeyFromKeyEvent: function (e) {
|
||||
let key = e.code;
|
||||
|
||||
key = keyMap[key] || key;
|
||||
|
||||
if (e.shiftKey) {
|
||||
key = 'Shift+' + key;
|
||||
}
|
||||
@@ -615,6 +617,10 @@ define('utils', [], function () {
|
||||
},
|
||||
};
|
||||
|
||||
const keyMap = {
|
||||
'NumpadEnter': 'Enter',
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use `Espo.Utils`.
|
||||
*/
|
||||
|
||||
@@ -437,7 +437,7 @@ function (marked, DOMPurify, /** typeof Handlebars */Handlebars) {
|
||||
return value.indexOf(name) !== -1;
|
||||
}
|
||||
|
||||
return value === name;
|
||||
return value === name || !value && !name;
|
||||
};
|
||||
|
||||
options.hash = options.hash || {};
|
||||
|
||||
@@ -254,23 +254,35 @@ define('views/admin/layouts/bottom-panels-detail', ['views/admin/layouts/side-pa
|
||||
},
|
||||
|
||||
onDrop: function () {
|
||||
let tabBreakIndex = 0;
|
||||
let tabBreakIndex = -1;
|
||||
|
||||
let $tabBreak = null;
|
||||
|
||||
this.$el.find('ul.enabled').children().each((i, li) => {
|
||||
let $li = $(li);
|
||||
|
||||
let name = $li.attr('data-name');
|
||||
|
||||
if (this.isTabName(name)) {
|
||||
if (name === this.TAB_BREAK_KEY) {
|
||||
$tabBreak = $li.clone();
|
||||
if (name !== this.TAB_BREAK_KEY) {
|
||||
let itemIndex = parseInt(name.split('_')[2]);
|
||||
|
||||
$li.attr('data-name', this.TAB_BREAK_KEY.slice(0, -3) + tabBreakIndex);
|
||||
if (itemIndex > tabBreakIndex) {
|
||||
tabBreakIndex = itemIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tabBreakIndex++;
|
||||
tabBreakIndex++;
|
||||
|
||||
this.$el.find('ul.enabled').children().each((i, li) => {
|
||||
let $li = $(li);
|
||||
let name = $li.attr('data-name');
|
||||
|
||||
if (this.isTabName(name) && name === this.TAB_BREAK_KEY) {
|
||||
$tabBreak = $li.clone();
|
||||
|
||||
$li.attr('data-name', this.TAB_BREAK_KEY.slice(0, -3) + tabBreakIndex);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -326,9 +338,11 @@ define('views/admin/layouts/bottom-panels-detail', ['views/admin/layouts/side-pa
|
||||
|
||||
newLayout[name] = layout[name];
|
||||
|
||||
if (this.isTabName(name) && this.itemsData[name]) {
|
||||
if (this.isTabName(name) && name !== this.TAB_BREAK_KEY /*&& this.itemsData[name]*/) {
|
||||
let data = this.itemsData[name] || {};
|
||||
|
||||
newLayout[name].tabBreak = true;
|
||||
newLayout[name].tabLabel = this.itemsData[name].tabLabel;
|
||||
newLayout[name].tabLabel = data.tabLabel;
|
||||
}
|
||||
else {
|
||||
delete newLayout[name].tabBreak;
|
||||
|
||||
@@ -26,14 +26,18 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
define('views/email-account/fields/folders', 'views/fields/array', function (Dep) {
|
||||
define('views/email-account/fields/folders', ['views/fields/array'], function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
getFoldersUrl: 'EmailAccount/action/getFolders',
|
||||
|
||||
setupOptions: function () {
|
||||
this.params.options = ['INBOX'];
|
||||
},
|
||||
|
||||
fetchFolders: function () {
|
||||
return new Promise(function (resolve) {
|
||||
return new Promise(resolve => {
|
||||
var data = {
|
||||
host: this.model.get('host'),
|
||||
port: this.model.get('port'),
|
||||
@@ -52,54 +56,45 @@ define('views/email-account/fields/folders', 'views/fields/array', function (Dep
|
||||
}
|
||||
|
||||
Espo.Ajax.postRequest(this.getFoldersUrl, data)
|
||||
.then(
|
||||
function (folders) {
|
||||
resolve(folders);
|
||||
}.bind(this)
|
||||
)
|
||||
.fail(
|
||||
function (xhr) {
|
||||
Espo.Ui.error(this.translate('couldNotConnectToImap', 'messages', 'EmailAccount'));
|
||||
.then(folders => {
|
||||
resolve(folders);
|
||||
})
|
||||
.catch(xhr =>{
|
||||
Espo.Ui.error(this.translate('couldNotConnectToImap', 'messages', 'EmailAccount'));
|
||||
|
||||
xhr.errorIsHandled = true;
|
||||
xhr.errorIsHandled = true;
|
||||
|
||||
resolve(["INBOX"]);
|
||||
}.bind(this)
|
||||
);
|
||||
}.bind(this));
|
||||
resolve(["INBOX"]);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
actionAddItem: function () {
|
||||
Espo.Ui.notify(this.translate('loading', 'messages'));
|
||||
|
||||
this.fetchFolders()
|
||||
.then(
|
||||
function (options) {
|
||||
Espo.Ui.notify(false);
|
||||
.then(options => {
|
||||
Espo.Ui.notify(false);
|
||||
|
||||
this.createView( 'addModal', this.addItemModalView, {options: options})
|
||||
.then(
|
||||
function (view) {
|
||||
view.render();
|
||||
this.createView( 'addModal', this.addItemModalView, {options: options})
|
||||
.then(view => {
|
||||
view.render();
|
||||
|
||||
view.once('add', function (item) {
|
||||
this.addValue(item);
|
||||
view.once('add', item =>{
|
||||
this.addValue(item);
|
||||
|
||||
view.close();
|
||||
}.bind(this));
|
||||
view.close();
|
||||
});
|
||||
|
||||
view.once('add-mass', function (items) {
|
||||
items.forEach(function (item) {
|
||||
this.addValue(item);
|
||||
}.bind(this));
|
||||
|
||||
view.close();
|
||||
}.bind(this));
|
||||
}.bind(this)
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
view.once('add-mass', items => {
|
||||
items.forEach(item => {
|
||||
this.addValue(item);
|
||||
});
|
||||
|
||||
view.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -271,7 +271,7 @@ define(
|
||||
.attr('tabindex', '0')
|
||||
.attr('data-action', 'addToContact')
|
||||
.attr('data-address', address)
|
||||
.text(this.translate('Add to Lead', 'labels', 'Email'))
|
||||
.text(this.translate('Add to Contact', 'labels', 'Email'))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ function (Dep, RegExpPattern, /** module:ui/multi-select*/MultiSelect) {
|
||||
|
||||
listTemplate: 'fields/array/list',
|
||||
|
||||
listLinkTemplate: 'fields/array/list-link',
|
||||
|
||||
detailTemplate: 'fields/array/detail',
|
||||
|
||||
editTemplate: 'fields/array/edit',
|
||||
|
||||
@@ -770,8 +770,8 @@ define('views/fields/link-multiple', ['views/fields/base', 'helpers/record-modal
|
||||
fetch: function () {
|
||||
let data = {};
|
||||
|
||||
data[this.idsName] = this.ids;
|
||||
data[this.nameHashName] = this.nameHash;
|
||||
data[this.idsName] = Espo.Utils.clone(this.ids);
|
||||
data[this.nameHashName] = Espo.Utils.clone(this.nameHash);
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@ define('views/global-search/global-search', ['view'], function (Dep) {
|
||||
'keydown input.global-search-input': function (e) {
|
||||
let key = Espo.Utils.getKeyFromKeyEvent(e);
|
||||
|
||||
if (e.code === 'Enter') {
|
||||
if (e.code === 'Enter' || key === 'Enter' || key === 'Control+Enter') {
|
||||
this.runSearch();
|
||||
|
||||
return;
|
||||
|
||||
@@ -748,6 +748,8 @@ define('views/modal', ['view'], function (Dep) {
|
||||
}
|
||||
|
||||
this.$el.find('footer button[data-name="'+name+'"]').removeClass('hidden');
|
||||
|
||||
this.adjustButtons();
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1063,7 +1063,7 @@ function (Dep, ViewRecordHelper, ActionItemSetup) {
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.panelSoftLockedTypeList.length; i++) {
|
||||
var iType = this.panelSoftLockedTypeList[i];
|
||||
let iType = this.panelSoftLockedTypeList[i];
|
||||
|
||||
if (iType === softLockedType) {
|
||||
continue;
|
||||
@@ -1076,13 +1076,13 @@ function (Dep, ViewRecordHelper, ActionItemSetup) {
|
||||
}
|
||||
}
|
||||
|
||||
var middleView = this.getView('middle');
|
||||
let middleView = this.getView('middle');
|
||||
|
||||
if (middleView) {
|
||||
middleView.showPanelInternal(name);
|
||||
}
|
||||
|
||||
var bottomView = this.getView('bottom');
|
||||
let bottomView = this.getView('bottom');
|
||||
|
||||
if (bottomView) {
|
||||
if ('showPanel' in bottomView) {
|
||||
@@ -1090,16 +1090,24 @@ function (Dep, ViewRecordHelper, ActionItemSetup) {
|
||||
}
|
||||
}
|
||||
else if (this.bottomView) {
|
||||
this.once('after:render', () => {
|
||||
var bottomView = this.getView('bottom');
|
||||
this.once('ready', () => {
|
||||
let view = this.getView('bottom');
|
||||
|
||||
if (bottomView && 'showPanel' in bottomView) {
|
||||
bottomView.showPanel(name);
|
||||
if (view) {
|
||||
if ('processShowPanel' in view) {
|
||||
view.processShowPanel(name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ('showPanel' in view) {
|
||||
view.showPanel(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var sideView = this.getView('side');
|
||||
let sideView = this.getView('side');
|
||||
|
||||
if (sideView) {
|
||||
if ('showPanel' in sideView) {
|
||||
@@ -1107,11 +1115,19 @@ function (Dep, ViewRecordHelper, ActionItemSetup) {
|
||||
}
|
||||
}
|
||||
else if (this.sideView) {
|
||||
this.once('after:render', () => {
|
||||
var sideView = this.getView('side');
|
||||
this.once('ready', () => {
|
||||
let view = this.getView('side');
|
||||
|
||||
if (sideView && 'showPanel' in sideView) {
|
||||
sideView.showPanel(name);
|
||||
if (view) {
|
||||
if ('processShowPanel' in view) {
|
||||
view.processShowPanel(name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ('showPanel' in view) {
|
||||
view.showPanel(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1154,13 +1170,13 @@ function (Dep, ViewRecordHelper, ActionItemSetup) {
|
||||
}
|
||||
}
|
||||
|
||||
var middleView = this.getView('middle');
|
||||
let middleView = this.getView('middle');
|
||||
|
||||
if (middleView) {
|
||||
middleView.hidePanelInternal(name);
|
||||
}
|
||||
|
||||
var bottomView = this.getView('bottom');
|
||||
let bottomView = this.getView('bottom');
|
||||
|
||||
if (bottomView) {
|
||||
if ('hidePanel' in bottomView) {
|
||||
@@ -1168,16 +1184,24 @@ function (Dep, ViewRecordHelper, ActionItemSetup) {
|
||||
}
|
||||
}
|
||||
else if (this.bottomView) {
|
||||
this.once('after:render', () => {
|
||||
var bottomView = this.getView('bottom');
|
||||
this.once('ready', () => {
|
||||
let view = this.getView('bottom');
|
||||
|
||||
if (bottomView && 'showPanel' in bottomView) {
|
||||
bottomView.hidePanel(name);
|
||||
if (view) {
|
||||
if ('processHidePanel' in view) {
|
||||
view.processHidePanel(name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ('hidePanel' in view) {
|
||||
view.hidePanel(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var sideView = this.getView('side');
|
||||
let sideView = this.getView('side');
|
||||
|
||||
if (sideView) {
|
||||
if ('hidePanel' in sideView) {
|
||||
@@ -1185,18 +1209,25 @@ function (Dep, ViewRecordHelper, ActionItemSetup) {
|
||||
}
|
||||
}
|
||||
else if (this.sideView) {
|
||||
this.once('after:render', () => {
|
||||
var sideView = this.getView('side');
|
||||
this.once('ready', () => {
|
||||
let view = this.getView('side');
|
||||
|
||||
if (sideView && 'hidePanel' in sideView) {
|
||||
sideView.hidePanel(name);
|
||||
if (view) {
|
||||
if ('processHidePanel' in view) {
|
||||
view.processHidePanel(name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ('hidePanel' in view) {
|
||||
view.hidePanel(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.recordHelper.setPanelStateParam(name, 'hidden', true);
|
||||
|
||||
|
||||
if (this.middlePanelDefs[name]) {
|
||||
this.controlMiddleTabVisibilityHide(this.middlePanelDefs[name].tabNumber);
|
||||
|
||||
|
||||
@@ -394,52 +394,57 @@ define('views/record/panels-container', ['view'], function (Dep) {
|
||||
return data;
|
||||
},
|
||||
|
||||
showPanel: function (name, softLockedType, callback) {
|
||||
if (this.recordHelper.getPanelStateParam(name, 'hiddenLocked')) {
|
||||
/**
|
||||
* @param {string} name
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasPanel: function (name) {
|
||||
return !!this.panelList.find(item => item.name === name);
|
||||
},
|
||||
|
||||
processShowPanel: function (name, callback, wasShown) {
|
||||
if (this.recordHelper.getPanelStateParam(name, 'hidden')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (softLockedType) {
|
||||
this.recordHelper
|
||||
.setPanelStateParam(name, 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked',
|
||||
false);
|
||||
if (!this.hasPanel(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.panelSoftLockedTypeList.length; i++) {
|
||||
var iType = this.panelSoftLockedTypeList[i];
|
||||
this.panelList.filter(item => item.name === name).forEach(item => {
|
||||
item.hidden = false;
|
||||
|
||||
if (iType === softLockedType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var iParam = 'hidden' + Espo.Utils.upperCaseFirst(iType) + 'Locked';
|
||||
|
||||
if (this.recordHelper.getPanelStateParam(name, iParam)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let wasShown = this.recordHelper.getPanelStateParam(name, 'hidden') === false;
|
||||
|
||||
this.recordHelper.setPanelStateParam(name, 'hidden', false);
|
||||
|
||||
var isFound = false;
|
||||
|
||||
this.panelList.forEach(d => {
|
||||
if (d.name === name) {
|
||||
d.hidden = false;
|
||||
isFound = true;
|
||||
|
||||
this.controlTabVisibilityShow(d.tabNumber);
|
||||
if (typeof item.tabNumber !== 'undefined') {
|
||||
this.controlTabVisibilityShow(item.tabNumber);
|
||||
}
|
||||
});
|
||||
|
||||
if (!isFound) {
|
||||
this.showPanelFinalize(name, callback, wasShown);
|
||||
},
|
||||
|
||||
processHidePanel: function (name, callback) {
|
||||
if (!this.recordHelper.getPanelStateParam(name, 'hidden')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.hasPanel(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.panelList.filter(item => item.name === name).forEach(item => {
|
||||
item.hidden = true;
|
||||
|
||||
if (typeof item.tabNumber !== 'undefined') {
|
||||
this.controlTabVisibilityHide(item.tabNumber);
|
||||
}
|
||||
});
|
||||
|
||||
this.hidePanelFinalize(name, callback);
|
||||
},
|
||||
|
||||
showPanelFinalize: function (name, callback, wasShown) {
|
||||
if (this.isRendered()) {
|
||||
var view = this.getView(name);
|
||||
let view = this.getView(name);
|
||||
|
||||
if (view) {
|
||||
view.$el.closest('.panel').removeClass('hidden');
|
||||
@@ -448,7 +453,7 @@ define('views/record/panels-container', ['view'], function (Dep) {
|
||||
view.trigger('show');
|
||||
|
||||
if (!wasShown && view.getFieldViews) {
|
||||
var fields = view.getFieldViews();
|
||||
let fields = view.getFieldViews();
|
||||
|
||||
if (fields) {
|
||||
for (let i in fields) {
|
||||
@@ -482,38 +487,9 @@ define('views/record/panels-container', ['view'], function (Dep) {
|
||||
});
|
||||
},
|
||||
|
||||
hidePanel: function (name, locked, softLockedType, callback) {
|
||||
this.recordHelper.setPanelStateParam(name, 'hidden', true);
|
||||
|
||||
if (locked) {
|
||||
this.recordHelper.setPanelStateParam(name, 'hiddenLocked', true);
|
||||
}
|
||||
|
||||
if (softLockedType) {
|
||||
this.recordHelper.setPanelStateParam(
|
||||
name,
|
||||
'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
var isFound = false;
|
||||
|
||||
this.panelList.forEach(d => {
|
||||
if (d.name === name) {
|
||||
d.hidden = true;
|
||||
isFound = true;
|
||||
|
||||
this.controlTabVisibilityHide(d.tabNumber);
|
||||
}
|
||||
});
|
||||
|
||||
if (!isFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
hidePanelFinalize: function (name, callback) {
|
||||
if (this.isRendered()) {
|
||||
var view = this.getView(name);
|
||||
let view = this.getView(name);
|
||||
|
||||
if (view) {
|
||||
view.$el.closest('.panel').addClass('hidden');
|
||||
@@ -537,6 +513,62 @@ define('views/record/panels-container', ['view'], function (Dep) {
|
||||
}
|
||||
},
|
||||
|
||||
showPanel: function (name, softLockedType, callback) {
|
||||
if (!this.hasPanel(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.recordHelper.getPanelStateParam(name, 'hiddenLocked')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (softLockedType) {
|
||||
let param = 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked';
|
||||
|
||||
this.recordHelper.setPanelStateParam(name, param, false);
|
||||
|
||||
for (let i = 0; i < this.panelSoftLockedTypeList.length; i++) {
|
||||
let iType = this.panelSoftLockedTypeList[i];
|
||||
|
||||
if (iType === softLockedType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let iParam = 'hidden' + Espo.Utils.upperCaseFirst(iType) + 'Locked';
|
||||
|
||||
if (this.recordHelper.getPanelStateParam(name, iParam)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let wasShown = this.recordHelper.getPanelStateParam(name, 'hidden') === false;
|
||||
|
||||
this.recordHelper.setPanelStateParam(name, 'hidden', false);
|
||||
|
||||
this.processShowPanel(name, callback, wasShown);
|
||||
},
|
||||
|
||||
hidePanel: function (name, locked, softLockedType, callback) {
|
||||
if (!this.hasPanel(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.recordHelper.setPanelStateParam(name, 'hidden', true);
|
||||
|
||||
if (locked) {
|
||||
this.recordHelper.setPanelStateParam(name, 'hiddenLocked', true);
|
||||
}
|
||||
|
||||
if (softLockedType) {
|
||||
let param = 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked';
|
||||
|
||||
this.recordHelper.setPanelStateParam(name, param, true);
|
||||
}
|
||||
|
||||
this.processHidePanel(name, callback);
|
||||
},
|
||||
|
||||
alterPanels: function (layoutData) {
|
||||
layoutData = layoutData || this.layoutData || {};
|
||||
|
||||
@@ -749,6 +781,26 @@ define('views/record/panels-container', ['view'], function (Dep) {
|
||||
this.$el.find(`.panel[data-tab="${tab}"]`).removeClass('tab-hidden');
|
||||
|
||||
this.adjustPanels();
|
||||
|
||||
this.panelList
|
||||
.filter(item => item.tabNumber === tab && item.name)
|
||||
.forEach(item => {
|
||||
let view = this.getView(item.name);
|
||||
|
||||
if (view) {
|
||||
view.trigger('tab-show');
|
||||
}
|
||||
});
|
||||
|
||||
this.panelList
|
||||
.filter(item => item.tabNumber !== tab && item.name)
|
||||
.forEach(item => {
|
||||
let view = this.getView(item.name);
|
||||
|
||||
if (view) {
|
||||
view.trigger('tab-hide');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -344,5 +344,28 @@ define('views/record/panels/bottom', ['view'], function (Dep) {
|
||||
|
||||
this.createView(viewKey, viewName, o);
|
||||
},
|
||||
|
||||
/**
|
||||
* Is tab-hidden.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isTabHidden: function () {
|
||||
if (this.defs.tabNumber === -1 || typeof this.defs.tabNumber === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parentView = this.getParentView();
|
||||
|
||||
if (!parentView) {
|
||||
return this.defs.tabNumber > 0;
|
||||
}
|
||||
|
||||
if (parentView && parentView.hasTabs) {
|
||||
return parentView.currentTab !== defs.tabNumber;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -384,5 +384,28 @@ define('views/record/panels/side', ['view'], function (Dep) {
|
||||
actionRefresh: function () {
|
||||
this.model.fetch();
|
||||
},
|
||||
|
||||
/**
|
||||
* Is tab-hidden.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isTabHidden: function () {
|
||||
if (this.defs.tabNumber === -1 || typeof this.defs.tabNumber === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parentView = this.getParentView();
|
||||
|
||||
if (!parentView) {
|
||||
return this.defs.tabNumber > 0;
|
||||
}
|
||||
|
||||
if (parentView && parentView.hasTabs) {
|
||||
return parentView.currentTab !== defs.tabNumber;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -326,7 +326,9 @@ define('views/record/search', ['view'], function (Dep) {
|
||||
|
||||
events: {
|
||||
'keydown input[data-name="textFilter"]': function (e) {
|
||||
if (e.code === 'Enter') {
|
||||
let key = Espo.Utils.getKeyFromKeyEvent(e);
|
||||
|
||||
if (e.code === 'Enter' || key === 'Enter' || key === 'Control+Enter') {
|
||||
this.search();
|
||||
|
||||
this.hideApplyFiltersButton();
|
||||
@@ -449,8 +451,6 @@ define('views/record/search', ['view'], function (Dep) {
|
||||
if (this.isSearchedWithAdvancedFilter) {
|
||||
this.showResetFiltersButton();
|
||||
|
||||
console.log(this.$applyFilters.get(0));
|
||||
|
||||
this.$applyFilters.focus();
|
||||
|
||||
return;
|
||||
|
||||
@@ -76,7 +76,7 @@ define('views/stream/panel', ['views/record/panels/relationship', 'lib!Textcompl
|
||||
}
|
||||
}*/
|
||||
},
|
||||
'keyup textarea[data-name="post"]': function () {
|
||||
'input textarea[data-name="post"]': function () {
|
||||
this.controlPreviewButton();
|
||||
this.controlPostButtonAvailability(this.$textarea.val());
|
||||
},
|
||||
|
||||
13
diff.js
13
diff.js
@@ -31,8 +31,19 @@
|
||||
* From a specified version to the current version or all packages needed for a release.
|
||||
|
||||
* Examples:
|
||||
* * `node diff 5.9.0` - builds an upgrade from 5.9.0 to the current version;
|
||||
* * `node diff 5.9.0` - builds an upgrade from 5.9.0 to the current version;
|
||||
* * `node diff --all` - builds all upgrades needed for a release.
|
||||
*
|
||||
* Data for upgrade packages is defined in `upgrades/{x.x|x.x.x-x.x.x}/data.json`.
|
||||
*
|
||||
* Parameters:
|
||||
* * `mandatoryFiles` – {string[]} – mandatory files to include in upgrade
|
||||
* (even files that were not changed in version control);
|
||||
* * `beforeUpgradeFiles` – {string[]} – files to copy in the beginning of the upgrade process;
|
||||
* * `manifest` – {object} – upgrade manifest parameters.
|
||||
*
|
||||
* Manifest parameters:
|
||||
* * `delete` – {string[]} – additional files to be deleted (usually those that are not in version control).
|
||||
*/
|
||||
|
||||
const Diff = require('./js/diff');
|
||||
|
||||
@@ -967,6 +967,10 @@ input.global-search-input {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
> .list-group-item.ui-sortable-handle {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1149,6 +1153,12 @@ input.global-search-input {
|
||||
color: var(--text-gray-color);
|
||||
}
|
||||
|
||||
.list-container.list-container-panel {
|
||||
> .no-data {
|
||||
padding: @panel-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-body .list-container > .no-data {
|
||||
color: var(--text-muted-color);
|
||||
}
|
||||
|
||||
@@ -95,6 +95,48 @@
|
||||
}
|
||||
}
|
||||
|
||||
.fc.fc-unthemed {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fc-unthemed .fc-time-grid .fc-slats .fc-minor td {
|
||||
//border-top-style: dashed;
|
||||
border-top-style: none;
|
||||
}
|
||||
|
||||
body .fc-now-indicator-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body .fc-now-indicator {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
body .fc-content-col > .fc-now-indicator-line {
|
||||
height: 1px;
|
||||
border-top: 1px solid var(--brand-danger);
|
||||
border-bottom-width: 0;
|
||||
background-color: var(--brand-danger);
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: inherit;
|
||||
left: -4px;
|
||||
border-radius: 50%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
background-color: var(--brand-danger);
|
||||
display: inline-block;
|
||||
top: -4px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.fc-today.fc-day-header > span,
|
||||
.fc-today > .fc-day-number {
|
||||
color: var(--state-warning-text);
|
||||
}
|
||||
|
||||
.dashlet-body {
|
||||
.fc-basicWeek-view td.fc-widget-content,
|
||||
.fc-month-view .fc-body > tr > td.fc-widget-content {
|
||||
|
||||
@@ -68,7 +68,8 @@
|
||||
}
|
||||
|
||||
.vis-current-time {
|
||||
background-color: var(--brand-danger-lighten-20) !important;
|
||||
background-color: var(--brand-danger) !important;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.calendar-container .timeline .vis-item,
|
||||
|
||||
@@ -253,7 +253,7 @@ class Diff
|
||||
|
||||
process.chdir(buildPath);
|
||||
|
||||
let fileList = [];
|
||||
let fileList = upgradeData.mandatoryFiles || [];
|
||||
|
||||
let stdout = cp.execSync('git diff --name-only ' + versionFrom).toString();
|
||||
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "7.2.2",
|
||||
"version": "7.2.6",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "7.2.2",
|
||||
"version": "7.2.6",
|
||||
"description": "Open-source CRM.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="rule 1G" stopProcessing="true">
|
||||
<match url="^" />
|
||||
<action type="Rewrite" url="index.php" appendQueryString="true" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
|
||||
</configuration>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="rule 1G" stopProcessing="true">
|
||||
<match url="^" />
|
||||
<action type="Rewrite" url="index.php" appendQueryString="true" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="rule 1G" stopProcessing="true">
|
||||
<match url="^" />
|
||||
<action type="Rewrite" url="index.php" appendQueryString="true" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
42
public/web.config
Normal file
42
public/web.config
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<defaultDocument>
|
||||
<files>
|
||||
<clear />
|
||||
<add value="index.php" />
|
||||
<add value="index.html" />
|
||||
</files>
|
||||
</defaultDocument>
|
||||
<security>
|
||||
<requestFiltering>
|
||||
<verbs allowUnlisted="false">
|
||||
<add verb="GET" allowed="true" />
|
||||
<add verb="POST" allowed="true" />
|
||||
<add verb="PUT" allowed="true" />
|
||||
<add verb="PATCH" allowed="true" />
|
||||
<add verb="DELETE" allowed="true" />
|
||||
</verbs>
|
||||
</requestFiltering>
|
||||
</security>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="rule 1G" stopProcessing="true">
|
||||
<match url="^api/v1/portal-access/(.*)$" />
|
||||
<action type="Rewrite" url="api/v1/portal-access/index.php" appendQueryString="true" />
|
||||
</rule>
|
||||
<rule name="rule 2G" stopProcessing="true">
|
||||
<match url="^api/v1/(.*)$" />
|
||||
<action type="Rewrite" url="api/v1/index.php" appendQueryString="true" />
|
||||
</rule>
|
||||
<rule name="rule 3G" stopProcessing="true">
|
||||
<match url="^portal/(.*)$" />
|
||||
<action type="Rewrite" url="portal/index.php" appendQueryString="true" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
<staticContent>
|
||||
<mimeMap fileExtension=".tpl" mimeType="text/plain" />
|
||||
</staticContent>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -1072,27 +1072,35 @@ class UtilTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertEquals($result, Util::concatPath($input));
|
||||
}
|
||||
|
||||
public function testArrayToObject()
|
||||
public function testArrayToObject(): void
|
||||
{
|
||||
$testArr= array(
|
||||
$testArr= [
|
||||
'useCache' => true,
|
||||
'sub' => array (
|
||||
'sub' => [
|
||||
'subV' => '125',
|
||||
'subO' => array(
|
||||
'subO' => [
|
||||
'subOV' => '125',
|
||||
),
|
||||
),
|
||||
);
|
||||
],
|
||||
'subList' => [
|
||||
'0',
|
||||
'1'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$testResult= (object) array(
|
||||
$testResult= (object) [
|
||||
'useCache' => true,
|
||||
);
|
||||
$testResult->sub = (object) array (
|
||||
'subV' => '125',
|
||||
);
|
||||
$testResult->sub->subO = (object) array (
|
||||
'subOV' => '125',
|
||||
);
|
||||
];
|
||||
|
||||
$testResult->sub = (object) [
|
||||
'subV' => '125',
|
||||
];
|
||||
|
||||
$testResult->sub->subO = (object) [
|
||||
'subOV' => '125',
|
||||
];
|
||||
|
||||
$testResult->sub->subList = ['0', '1'];
|
||||
|
||||
$this->assertEquals($testResult, Util::arrayToObject($testArr));
|
||||
}
|
||||
|
||||
5
upgrades/7.2.4-7.2.5/data.json
Normal file
5
upgrades/7.2.4-7.2.5/data.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mandatoryFiles": [
|
||||
"bin/command"
|
||||
]
|
||||
}
|
||||
14
upgrades/7.2.5-7.2.6/data.json
Normal file
14
upgrades/7.2.5-7.2.6/data.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"mandatoryFiles": [
|
||||
"application/Espo/Core/Authentication/LDAP/Client.php",
|
||||
"application/Espo/Core/Authentication/LDAP/ClientFactory.php",
|
||||
"application/Espo/Core/Authentication/LDAP/Utils.php"
|
||||
],
|
||||
"manifest": {
|
||||
"delete": [
|
||||
"application/Espo/Core/Authentication/Ldap/Client.php",
|
||||
"application/Espo/Core/Authentication/Ldap/ClientFactory.php",
|
||||
"application/Espo/Core/Authentication/Ldap/Utils.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"mandatoryFiles": [
|
||||
"bin/command"
|
||||
],
|
||||
"manifest": {
|
||||
"delete": [
|
||||
"client/css/espo/espo-vertical.css",
|
||||
|
||||
53
web.config
53
web.config
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="RequestBlocking1" stopProcessing="true">
|
||||
<match url="^/?data/config\.php$" />
|
||||
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." />
|
||||
</rule>
|
||||
<rule name="RequestBlocking2" stopProcessing="true">
|
||||
<match url="^/?data/logs/" />
|
||||
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." />
|
||||
</rule>
|
||||
<rule name="RequestBlocking3" stopProcessing="true">
|
||||
<match url="^/?data/cache/" />
|
||||
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." />
|
||||
</rule>
|
||||
<rule name="RequestBlocking4" stopProcessing="true">
|
||||
<match url="^/?data/upload/" />
|
||||
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." />
|
||||
</rule>
|
||||
<rule name="RequestBlocking5" stopProcessing="true">
|
||||
<match url="^/?application/" />
|
||||
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." />
|
||||
</rule>
|
||||
<rule name="RequestBlocking6" stopProcessing="true">
|
||||
<match url="^/?custom/" />
|
||||
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." />
|
||||
</rule>
|
||||
<rule name="RequestBlocking7" stopProcessing="true">
|
||||
<match url="^/?vendor/" />
|
||||
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
|
||||
<defaultDocument>
|
||||
<files>
|
||||
<clear />
|
||||
<add value="index.php" />
|
||||
<add value="index.html" />
|
||||
</files>
|
||||
</defaultDocument>
|
||||
|
||||
<staticContent>
|
||||
<mimeMap fileExtension=".tpl" mimeType="text/plain" />
|
||||
<mimeMap fileExtension=".json" mimeType="application/json" />
|
||||
<mimeMap fileExtension=".woff" mimeType="application/font-woff" />
|
||||
</staticContent>
|
||||
</system.webServer>
|
||||
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user