diff --git a/application/Espo/Tools/Stream/RecordService.php b/application/Espo/Tools/Stream/RecordService.php index 7712764489..9f4af032ad 100644 --- a/application/Espo/Tools/Stream/RecordService.php +++ b/application/Espo/Tools/Stream/RecordService.php @@ -424,7 +424,8 @@ class RecordService { if ( $note->getType() === Note::TYPE_POST || - $note->getType() === Note::TYPE_EMAIL_RECEIVED + $note->getType() === Note::TYPE_EMAIL_RECEIVED || + $note->getType() === Note::TYPE_EMAIL_SENT ) { $note->loadAttachments(); } diff --git a/application/Espo/Tools/Stream/Service.php b/application/Espo/Tools/Stream/Service.php index 18431582c5..6cc0f712b1 100644 --- a/application/Espo/Tools/Stream/Service.php +++ b/application/Espo/Tools/Stream/Service.php @@ -496,20 +496,18 @@ class Service $note->setSuperParent($email->getAccount()); } - $withContent = in_array($entityType, $this->config->get('streamEmailWithContentEntityTypeList', [])); + $data = [ + 'emailId' => $email->getId(), + 'emailName' => $email->getSubject(), + 'isInitial' => $isInitial, + 'hasAttachment' => count($email->getAttachmentIdList()) > 0, + ]; + + $withContent = $this->toStoreEmailContent($entityType); if ($withContent) { $note->setPost($email->getBodyPlain()); - } - - $data = []; - - $data['emailId'] = $email->getId(); - $data['emailName'] = $email->getSubject(); - $data['isInitial'] = $isInitial; - - if ($withContent) { - $data['attachmentsIds'] = $email->get('attachmentsIds'); + $data['attachmentsIds'] = $email->getAttachmentIdList(); } $from = $email->getFromAddress(); @@ -532,7 +530,7 @@ class Service } } - $note->setData((object) $data); + $note->setData($data); $this->entityManager->saveEntity($note); @@ -556,19 +554,17 @@ class Service $note->setSuperParent($email->getAccount()); } - $withContent = in_array($entityType, $this->config->get('streamEmailWithContentEntityTypeList', [])); + $data = [ + 'emailId' => $email->getId(), + 'emailName' => $email->getSubject(), + 'hasAttachment' => count($email->getAttachmentIdList()) > 0, + ]; + + $withContent = $this->toStoreEmailContent($entityType); if ($withContent) { $note->setPost($email->getBodyPlain()); - } - - $data = []; - - $data['emailId'] = $email->getId(); - $data['emailName'] = $email->getSubject(); - - if ($withContent) { - $data['attachmentsIds'] = $email->get('attachmentsIds'); + $data['attachmentsIds'] = $email->getAttachmentIdList(); } $user = $this->user; @@ -578,7 +574,7 @@ class Service if (!$user->isSystem()) { $person = $user; } else { - $from = $email->get('from'); + $from = $email->getFromAddress(); if ($from) { $person = $this->getEmailAddressRepository()->getEntityByAddress($from); @@ -591,7 +587,7 @@ class Service $data['personEntityId'] = $person->getId(); } - $note->set('data', (object) $data); + $note->setData($data); $this->entityManager->saveEntity($note); @@ -1369,4 +1365,9 @@ class Service $this->entityManager->saveEntity($entity, [SaveOption::SKIP_ALL => true]); } + + private function toStoreEmailContent(string $entityType): bool + { + return in_array($entityType, $this->config->get('streamEmailWithContentEntityTypeList', [])); + } } diff --git a/client/res/templates/stream/notes/email-received.tpl b/client/res/templates/stream/notes/email-received.tpl index 9699417a5d..e48d38bad5 100644 --- a/client/res/templates/stream/notes/email-received.tpl +++ b/client/res/templates/stream/notes/email-received.tpl @@ -17,6 +17,11 @@ data-id="{{emailId}}" data-scope="Email" >{{{message}}} + @@ -28,6 +33,17 @@ >{{emailName}} +{{#if detailsIsShown}} + {{#if bodyField}} +
+
{{{bodyField}}}
+ {{#if attachmentsField}} +
{{{attachmentsField}}}
+ {{/if}} +
+ {{/if}} +{{/if}} + {{#if hasPost}}
{{{post}}} @@ -46,4 +62,3 @@ {{/if}}
- diff --git a/client/res/templates/stream/notes/email-sent.tpl b/client/res/templates/stream/notes/email-sent.tpl index ae8392ff9f..2c78f1e78c 100644 --- a/client/res/templates/stream/notes/email-sent.tpl +++ b/client/res/templates/stream/notes/email-sent.tpl @@ -17,6 +17,11 @@ data-id="{{emailId}}" data-scope="Email" >{{{message}}} + @@ -28,6 +33,17 @@ >{{emailName}} +{{#if detailsIsShown}} + {{#if bodyField}} +
+
{{{bodyField}}}
+ {{#if attachmentsField}} +
{{{attachmentsField}}}
+ {{/if}} +
+ {{/if}} +{{/if}} + {{#if hasPost}}
{{{post}}} diff --git a/client/src/views/email/fields/body.js b/client/src/views/email/fields/body.js index 1ece9f87b5..54a12b442d 100644 --- a/client/src/views/email/fields/body.js +++ b/client/src/views/email/fields/body.js @@ -90,6 +90,10 @@ class EmailBodyFieldView extends WysiwygFieldView { afterRender() { super.afterRender(); + if (!this.element) { + return; + } + this.controlInsertFieldButton(); if (this.isReadMode() && this.replyPart) { diff --git a/client/src/views/stream/notes/email-received.js b/client/src/views/stream/notes/email-received.js index 0e1fd2cb05..601f8e61aa 100644 --- a/client/src/views/stream/notes/email-received.js +++ b/client/src/views/stream/notes/email-received.js @@ -27,28 +27,68 @@ ************************************************************************/ import NoteStreamView from 'views/stream/note'; +import EmailBodyFieldView from 'views/email/fields/body'; +import AttachmentMultipleFieldView from 'views/fields/attachment-multiple'; class EmailReceivedNoteStreamView extends NoteStreamView { template = 'stream/notes/email-received' isRemovable = false isSystemAvatar = true + detailsIsShown = false + + /** + * @private + * @type {import('views/fields/base').default} + */ + bodyFieldView + + /** + * @private + * @type {import('views/fields/attachment-multiple').default} + */ + attachmentsFieldView + + /** + * @private + * @type {import('model').default} + */ + formModel + + /** + * @private + * @type {string} + */ + emailId data() { return { ...super.data(), emailId: this.emailId, emailName: this.emailName, - hasPost: this.hasPost, + hasPost: this.hasPost && !this.detailsIsShown, hasAttachments: this.hasAttachments, emailIconClassName: this.getMetadata().get(['clientDefs', 'Email', 'iconClass']) || '', isPinned: this.isThis && this.model.get('isPinned') && this.model.collection && !this.model.collection.pinnedList, + detailsIsShown: this.detailsIsShown, }; } setup() { - const data = /** @type {Record} */this.model.get('data') || {}; + this.addActionHandler('expandDetails', () => this.toggleDetails()); + + const data = + /** + * @type {{ + * emailId: string, + * emailName: string, + * personEntityType?: string, + * personEntityId?: string, + * personEntityName?: string, + * isInitial?: boolean, + * }} */ + this.model.get('data') || {}; this.emailId = data.emailId; this.emailName = data.emailName; @@ -118,6 +158,70 @@ class EmailReceivedNoteStreamView extends NoteStreamView { this.createMessage(); } + + /** + * @private + * Warning: The same method exists in email-sent. + */ + async toggleDetails() { + this.detailsIsShown = !this.detailsIsShown; + + if (!this.detailsIsShown && this.formModel) { + this.formModel.abortLastFetch(); + + Espo.Ui.notify(); + } + + await this.reRender(); + + if (!this.detailsIsShown || !this.emailId) { + return; + } + + if (this.bodyFieldView) { + this.bodyFieldView.toShowQuotePart = false; + + await this.bodyFieldView.reRender(); + + return; + } + + this.formModel = await this.getModelFactory().create('Email'); + + this.formModel.id = this.emailId; + + Espo.Ui.notify(' ... '); + + await this.formModel.fetch(); + + this.bodyFieldView = new EmailBodyFieldView({ + name: 'body', + model: this.formModel, + mode: 'detail', + readOnly: true, + }); + + await this.assignView('bodyField', this.bodyFieldView, '[data-name="body"]'); + + if ( + !this.hasAttachments && + this.formModel.attributes.attachmentsIds && + this.formModel.attributes.attachmentsIds.length + ) { + this.attachmentsFieldView = new AttachmentMultipleFieldView({ + name: 'attachments', + model: this.formModel, + mode: 'detail', + readOnly: true, + }); + + await this.assignView('attachmentsField', this.attachmentsFieldView, '[data-name="attachments"]'); + } + + Espo.Ui.notify(); + + await this.reRender(); + } } export default EmailReceivedNoteStreamView; diff --git a/client/src/views/stream/notes/email-sent.js b/client/src/views/stream/notes/email-sent.js index 25cf5e92bc..d7926cb64b 100644 --- a/client/src/views/stream/notes/email-sent.js +++ b/client/src/views/stream/notes/email-sent.js @@ -27,27 +27,66 @@ ************************************************************************/ import NoteStreamView from 'views/stream/note'; +import EmailBodyFieldView from 'views/email/fields/body'; +import AttachmentMultipleFieldView from 'views/fields/attachment-multiple'; class EmailSentNoteStreamView extends NoteStreamView { template = 'stream/notes/email-sent' isRemovable = false + /** + * @private + * @type {import('views/fields/base').default} + */ + bodyFieldView + + /** + * @private + * @type {import('views/fields/attachment-multiple').default} + */ + attachmentsFieldView + + /** + * @private + * @type {import('model').default} + */ + formModel + + /** + * @private + * @type {string} + */ + emailId + data() { return { ...super.data(), emailId: this.emailId, emailName: this.emailName, - hasPost: this.hasPost, + hasPost: this.hasPost && !this.detailsIsShown, hasAttachments: this.hasAttachments, emailIconClassName: this.getMetadata().get(['clientDefs', 'Email', 'iconClass']) || '', isPinned: this.isThis && this.model.get('isPinned') && this.model.collection && !this.model.collection.pinnedList, + detailsIsShown: this.detailsIsShown, }; } setup() { - const data = /** @type {Record} */this.model.get('data') || {}; + this.addActionHandler('expandDetails', () => this.toggleDetails()); + + const data = + /** + * @type {{ + * emailId: string, + * emailName: string, + * personEntityType?: string, + * personEntityId?: string, + * personEntityName?: string, + * isInitial?: boolean, + * }} */ + this.model.get('data') || {}; this.emailId = data.emailId; this.emailName = data.emailName; @@ -102,6 +141,70 @@ class EmailSentNoteStreamView extends NoteStreamView { this.createMessage(); } + + /** + * @private + * Warning: The same method exists in email-received. + */ + async toggleDetails() { + this.detailsIsShown = !this.detailsIsShown; + + if (!this.detailsIsShown && this.formModel) { + this.formModel.abortLastFetch(); + + Espo.Ui.notify(); + } + + await this.reRender(); + + if (!this.detailsIsShown || !this.emailId) { + return; + } + + if (this.bodyFieldView) { + this.bodyFieldView.toShowQuotePart = false; + + await this.bodyFieldView.reRender(); + + return; + } + + this.formModel = await this.getModelFactory().create('Email'); + + this.formModel.id = this.emailId; + + Espo.Ui.notify(' ... '); + + await this.formModel.fetch(); + + this.bodyFieldView = new EmailBodyFieldView({ + name: 'body', + model: this.formModel, + mode: 'detail', + readOnly: true, + }); + + await this.assignView('bodyField', this.bodyFieldView, '[data-name="body"]'); + + if ( + !this.hasAttachments && + this.formModel.attributes.attachmentsIds && + this.formModel.attributes.attachmentsIds.length + ) { + this.attachmentsFieldView = new AttachmentMultipleFieldView({ + name: 'attachments', + model: this.formModel, + mode: 'detail', + readOnly: true, + }); + + await this.assignView('attachmentsField', this.attachmentsFieldView, '[data-name="attachments"]'); + } + + Espo.Ui.notify(); + + await this.reRender(); + } } export default EmailSentNoteStreamView;