diff --git a/application/Espo/Entities/Email.php b/application/Espo/Entities/Email.php index f3616975ab..c069bd744e 100644 --- a/application/Espo/Entities/Email.php +++ b/application/Espo/Entities/Email.php @@ -74,6 +74,12 @@ class Email extends Entity public const LINK_REPLIES = 'replies'; + public const ADDRESS_FROM = 'from'; + public const ADDRESS_TO = 'to'; + public const ADDRESS_CC = 'cc'; + public const ADDRESS_BCC = 'bcc'; + public const ADDRESS_REPLY_TO = 'replyTo'; + public function get(string $attribute): mixed { if ($attribute === 'subject') { diff --git a/application/Espo/Modules/Crm/Tools/Activities/Service.php b/application/Espo/Modules/Crm/Tools/Activities/Service.php index 40eaf19738..5c62192454 100644 --- a/application/Espo/Modules/Crm/Tools/Activities/Service.php +++ b/application/Espo/Modules/Crm/Tools/Activities/Service.php @@ -69,6 +69,7 @@ use Espo\Core\FieldProcessing\ListLoadProcessor; use Espo\Core\FieldProcessing\Loader\Params as FieldLoaderParams; use Espo\Core\Record\ServiceContainer as RecordServiceContainer; +use Espo\Repositories\Email as EmailRepository; use LogicException; use PDO; use RuntimeException; @@ -137,6 +138,8 @@ class Service 'status', Field::CREATED_AT, ['false', 'hasAttachment'], + ['null', 'fromEmailAddressName'], + ['null', 'fromString'], ]) ->leftJoin( 'MeetingUser', @@ -210,6 +213,8 @@ class Service 'status', Field::CREATED_AT, ['false', 'hasAttachment'], + ['null', 'fromEmailAddressName'], + ['null', 'fromString'], ]) ->leftJoin( 'CallUser', @@ -284,6 +289,8 @@ class Service 'status', Field::CREATED_AT, 'hasAttachment', + 'fromEmailAddressName', + 'fromString', ]) ->leftJoin( 'EmailUser', @@ -357,6 +364,8 @@ class Service 'status', Field::CREATED_AT, ['false', 'hasAttachment'], + ['null', 'fromEmailAddressName'], + ['null', 'fromString'], ]); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); @@ -485,6 +494,8 @@ class Service 'status', Field::CREATED_AT, 'hasAttachment', + 'fromEmailAddressName', + 'fromString', ]); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); @@ -726,7 +737,11 @@ class Service } $itemEntity = $this->entityManager->getNewEntity($itemEntityType); - $itemEntity->set($row); + + $itemEntity->setMultiple($row); + $itemEntity->setAsFetched(); + + $this->prepareEntity($itemEntity); $collection->append($itemEntity); } @@ -738,6 +753,20 @@ class Service return RecordCollection::create($collection, $totalCount); } + private function prepareEntity(Entity $entity): void + { + if ($entity instanceof Email) { + $repo = $this->entityManager->getRepository(Email::ENTITY_TYPE); + + if (!$repo instanceof EmailRepository) { + throw new RuntimeException(); + } + + $repo->loadFromField($entity); + $repo->loadNameHash($entity, [Email::ADDRESS_FROM]); + } + } + /** * @throws Forbidden */ @@ -1082,6 +1111,8 @@ class Service 'status', Field::CREATED_AT, ['false', 'hasAttachment'], + ['null', 'fromEmailAddressName'], + ['null', 'fromString'], ]); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); diff --git a/application/Espo/Repositories/Email.php b/application/Espo/Repositories/Email.php index 21b71dda10..3f814efbde 100644 --- a/application/Espo/Repositories/Email.php +++ b/application/Espo/Repositories/Email.php @@ -56,11 +56,11 @@ class Email extends Database implements { use Di\EmailFilterManagerSetter; - private const ADDRESS_FROM = 'from'; - private const ADDRESS_TO = 'to'; - private const ADDRESS_CC = 'cc'; - private const ADDRESS_BCC = 'bcc'; - private const ADDRESS_REPLY_TO = 'replyTo'; + private const ADDRESS_FROM = EmailEntity::ADDRESS_FROM; + private const ADDRESS_TO = EmailEntity::ADDRESS_TO; + private const ADDRESS_CC = EmailEntity::ADDRESS_CC; + private const ADDRESS_BCC = EmailEntity::ADDRESS_BCC; + private const ADDRESS_REPLY_TO = EmailEntity::ADDRESS_REPLY_TO; private const ATTR_FROM_EMAIL_ADDRESS_ID = 'fromEmailAddressId'; private const ATTR_FROM_EMAIL_ADDRESS_NAME = 'fromEmailAddressName'; @@ -571,7 +571,8 @@ class Email extends Database implements if ( $user instanceof UserEntity && - $entity->getStatus() !== EmailEntity::STATUS_DRAFT + $entity->getStatus() !== EmailEntity::STATUS_DRAFT && + $user->getType() !== UserEntity::TYPE_PORTAL ) { $entity->setSentBy($user); } diff --git a/client/modules/crm/src/views/record/panels/history.js b/client/modules/crm/src/views/record/panels/history.js index 6c645b6cc1..2121fe91a6 100644 --- a/client/modules/crm/src/views/record/panels/history.js +++ b/client/modules/crm/src/views/record/panels/history.js @@ -55,7 +55,7 @@ class HistoryPanelView extends ActivitiesPanelView { soft: true }, { - name: 'status', + name: 'from', }, { name: 'hasAttachment', diff --git a/client/src/views/email/fields/from-address-varchar.js b/client/src/views/email/fields/from-address-varchar.js index 6f4bdea8bd..2f9d8dbd4e 100644 --- a/client/src/views/email/fields/from-address-varchar.js +++ b/client/src/views/email/fields/from-address-varchar.js @@ -33,6 +33,11 @@ import EmailHelper from 'email-helper'; class EmailFromAddressVarchar extends BaseFieldView { + // language=Handlebars + listTemplateContent = ` + {{#if value}}{{{value}}}{{/if}} + ` + detailTemplate = 'email/fields/email-address-varchar/detail' validations = ['required', 'email'] @@ -164,7 +169,7 @@ class EmailFromAddressVarchar extends BaseFieldView { } getValueForDisplay() { - if (this.mode === this.MODE_DETAIL) { + if (this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST) { const address = this.model.get(this.name); return this.getDetailAddressHtml(address); @@ -194,45 +199,65 @@ class EmailFromAddressVarchar extends BaseFieldView { let avatarHtml = ''; if (entityType === 'User') { - avatarHtml = this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link'); + const size = this.mode === this.MODE_DETAIL ? 18 : 16; + + avatarHtml = this.getHelper().getAvatarHtml(id, 'small', size, 'avatar-link'); } - return $('
') + const title = this.mode === this.MODE_LIST ? name : null; + const className = this.mode === this.MODE_LIST ? 'text-default' : null; + + const $item = $('
') .append( avatarHtml, $('') .attr('href', `#${entityType}/view/${id}`) .attr('data-scope', entityType) .attr('data-id', id) + .attr('title', title) + .addClass(className) .text(name), + ); + + if (this.mode === this.MODE_DETAIL) { + $item.append( ' ', $('').addClass('text-muted middle-dot'), ' ', $('').text(address) - ) - .get(0).outerHTML; + ); + } + + return $item.get(0).outerHTML; } const $div = $('
'); + $div.addClass('email-address-lines-container') - if (this.getAcl().check('Contact', 'create') || this.getAcl().check('Lead', 'create')) { + if ( + this.mode !== this.MODE_LIST && + (this.getAcl().check('Contact', 'create') || this.getAcl().check('Lead', 'create')) + ) { $div.append( this.getCreateHtml(address) ); } if (name) { - $div.append( - $('') - .addClass('email-address-line') - .text(name) - .append( - ' ', - $('').addClass('text-muted middle-dot'), - ' ', - $('').text(address) - ) - ); + const $span = $('') + .addClass('email-address-line') + .text(name); + + if (this.mode === this.MODE_DETAIL) { + $span.append( + ' ', + $('').addClass('text-muted middle-dot'), + ' ', + $('').text(address), + ); + } + + $div.append($span); return $div.get(0).outerHTML; } diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less index 27579d8efe..6717315b3f 100644 --- a/frontend/less/espo/custom.less +++ b/frontend/less/espo/custom.less @@ -2607,6 +2607,20 @@ div:has(.avatar-in-input) { } } +.expanded-row { + .cell { + > .email-address-detail-item { + display: inline-block; + } + + > .email-address-lines-container { + display: inline-block; + + color: var(--gray-soft); + } + } +} + .email-address-detail-item { > img.avatar { margin-right: var(--5px);