diff --git a/application/Espo/Modules/Crm/Controllers/KnowledgeBaseArticle.php b/application/Espo/Modules/Crm/Controllers/KnowledgeBaseArticle.php index 2c3f5e65e8..c21c6d3118 100644 --- a/application/Espo/Modules/Crm/Controllers/KnowledgeBaseArticle.php +++ b/application/Espo/Modules/Crm/Controllers/KnowledgeBaseArticle.php @@ -31,24 +31,66 @@ namespace Espo\Modules\Crm\Controllers; use Espo\Core\Exceptions\BadRequest; use Espo\Core\Api\Request; +use Espo\Core\Exceptions\Error; +use Espo\Core\Exceptions\Forbidden; +use Espo\Core\Exceptions\NotFound; use Espo\Core\Utils\Json; use Espo\Modules\Crm\Services\KnowledgeBaseArticle as Service; +use Espo\Modules\Crm\Tools\KnowledgeBase\Service as KBService; +use Espo\Tools\Attachment\FieldData; use stdClass; class KnowledgeBaseArticle extends \Espo\Core\Controllers\Record { + /** + * @throws BadRequest + * @throws Forbidden + * @throws NotFound + */ public function postActionGetCopiedAttachments(Request $request): stdClass { $data = $request->getParsedBody(); - if (empty($data->id)) { - throw new BadRequest(); + $id = $data->id ?? null; + $field = $data->field ?? null; + $parentType = $data->parentType ?? null; + $relatedType = $data->relatedType ?? null; + + if (!$id || !$field) { + throw new BadRequest("No `id` or `field`."); } - $id = $data->id; + try { + $fieldData = new FieldData( + $field, + $parentType, + $relatedType + ); + } + catch (Error $e) { + throw new BadRequest($e->getMessage()); + } - return $this->getArticleService()->getCopiedAttachments($id); + $list = $this->injectableFactory + ->create(KBService::class) + ->copyAttachments($id, $fieldData); + + $ids = array_map( + fn ($item) => $item->getId(), + $list + ); + + $names = (object) []; + + foreach ($list as $item) { + $names->{$item->getId()} = $item->getName(); + } + + return (object) [ + 'ids' => $ids, + 'names' => $names, + ]; } public function postActionMoveToTop(Request $request): bool diff --git a/application/Espo/Modules/Crm/Entities/KnowledgeBaseArticle.php b/application/Espo/Modules/Crm/Entities/KnowledgeBaseArticle.php index 52e3618bb5..66a7f20a21 100644 --- a/application/Espo/Modules/Crm/Entities/KnowledgeBaseArticle.php +++ b/application/Espo/Modules/Crm/Entities/KnowledgeBaseArticle.php @@ -47,4 +47,13 @@ class KnowledgeBaseArticle extends \Espo\Core\ORM\Entity /** @var LinkMultiple */ return $this->getValueObject('teams'); } + + /** + * @return string[] + */ + public function getAttachmentIdList(): array + { + /** @var string[] */ + return $this->getLinkMultipleIdList('attachments'); + } } diff --git a/application/Espo/Modules/Crm/Services/KnowledgeBaseArticle.php b/application/Espo/Modules/Crm/Services/KnowledgeBaseArticle.php index 9a5ccc8d04..16c5e69885 100644 --- a/application/Espo/Modules/Crm/Services/KnowledgeBaseArticle.php +++ b/application/Espo/Modules/Crm/Services/KnowledgeBaseArticle.php @@ -29,19 +29,15 @@ namespace Espo\Modules\Crm\Services; -use Espo\Core\Exceptions\BadRequest; use Espo\Core\Exceptions\Forbidden; use Espo\Core\Exceptions\NotFound; use Espo\Core\Exceptions\Error; use Espo\Services\Record; -use Espo\Entities\Attachment; use Espo\Modules\Crm\Entities\KnowledgeBaseArticle as KnowledgeBaseArticleEntity; use Espo\Core\Di; use Espo\Core\Select\SearchParams; -use stdClass; - /** * @extends Record */ @@ -53,69 +49,6 @@ class KnowledgeBaseArticle extends Record implements protected $readOnlyAttributeList = ['order']; - public function getCopiedAttachments(string $id, ?string $parentType = null, ?string $parentId = null): stdClass - { - $ids = []; - $names = (object) []; - - if (empty($id)) { - throw new BadRequest(); - } - - $entity = $this->entityManager->getEntity('KnowledgeBaseArticle', $id); - - if (!$entity) { - throw new NotFound(); - } - - if (!$this->acl->checkEntity($entity, 'read')) { - throw new Forbidden(); - } - - $entity->loadLinkMultipleField('attachments'); - - $attachmentsIds = $entity->get('attachmentsIds'); - - foreach ($attachmentsIds as $attachmentId) { - /** @var Attachment|null $source */ - $source = $this->entityManager->getEntity('Attachment', $attachmentId); - - if ($source) { - $attachment = $this->entityManager->getNewEntity('Attachment'); - - $attachment->set('role', 'Attachment'); - $attachment->set('type', $source->get('type')); - $attachment->set('size', $source->get('size')); - $attachment->set('global', $source->get('global')); - $attachment->set('name', $source->get('name')); - $attachment->set('sourceId', $source->getSourceId()); - $attachment->set('storage', $source->get('storage')); - - if (!empty($parentType) && !empty($parentId)) { - $attachment->set('parentType', $parentType); - $attachment->set('parentId', $parentId); - } - - if ($this->fileStorageManager->exists($source)) { - $this->entityManager->saveEntity($attachment); - - $contents = $this->fileStorageManager->getContents($source); - - $this->fileStorageManager->putContents($attachment, $contents); - - $ids[] = $attachment->getId(); - - $names->{$attachment->getId()} = $attachment->get('name'); - } - } - } - - return (object) [ - 'ids' => $ids, - 'names' => $names, - ]; - } - /** * @param ?array $where * @throws NotFound diff --git a/application/Espo/Modules/Crm/Tools/KnowledgeBase/Service.php b/application/Espo/Modules/Crm/Tools/KnowledgeBase/Service.php new file mode 100644 index 0000000000..68e3f9de41 --- /dev/null +++ b/application/Espo/Modules/Crm/Tools/KnowledgeBase/Service.php @@ -0,0 +1,119 @@ +entityManager = $entityManager; + $this->attachmentAccessChecker = $attachmentAccessChecker; + $this->serviceContainer = $serviceContainer; + } + + /** + * Copy article attachments for re-using (e.g. in an email). + * + * @return Attachment[] + * @throws NotFound + * @throws Forbidden + */ + public function copyAttachments(string $id, FieldData $fieldData): array + { + /** @var ?KnowledgeBaseArticle $entity */ + $entity = $this->serviceContainer + ->get(KnowledgeBaseArticle::ENTITY_TYPE) + ->getEntity($id); + + if (!$entity) { + throw new NotFound(); + } + + $this->attachmentAccessChecker->check($fieldData); + + $list = []; + + foreach ($entity->getAttachmentIdList() as $attachmentId) { + $attachment = $this->copyAttachment($attachmentId, $fieldData); + + if ($attachment) { + $list[] = $attachment; + } + } + + return $list; + } + + private function copyAttachment(string $attachmentId, FieldData $fieldData): ?Attachment + { + /** @var ?Attachment $attachment */ + $attachment = $this->entityManager + ->getRDBRepositoryByClass(Attachment::class) + ->getById($attachmentId); + + if (!$attachment) { + return null; + } + + $copied = $this->getAttachmentRepository()->getCopiedAttachment($attachment); + + $copied->set('parentType', $fieldData->getParentType()); + $copied->set('relatedType', $fieldData->getRelatedType()); + $copied->setTargetField($fieldData->getField()); + $copied->setRole(Attachment::ROLE_ATTACHMENT); + + $this->getAttachmentRepository()->save($copied); + + return $copied; + } + + private function getAttachmentRepository(): AttachmentRepository + { + /** @var AttachmentRepository */ + return $this->entityManager->getRepositoryByClass(Attachment::class); + } +} diff --git a/application/Espo/Tools/Attachment/Service.php b/application/Espo/Tools/Attachment/Service.php index 21444d03eb..07e1cd1f17 100644 --- a/application/Espo/Tools/Attachment/Service.php +++ b/application/Espo/Tools/Attachment/Service.php @@ -101,8 +101,8 @@ class Service $copied->set('parentType', $data->getParentType()); $copied->set('relatedType', $data->getRelatedType()); - $copied->set('field', $data->getField()); - $copied->set('role', Attachment::ROLE_ATTACHMENT); + $copied->setTargetField($data->getField()); + $copied->setRole(Attachment::ROLE_ATTACHMENT); $this->getAttachmentRepository()->save($copied); diff --git a/client/modules/crm/src/knowledge-base-helper.js b/client/modules/crm/src/knowledge-base-helper.js index 0615416a99..c9e78626ad 100644 --- a/client/modules/crm/src/knowledge-base-helper.js +++ b/client/modules/crm/src/knowledge-base-helper.js @@ -52,8 +52,10 @@ define('crm:knowledge-base-helper', ['ajax'], function (Ajax) { model.get('name'); Ajax.postRequest('KnowledgeBaseArticle/action/getCopiedAttachments', { - id: model.id - }).then((data) => { + id: model.id, + parentType: 'Email', + field : 'attachments', + }).then(data => { attributes.attachmentsIds = data.ids; attributes.attachmentsNames = data.names; attributes.isHtml = true;