Compare commits

...

45 Commits
7.2.1 ... 7.2.4

Author SHA1 Message Date
Yuri Kuznetsov
2420746f1b folders save fix 2022-09-16 21:46:42 +03:00
Yuri Kuznetsov
2dfd00dd2e cs fix 2022-09-16 21:46:09 +03:00
Yuri Kuznetsov
e043bb48e9 css fix 2022-09-16 14:23:44 +03:00
Yuri Kuznetsov
9aaef9d957 v 2022-09-16 10:08:10 +03:00
Yuri Kuznetsov
87449aae67 cleanup 2022-09-16 10:06:48 +03:00
Yuri Kuznetsov
191d064fe1 exception log message interface 2022-09-16 10:01:22 +03:00
Yuri Kuznetsov
dcb3e2feaf typo fix 2022-09-16 09:44:22 +03:00
Yuri Kuznetsov
da9a423e59 css fix 2022-09-15 19:04:46 +03:00
Yuri Kuznetsov
3be4510e63 css fix 2022-09-15 18:55:02 +03:00
Yuri Kuznetsov
a6bb5a239b style fix 2022-09-15 18:05:38 +03:00
Yuri Kuznetsov
a5fb42609b calendar style fix 2022-09-15 16:30:09 +03:00
Yuri Kuznetsov
40c2c1718e calendar minor none 2022-09-15 16:19:09 +03:00
Yuri Kuznetsov
aeecfd63da color fix 2022-09-15 15:53:55 +03:00
Yuri Kuznetsov
1da1e6da9b fix 2022-09-15 15:52:16 +03:00
Yuri Kuznetsov
bf9f23ebdd dev 2022-09-15 15:45:16 +03:00
Yuri Kuznetsov
7beb4f8d83 fix 2022-09-15 15:33:36 +03:00
Yuri Kuznetsov
991b859643 calendar now circle 2022-09-15 15:31:40 +03:00
Yuri Kuznetsov
9bd74e08db disable email fields layout 2022-09-15 14:52:36 +03:00
Yuri Kuznetsov
cfdf65025d fix stream post input event 2022-09-15 13:32:29 +03:00
Yuri Kuznetsov
bf471e654c calendar now indicator 2022-09-15 12:42:53 +03:00
Yuri Kuznetsov
5feee1cf55 calendar today text color 2022-09-15 12:21:51 +03:00
Yuri Kuznetsov
6af6fc017b lead capture fix 2022-09-15 10:44:39 +03:00
Yuri Kuznetsov
8ee9a792fc lead industry acl fix 2022-09-15 10:09:37 +03:00
Yuri Kuznetsov
9ef1c5928f fix campaign bottom view 2022-09-14 16:50:50 +03:00
Yuri Kuznetsov
64c933e365 v 2022-09-14 12:52:15 +03:00
Yuri Kuznetsov
32055f3d6e fix upload in chunks check 2022-09-14 10:10:45 +03:00
Yuri Kuznetsov
804acae44b fix extension check 2022-09-13 20:51:34 +03:00
Yuri Kuznetsov
6b3f37c00e panel show hide ref 2022-09-13 20:32:24 +03:00
Yuri Kuznetsov
4bdc4878cd fix bottom tab layout 2022-09-13 17:41:33 +03:00
Yuri Kuznetsov
625d2bc128 fix tab race condition error 2022-09-13 17:27:44 +03:00
Yuri Kuznetsov
0af5bb1b4b fix bottom tabs fetch 2022-09-13 14:26:48 +03:00
Yuri Kuznetsov
7a0d59357c panel hide/show concurrency issue fix 2022-09-13 14:16:55 +03:00
Yuri Kuznetsov
adbb46d02c system template inline attachments 2022-09-13 10:12:28 +03:00
Yuri Kuznetsov
ac3884179e cs fix 2022-09-13 10:08:31 +03:00
Yuri Kuznetsov
7cf1af188d fix attachment replated validation 2022-09-13 09:36:47 +03:00
Yuri Kuznetsov
21b695e4ef style fix 2022-09-13 09:30:50 +03:00
Yuri Kuznetsov
fbda66defc fix 2022-09-13 09:25:49 +03:00
Yuri Kuznetsov
beb4435ee0 diff closest 2022-09-12 15:07:32 +03:00
Yuri Kuznetsov
6a4c78c1cb fix language settings 2022-09-12 14:51:05 +03:00
Yuri Kuznetsov
dc7f5d8e66 fix grunt zip 2022-09-12 14:06:54 +03:00
Yuri Kuznetsov
872a4c2f2c fix typo 2022-09-12 12:35:27 +03:00
Yuri Kuznetsov
8a5204bc4c css fix 2022-09-12 12:03:26 +03:00
Yuri Kuznetsov
acbf6a1742 v 2022-09-12 11:22:07 +03:00
Yuri Kuznetsov
b2d3d56a85 attachment-multiple link 2022-09-12 11:20:48 +03:00
Yuri Kuznetsov
8b83fa640f note relate icon 2022-09-12 10:45:16 +03:00
41 changed files with 624 additions and 256 deletions

View File

@@ -381,7 +381,7 @@ module.exports = grunt => {
});
grunt.registerTask('upgrade', () => {
cp.execSync("node diff --all --vendor", {stdio: 'inherit'});
cp.execSync("node diff --closest", {stdio: 'inherit'});
});
grunt.registerTask('unit-tests-run', () => {
@@ -396,7 +396,7 @@ module.exports = grunt => {
cp.execSync("composer run-script setConfigParams", {stdio: 'ignore'});
});
grunt.registerTask('zip', () => {
grunt.registerTask('zip', function () { // Don't change to arrow-function.
const archiver = require('archiver');
let resolve = this.async();
@@ -425,8 +425,9 @@ module.exports = grunt => {
archive
.directory(currentPath + '/build/' + folder, folder)
.pipe(zipOutput)
.finalize();
.pipe(zipOutput);
archive.finalize();
});
grunt.registerTask('npm-install', () => {

View File

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

View File

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

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

View File

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

View File

@@ -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&amp;', '?entryPoint=attachment&', $html);
if (!$skipInlineAttachmentHandling) {
$html = str_replace('?entryPoint=attachment&amp;', '?entryPoint=attachment&', $html);
}
if ($this->entityManager) {
if (!$skipInlineAttachmentHandling && $this->entityManager) {
/** @var string $html */
$html = preg_replace_callback(
'/\?entryPoint=attachment\&id=([A-Za-z0-9]*)/',

View File

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

View File

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

View File

@@ -4,6 +4,10 @@
"scope": "Opportunity",
"field": "leadSource"
},
"Account.options.industry": {
"scope": "Lead",
"field": "industry"
},
"Meeting": {
"scope": "Call"
}

View File

@@ -3,6 +3,10 @@
"entityDefs.Lead.fields.source.options": {
"scope": "Opportunity",
"field": "leadSource"
},
"entityDefs.Account.fields.industry.options": {
"scope": "Lead",
"field": "industry"
}
},
"frontendHiddenPathList": [

View File

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

View File

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

View File

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

View File

@@ -44,7 +44,6 @@
"monitoredFolders": {
"type": "array",
"default": ["INBOX"],
"options": ["INBOX"],
"view": "views/email-account/fields/folders",
"displayAsList": true,
"noEmptyString": true,

View File

@@ -43,7 +43,6 @@
"monitoredFolders": {
"type": "array",
"default": ["INBOX"],
"options": ["INBOX"],
"view": "views/inbound-email/fields/folders",
"displayAsList": true,
"noEmptyString": true,

View File

@@ -235,7 +235,6 @@
},
"language": {
"type": "enum",
"options": ["en_US"],
"default": "en_US",
"view": "views/settings/fields/language",
"isSorted": true

View File

@@ -10,7 +10,7 @@
"chartColorAlternativeList": ["#7492cc", "#c29c4a", "#a1404a", "#6a5f96", "#b07e53"],
"calendarColors": {
"": "#a58dc7a0",
"Meeting": "#6680b3",
"Meeting": "#697da5",
"Call": "#a1404a",
"Task": "#5d8a55"
},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -739,7 +739,7 @@ define('views/admin/entity-manager/edit', ['view', 'model'], function (Dep, Mode
if (
this.getMetadata()
.get(['entityDefs', foreignEntityType, 'fields', item, 'foreingAccessDisabled'])
.get(['entityDefs', foreignEntityType, 'fields', item, 'foreignAccessDisabled'])
) {
return false;
}

View File

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

View File

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

View File

@@ -343,7 +343,7 @@ function (Dep, FileUpload) {
getEditPreview: function (name, type, id) {
if (!~this.previewTypeList.indexOf(type)) {
return name;
return null;
}
return $('<img>')
@@ -368,11 +368,9 @@ function (Dep, FileUpload) {
if (this.showPreviews) {
let html = this.getEditPreview(name, type, id);
if (!html) {
return $text.get(0).outerHTML;
if (html) {
return html;
}
return html;
}
let url = this.getBasePath() + '?entryPoint=download&id=' + id;

View File

@@ -627,11 +627,9 @@ define('views/fields/file', ['views/fields/link', 'helpers/file-upload'], functi
if (this.showPreview) {
let html = this.getEditPreview(name, type, id);
if (!html) {
return $text.get(0).outerHTML;
if (html) {
return html;
}
return html;
}
let url = this.getBasePath() + '?entryPoint=download&id=' + id;

View File

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

View File

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

View File

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

View File

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

View File

@@ -37,6 +37,7 @@ define('views/stream/notes/relate', ['views/stream/note'], function (Dep) {
data: function () {
return _.extend({
relatedTypeString: this.translateEntityType(this.entityType),
iconHtml: this.getIconHtml(this.entityType, this.entityId),
}, Dep.prototype.data.call(this));
},

View File

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

30
diff.js
View File

@@ -47,6 +47,7 @@ let isDev = false;
let isAll = false;
let withVendor = true;
let forceScripts = false;
let isClosest = false;
if (process.argv.length > 1) {
for (let i in process.argv) {
@@ -67,6 +68,10 @@ if (process.argv.length > 1) {
forceScripts = true;
}
if (process.argv[i] === '--closest') {
isClosest = true;
}
if (~process.argv[i].indexOf('--acceptedVersion=')) {
acceptedVersionName = process.argv[i].substr(('--acceptedVersion=').length);
}
@@ -75,22 +80,33 @@ if (process.argv.length > 1) {
let espoPath = path.dirname(fs.realpathSync(__filename));
if (isAll || isClosest) {
acceptedVersionName = null;
}
let diff = new Diff(espoPath, {
isAll: isAll,
isDev: isDev,
withVendor: withVendor,
forceScripts: forceScripts,
acceptedVersionName: !isAll ? acceptedVersionName : null,
acceptedVersionName: acceptedVersionName,
});
if (isAll) {
diff.buildAllUpgradePackages();
}
(() => {
if (isAll) {
diff.buildAllUpgradePackages();
return;
}
if (isClosest) {
diff.buildClosestUpgradePackages();
return;
}
if (!isAll) {
if (!versionFrom) {
throw new Error("No 'version' specified.");
}
diff.buildUpgradePackage(versionFrom);
}
})();

View File

@@ -967,6 +967,10 @@ input.global-search-input {
border-bottom-width: 1px;
}
}
> .list-group-item.ui-sortable-handle {
cursor: default;
}
}
@@ -1045,6 +1049,10 @@ input.global-search-input {
&:last-child {
margin-bottom: 0;
}
> input[type=checkbox] {
margin: 3px 3px 0 0;
}
}
.multi-enum-item-label-container {

View File

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

View File

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

View File

@@ -27,14 +27,12 @@
************************************************************************/
const fs = require('fs');
const sys = require('util');
const cp = require('child_process');
const archiver = require('archiver');
const process = require('process');
const buildUtils = require('./build-utils');
const exec = cp.exec;
const execSync = cp.execSync;
/**
* Builds upgrade packages.
@@ -59,13 +57,19 @@ class Diff
return tagList;
}
buildClosestUpgradePackages() {
let versionFromList = this._getPreviousVersionList(true);
this.buildMultipleUpgradePackages(versionFromList);
}
buildAllUpgradePackages() {
let versionFromList = this._getPreviousVersionList();
this.buildMultipleUpgradePackages(versionFromList);
}
_getPreviousVersionList() {
_getPreviousVersionList(closest) {
let dirInitial = process.cwd();
let version = (require(this.espoPath + '/package.json') || {}).version;
@@ -105,6 +109,12 @@ class Diff
for (let i = 0; i < tagList.length; i++) {
let tag = tagList[i];
let patchVersionNumberI = tag.split('.')[2];
if (closest && parseInt(patchVersionNumberI) !== parseInt(hotfixVersionNumber) - 1) {
break;
}
if (tag === version) {
continue;
}
@@ -112,8 +122,6 @@ class Diff
if (!~tag.indexOf('beta') && !~tag.indexOf('alpha')) {
versionFromList.push(tag);
let patchVersionNumberI = tag.split('.')[2];
if (patchVersionNumberI === '0') {
break;
}
@@ -156,7 +164,7 @@ class Diff
return new Promise(resolve => {
let acceptedVersionName = params.acceptedVersionName || versionFrom;
let isDev = params.isDev;
let withVendor = params.withVendor;
let withVendor = params.withVendor ?? true;
let forceScripts = params.forceScripts;
let version = (require(espoPath + '/package.json') || {}).version;

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "espocrm",
"version": "7.2.1",
"version": "7.2.4",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "espocrm",
"version": "7.2.1",
"version": "7.2.4",
"description": "Open-source CRM.",
"repository": {
"type": "git",