diff --git a/module/Olcs/src/Controller/ConversationsController.php b/module/Olcs/src/Controller/ConversationsController.php index 43f9e86fe..ee89b3c6d 100644 --- a/module/Olcs/src/Controller/ConversationsController.php +++ b/module/Olcs/src/Controller/ConversationsController.php @@ -4,15 +4,18 @@ namespace Olcs\Controller; +use Common\Category; use Common\Controller\Interfaces\ToggleAwareInterface; use Common\Controller\Lva\AbstractController; use Common\FeatureToggle; use Common\Form\Form; +use Common\Service\Helper\FileUploadHelperService; use Common\Service\Helper\FlashMessengerHelperService; use Common\Service\Helper\FormHelperService; use Common\Service\Table\TableFactory; use Dvsa\Olcs\Transfer\Command\Messaging\Conversation\Create; use Dvsa\Olcs\Transfer\Command\Messaging\Message\Create as CreateMessageCommand; +use Dvsa\Olcs\Transfer\Query\Messaging\Documents; use Dvsa\Olcs\Transfer\Query\Messaging\Messages\ByConversation as ByConversationQuery; use Dvsa\Olcs\Transfer\Query\Messaging\Conversations\ByOrganisation as ByOrganisationQuery; use Dvsa\Olcs\Utils\Translation\NiTextTranslation; @@ -36,6 +39,7 @@ class ConversationsController extends AbstractController implements ToggleAwareI protected TableFactory $tableFactory; protected FormHelperService $formHelperService; protected Navigation $navigationService; + protected FileUploadHelperService $uploadHelper; public function __construct( NiTextTranslation $niTextTranslationUtil, @@ -43,12 +47,14 @@ public function __construct( FlashMessengerHelperService $flashMessengerHelper, TableFactory $tableFactory, FormHelperService $formHelperService, - Navigation $navigationService + Navigation $navigationService, + FileUploadHelperService $uploadHelper ) { $this->flashMessengerHelper = $flashMessengerHelper; $this->tableFactory = $tableFactory; $this->formHelperService = $formHelperService; $this->navigationService = $navigationService; + $this->uploadHelper = $uploadHelper; parent::__construct($niTextTranslationUtil, $authService); } @@ -110,13 +116,17 @@ private function submitConversation(\Laminas\Form\Form $form) { $response = $this->handleCommand($this->mapFormDataToCommand($form)); if (!$response->isOk()) { - $this->flashMessengerHelper->addErrorMessage('There was an server error when submitting your conversation; please try later'); + $this->flashMessengerHelper->addErrorMessage( + 'There was an server error when submitting your conversation; please try later', + ); return $this->addAction(); } $conversationId = $response->getResult()['id']['conversation'] ?? null; if (empty($conversationId)) { - $this->flashMessengerHelper->addErrorMessage('There was an server error when submitting your conversation; please try later'); + $this->flashMessengerHelper->addErrorMessage( + 'There was an server error when submitting your conversation; please try later', + ); return $this->addAction(); } @@ -137,7 +147,7 @@ private function mapFormDataToCommand(\Laminas\Form\Form $form): Create $appOrLicNoPrefix = substr($data['form-actions']['appOrLicNo'], 0, 1); $appOrLicNoSuffix = substr($data['form-actions']['appOrLicNo'], 1); - switch($appOrLicNoPrefix) { + switch ($appOrLicNoPrefix) { case MessagingAppOrLicNo::PREFIX_LICENCE: $processedData['licence'] = $appOrLicNoSuffix; break; @@ -176,21 +186,25 @@ public function viewAction() } $form = $this->formHelperService->createForm(ReplyForm::class, true, false); + $form->get('correlationId')->setValue(sha1(microtime())); $this->formHelperService->setFormActionFromRequest($form, $this->getRequest()); - $table = $this->tableFactory - ->buildTable('messages-view', $messages, $params); + $table = $this->tableFactory->buildTable('messages-view', $messages, $params); + + $canUploadFiles = $this->getCurrentOrganisation()['isMessagingFileUploadEnabled']; $view = new ViewModel( [ - 'table' => $table, - 'form' => $form, - 'canReply' => $canReply, + 'table' => $table, + 'form' => $form, + 'canReply' => $canReply, + 'openReply' => false, + 'canUploadFiles' => $canUploadFiles, ], ); $view->setTemplate('messages-view'); - if ($this->getRequest()->isPost() && $this->params()->fromPost('action') === 'reply') { + if ($this->getRequest()->isPost()) { return $this->parseReply($view, $form); } @@ -200,10 +214,21 @@ public function viewAction() /** @return Response|ViewModel */ protected function parseReply(ViewModel $view, Form $form) { - $form->setData((array)$this->params()->fromPost()); + $form->setData((array)$this->getRequest()->getPost()); $form->get('id')->setValue($this->params()->fromRoute('conversation')); - if (!$form->isValid()) { + $hasProcessedFiles = $this->processFiles( + $form, + 'form-actions->file', + [$this, 'processFileUpload'], + [$this, 'deleteFile'], + [$this, 'getUploadedFiles'], + 'form-actions->file->fileCount', + ); + + $view->setVariable('openReply', $hasProcessedFiles); + + if ($hasProcessedFiles || $this->params()->fromPost('action') !== 'reply' || !$form->isValid()) { return $view; } @@ -211,6 +236,7 @@ protected function parseReply(ViewModel $view, Form $form) CreateMessageCommand::create( [ 'conversation' => $this->params()->fromRoute('conversationId'), + 'correlationId' => $this->getRequest()->getPost('correlationId'), 'messageContent' => $form->get('form-actions')->get('reply')->getValue(), ], ), @@ -225,4 +251,31 @@ protected function parseReply(ViewModel $view, Form $form) return parent::indexAction(); } + + public function processFileUpload($file) + { + $dtoData = [ + 'category' => Category::CATEGORY_LICENSING, + 'subCategory' => Category::DOC_SUB_CATEGORY_MESSAGING, + 'description' => $file['name'], + 'isExternal' => true, + 'messagingConversation' => $this->params()->fromRoute('conversationId'), + 'correlationId' => $this->getRequest()->getPost('correlationId'), + ]; + + $this->uploadFile($file, $dtoData); + } + + public function getUploadedFiles() + { + $params = [ + 'category' => Category::CATEGORY_LICENSING, + 'subCategory' => Category::DOC_SUB_CATEGORY_MESSAGING, + 'correlationId' => $this->getRequest()->getPost('correlationId'), + ]; + + $response = $this->handleQuery(Documents::create($params)); + + return $response->getResult(); + } } diff --git a/module/Olcs/src/Controller/Factory/ConversationsControllerFactory.php b/module/Olcs/src/Controller/Factory/ConversationsControllerFactory.php index ed687c40b..2e35f0953 100644 --- a/module/Olcs/src/Controller/Factory/ConversationsControllerFactory.php +++ b/module/Olcs/src/Controller/Factory/ConversationsControllerFactory.php @@ -4,6 +4,7 @@ namespace Olcs\Controller\Factory; +use Common\Service\Helper\FileUploadHelperService; use Common\Service\Helper\FlashMessengerHelperService; use Common\Service\Helper\FormHelperService; use Common\Service\Table\TableFactory; @@ -27,6 +28,7 @@ public function __invoke( $tableFactory = $container->get(TableFactory::class); $formHelperService = $container->get(FormHelperService::class); $navigationService = $container->get(Navigation::class); + $uploadHolder = $container->get(FileUploadHelperService::class); return new ConversationsController( $niTextTranslationUtil, @@ -34,7 +36,8 @@ public function __invoke( $flashMessengerHelper, $tableFactory, $formHelperService, - $navigationService + $navigationService, + $uploadHolder ); } } diff --git a/module/Olcs/src/Form/Model/Fieldset/Message/Reply.php b/module/Olcs/src/Form/Model/Fieldset/Message/Reply.php index 3f432d797..fbbeb00e3 100644 --- a/module/Olcs/src/Form/Model/Fieldset/Message/Reply.php +++ b/module/Olcs/src/Form/Model/Fieldset/Message/Reply.php @@ -5,9 +5,11 @@ namespace Olcs\Form\Model\Fieldset\Message; use Common\Form\Elements\InputFilters\ActionButton; +use Common\Form\Model\Fieldset\MultipleFileUpload; use Laminas\Form\Annotation as Form; -use Laminas\Form\Element\File; use Laminas\Form\Element\Textarea; +use Laminas\Filter\StringTrim; +use Laminas\Validator\StringLength; class Reply { @@ -18,12 +20,19 @@ class Reply * }) * @Form\Options({"label": "You can enter up to 1000 characters"}) * @Form\Required(true) - * @Form\Type(\Laminas\Form\Element\Textarea::class) - * @Form\Filter(\Laminas\Filter\StringTrim::class) - * @Form\Validator(\Laminas\Validator\StringLength::class, options={"min": 5, "max": 1000}) + * @Form\Type(Textarea::class) + * @Form\Filter(StringTrim::class) + * @Form\Validator(StringLength::class, options={"min": 5, "max": 1000}) */ public ?TextArea $reply = null; + /** + * @Form\Name("file") + * @Form\Attributes({"id": "file"}) + * @Form\ComposedObject(MultipleFileUpload::class) + */ + public ?MultipleFileUpload $file = null; + /** * @Form\Attributes({ * "type": "submit", @@ -34,7 +43,7 @@ class Reply * @Form\Options({ * "label": "Send message" * }) - * @Form\Type(\Common\Form\Elements\InputFilters\ActionButton::class) + * @Form\Type(ActionButton::class) */ public ?ActionButton $send = null; } diff --git a/module/Olcs/src/Form/Model/Form/Message/Reply.php b/module/Olcs/src/Form/Model/Form/Message/Reply.php index a4f6a83bc..ad9b7d488 100644 --- a/module/Olcs/src/Form/Model/Form/Message/Reply.php +++ b/module/Olcs/src/Form/Model/Form/Message/Reply.php @@ -4,6 +4,7 @@ namespace Olcs\Form\Model\Form\Message; +use Common\Form\Model\Fieldset\MultipleFileUpload; use Laminas\Form\Annotation as Form; use Laminas\Form\Element\Hidden; use Olcs\Form\Model\Fieldset\Message\Reply as ReplyFieldset; @@ -23,6 +24,12 @@ class Reply */ public ?Hidden $id = null; + /** + * @Form\Attributes({"value": ""}) + * @Form\Type(\Laminas\Form\Element\Hidden::class) + */ + public ?Hidden $correlationId = null; + /** * @Form\Attributes({"value": "reply"}) * @Form\Type(\Laminas\Form\Element\Hidden::class) diff --git a/module/Olcs/view/messages-view.phtml b/module/Olcs/view/messages-view.phtml index 62ceeac4c..fe5a60f5d 100644 --- a/module/Olcs/view/messages-view.phtml +++ b/module/Olcs/view/messages-view.phtml @@ -21,7 +21,7 @@ echo $this->partial( ?> canReply): ?> -
+
openReply): ?>open> Send a reply diff --git a/test/Olcs/src/Controller/ConversationsControllerTest.php b/test/Olcs/src/Controller/ConversationsControllerTest.php index 87bc2d90f..fe27fc987 100644 --- a/test/Olcs/src/Controller/ConversationsControllerTest.php +++ b/test/Olcs/src/Controller/ConversationsControllerTest.php @@ -6,11 +6,14 @@ use Common\Controller\Plugin\HandleQuery; use Common\Controller\Plugin\Redirect; use Common\Form\Form; +use Common\Rbac\User; use Common\Service\Cqrs\Response; +use Common\Service\Helper\FileUploadHelperService; use Common\Service\Helper\FlashMessengerHelperService; use Common\Service\Helper\FormHelperService; use Common\Service\Table\TableFactory; use Dvsa\Olcs\Utils\Translation\NiTextTranslation; +use Laminas\Form\Element\Hidden; use Laminas\Form\Element\Text; use Laminas\Form\Fieldset; use Laminas\Http\Request; @@ -40,6 +43,7 @@ public function setUp(): void $this->mockNavigation = m::mock(Navigation::class)->shouldIgnoreMissing(); $this->mockForm = m::mock(Form::class); $this->mockParams = m::mock(Params::class); + $this->mockUploadHelper = m::mock(FileUploadHelperService::class); $this->sut = m::mock(Sut::class) ->makePartial() @@ -52,6 +56,7 @@ public function setUp(): void $this->setMockedProperties($reflectionClass, 'tableFactory', $this->mockTableFactory); $this->setMockedProperties($reflectionClass, 'formHelperService', $this->mockFormHelperService); $this->setMockedProperties($reflectionClass, 'navigationService', $this->mockNavigation); + $this->setMockedProperties($reflectionClass, 'uploadHelper', $this->mockUploadHelper); $this->mockFormHelperService->shouldReceive('createForm') ->once() @@ -81,13 +86,15 @@ public function testViewAction(): void $mockResponse->shouldReceive('isOk') ->andReturn(true); $mockResponse->shouldReceive('getResult') - ->andReturn([ - 'extra' => [ - 'conversation' => [ - 'isClosed' => true, + ->andReturn( + [ + 'extra' => [ + 'conversation' => [ + 'isClosed' => true, + ], ], ], - ]); + ); $mockHandleQuery = m::mock(HandleQuery::class) ->makePartial(); @@ -107,11 +114,29 @@ public function testViewAction(): void ->with('conversationId') ->andReturn(1); + $mockUser = m::mock(User::class); + $mockUser->shouldReceive('getUserData') + ->once() + ->andReturn( + [ + 'organisationUsers' => [ + [ + 'organisation' => [ + 'isMessagingFileUploadEnabled' => true, + ], + ], + ], + ], + ); + $this->sut->shouldReceive('params') ->andReturn($this->mockParams); $this->sut->shouldReceive('plugin') ->with('handleQuery') ->andReturn($mockHandleQuery); + $this->sut->shouldReceive('plugin') + ->with('currentUser') + ->andReturn($mockUser); $table = ''; @@ -127,6 +152,15 @@ public function testViewAction(): void ->shouldReceive('findBy->setActive') ->once(); + $mockFormElement = m::mock(Hidden::class); + $mockFormElement->shouldReceive('setValue') + ->once(); + + $this->mockForm->shouldReceive('get') + ->once() + ->with('correlationId') + ->andReturn($mockFormElement); + $view = $this->sut->viewAction(); $this->assertInstanceOf(ViewModel::class, $view); $this->assertEquals($table, $view->getVariable('table')); @@ -138,15 +172,23 @@ public function testReply(): void $mockRequest->shouldReceive('isPost') ->once() ->andReturn(true); + $mockRequest->shouldReceive('getPost') + ->once() + ->withNoArgs() + ->andReturn([]); + $mockRequest->shouldReceive('getPost') + ->with('correlationId') + ->once() + ->andReturn('123'); + + $this->mockForm->shouldReceive('setData') + ->once() + ->with([]); $this->mockParams->shouldReceive('fromPost') ->once() ->with('action') ->andReturn('reply'); - $this->mockParams->shouldReceive('fromPost') - ->once() - ->withNoArgs() - ->andReturn(['a' => 'b']); $this->mockParams->shouldReceive('fromRoute') ->once() ->with('conversation') @@ -157,7 +199,7 @@ public function testReply(): void ->andReturn(['a' => 'b']); $this->sut->shouldReceive('getRequest') - ->twice() + ->times(5) ->andReturn($mockRequest); $mockCommandReturn = m::mock(Response::class); @@ -181,10 +223,6 @@ function ($command) { ->with('handleCommand') ->andReturn($mockCommandHandler); - $this->mockForm->shouldReceive('setData') - ->once() - ->with(['a' => 'b']); - $mockFormElement = m::mock(Text::class); $mockFormElement->shouldReceive('setValue') ->once() @@ -229,6 +267,46 @@ function ($command) { ->with('conversations/view', ['a' => 'b']) ->andReturn($mockViewModel); + $this->mockUploadHelper + ->shouldReceive('setForm') + ->once() + ->andReturn($this->mockUploadHelper); + + $this->mockUploadHelper + ->shouldReceive('setSelector') + ->once() + ->andReturn($this->mockUploadHelper); + + $this->mockUploadHelper + ->shouldReceive('setUploadCallback') + ->once() + ->andReturn($this->mockUploadHelper); + + $this->mockUploadHelper + ->shouldReceive('setDeleteCallback') + ->once() + ->andReturn($this->mockUploadHelper); + + $this->mockUploadHelper + ->shouldReceive('setLoadCallback') + ->once() + ->andReturn($this->mockUploadHelper); + + $this->mockUploadHelper + ->shouldReceive('setRequest') + ->once() + ->andReturn($this->mockUploadHelper); + + $this->mockUploadHelper + ->shouldReceive('setCountSelector') + ->once() + ->andReturn($this->mockUploadHelper); + + $this->mockUploadHelper + ->shouldReceive('process') + ->once() + ->andReturn(false); + $this->sut->shouldReceive('plugin') ->once() ->with('redirect')