[FEATURE] Add fluid debug information to admin panel 95/50095/8
authorFrank Naegler <frank.naegler@typo3.org>
Mon, 9 May 2016 18:36:59 +0000 (20:36 +0200)
committerJan Helke <typo3@helke.de>
Mon, 24 Oct 2016 18:00:30 +0000 (20:00 +0200)
This patch adds a new option to the admin panel to show debug information
about partials and sections in frontend.

Resolves: #76085
Releases: master
Change-Id: I3d37118280ef713db6d92a734f30c0f8bd4ae8eb
Reviewed-on: https://review.typo3.org/50095
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Jan Helke <typo3@helke.de>
Tested-by: Jan Helke <typo3@helke.de>
typo3/sysext/core/Documentation/Changelog/master/Feature-76085-AddFluidDebugInformationToAdminPanel.rst [new file with mode: 0644]
typo3/sysext/fluid/Classes/Core/ViewHelper/ViewHelperResolver.php
typo3/sysext/fluid/Classes/View/TemplatePaths.php
typo3/sysext/fluid/Classes/ViewHelpers/Debug/RenderViewHelper.php [new file with mode: 0644]
typo3/sysext/frontend/Classes/View/AdminPanelView.php
typo3/sysext/lang/locallang_tsfe.xlf

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-76085-AddFluidDebugInformationToAdminPanel.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-76085-AddFluidDebugInformationToAdminPanel.rst
new file mode 100644 (file)
index 0000000..5d51c00
--- /dev/null
@@ -0,0 +1,22 @@
+.. include:: ../../Includes.txt
+
+============================================================
+Feature: #76085 - Add fluid debug information to admin panel
+============================================================
+
+See :issue:`76085`
+
+Description
+===========
+
+A new setting in the admin panel (Preview > Show fluid debug output) enable fluid debug output.
+If the checkbox is enabled, the path to the template file of a partial and the name of a section will be shown in the
+frontend directly above the markup.
+With this feature an integrator can easily find the correct template and section.
+
+Impact
+======
+
+Activating this option can break the output in frontend or result in unexpected behavior.
+
+.. index:: Frontend
\ No newline at end of file
index b2b854e..cf9206a 100644 (file)
@@ -14,6 +14,7 @@ namespace TYPO3\CMS\Fluid\Core\ViewHelper;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
@@ -53,6 +54,23 @@ class ViewHelperResolver extends \TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperRes
     ];
 
     /**
+     * ViewHelperResolver constructor
+     *
+     * Responsible for adding a third namespace in case this is requested from
+     * the admin panel - causes overlaying of `f:` with `f:debug`.
+     */
+    public function __construct()
+    {
+        $configuration = $this->getBackendUser()->uc['TSFE_adminConfig'];
+        if (TYPO3_MODE === 'FE'
+            && isset($configuration['preview_showFluidDebug'])
+            && $configuration['preview_showFluidDebug']
+        ) {
+            $this->namespaces['f'][] = 'TYPO3\\CMS\\Fluid\\ViewHelpers\\Debug';
+        }
+    }
+
+    /**
      * @param string $viewHelperClassName
      * @return \TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperInterface
      */
@@ -68,4 +86,12 @@ class ViewHelperResolver extends \TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperRes
     {
         return GeneralUtility::makeInstance(ObjectManager::class);
     }
+
+    /**
+     * @return BackendUserAuthentication
+     */
+    protected function getBackendUser()
+    {
+        return $GLOBALS['BE_USER'];
+    }
 }
index ee0fa28..f4b2efb 100644 (file)
@@ -208,4 +208,16 @@ class TemplatePaths extends \TYPO3Fluid\Fluid\View\TemplatePaths
             ArrayUtility::sortArrayWithIntegerKeys($partialRootPaths)
         );
     }
+
+    /**
+     * Public API for currently protected method. Can be dropped when switching to
+     * Fluid 1.1.0 or above.
+     *
+     * @param string $partialName
+     * @return string
+     */
+    public function getPartialPathAndFilename($partialName)
+    {
+        return parent::getPartialPathAndFilename($partialName);
+    }
 }
diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/Debug/RenderViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/Debug/RenderViewHelper.php
new file mode 100644 (file)
index 0000000..035f37e
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+namespace TYPO3\CMS\Fluid\ViewHelpers\Debug;
+
+/*
+ * 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\Utility\PathUtility;
+use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
+
+/**
+ * Debuggable version of f:render - performs the same
+ * rendering operation but wraps the output with HTML
+ * that can be inspected with the admin panel in FE.
+ *
+ * Replaces `f:render` when the admin panel decides
+ * (see ViewHelperResolver class). Also possible to use
+ * explicitly by using `f:debug.render` instead of the
+ * normal `f:render` statement.
+ *
+ * @api
+ */
+class RenderViewHelper extends AbstractViewHelper
+{
+
+    /**
+     * @var bool
+     */
+    protected $escapeOutput = false;
+
+    /**
+     * @return void
+     */
+    public function initializeArguments()
+    {
+        parent::initializeArguments();
+        $this->registerArgument('section', 'string', 'Section to render - combine with partial to render section in partial', false, null);
+        $this->registerArgument('partial', 'string', 'Partial to render, with or without section', false, null);
+        $this->registerArgument('arguments', 'array', 'Array of variables to be transferred. Use {_all} for all variables', false, []);
+        $this->registerArgument('optional', 'boolean', 'If TRUE, considers the *section* optional. Partial never is.', false, false);
+        $this->registerArgument('default', 'mixed', 'Value (usually string) to be displayed if the section or partial does not exist', false, null);
+        $this->registerArgument('contentAs', 'string', 'If used, renders the child content and adds it as a template variable with this name for use in the partial/section', false, null);
+    }
+
+    /**
+     * Renders the content.
+     *
+     * @return string
+     * @api
+     */
+    public function render()
+    {
+        $section = $this->arguments['section'];
+        $partial = $this->arguments['partial'];
+        $arguments = (array) $this->arguments['arguments'];
+        $optional = (boolean) $this->arguments['optional'];
+        $contentAs = $this->arguments['contentAs'];
+        $tagContent = $this->renderChildren();
+
+        if ($contentAs !== null) {
+            $arguments[$contentAs] = $tagContent;
+        }
+
+        $content = '';
+        if ($partial !== null) {
+            $content = $this->viewHelperVariableContainer->getView()->renderPartial($partial, $section, $arguments, $optional);
+        } elseif ($section !== null) {
+            $content = $this->viewHelperVariableContainer->getView()->renderSection($section, $arguments, $optional);
+        }
+        // Replace empty content with default value. If default is
+        // not set, NULL is returned and cast to a new, empty string
+        // outside of this ViewHelper.
+        if ($content === '') {
+            $content = isset($this->arguments['default']) ? $this->arguments['default'] : $tagContent;
+        }
+
+        $cssRules = [];
+        $cssRules[] = 'display: block';
+        $cssRules[] = 'background-color: #fff';
+        $cssRules[] = 'padding: 5px';
+        $cssRules[] = 'border: 1px solid #f00';
+        $cssRules[] = 'color: #000';
+        $cssRules[] = 'overflow: hidden';
+        $cssWrapper = implode(';', $cssRules);
+        $cssRules[] = 'font-size: 11px';
+        $cssRules[] = 'font-family: Monospace';
+        $cssTitle = implode(';', $cssRules);
+
+        $debugInfo = [];
+        if (isset($this->arguments['partial'])) {
+            $path = $this->renderingContext->getTemplatePaths()->getPartialPathAndFilename($partial);
+            $path = PathUtility::stripPathSitePrefix($path);
+            $path = str_replace('typo3conf/ext/', 'EXT:', $path);
+            $path = str_replace('typo3/sysext/', 'EXT:', $path);
+            $debugInfo['Partial'] = 'Partial: ' . $path;
+        }
+        if (isset($this->arguments['section'])) {
+            $debugInfo['Section'] = 'Section: ' . htmlspecialchars($section);
+        }
+
+        $debugContent = sprintf(
+            '<strong>%s</strong>',
+            implode('<br />', $debugInfo)
+        );
+
+        return sprintf(
+            '<div class="t3js-debug-template" title="%s" style="%s"><span style="%s">%s</span>%s</div>',
+            htmlspecialchars(implode('/', array_keys($debugInfo))),
+            $cssTitle,
+            $cssWrapper,
+            $debugContent,
+            $content
+        );
+    }
+}
index d1cdf9c..b7aa915 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Frontend\View;
  */
 
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
@@ -156,13 +157,17 @@ class AdminPanelView
             $beUser->uc['TSFE_adminConfig'] = array_merge(!is_array($beUser->uc['TSFE_adminConfig']) ? [] : $beUser->uc['TSFE_adminConfig'], $input);
             unset($beUser->uc['TSFE_adminConfig']['action']);
             // Actions:
-            if ($input['action']['clearCache'] && $this->isAdminModuleEnabled('cache')) {
+            if (($input['action']['clearCache'] && $this->isAdminModuleEnabled('cache')) || isset($input['preview_showFluidDebug'])) {
                 $beUser->extPageInTreeInfo = [];
                 $theStartId = (int)$input['cache_clearCacheId'];
                 $this->getTypoScriptFrontendController()->clearPageCacheContent_pidList($beUser->extGetTreeList($theStartId, $this->extGetFeAdminValue('cache', 'clearCacheLevels'), 0, $beUser->getPagePermsClause(1)) . $theStartId);
             }
             // Saving
             $beUser->writeUC();
+            // Flush fluid template cache
+            $cacheManager = new CacheManager();
+            $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
+            $cacheManager->getCache('fluid_template')->flush();
         }
         $this->getTimeTracker()->LR = $this->extGetFeAdminValue('tsdebug', 'LR');
         if ($this->extGetFeAdminValue('cache', 'noCache')) {
@@ -383,6 +388,8 @@ class AdminPanelView
             $this->extNeedUpdate = true;
             $out .= $this->extGetItem('preview_showHiddenPages', '', '<input type="hidden" name="TSFE_ADMIN_PANEL[preview_showHiddenPages]" value="0" /><input type="checkbox" id="preview_showHiddenPages" name="TSFE_ADMIN_PANEL[preview_showHiddenPages]" value="1"' . ($this->getBackendUser()->uc['TSFE_adminConfig']['preview_showHiddenPages'] ? ' checked="checked"' : '') . ' />');
             $out .= $this->extGetItem('preview_showHiddenRecords', '', '<input type="hidden" name="TSFE_ADMIN_PANEL[preview_showHiddenRecords]" value="0" /><input type="checkbox" id="preview_showHiddenRecords" name="TSFE_ADMIN_PANEL[preview_showHiddenRecords]" value="1"' . ($this->getBackendUser()->uc['TSFE_adminConfig']['preview_showHiddenRecords'] ? ' checked="checked"' : '') . ' />');
+            $out .= $this->extGetItem('preview_showFluidDebug', '', '<input type="hidden" name="TSFE_ADMIN_PANEL[preview_showFluidDebug]" value="0" /><input type="checkbox" id="preview_showFluidDebug" name="TSFE_ADMIN_PANEL[preview_showFluidDebug]" value="1"' . (!empty($this->getBackendUser()->uc['TSFE_adminConfig']['preview_showFluidDebug']) ? ' checked="checked"' : '') . ' />');
+
             // Simulate date
             $out .= $this->extGetItem('preview_simulateDate', '<input type="text" id="preview_simulateDate" name="TSFE_ADMIN_PANEL[preview_simulateDate]_hr" onchange="TSFEtypo3FormFieldGet(\'TSFE_ADMIN_PANEL[preview_simulateDate]\', \'datetime\', \'\', 1,0);" /><input type="hidden" name="TSFE_ADMIN_PANEL[preview_simulateDate]" value="' . $this->getBackendUser()->uc['TSFE_adminConfig']['preview_simulateDate'] . '" />');
             $this->extJSCODE .= 'TSFEtypo3FormFieldSet("TSFE_ADMIN_PANEL[preview_simulateDate]", "datetime", "", 0, 0);';
index 498ba90..6f53f23 100644 (file)
@@ -18,6 +18,9 @@
                        <trans-unit id="preview_showHiddenRecords">
                                <source>Show hidden records</source>
                        </trans-unit>
+                       <trans-unit id="preview_showFluidDebug">
+                               <source>Show fluid debug output</source>
+                       </trans-unit>
                        <trans-unit id="preview_simulateDate">
                                <source>Simulate time</source>
                        </trans-unit>