[FEATURE] Introduce PreviewAspect for handling the preview flag 11/61311/6
authorSusanne Moog <susanne.moog@typo3.com>
Wed, 17 Jul 2019 15:37:44 +0000 (17:37 +0200)
committerAndreas Fernandez <a.fernandez@scripting-base.de>
Fri, 19 Jul 2019 08:39:10 +0000 (10:39 +0200)
The public internal property of TSFE "fePreview" has been deprecated
in favor of a dedicated aspect handling the frontend preview flag.

As a first step, the frontend preview aspect only contains a boolean
flag holding the current preview state - but it might be extended with
additional information on the preview mode at a later point.

This is also a first step to remove the TSFE dependencies from the
AdminPanel initialisation process.

Resolves: #88791
Releases: master
Change-Id: I2994d69d824b1b107a3a878b87c9dd2f7daf3ed2
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/61311
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
typo3/sysext/adminpanel/Classes/Modules/PreviewModule.php
typo3/sysext/core/Classes/Context/Context.php
typo3/sysext/core/Documentation/Changelog/master/Feature-88791-IntroducePreviewAspectInContext.rst [new file with mode: 0644]
typo3/sysext/frontend/Classes/Aspect/PreviewAspect.php [new file with mode: 0644]
typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
typo3/sysext/frontend/Classes/Hooks/FrontendHooks.php
typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php
typo3/sysext/install/Configuration/ExtensionScanner/Php/PropertyPublicMatcher.php

index ce0ae8d..32ba16b 100644 (file)
@@ -28,6 +28,7 @@ use TYPO3\CMS\Core\Context\UserAspect;
 use TYPO3\CMS\Core\Context\VisibilityAspect;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Fluid\View\StandaloneView;
+use TYPO3\CMS\Frontend\Aspect\PreviewAspect;
 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
@@ -161,8 +162,10 @@ class PreviewModule extends AbstractModule implements InitializableInterface, Pa
     ): void {
         $context = GeneralUtility::makeInstance(Context::class);
         $typoScriptFrontendController = $this->getTypoScriptFrontendController();
-        $typoScriptFrontendController->clear_preview();
-        $typoScriptFrontendController->fePreview = 1;
+        $this->clearPreviewSettings($context);
+
+        // Set preview flag
+        $context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class, true));
 
         // Modify visibility settings (hidden pages + hidden content)
         $context->setAspect(
@@ -208,7 +211,7 @@ class PreviewModule extends AbstractModule implements InitializableInterface, Pa
             );
         }
         if (!$simulateUserGroup && !$simTime && !$showHiddenPages && !$showHiddenRecords) {
-            $typoScriptFrontendController->fePreview = 0;
+            $context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class));
         }
     }
 
@@ -245,6 +248,14 @@ class PreviewModule extends AbstractModule implements InitializableInterface, Pa
         return $simTime ?? null;
     }
 
+    protected function clearPreviewSettings(Context $context): void
+    {
+        $GLOBALS['SIM_EXEC_TIME'] = $GLOBALS['EXEC_TIME'];
+        $GLOBALS['SIM_ACCESS_TIME'] = $GLOBALS['ACCESS_TIME'];
+        $context->setAspect('date', GeneralUtility::makeInstance(DateTimeAspect::class, new \DateTimeImmutable('@' . $GLOBALS['SIM_EXEC_TIME'])));
+        $context->setAspect('visibility', GeneralUtility::makeInstance(VisibilityAspect::class));
+    }
+
     /**
      * Returns a string array with css files that will be rendered after the module
      *
index 8ea437a..b8516ee 100644 (file)
@@ -44,6 +44,7 @@ use TYPO3\CMS\Core\SingletonInterface;
  * - frontend.user
  * - backend.user
  * - language
+ * - frontend.preview [if EXT:frontend is loaded]
  */
 class Context implements SingletonInterface
 {
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-88791-IntroducePreviewAspectInContext.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-88791-IntroducePreviewAspectInContext.rst
new file mode 100644 (file)
index 0000000..6fe79d8
--- /dev/null
@@ -0,0 +1,27 @@
+.. include:: ../../Includes.txt
+
+====================================================
+Feature: #88791 - Introduce PreviewAspect in Context
+====================================================
+
+See :issue:`88791`
+
+Description
+===========
+
+A PreviewAspect for handling the preview flag has been introduced. This aspect may be used to indicate that the
+frontend is in preview mode (for example in case a workspace is previewed or hidden pages or records should be shown).
+
+Impact
+======
+
+The Context API has a new Aspect called "frontend.preview". It can be used to determine whether the frontend is currently in preview mode.
+
+.. code-block:: php
+
+   GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('frontend.preview', 'isPreview');
+
+This Aspect replaces the now deprecated property :php:`TypoScriptFrontendController->fePreview`. Accessing this property
+triggers a PHP deprecation message, and fetches the information from the new Context Aspect instead.
+
+.. index:: Frontend, PHP-API, ext:core
diff --git a/typo3/sysext/frontend/Classes/Aspect/PreviewAspect.php b/typo3/sysext/frontend/Classes/Aspect/PreviewAspect.php
new file mode 100644 (file)
index 0000000..5019a61
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Frontend\Aspect;
+
+/*
+ * 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\Context\AspectInterface;
+use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
+
+/**
+ * The aspect contains information on whether TYPO3 is currently in preview mode
+ *
+ * Allowed properties:
+ * - isPreview
+ */
+class PreviewAspect implements AspectInterface
+{
+
+    /**
+     * @var bool
+     */
+    private $isPreview;
+
+    public function __construct(bool $isPreview = false)
+    {
+        $this->isPreview = $isPreview;
+    }
+
+    public function isPreview(): bool
+    {
+        return $this->isPreview;
+    }
+
+    /**
+     * Get a property from aspect
+     *
+     * @param string $name
+     * @return mixed
+     * @throws AspectPropertyNotFoundException
+     */
+    public function get(string $name)
+    {
+        switch ($name) {
+            case 'isPreview':
+                return $this->isPreview;
+        }
+        throw new AspectPropertyNotFoundException('Property "' . $name . '" not found in Aspect "' . __CLASS__ . '".', 1563375558);
+    }
+}
index e7a061a..62f671e 100644 (file)
@@ -22,7 +22,6 @@ use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Charset\CharsetConverter;
 use TYPO3\CMS\Core\Charset\UnknownCharsetException;
-use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Context\DateTimeAspect;
 use TYPO3\CMS\Core\Context\LanguageAspect;
@@ -65,6 +64,7 @@ use TYPO3\CMS\Core\Utility\HttpUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
 use TYPO3\CMS\Core\Utility\RootlineUtility;
+use TYPO3\CMS\Frontend\Aspect\PreviewAspect;
 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
@@ -89,11 +89,6 @@ use TYPO3\CMS\Frontend\Resource\FilePathSanitizer;
 class TypoScriptFrontendController implements LoggerAwareInterface
 {
     use LoggerAwareTrait;
-    use PublicPropertyDeprecationTrait;
-
-    private $deprecatedPublicProperties = [
-        'sys_language_isocode' => 'Using $TSFE->sys_language_isocode will not be available anymore in TYPO3 v11.0. Use the current Site Language object and its method "getTwoLetterIsoCode()" instead.'
-    ];
 
     /**
      * The page id (int)
@@ -234,8 +229,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      * page.
      * @var int
      * @internal
+     * @deprecated will be removed in TYPO3 v11.0. don't use it anymore, as this is now within PreviewAspect
      */
-    public $fePreview = 0;
+    protected $fePreview = 0;
 
     /**
      * Value that contains the simulated usergroup if any
@@ -478,7 +474,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
     /**
      * Is set to the iso code of the current language
      * @var string
-     * @deprecated don't use it anymore, as this is now within SiteLanguage->getTwoLetterIsoCode()
+     * @deprecated will be removed in TYPO3 v11.0. don't use it anymore, as this is now within SiteLanguage->getTwoLetterIsoCode()
      */
     protected $sys_language_isocode = '';
 
@@ -721,6 +717,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface
             trigger_error('TypoScriptFrontendController requires a context object as first constructor argument in TYPO3 v11.0, now falling back to the global Context. This fallback layer will be removed in TYPO3 v11.0', E_USER_DEPRECATED);
             $this->context = GeneralUtility::makeInstance(Context::class);
         }
+        if (!$this->context->hasAspect('frontend.preview')) {
+            $this->context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class));
+        }
     }
 
     /**
@@ -949,14 +948,14 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      */
     public function clear_preview()
     {
-        if ($this->fePreview
+        if ($this->context->getPropertyFromAspect('frontend.preview', 'isPreview')
             || $GLOBALS['EXEC_TIME'] !== $GLOBALS['SIM_EXEC_TIME']
             || $this->context->getPropertyFromAspect('visibility', 'includeHiddenPages', false)
             || $this->context->getPropertyFromAspect('visibility', 'includeHiddenContent', false)
         ) {
             $GLOBALS['SIM_EXEC_TIME'] = $GLOBALS['EXEC_TIME'];
             $GLOBALS['SIM_ACCESS_TIME'] = $GLOBALS['ACCESS_TIME'];
-            $this->fePreview = 0;
+            $this->context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class));
             $this->context->setAspect('date', GeneralUtility::makeInstance(DateTimeAspect::class, new \DateTimeImmutable('@' . $GLOBALS['SIM_EXEC_TIME'])));
             $this->context->setAspect('visibility', GeneralUtility::makeInstance(VisibilityAspect::class));
         }
@@ -988,13 +987,14 @@ class TypoScriptFrontendController implements LoggerAwareInterface
         // If there is a Backend login we are going to check for any preview settings
         $originalFrontendUserGroups = $this->applyPreviewSettings($this->getBackendUser());
         // If the front-end is showing a preview, caching MUST be disabled.
-        if ($this->fePreview) {
+        $isPreview = $this->context->getPropertyFromAspect('frontend.preview', 'isPreview');
+        if ($isPreview) {
             $this->disableCache();
         }
         // Now, get the id, validate access etc:
         $this->fetch_the_id();
         // Check if backend user has read access to this page. If not, recalculate the id.
-        if ($this->isBackendUserLoggedIn() && $this->fePreview && !$this->getBackendUser()->doesUserHaveAccess($this->page, Permission::PAGE_SHOW)) {
+        if ($this->isBackendUserLoggedIn() && $isPreview && !$this->getBackendUser()->doesUserHaveAccess($this->page, Permission::PAGE_SHOW)) {
             // Resetting
             $this->clear_preview();
             $this->fe_user->user[$this->fe_user->usergroup_column] = $originalFrontendUserGroups;
@@ -1031,7 +1031,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
     /**
      * Evaluates admin panel or workspace settings to see if
      * visibility settings like
-     * - $fePreview
+     * - Preview Aspect: isPreview
      * - Visibility Aspect: includeHiddenPages
      * - Visibility Aspect: includeHiddenContent
      * - $simUserGroup
@@ -1053,7 +1053,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
 
         // The preview flag is set if the current page turns out to be hidden
         if ($this->id && $this->determineIdIsHiddenPage()) {
-            $this->fePreview = 1;
+            $this->context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class, true));
             /** @var VisibilityAspect $aspect */
             $aspect = $this->context->getAspect('visibility');
             $newAspect = GeneralUtility::makeInstance(VisibilityAspect::class, true, $aspect->includeHiddenContent(), $aspect->includeDeletedRecords());
@@ -1061,7 +1061,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
         }
         // The preview flag will be set if an offline workspace will be previewed
         if ($this->whichWorkspace() > 0) {
-            $this->fePreview = 1;
+            $this->context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class, true));
         }
         return $this->simUserGroup ? $originalFrontendUserGroup : null;
     }
@@ -2240,9 +2240,6 @@ class TypoScriptFrontendController implements LoggerAwareInterface
             $this->updateRootLinesWithTranslations();
         }
 
-        // @deprecated - can be removed in TYPO3 v11.0
-        $this->sys_language_isocode = $this->language->getTwoLetterIsoCode();
-
         $_params = [];
         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'] ?? [] as $_funcRef) {
             GeneralUtility::callUserFunction($_funcRef, $_params, $this);
@@ -3917,4 +3914,102 @@ class TypoScriptFrontendController implements LoggerAwareInterface
     {
         return $this->pageArguments;
     }
+
+    /**
+     * Deprecation messages for TYPO3 10 - public properties of TSFE which have been (re)moved
+     */
+
+    /**
+     * Checks if the property of the given name is set.
+     *
+     * Unmarked protected properties must return false as usual.
+     * Marked properties are evaluated by isset().
+     *
+     * This method is not called for public properties.
+     *
+     * @param string $propertyName
+     * @return bool
+     */
+    public function __isset(string $propertyName)
+    {
+        switch ($propertyName) {
+            case 'sys_language_isocode':
+                trigger_error('Property $TSFE->sys_language_isocode is not in use anymore as this information is now stored within the SiteLanguage object. Will be removed in TYPO3 v11.0.', E_USER_DEPRECATED);
+                return isset($this->$propertyName);
+            case 'fePreview':
+                trigger_error('Property $TSFE->fePreview is not in use anymore as this information is now stored within the FrontendPreview aspect.', E_USER_DEPRECATED);
+                return $this->context->hasAspect('frontend.preview');
+        }
+        return false;
+    }
+
+    /**
+     * Gets the value of the property of the given name if tagged.
+     *
+     * The evaluation is done in the assumption that this method is never
+     * reached for a public property.
+     *
+     * @param string $propertyName
+     * @return mixed
+     */
+    public function __get(string $propertyName)
+    {
+        switch ($propertyName) {
+            case 'sys_language_isocode':
+                trigger_error('Property $TSFE->sys_language_isocode is not in use anymore as this information is now stored within the SiteLanguage object. Will be removed in TYPO3 v11.0.', E_USER_DEPRECATED);
+                return $this->sys_language_isocode ?? $this->language->getTwoLetterIsoCode();
+            case 'fePreview':
+                trigger_error('Property $TSFE->fePreview is not in use anymore as this information is now stored within the FrontendPreview aspect.', E_USER_DEPRECATED);
+                if ($this->context->hasAspect('frontend.preview')) {
+                    return $this->context->getPropertyFromAspect('frontend.preview', 'isPreview');
+                }
+                break;
+
+        }
+        return $this->$propertyName;
+    }
+
+    /**
+     * Sets the property of the given name if tagged.
+     *
+     * Additionally it's allowed to set unknown properties.
+     *
+     * The evaluation is done in the assumption that this method is never
+     * reached for a public property.
+     *
+     * @param string $propertyName
+     * @param mixed $propertyValue
+     */
+    public function __set(string $propertyName, $propertyValue)
+    {
+        switch ($propertyName) {
+            case 'sys_language_isocode':
+                trigger_error('Property $TSFE->sys_language_isocode is not in use anymore as this information is now stored within the SiteLanguage object. Will be removed in TYPO3 v11.0.', E_USER_DEPRECATED);
+                break;
+            case 'fePreview':
+                trigger_error('Property $TSFE->fePreview is not in use anymore as this information is now stored within the FrontendPreview aspect.', E_USER_DEPRECATED);
+                $this->context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class, (bool)$propertyValue));
+                break;
+        }
+        $this->$propertyName = $propertyValue;
+    }
+
+    /**
+     * Unsets the property of the given name if tagged.
+     *
+     * @param string $propertyName
+     */
+    public function __unset(string $propertyName)
+    {
+        switch ($propertyName) {
+            case 'sys_language_isocode':
+                trigger_error('Property $TSFE->sys_language_isocode is not in use anymore as this information is now stored within the SiteLanguage object. Will be removed in TYPO3 v11.0.', E_USER_DEPRECATED);
+                break;
+            case 'fePreview':
+                trigger_error('Property $TSFE->fePreview is not in use anymore as this information is now stored within the FrontendPreview aspect.', E_USER_DEPRECATED);
+                $this->context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class, false));
+                break;
+        }
+        unset($this->$propertyName);
+    }
 }
index 87bf490..d4652ff 100644 (file)
@@ -34,7 +34,9 @@ class FrontendHooks
      */
     public function displayPreviewInfoMessage($params, TypoScriptFrontendController $controller)
     {
-        if (!$controller->fePreview || $controller->doWorkspacePreview() || ($controller->config['config']['disablePreviewNotification'] ?? false)) {
+        $isInPreviewMode = $controller->getContext()->hasAspect('frontend.preview')
+            && $controller->getContext()->getPropertyFromAspect('frontend.preview', 'isPreview');
+        if (!$isInPreviewMode || $controller->doWorkspacePreview() || ($controller->config['config']['disablePreviewNotification'] ?? false)) {
             return;
         }
         if ($controller->config['config']['message_preview']) {
index 7af1b35..44b746e 100644 (file)
@@ -27,6 +27,7 @@ use TYPO3\CMS\Core\Routing\PageArguments;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\Aspect\PreviewAspect;
 use TYPO3\CMS\Frontend\Controller\ErrorController;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
@@ -65,6 +66,7 @@ class TypoScriptFrontendInitialization implements MiddlewareInterface
             );
         }
         $context = GeneralUtility::makeInstance(Context::class);
+        $context->setAspect('frontend.preview', GeneralUtility::makeInstance(PreviewAspect::class));
 
         $controller = GeneralUtility::makeInstance(
             TypoScriptFrontendController::class,
index 4c46915..0898bbd 100644 (file)
@@ -564,4 +564,9 @@ return [
             'Breaking-88741-CHashCalculationInIndexedSearchRemoved.rst',
         ],
     ],
+    'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->fePreview' => [
+        'restFiles' => [
+            'Feature-88791-IntroducePreviewAspectInContext.rst',
+        ],
+    ],
 ];