[FEATURE] Introduce service to handle multiple flash message queues 71/17571/8
authorAlexander Schnitzler <alex.schnitzler@typovision.de>
Thu, 17 Jan 2013 08:35:47 +0000 (09:35 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 25 Mar 2013 14:59:57 +0000 (15:59 +0100)
Currently there is just one system wide flash message queue. This
unfortunetly leads to problems using flash messages in extbase as
one cannot address a subset of messages to a dedicated extension/
plugin/controller/action. To resolve this this patch introduces a
flash message service that handles multiple queues which falls
back to the known behaviour by default.

Releases: 6.1
Resolves: #44593
Change-Id: I445499f7936d51ddd5ee89fc537974685b1843c7
Reviewed-on: https://review.typo3.org/17571
Reviewed-by: Andreas Wolf
Reviewed-by: Stefan Neufeind
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
typo3/sysext/core/Classes/Messaging/FlashMessageQueue.php
typo3/sysext/core/Classes/Messaging/FlashMessageService.php [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Messaging/FlashMessageQueueTest.php [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Messaging/FlashMessageServiceTest.php [new file with mode: 0644]

index 638c4db..33ae8cf 100644 (file)
@@ -5,6 +5,7 @@ namespace TYPO3\CMS\Core\Messaging;
  *  Copyright notice
  *
  *  (c) 2009-2013 Rupert Germann <rupi@gmx.li>
+ *  (c) 2013 Alexander Schnitzler <alex.schnitzler@typovision.de>
  *  All rights reserved
  *
  *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -30,44 +31,68 @@ namespace TYPO3\CMS\Core\Messaging;
  * A class which collects and renders flash messages.
  *
  * @author Rupert Germann <rupi@gmx.li>
+ * @author Alexander Schnitzler <alex.schnitzler@typovision.de>
  */
-class FlashMessageQueue {
-
-       static protected $messages = array();
+class FlashMessageQueue extends \SplQueue {
 
        /**
-        * Static class, no instances allowed.
+        * A unique identifier for this queue
+        *
+        * @var string
         */
-       protected function __construct() {
+       protected $identifier;
 
+       /**
+        * @param string $identifier The unique identifier for this queue
+        */
+       public function __construct($identifier) {
+               $this->identifier = $identifier;
        }
 
        /**
         * Adds a message either to the BE_USER session (if the $message has the storeInSession flag set)
-        * or it adds the message to self::$messages.
+        * or it enqueues the message.
         *
-        * @param object $message Instance of t3lib_FlashMessage, representing a message
+        * @param \TYPO3\CMS\Core\Messaging\FlashMessage $message Instance of \TYPO3\CMS\Core\Messaging\FlashMessage, representing a message
         * @return void
         */
-       static public function addMessage(\TYPO3\CMS\Core\Messaging\FlashMessage $message) {
+       public function enqueue(\TYPO3\CMS\Core\Messaging\FlashMessage $message) {
                if ($message->isSessionMessage()) {
-                       $queuedFlashMessages = self::getFlashMessagesFromSession();
-                       $queuedFlashMessages[] = $message;
-                       self::storeFlashMessagesInSession($queuedFlashMessages);
+                       $this->addFlashMessageToSession($message);
                } else {
-                       self::$messages[] = $message;
+                       parent::enqueue($message);
                }
        }
 
        /**
+        * @return void
+        */
+       public function dequeue() {
+               // deliberately empty
+       }
+
+       /**
+        * Adds the given flash message to the array of
+        * flash messages that will be stored in the session.
+        *
+        * @param \TYPO3\CMS\Core\Messaging\FlashMessage $message
+        * @return void
+        */
+       protected function addFlashMessageToSession(\TYPO3\CMS\Core\Messaging\FlashMessage $message) {
+               $queuedFlashMessages = $this->getFlashMessagesFromSession();
+               $queuedFlashMessages[] = $message;
+               $this->storeFlashMessagesInSession($queuedFlashMessages);
+       }
+
+       /**
         * Returns all messages from the current PHP session and from the current request.
         *
-        * @return array Array of t3lib_FlashMessage objects
+        * @return array Array of \TYPO3\CMS\Core\Messaging\FlashMessage objects
         */
-       static public function getAllMessages() {
+       protected function getAllMessages() {
                // Get messages from user session
-               $queuedFlashMessagesFromSession = self::getFlashMessagesFromSession();
-               $queuedFlashMessages = array_merge($queuedFlashMessagesFromSession, self::$messages);
+               $queuedFlashMessagesFromSession = $this->getFlashMessagesFromSession();
+               $queuedFlashMessages = array_merge($queuedFlashMessagesFromSession, $this->toArray());
                return $queuedFlashMessages;
        }
 
@@ -76,25 +101,25 @@ class FlashMessageQueue {
         * After fetching the messages the internal queue and the message queue in the session
         * will be emptied.
         *
-        * @return array Array of t3lib_FlashMessage objects
+        * @return array Array of \TYPO3\CMS\Core\Messaging\FlashMessage objects
         */
-       static public function getAllMessagesAndFlush() {
-               $queuedFlashMessages = self::getAllMessages();
+       protected function getAllMessagesAndFlush() {
+               $queuedFlashMessages = $this->getAllMessages();
                // Reset messages in user session
-               self::removeAllFlashMessagesFromSession();
+               $this->removeAllFlashMessagesFromSession();
                // Reset internal messages
-               self::$messages = array();
+               $this->clear();
                return $queuedFlashMessages;
        }
 
        /**
         * Stores given flash messages in the session
         *
-        * @param array $flashMessages Array of t3lib_FlashMessage
+        * @param array $flashMessages Array of \TYPO3\CMS\Core\Messaging\FlashMessage
         * @return void
         */
-       static protected function storeFlashMessagesInSession(array $flashMessages) {
-               self::getUserByContext()->setAndSaveSessionData('core.template.flashMessages', $flashMessages);
+       protected function storeFlashMessagesInSession(array $flashMessages) {
+               $this->getUserByContext()->setAndSaveSessionData($this->identifier, $flashMessages);
        }
 
        /**
@@ -102,18 +127,18 @@ class FlashMessageQueue {
         *
         * @return void
         */
-       static protected function removeAllFlashMessagesFromSession() {
-               self::getUserByContext()->setAndSaveSessionData('core.template.flashMessages', NULL);
+       protected function removeAllFlashMessagesFromSession() {
+               $this->getUserByContext()->setAndSaveSessionData($this->identifier, NULL);
        }
 
        /**
         * Returns current flash messages from the session, making sure to always
         * return an array.
         *
-        * @return array An array of t3lib_FlashMessage flash messages.
+        * @return array An array of \TYPO3\CMS\Core\Messaging\FlashMessage flash messages.
         */
-       static protected function getFlashMessagesFromSession() {
-               $flashMessages = self::getUserByContext()->getSessionData('core.template.flashMessages');
+       protected function getFlashMessagesFromSession() {
+               $flashMessages = $this->getUserByContext()->getSessionData($this->identifier);
                return is_array($flashMessages) ? $flashMessages : array();
        }
 
@@ -122,7 +147,7 @@ class FlashMessageQueue {
         *
         * @return object User object
         */
-       static protected function getUserByContext() {
+       protected function getUserByContext() {
                return TYPO3_MODE === 'BE' ? $GLOBALS['BE_USER'] : $GLOBALS['TSFE']->fe_user;
        }
 
@@ -131,18 +156,121 @@ class FlashMessageQueue {
         *
         * @return string All flash messages in the queue rendered as HTML.
         */
-       static public function renderFlashMessages() {
+       protected function renderFlashMessages() {
                $content = '';
-               $flashMessages = self::getAllMessagesAndFlush();
+               $flashMessages = $this->getAllMessagesAndFlush();
                if (count($flashMessages)) {
                        foreach ($flashMessages as $flashMessage) {
+                               /** @var $flashMessage \TYPO3\CMS\Core\Messaging\FlashMessage */
                                $content .= $flashMessage->render();
                        }
                }
                return $content;
        }
 
-}
+       /**
+        * Returns all items of the queue as array
+        *
+        * @return array
+        */
+       public function toArray() {
+               $array = array();
+               $this->rewind();
+               while ($this->valid()) {
+                       $array[] = $this->current();
+                       $this->next();
+               }
+               return $array;
+       }
 
+       /**
+        * Removes all items from the queue
+        *
+        * @return void
+        */
+       public function clear() {
+               $this->rewind();
+               while (!$this->isEmpty()) {
+                       parent::dequeue();
+               }
+       }
+
+       /**
+        * This method provides a fallback for deprecated static calls like:
+        * FlashMessageQueue::renderFlashMessages,
+        * FlashMessageQueue::getAllMessagesAndFlush,
+        * FlashMessageQueue::getAllMessages and
+        * FlashMessageQueue::addMessage
+        *
+        * From 6.3 on __callStatic and __call will be removed and the
+        * protected non static methods "renderFlashMessages",
+        * "getAllMessagesAndFlush", "getAllMessages" and "addMessage"
+        * will be made public.
+        *
+        * @param string $name
+        * @param array $arguments
+        * @throws \RuntimeException
+        * @return void|array|string
+        * @deprecated since 6.1 will be removed in 6.3
+        */
+       static public function __callStatic($name, array $arguments) {
+               \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction();
+               /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
+               $flashMessageService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Core\Messaging\FlashMessageService');
+               $identifier = 'core.template.flashMessages';
+               switch ($name) {
+                       case 'renderFlashMessages':
+                               return $flashMessageService->getMessageQueueByIdentifier($identifier)->renderFlashMessages();
+                               break;
+                       case 'getAllMessagesAndFlush':
+                               return $flashMessageService->getMessageQueueByIdentifier($identifier)->getAllMessagesAndFlush();
+                               break;
+                       case 'getAllMessages':
+                               return $flashMessageService->getMessageQueueByIdentifier($identifier)->getAllMessages();
+                               break;
+                       case 'addMessage':
+                               $flashMessageService->getMessageQueueByIdentifier($identifier)->enqueue(current($arguments));
+                               break;
+                       default:
+                               throw new \RuntimeException('The requested method "' . $name . '" cannot be called via __callStatic.', 1363300030);
+                               break;
+               }
+       }
+
+       /**
+        * This method is deprecated but will not log a deprecation
+        * message because once the here used method names are 'free'
+        * again they will be implemented natively in this class. This
+        * is not possible at the moment because these methods have
+        * been static and need to be statically callable through
+        * __callStatic until 6.3.
+        *
+        * @param string $name
+        * @param array $arguments
+        * @throws \RuntimeException
+        * @return void|array|string
+        * @see __callStatic
+        * @deprecated since 6.1 will be removed in 6.3
+        */
+       public function __call($name, array $arguments) {
+               switch ($name) {
+                       case 'renderFlashMessages':
+                               return $this->renderFlashMessages();
+                               break;
+                       case 'getAllMessagesAndFlush':
+                               return $this->getAllMessagesAndFlush();
+                               break;
+                       case 'getAllMessages':
+                               return $this->getAllMessages();
+                               break;
+                       case 'addMessage':
+                               $this->enqueue(current($arguments));
+                               break;
+                       default:
+                               throw new \RuntimeException('The requested method "' . $name . '" cannot be called via __call.', 1363300072);
+                               break;
+               }
+       }
+}
 
 ?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Messaging/FlashMessageService.php b/typo3/sysext/core/Classes/Messaging/FlashMessageService.php
new file mode 100644 (file)
index 0000000..73a0eab
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+namespace TYPO3\CMS\Core\Messaging;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Alexander Schnitzler <alex.schnitzler@typovision.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+/**
+ * A class representing flash messages.
+ *
+ * @author Alexander Schnitzler <alex.schnitzler@typovision.de>
+ */
+class FlashMessageService implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * Array of \TYPO3\CMS\Core\Messaging\FlashMessageQueue objects
+        *
+        * @var array
+        */
+       protected $flashMessageQueues = array();
+
+       /**
+        * Return the message queue for the given identifier.
+        * If no queue exists, an empty one will be created.
+        *
+        * @param string $identifier
+        * @return \TYPO3\CMS\Core\Messaging\FlashMessageQueue
+        * @api
+        */
+       public function getMessageQueueByIdentifier($identifier = 'core.template.flashMessages') {
+               if (!isset($this->flashMessageQueues[$identifier])) {
+                       $this->flashMessageQueues[$identifier] = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Core\Messaging\FlashMessageQueue', $identifier);
+               }
+               return $this->flashMessageQueues[$identifier];
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/Unit/Messaging/FlashMessageQueueTest.php b/typo3/sysext/core/Tests/Unit/Messaging/FlashMessageQueueTest.php
new file mode 100644 (file)
index 0000000..81fda75
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Unit\Messaging;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Alexander Schnitzler <alex.schnitzler@typovision.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+/**
+ * Testcase for the TYPO3\CMS\Core\Messaging\FlashMessageQueue class.
+ *
+ * @author Alexander Schnitzler <alex.schnitzler@typovision.de>
+ */
+class FlashMessageQueueTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
+
+       /**
+        * @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue|\PHPUnit_Framework_MockObject_MockObject|\Tx_Phpunit_Interface_AccessibleObject
+        */
+       protected $flashMessageQueue;
+
+       /**
+        * @var \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject
+        */
+       protected $frontendUser;
+
+       public function setUp() {
+               $this->frontendUser = $this->getMock('TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication', array('dummy'));
+               $this->flashMessageQueue = $this->getAccessibleMock(
+                       'TYPO3\CMS\Core\Messaging\FlashMessageQueue',
+                       array('getUserByContext'),
+                       array('core.template.flashMessages')
+               );
+
+               $this->flashMessageQueue->expects($this->any())->method('getUserByContext')->will($this->returnValue($this->frontendUser));
+       }
+
+       /**
+        * @test
+        */
+       public function userSessionInitiallyIsEmpty() {
+               $this->assertSame(array(), $this->flashMessageQueue->_call('getFlashMessagesFromSession'));
+       }
+
+       /**
+        * @test
+        */
+       public function enqueueTransientFlashMessageKeepsSessionEmpty() {
+               $this->flashMessageQueue->enqueue(new \TYPO3\CMS\Core\Messaging\FlashMessage('Foo', 'Bar', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, FALSE));
+
+               $this->assertSame(array(), $this->flashMessageQueue->_call('getFlashMessagesFromSession'));
+       }
+
+       /**
+        * @test
+        */
+       public function enqueueSessionFlashMessageWritesSessionEntry() {
+               $flashMessage = new \TYPO3\CMS\Core\Messaging\FlashMessage('Foo', 'Bar', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, TRUE);
+               $this->flashMessageQueue->enqueue($flashMessage);
+
+               $this->assertSame(array($flashMessage), $this->flashMessageQueue->_call('getFlashMessagesFromSession'));
+       }
+
+       /**
+        * @test
+        */
+       public function getAllMessagesReturnsSessionFlashMessageAndTransientFlashMessage() {
+               $flashMessage1 = new \TYPO3\CMS\Core\Messaging\FlashMessage('Transient', 'Title', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, FALSE);
+               $flashMessage2 = new \TYPO3\CMS\Core\Messaging\FlashMessage('Session', 'Title', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, TRUE);
+               $this->flashMessageQueue->enqueue($flashMessage1);
+               $this->flashMessageQueue->enqueue($flashMessage2);
+
+               $this->assertCount(2, $this->flashMessageQueue->getAllMessages());
+       }
+
+       /**
+        * @test
+        */
+       public function clearClearsTheQueue() {
+               $flashMessage1 = new \TYPO3\CMS\Core\Messaging\FlashMessage('Transient', 'Title', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, FALSE);
+               $flashMessage2 = new \TYPO3\CMS\Core\Messaging\FlashMessage('Transient', 'Title', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, FALSE);
+               $this->flashMessageQueue->enqueue($flashMessage1);
+               $this->flashMessageQueue->enqueue($flashMessage2);
+               $this->flashMessageQueue->clear();
+
+               $this->assertSame(0, $this->flashMessageQueue->count());
+       }
+
+       /**
+        * @test
+        */
+       public function toArrayOnlyRespectsTransientFlashMessages() {
+               $flashMessage1 = new \TYPO3\CMS\Core\Messaging\FlashMessage('Transient', 'Title', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, FALSE);
+               $flashMessage2 = new \TYPO3\CMS\Core\Messaging\FlashMessage('Transient', 'Title', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, TRUE);
+               $this->flashMessageQueue->enqueue($flashMessage1);
+               $this->flashMessageQueue->enqueue($flashMessage2);
+
+               $this->assertCount(1, $this->flashMessageQueue->toArray());
+       }
+
+       /**
+        * @test
+        */
+       public function toArrayReturnsEmptyArrayWithForEmptyQueue() {
+               $this->assertSame(array(), $this->flashMessageQueue->toArray());
+       }
+
+       /**
+        * @test
+        */
+       public function getAllMessagesAndFlushClearsSessionStack() {
+               $flashMessage = new \TYPO3\CMS\Core\Messaging\FlashMessage('Transient', 'Title', \TYPO3\CMS\Core\Messaging\FlashMessage::OK, TRUE);
+               $this->flashMessageQueue->enqueue($flashMessage);
+               $this->flashMessageQueue->getAllMessagesAndFlush();
+
+               /** @var $frontendUser \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication */
+               $frontendUser = $this->flashMessageQueue->_call('getUserByContext');
+
+               $this->assertNull($frontendUser->getSessionData('core.template.flashMessages'));
+       }
+
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/Unit/Messaging/FlashMessageServiceTest.php b/typo3/sysext/core/Tests/Unit/Messaging/FlashMessageServiceTest.php
new file mode 100644 (file)
index 0000000..eb5dbe4
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Unit\Messaging;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Alexander Schnitzler <alex.schnitzler@typovision.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+/**
+ * Testcase for the TYPO3\CMS\Core\Messaging\FlashMessageService class.
+ *
+ * @author Alexander Schnitzler <alex.schnitzler@typovision.de>
+ */
+class FlashMessageServiceTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
+
+       /**
+        * @var \TYPO3\CMS\Core\Messaging\FlashMessageService|\PHPUnit_Framework_MockObject_MockObject|\Tx_Phpunit_Interface_AccessibleObject
+        */
+       protected $flashMessageService;
+
+       public function setUp() {
+               $this->flashMessageService = $this->getAccessibleMock('TYPO3\CMS\Core\Messaging\FlashMessageService', array('dummy'));
+       }
+
+       /**
+        * @test
+        */
+       public function flashMessageServiceInitiallyIsEmpty() {
+               $this->assertSame(array(), $this->flashMessageService->_get('flashMessageQueues'));
+       }
+
+       /**
+        * @test
+        */
+       public function getMessageQueueByIdentifierRegistersNewFlashmessageQueuesOnlyOnce() {
+               $this->assertSame(
+                       $this->flashMessageService->getMessageQueueByIdentifier('core.template.flashMessages'),
+                       $this->flashMessageService->getMessageQueueByIdentifier('core.template.flashMessages')
+               );
+       }
+}
+
+?>
\ No newline at end of file