[FEATURE] Introduce hook for custom result browser implementations 85/48185/11
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Tue, 17 May 2016 10:14:29 +0000 (12:14 +0200)
committerGeorg Ringer <georg.ringer@gmail.com>
Sat, 23 Jul 2016 14:32:45 +0000 (16:32 +0200)
The hook ``[\TYPO3\CMS\Frontend\Plugin\AbstractPlugin::class]['pi_list_browseresults']``
may be used to register a custom result browser implementation without the
need to override the method ``AbstractPlugin::pi_list_browseresults`` in
each pi-based plugin.
This is helpful if an implementation should be used for either all or
specific plugins.

Resolves: #76209
Releases: master
Change-Id: Iefab37eaac51c43a7daa225e3cf2f335f6d3975f
Reviewed-on: https://review.typo3.org/48185
Tested-by: Bamboo TYPO3com <info@typo3.com>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
typo3/sysext/core/Documentation/Changelog/master/Feature-76209-HookToRegisterCustomResultBrowsersInAbstractPlugin.rst [new file with mode: 0644]
typo3/sysext/frontend/Classes/Plugin/AbstractPlugin.php
typo3/sysext/frontend/Tests/Unit/Fixtures/ResultBrowserPluginHook.php [new file with mode: 0644]
typo3/sysext/frontend/Tests/Unit/Plugin/AbstractPluginTest.php

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-76209-HookToRegisterCustomResultBrowsersInAbstractPlugin.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-76209-HookToRegisterCustomResultBrowsersInAbstractPlugin.rst
new file mode 100644 (file)
index 0000000..1b130dd
--- /dev/null
@@ -0,0 +1,32 @@
+===========================================================================
+Feature: #76209 - Hook to register custom result browsers in AbstractPlugin
+===========================================================================
+
+Description
+===========
+
+The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\TYPO3\CMS\Frontend\Plugin\AbstractPlugin::class]['pi_list_browseresults']` allows
+registering custom result browser implementations. This approach allows to override the default implementation of
+:php:`AbstractPlugin::pi_list_browseresults()` for either all extensions or only for specific ones.
+
+
+Impact
+======
+
+The hook may be registered in ``ext_localconf.php``:
+
+.. code-block:: php
+
+       $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\TYPO3\CMS\Frontend\Plugin\AbstractPlugin::class]['pi_list_browseresults'][1463475262] = \Vendor\ExtensionKey\Hook\ResultBrowserHook::class
+
+The registered class must implement the method :php:`pi_list_browseresults` with the following arguments:
+
+* int ``$showResultCount`` Determines how the results of the page browser will be shown
+* string ``$tableParams`` Attributes for the table tag which is wrapped around the table cells containing the browse links
+* array ``$wrapArr`` Array with elements to overwrite the default $wrapper-array
+* string ``$pointerName`` Variable name for the pointer
+* bool ``$hscText`` Enable htmlspecialchars() for the pi_getLL function
+* bool ``$forceOutput`` Forces the output of the page browser if you set this option to ``true``
+* object ``$pObj`` The AbstractPlugin instance calling the hook
+
+.. index:: php, plugin, hook
\ No newline at end of file
index 4056b0d..defa4ae 100644 (file)
@@ -500,7 +500,7 @@ class AbstractPlugin
      * If $wrapArr['showResultsNumbersWrap'] is set, the formatting string is expected to hold template markers (###FROM###, ###TO###, ###OUT_OF###, ###FROM_TO###, ###CURRENT_PAGE###, ###TOTAL_PAGES###)
      * otherwise the formatting string is expected to hold sprintf-markers (%s) for from, to, outof (in that sequence)
      *
-     * @param int $showResultCount Determines how the results of the pagerowser will be shown. See description below
+     * @param int $showResultCount Determines how the results of the page browser will be shown. See description below
      * @param string $tableParams Attributes for the table tag which is wrapped around the table cells containing the browse links
      * @param array $wrapArr Array with elements to overwrite the default $wrapper-array.
      * @param string $pointerName varname for the pointer.
@@ -510,6 +510,19 @@ class AbstractPlugin
      */
     public function pi_list_browseresults($showResultCount = 1, $tableParams = '', $wrapArr = array(), $pointerName = 'pointer', $hscText = true, $forceOutput = false)
     {
+        if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][AbstractPlugin::class]['pi_list_browseresults'])
+            && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][AbstractPlugin::class]['pi_list_browseresults'])
+        ) {
+            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][AbstractPlugin::class]['pi_list_browseresults'] as $classRef) {
+                $hookObj = GeneralUtility::makeInstance($classRef);
+                if (method_exists($hookObj, 'pi_list_browseresults')) {
+                    $pageBrowser = $hookObj->pi_list_browseresults($showResultCount, $tableParams, $wrapArr, $pointerName, $hscText, $forceOutput, $this);
+                    if (is_string($pageBrowser) && trim($pageBrowser) !== '') {
+                        return $pageBrowser;
+                    }
+                }
+            }
+        }
         // example $wrapArr-array how it could be traversed from an extension
         /* $wrapArr = array(
         'browseBoxWrap' => '<div class="browseBoxWrap">|</div>',
diff --git a/typo3/sysext/frontend/Tests/Unit/Fixtures/ResultBrowserPluginHook.php b/typo3/sysext/frontend/Tests/Unit/Fixtures/ResultBrowserPluginHook.php
new file mode 100644 (file)
index 0000000..b4cca96
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+namespace TYPO3\CMS\Frontend\Tests\Unit\Fixtures;
+
+/*
+ * 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\Frontend\Plugin\AbstractPlugin;
+
+/**
+ * Class using the [\TYPO3\CMS\Frontend\Plugin\AbstractPlugin::class]['pi_list_browseresults'] hook
+ */
+class ResultBrowserPluginHook
+{
+    /**
+     * Returns a results browser. This method is a stub to test the hook in AbstractPlugin::pi_list_browseresults().
+     *
+     * @param int $showResultCount Determines how the results of the pagerowser will be shown. See description below
+     * @param string $tableParams Attributes for the table tag which is wrapped around the table cells containing the browse links
+     * @param array $wrapArr Array with elements to overwrite the default $wrapper-array.
+     * @param string $pointerName varname for the pointer.
+     * @param bool $hscText Enable htmlspecialchars() for the pi_getLL function (set this to FALSE if you want f.e use images instead of text for links like 'previous' and 'next').
+     * @param bool $forceOutput Forces the output of the page browser if you set this option to "TRUE" (otherwise it's only drawn if enough entries are available)
+     * @param AbstractPlugin $pObj Instance of the calling object
+     * @return string Output HTML-Table, wrapped in <div>-tags with a class attribute (if $wrapArr is not passed,
+     */
+    public function pi_list_browseresults($showResultCount, $tableParams, array $wrapArr, $pointerName, $hscText, $forceOutput, AbstractPlugin $pObj)
+    {
+    }
+}
index 7db06b9..757ff0a 100644 (file)
@@ -13,16 +13,20 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\Plugin;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Tests\UnitTestCase;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 use TYPO3\CMS\Frontend\ContentObject\TextContentObject;
+use TYPO3\CMS\Frontend\Plugin\AbstractPlugin;
+use TYPO3\CMS\Frontend\Tests\Unit\Fixtures\ResultBrowserPluginHook;
 
 /**
  * Testcase for TYPO3\CMS\Frontend\Plugin\AbstractPlugin
  */
-class AbstractPluginTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
+class AbstractPluginTest extends UnitTestCase
 {
     /**
-     * @var \TYPO3\CMS\Frontend\Plugin\AbstractPlugin
+     * @var AbstractPlugin
      */
     protected $abstractPlugin;
 
@@ -42,7 +46,7 @@ class AbstractPluginTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         $GLOBALS['TSFE'] = new \stdClass();
         $GLOBALS['TSFE']->cObjectDepthCounter = 100;
 
-        $this->abstractPlugin = new \TYPO3\CMS\Frontend\Plugin\AbstractPlugin();
+        $this->abstractPlugin = new AbstractPlugin();
         $contentObjectRenderer = new ContentObjectRenderer();
         $contentObjectRenderer->setContentObjectClassMap(array(
             'TEXT' => TextContentObject::class,
@@ -136,4 +140,87 @@ class AbstractPluginTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         $this->abstractPlugin->pi_setPiVarDefaults();
         $this->assertEquals($expected, $this->abstractPlugin->piVars);
     }
+
+    /**
+     * Data provider for multiple registered result browser implementations
+     *
+     * @return array
+     */
+    public function registeredResultBrowserProvider()
+    {
+        return [
+            'Result browser returning false' => [
+                'className' => $this->getUniqueId('tx_coretest'),
+                'returnValue' => false,
+                'expected' => ''
+            ],
+            'Result browser returning null' => [
+                'className' => $this->getUniqueId('tx_coretest'),
+                'returnValue' => null,
+                'expected' => ''
+            ],
+            'Result browser returning whitespace string' => [
+                'className' => $this->getUniqueId('tx_coretest'),
+                'returnValue' => '   ',
+                'expected' => ''
+            ],
+            'Result browser returning HTML' => [
+                'className' => $this->getUniqueId('tx_coretest'),
+                'returnValue' => '<div><a href="index.php?id=1&pointer=1">1</a><a href="index.php?id=1&pointer=2">2</a><a href="index.php?id=1&pointer=3">3</a><a href="index.php?id=1&pointer=4">4</a></div>',
+                'expected' => '<div><a href="index.php?id=1&pointer=1">1</a><a href="index.php?id=1&pointer=2">2</a><a href="index.php?id=1&pointer=3">3</a><a href="index.php?id=1&pointer=4">4</a></div>'
+            ],
+            'Result browser returning a truthy integer as string' => [
+                'className' => $this->getUniqueId('tx_coretest'),
+                'returnValue' => '1',
+                'expected' => '1'
+            ],
+            'Result browser returning a falsy integer' => [
+                'className' => $this->getUniqueId('tx_coretest'),
+                'returnValue' => 0,
+                'expected' => ''
+            ],
+            'Result browser returning a truthy integer' => [
+                'className' => $this->getUniqueId('tx_coretest'),
+                'returnValue' => 1,
+                'expected' => ''
+            ],
+            'Result browser returning a positive integer' => [
+                'className' => $this->getUniqueId('tx_coretest'),
+                'returnValue' => 42,
+                'expected' => ''
+            ]
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider registeredResultBrowserProvider
+     *
+     * @param string $className
+     * @param mixed $returnValue
+     * @param string $expected
+     */
+    public function registeredResultBrowsersAreUsed($className, $returnValue, $expected)
+    {
+        $resultBrowserHook = $this->getMockBuilder(ResultBrowserPluginHook::class)
+            ->setMockClassName($className)
+            ->setMethods(array('pi_list_browseresults'))
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        // Register hook mock object
+        GeneralUtility::addInstance($className, $resultBrowserHook);
+        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][AbstractPlugin::class]['pi_list_browseresults'] = [$className];
+
+        $resultBrowserHook->expects($this->atLeastOnce())
+            ->method('pi_list_browseresults')
+            ->with(1, '', [], 'pointer', true, false, $this->abstractPlugin)
+            ->will($this->returnValue($returnValue));
+
+        $actualReturnValue = $this->abstractPlugin->pi_list_browseresults();
+
+        $this->assertSame($expected, $actualReturnValue);
+
+        unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][AbstractPlugin::class]['pi_list_browseresults']);
+    }
 }