identifier = $identifier; } /** * @return string */ public function getIdentifier() { return $this->identifier; } /** * Adds a message either to the BE_USER session (if the $message has the storeInSession flag set) * or it enqueues the message. * * @param FlashMessage $message Instance of \TYPO3\CMS\Core\Messaging\FlashMessage, representing a message * @throws \TYPO3\CMS\Core\Exception */ public function enqueue($message): void { if (!($message instanceof FlashMessage)) { throw new Exception( 'FlashMessageQueue::enqueue() expects an object of type \TYPO3\CMS\Core\Messaging\FlashMessage but got type "' . get_debug_type($message) . '"', 1376833554 ); } if ($message->isSessionMessage()) { $this->addFlashMessageToSession($message); } else { parent::enqueue($message); } } public function addMessage(FlashMessage ...$messages) { foreach ($messages as $message) { $this->enqueue($message); } } /** * This method is empty, as it will not move any flash message (e.g. from the session) * * @phpstan-return null */ public function dequeue(): mixed { // deliberately empty return null; } /** * Adds the given flash message to the array of * flash messages that will be stored in the session. */ protected function addFlashMessageToSession(FlashMessage $message) { $queuedFlashMessages = $this->getFlashMessagesFromSession(); $queuedFlashMessages[] = $message; $this->storeFlashMessagesInSession($queuedFlashMessages); } /** * Returns all messages from the current PHP session and from the current request. * * @param ContextualFeedbackSeverity|null $severity * @return FlashMessage[] */ public function getAllMessages(?ContextualFeedbackSeverity $severity = null) { // Get messages from user session $queuedFlashMessagesFromSession = $this->getFlashMessagesFromSession(); $queuedFlashMessages = array_merge($queuedFlashMessagesFromSession, $this->toArray()); if ($severity !== null) { $filteredFlashMessages = []; foreach ($queuedFlashMessages as $message) { if ($message->getSeverity() === $severity) { $filteredFlashMessages[] = $message; } } return $filteredFlashMessages; } return $queuedFlashMessages; } /** * Returns all messages from the current PHP session and from the current request. * After fetching the messages the internal queue and the message queue in the session * will be emptied. * * @param ContextualFeedbackSeverity|null $severity * @return FlashMessage[] */ public function getAllMessagesAndFlush(?ContextualFeedbackSeverity $severity = null) { $queuedFlashMessages = $this->getAllMessages($severity); // Reset messages in user session $this->removeAllFlashMessagesFromSession($severity); // Reset internal messages $this->clear($severity); return $queuedFlashMessages; } /** * Stores given flash messages in the session * * @param FlashMessage[]|null $flashMessages */ protected function storeFlashMessagesInSession(?array $flashMessages = null) { if (is_array($flashMessages)) { $flashMessages = array_map(json_encode(...), $flashMessages); } $user = $this->getUserByContext(); $user?->setAndSaveSessionData($this->identifier, $flashMessages); } /** * Removes all flash messages from the session * * @param ContextualFeedbackSeverity|null $severity */ protected function removeAllFlashMessagesFromSession(?ContextualFeedbackSeverity $severity = null) { if (!$this->getUserByContext() instanceof AbstractUserAuthentication) { return; } if ($severity === null) { $this->storeFlashMessagesInSession(); } else { $messages = $this->getFlashMessagesFromSession(); foreach ($messages as $index => $message) { if ($message->getSeverity() === $severity) { unset($messages[$index]); } } $this->storeFlashMessagesInSession($messages); } } /** * Returns current flash messages from the session, making sure to always * return an array. * * @return FlashMessage[] */ protected function getFlashMessagesFromSession(): array { $sessionMessages = []; $user = $this->getUserByContext(); if ($user !== null) { $messagesFromSession = $user->getSessionData($this->identifier); $messagesFromSession = is_array($messagesFromSession) ? $messagesFromSession : []; foreach ($messagesFromSession as $messageData) { $sessionMessages[] = FlashMessage::createFromArray(json_decode($messageData, true)); } } return $sessionMessages; } /** * Gets user object by context. * This class is also used in install tool, where $GLOBALS['BE_USER'] is not set and can be null. * * @todo: This construct needs to be removed. Methods that use this should be changed to * get the user hand over explicitly from the caller! A patch should make this global * access a b/w compat fallback only and adapt consuming methods accordingly. */ protected function getUserByContext(): ?AbstractUserAuthentication { if (($GLOBALS['TYPO3_REQUEST'] ?? null) && $GLOBALS['TYPO3_REQUEST']->getAttribute('frontend.user') instanceof FrontendUserAuthentication ) { return $GLOBALS['TYPO3_REQUEST']->getAttribute('frontend.user'); } return $GLOBALS['BE_USER'] ?? null; } /** * Fetches and renders all available flash messages from the queue. * * @param FlashMessageRendererInterface|null $flashMessageRenderer * @return string All flash messages in the queue rendered by context based FlashMessageRendererResolver. */ public function renderFlashMessages(?FlashMessageRendererInterface $flashMessageRenderer = null) { $content = ''; $flashMessages = $this->getAllMessagesAndFlush(); if (!empty($flashMessages)) { if ($flashMessageRenderer === null) { $flashMessageRenderer = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)->resolve(); } $content = $flashMessageRenderer->render($flashMessages); } return $content; } /** * Returns all items of the queue as array * * @return FlashMessage[] */ public function toArray() { $array = []; $this->rewind(); while ($this->valid()) { $array[] = $this->current(); $this->next(); } return $array; } /** * Removes all items from the queue * * @param ContextualFeedbackSeverity|null $severity */ public function clear(?ContextualFeedbackSeverity $severity = null) { $this->rewind(); if ($severity === null) { while (!$this->isEmpty()) { parent::dequeue(); } } else { $keysToRemove = []; while ($cur = $this->current()) { if ($cur->getSeverity() === $severity) { $keysToRemove[] = $this->key(); } $this->next(); } // keys are renumbered when unsetting elements // so unset them from last to first $keysToRemove = array_reverse($keysToRemove); foreach ($keysToRemove as $key) { $this->offsetUnset($key); } } } /** * @return array Data which can be serialized by json_encode() */ public function jsonSerialize(): array { return $this->toArray(); } }