[TASK] Extract request processing from OpendocsToolbarItem 58/56358/12
authorMathias Brodala <mbrodala@pagemachine.de>
Sun, 18 Mar 2018 14:13:53 +0000 (15:13 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Thu, 29 Mar 2018 15:17:11 +0000 (17:17 +0200)
Change-Id: I786353379221af8da1436b77f654779d40fa77d3
Resolves: #84412
Releases: master
Reviewed-on: https://review.typo3.org/56358
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Tested-by: Frank Naegler <frank.naegler@typo3.org>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/opendocs/Classes/Backend/ToolbarItems/OpendocsToolbarItem.php
typo3/sysext/opendocs/Classes/Controller/OpenDocumentController.php [new file with mode: 0644]
typo3/sysext/opendocs/Classes/Service/OpenDocumentService.php [new file with mode: 0644]
typo3/sysext/opendocs/Configuration/Backend/AjaxRoutes.php
typo3/sysext/opendocs/Tests/Unit/Service/OpenDocumentServiceTest.php [new file with mode: 0644]

index 55257aa..2db37a5 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+declare(strict_types = 1);
 namespace TYPO3\CMS\Opendocs\Backend\ToolbarItems;
 
 /*
@@ -14,38 +15,37 @@ namespace TYPO3\CMS\Opendocs\Backend\ToolbarItems;
  * The TYPO3 project - inspiring people to share!
  */
 
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Fluid\View\StandaloneView;
+use TYPO3\CMS\Opendocs\Service\OpenDocumentService;
 
 /**
  * Main functionality to render a list of all open documents in the top bar of the TYPO3 Backend
- *
- * This class also contains hooks and AJAX calls related to the toolbar item dynamic updating processing
  */
 class OpendocsToolbarItem implements ToolbarItemInterface
 {
     /**
      * @var array
      */
-    protected $openDocs = [];
+    protected $recentDocs = [];
 
     /**
-     * @var array
+     * @var OpenDocumentService
      */
-    protected $recentDocs = [];
+    protected $documentService;
 
     /**
-     * Constructor
+     * Set up dependencies
+     *
+     * @param OpenDocumentService|null $documentService
      */
-    public function __construct()
+    public function __construct(OpenDocumentService $documentService = null)
     {
-        $this->loadDocsFromUserSession();
+        $this->documentService = $documentService ?: GeneralUtility::makeInstance(OpenDocumentService::class);
     }
 
     /**
@@ -55,21 +55,9 @@ class OpendocsToolbarItem implements ToolbarItemInterface
      */
     public function checkAccess()
     {
-        $conf = $this->getBackendUser()->getTSConfig('backendToolbarItem.tx_opendocs.disabled');
-        return (int)$conf['value'] !== 1;
-    }
+        $disabled = $this->getBackendUser()->getTSConfig('backendToolbarItem.tx_opendocs.disabled');
 
-    /**
-     * Loads the opened and recently opened documents from the user
-     */
-    public function loadDocsFromUserSession()
-    {
-        $backendUser = $this->getBackendUser();
-        $openDocs = $backendUser->getModuleData('FormEngine', 'ses');
-        if ($openDocs !== null) {
-            list($this->openDocs, ) = $openDocs;
-        }
-        $this->recentDocs = $backendUser->getModuleData('opendocs::recent') ?: [];
+        return (int)$disabled['value'] !== 1;
     }
 
     /**
@@ -80,11 +68,22 @@ class OpendocsToolbarItem implements ToolbarItemInterface
     public function getItem()
     {
         $view = $this->getFluidTemplateObject('ToolbarItem.html');
-        $view->assign('numDocs', count($this->openDocs));
+        $view->assign('numDocs', count($this->documentService->getOpenDocuments()));
+
         return $view->render();
     }
 
     /**
+     * This item has a drop down
+     *
+     * @return bool
+     */
+    public function hasDropDown()
+    {
+        return true;
+    }
+
+    /**
      * Render drop down via Fluid
      *
      * @return string HTML
@@ -93,69 +92,18 @@ class OpendocsToolbarItem implements ToolbarItemInterface
     {
         $view = $this->getFluidTemplateObject('DropDown.html');
         $view->assignMultiple([
-            'openDocuments' => $this->getMenuEntries($this->openDocs),
+            'openDocuments' => $this->getMenuEntries($this->documentService->getOpenDocuments()),
             // If there are "recent documents" in the list, add them
-            'recentDocuments' => $this->getMenuEntries($this->recentDocs)
+            'recentDocuments' => $this->getMenuEntries($this->documentService->getRecentDocuments()),
         ]);
-        return $view->render();
-    }
-
-    /**
-     * Get menu entries for all eligible records
-     *
-     * @param array $documents
-     * @return array
-     */
-    protected function getMenuEntries(array $documents): array
-    {
-        $entries = [];
-        foreach ($documents as $md5sum => $document) {
-            $menuEntry = $this->getMenuEntry($document, $md5sum);
-            if (is_array($menuEntry)) {
-                $entries[] = $menuEntry;
-            }
-        }
-        return $entries;
-    }
 
-    /**
-     * Returns the data for a recent or open document
-     *
-     * @param array $document
-     * @param string $md5sum
-     * @return array The data of a recent or closed document, or null if no record was found (e.g. deleted)
-     */
-    protected function getMenuEntry($document, $md5sum)
-    {
-        $table = $document[3]['table'];
-        $uid = $document[3]['uid'];
-        $record = BackendUtility::getRecordWSOL($table, $uid);
-        if (!is_array($record)) {
-            // Record seems to be deleted
-            return null;
-        }
-        $result = [];
-        $result['table'] = $table;
-        $result['record'] = $record;
-        $label = htmlspecialchars(strip_tags(htmlspecialchars_decode($document[0])));
-        $result['label'] = $label;
-        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
-        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
-        $link = (string)$uriBuilder->buildUriFromRoute('record_edit') . '&' . $document[2];
-        $pageId = (int)$document[3]['uid'];
-        if ($document[3]['table'] !== 'pages') {
-            $pageId = (int)$document[3]['pid'];
-        }
-        $onClickCode = 'jump(' . GeneralUtility::quoteJSvalue($link) . ', \'web_list\', \'web\', ' . $pageId . '); TYPO3.OpendocsMenu.toggleMenu(); return false;';
-        $result['onClickCode'] = $onClickCode;
-        $result['md5sum'] = $md5sum;
-        return $result;
+        return $view->render();
     }
 
     /**
      * No additional attributes
      *
-     * @return string List item HTML attibutes
+     * @return array List of attributes
      */
     public function getAdditionalAttributes()
     {
@@ -163,18 +111,15 @@ class OpendocsToolbarItem implements ToolbarItemInterface
     }
 
     /**
-     * This item has a drop down
+     * Position relative to others
      *
-     * @return bool
+     * @return int
      */
-    public function hasDropDown()
+    public function getIndex()
     {
-        return true;
+        return 30;
     }
 
-    /*******************
-     ***    HOOKS    ***
-     *******************/
     /**
      * Called as a hook in \TYPO3\CMS\Backend\Utility\BackendUtility::getUpdateSignalCode, calls a JS function to change
      * the number of opened documents
@@ -184,69 +129,68 @@ class OpendocsToolbarItem implements ToolbarItemInterface
     public function updateNumberOfOpenDocsHook(&$params)
     {
         $params['JScode'] = '
-                       if (top && top.TYPO3.OpendocsMenu) {
-                               top.TYPO3.OpendocsMenu.updateMenu();
-                       }
-               ';
+            if (top && top.TYPO3.OpendocsMenu) {
+                top.TYPO3.OpendocsMenu.updateMenu();
+            }
+        ';
     }
 
-    /******************
-     *** AJAX CALLS ***
-     ******************/
     /**
-     * Closes a document in the session and
+     * Get menu entries for all eligible records
      *
-     * @param ServerRequestInterface $request
-     * @return ResponseInterface
+     * @param array $documents
+     * @return array
      */
-    public function closeDocument(ServerRequestInterface $request): ResponseInterface
+    protected function getMenuEntries(array $documents): array
     {
-        $md5sum = $request->getParsedBody()['md5sum'] ?? $request->getQueryParams()['md5sum'];
-        if ($md5sum && isset($this->openDocs[$md5sum])) {
-            $backendUser = $this->getBackendUser();
-            // Add the document to be closed to the recent documents
-            $this->recentDocs = array_merge([$md5sum => $this->openDocs[$md5sum]], $this->recentDocs);
-            // Allow a maximum of 8 recent documents
-            if (count($this->recentDocs) > 8) {
-                $this->recentDocs = array_slice($this->recentDocs, 0, 8);
+        $entries = [];
+
+        foreach ($documents as $identifier => $document) {
+            $menuEntry = $this->getMenuEntry($document, $identifier);
+
+            if (!empty($menuEntry)) {
+                $entries[] = $menuEntry;
             }
-            // Remove it from the list of the open documents, and store the status
-            unset($this->openDocs[$md5sum]);
-            list(, $docDat) = $backendUser->getModuleData('FormEngine', 'ses');
-            $backendUser->pushModuleData('FormEngine', [$this->openDocs, $docDat]);
-            $backendUser->pushModuleData('opendocs::recent', $this->recentDocs);
         }
-        return $this->renderMenu();
-    }
 
-    /**
-     * Renders the menu so that it can be returned as response to an AJAX call
-     *
-     * @return ResponseInterface
-     */
-    public function renderMenu(): ResponseInterface
-    {
-        return new HtmlResponse($this->getDropDown());
+        return $entries;
     }
 
     /**
-     * Position relative to others
+     * Returns the data for a recent or open document
      *
-     * @return int
+     * @param array $document
+     * @param string $identifier
+     * @return array The data of a recent or closed document, or empty array if no record was found (e.g. deleted)
      */
-    public function getIndex()
+    protected function getMenuEntry(array $document, string $identifier): array
     {
-        return 30;
-    }
+        $table = $document[3]['table'];
+        $uid = $document[3]['uid'];
+        $record = BackendUtility::getRecordWSOL($table, $uid);
 
-    /**
-     * Returns the current BE user.
-     *
-     * @return BackendUserAuthentication
-     */
-    protected function getBackendUser()
-    {
-        return $GLOBALS['BE_USER'];
+        if (!is_array($record)) {
+            // Record seems to be deleted
+            return [];
+        }
+
+        $result = [];
+        $result['table'] = $table;
+        $result['record'] = $record;
+        $result['label'] = htmlspecialchars(strip_tags(htmlspecialchars_decode($document[0])));
+        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
+        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
+        $uri = (string)$uriBuilder->buildUriFromRoute('record_edit') . '&' . $document[2];
+        $pid = (int)$document[3]['pid'];
+
+        if ($document[3]['table'] === 'pages') {
+            $pid = (int)$document[3]['uid'];
+        }
+
+        $result['onClickCode'] = 'jump(' . GeneralUtility::quoteJSvalue($uri) . ', \'web_list\', \'web\', ' . $pid . '); TYPO3.OpendocsMenu.toggleMenu(); return false;';
+        $result['md5sum'] = $identifier;
+
+        return $result;
     }
 
     /**
@@ -261,13 +205,20 @@ class OpendocsToolbarItem implements ToolbarItemInterface
         $view->setLayoutRootPaths(['EXT:opendocs/Resources/Private/Layouts']);
         $view->setPartialRootPaths([
             'EXT:backend/Resources/Private/Partials/ToolbarItems',
-            'EXT:opendocs/Resources/Private/Partials/ToolbarItems'
+            'EXT:opendocs/Resources/Private/Partials/ToolbarItems',
         ]);
         $view->setTemplateRootPaths(['EXT:opendocs/Resources/Private/Templates/ToolbarItems']);
-
         $view->setTemplate($filename);
-
         $view->getRequest()->setControllerExtensionName('Opendocs');
+
         return $view;
     }
+
+    /**
+     * @return BackendUserAuthentication
+     */
+    protected function getBackendUser(): BackendUserAuthentication
+    {
+        return $GLOBALS['BE_USER'];
+    }
 }
diff --git a/typo3/sysext/opendocs/Classes/Controller/OpenDocumentController.php b/typo3/sysext/opendocs/Classes/Controller/OpenDocumentController.php
new file mode 100644 (file)
index 0000000..f124564
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Opendocs\Controller;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Backend\Backend\ToolbarItems\SystemInformationToolbarItem;
+use TYPO3\CMS\Core\Http\HtmlResponse;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Opendocs\Backend\ToolbarItems\OpendocsToolbarItem;
+use TYPO3\CMS\Opendocs\Service\OpenDocumentService;
+
+/**
+ * Controller for documents processing
+ *
+ * Contains AJAX endpoints of the open docs toolbar item click actions
+ */
+class OpenDocumentController
+{
+    /**
+     * @var OpenDocumentService
+     */
+    protected $documents;
+
+    /**
+     * @var SystemInformationToolbarItem
+     */
+    protected $toolbarItem;
+
+    /**
+     * Set up dependencies
+     */
+    public function __construct()
+    {
+        $this->documents = GeneralUtility::makeInstance(OpenDocumentService::class);
+        $this->toolbarItem = GeneralUtility::makeInstance(OpendocsToolbarItem::class);
+    }
+
+    /**
+     * Renders the menu so that it can be returned as response to an AJAX call
+     *
+     * @return ResponseInterface
+     */
+    public function renderMenu(): ResponseInterface
+    {
+        return new HtmlResponse($this->toolbarItem->getDropDown());
+    }
+
+    /**
+     * Closes a document and returns the updated menu
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     */
+    public function closeDocument(ServerRequestInterface $request): ResponseInterface
+    {
+        $identifier = $request->getParsedBody()['md5sum'] ?? $request->getQueryParams()['md5sum'];
+
+        if ($identifier) {
+            $this->documents->closeDocument($identifier);
+        }
+
+        return $this->renderMenu();
+    }
+}
diff --git a/typo3/sysext/opendocs/Classes/Service/OpenDocumentService.php b/typo3/sysext/opendocs/Classes/Service/OpenDocumentService.php
new file mode 100644 (file)
index 0000000..2dafd26
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Opendocs\Service;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+
+/**
+ * Access to open and recent documents
+ */
+class OpenDocumentService
+{
+    /**
+     * @var BackendUserAuthentication
+     */
+    protected $backendUser;
+
+    /**
+     * Constructor
+     */
+    public function __construct(BackendUserAuthentication $backendUser = null)
+    {
+        $this->backendUser = $backendUser ?: $GLOBALS['BE_USER'];
+    }
+
+    /**
+     * Get the list of open documents for the current user
+     *
+     * @return array
+     */
+    public function getOpenDocuments(): array
+    {
+        $openDocuments = [];
+        $sessionOpenDocuments = $this->backendUser->getModuleData('FormEngine', 'ses');
+
+        if ($sessionOpenDocuments !== null) {
+            $openDocuments = $sessionOpenDocuments[0];
+        }
+
+        return $openDocuments;
+    }
+
+    /**
+     * Get the list of recent documents for the current user
+     *
+     * @return array
+     */
+    public function getRecentDocuments(): array
+    {
+        return $this->backendUser->getModuleData('opendocs::recent') ?: [];
+    }
+
+    /**
+     * Close a document and add it to the list of recent documents
+     *
+     * @param string $identifier a document identifier (MD5 hash)
+     */
+    public function closeDocument(string $identifier): void
+    {
+        $openDocuments = $this->getOpenDocuments();
+
+        if (!isset($openDocuments[$identifier])) {
+            return;
+        }
+
+        $document = $openDocuments[$identifier];
+        unset($openDocuments[$identifier]);
+
+        $this->storeOpenDocuments($openDocuments);
+        $this->addToRecentDocuments($identifier, $document);
+    }
+
+    /**
+     * Store a list of open documents
+     *
+     * @param array $openDocuments
+     */
+    protected function storeOpenDocuments(array $openDocuments): void
+    {
+        list(, $lastOpenDocumentIdentifier) = $this->backendUser->getModuleData('FormEngine', 'ses');
+        $this->backendUser->pushModuleData('FormEngine', [$openDocuments, $lastOpenDocumentIdentifier]);
+    }
+
+    /**
+     * Add a document to the list of recent documents
+     *
+     * @param string $identifier identifier of the document
+     * @param array $document document data
+     */
+    protected function addToRecentDocuments(string $identifier, array $document): void
+    {
+        $recentDocuments = $this->getRecentDocuments();
+        $recentDocuments = array_merge(
+            [$identifier => $document],
+            $recentDocuments
+        );
+
+        // Allow a maximum of 8 recent documents
+        if (count($recentDocuments) > 8) {
+            $recentDocuments = array_slice($recentDocuments, 0, 8);
+        }
+
+        $this->backendUser->pushModuleData('opendocs::recent', $recentDocuments);
+    }
+}
index 75ad4e3..0f689bd 100644 (file)
@@ -7,12 +7,12 @@ return [
     // Render the opendocs toolbar item
     'opendocs_menu' => [
         'path' => '/opendocs/menu',
-        'target' => \TYPO3\CMS\Opendocs\Backend\ToolbarItems\OpendocsToolbarItem::class . '::renderMenu'
+        'target' => \TYPO3\CMS\Opendocs\Controller\OpenDocumentController::class . '::renderMenu'
     ],
 
     // Close a document
     'opendocs_closedoc' => [
         'path' => '/opendocs/close',
-        'target' => \TYPO3\CMS\Opendocs\Backend\ToolbarItems\OpendocsToolbarItem::class . '::closeDocument'
+        'target' => \TYPO3\CMS\Opendocs\Controller\OpenDocumentController::class . '::closeDocument'
     ],
 ];
diff --git a/typo3/sysext/opendocs/Tests/Unit/Service/OpenDocumentServiceTest.php b/typo3/sysext/opendocs/Tests/Unit/Service/OpenDocumentServiceTest.php
new file mode 100644 (file)
index 0000000..13b5a65
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\Opendocs\Tests\Unit\Service;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Opendocs\Service\OpenDocumentService;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Test case
+ */
+class OpenDocumentServiceTest extends UnitTestCase
+{
+    /**
+     * @var OpenDocumentService
+     */
+    protected $subject;
+
+    /**
+     * @var BackendUserAuthentication|\Prophecy\Prophecy\ObjectProphecy
+     */
+    protected $backendUser;
+
+    /**
+     * Set up this test case
+     */
+    protected function setUp()
+    {
+        $this->backendUser = $this->prophesize(BackendUserAuthentication::class);
+        $this->subject = new OpenDocumentService($this->backendUser->reveal());
+    }
+
+    /**
+     * @test
+     */
+    public function getsOpenDocumentsFromUserSession()
+    {
+        $this->backendUser->getModuleData('FormEngine', 'ses')->willReturn([
+            [
+                'identifier1' => [ 'data1' ],
+                'identifier2' => [ 'data2' ],
+            ],
+            'identifier2',
+        ]);
+
+        $openDocuments = $this->subject->getOpenDocuments();
+        $expected = [
+            'identifier1' => [ 'data1' ],
+            'identifier2' => [ 'data2' ],
+        ];
+
+        $this->assertEquals($expected, $openDocuments);
+    }
+
+    /**
+     * @test
+     */
+    public function handlesUserSessionWithoutOpenDocuments()
+    {
+        $this->backendUser->getModuleData('FormEngine', 'ses')->willReturn();
+
+        $openDocuments = $this->subject->getOpenDocuments();
+
+        $this->assertEquals([], $openDocuments);
+    }
+
+    /**
+     * @test
+     */
+    public function getsRecentDocumentsFromUserSession()
+    {
+        $this->backendUser->getModuleData('opendocs::recent')->willReturn([
+            'identifier1' => [ 'data1' ],
+        ]);
+
+        $recentDocuments = $this->subject->getRecentDocuments();
+        $expected = [
+            'identifier1' => [ 'data1' ],
+        ];
+
+        $this->assertEquals($expected, $recentDocuments);
+    }
+
+    /**
+     * @test
+     */
+    public function handlesUserSessionWithoutRecentDocuments()
+    {
+        $this->backendUser->getModuleData('opendocs::recent')->willReturn();
+
+        $recentDocuments = $this->subject->getRecentDocuments();
+
+        $this->assertEquals([], $recentDocuments);
+    }
+
+    /**
+     * @test
+     */
+    public function closesDocument()
+    {
+        $this->backendUser->getModuleData('FormEngine', 'ses')->willReturn([
+            [
+                'identifier8' => [ 'data8' ],
+                'identifier9' => [ 'data9' ],
+            ],
+            'identifier9',
+        ]);
+        $this->backendUser->getModuleData('opendocs::recent')->willReturn([
+                'identifier8' => [ 'data8' ],
+                'identifier7' => [ 'data7' ],
+                'identifier6' => [ 'data6' ],
+                'identifier5' => [ 'data5' ],
+                'identifier4' => [ 'data4' ],
+                'identifier3' => [ 'data3' ],
+                'identifier2' => [ 'data2' ],
+                'identifier1' => [ 'data1' ],
+        ]);
+
+        $expectedOpenDocumentsData = [
+            [
+                'identifier8' => [ 'data8' ],
+            ],
+            'identifier9',
+        ];
+        $this->backendUser->pushModuleData('FormEngine', $expectedOpenDocumentsData)->shouldBeCalled();
+
+        $expectedRecentDocumentsData = [
+                'identifier9' => [ 'data9' ],
+                'identifier8' => [ 'data8' ],
+                'identifier7' => [ 'data7' ],
+                'identifier6' => [ 'data6' ],
+                'identifier5' => [ 'data5' ],
+                'identifier4' => [ 'data4' ],
+                'identifier3' => [ 'data3' ],
+                'identifier2' => [ 'data2' ],
+        ];
+        $this->backendUser->pushModuleData('opendocs::recent', $expectedRecentDocumentsData)->shouldBeCalled();
+
+        $this->subject->closeDocument('identifier9');
+        $this->subject->closeDocument('identifier9');
+        $this->subject->closeDocument('unknownIdentifier');
+    }
+}