From 20acc4019a14c97431400a4c13b7762c1bbcecc2 Mon Sep 17 00:00:00 2001
From: Oliver Hader <oliver@typo3.org>
Date: Fri, 21 Mar 2014 23:13:57 +0100
Subject: [PATCH] [FEATURE] Enable queries using "root" and "-1" for pidInList

The version preview in the frontend rendering process basically
selects only the live versions and then overlays those records
with possible versions. Since the MM handling is changed to
directly point to the most specific version in that regard (this
is an exception to the "always live, overlay then" approach), the
versions also need to be selected directly. ContentObjectRenderer
uses the method PageRepository::enableFields() which adds an
additional SQL WHERE part, like " AND tablename.pid<>-1" to
exclude possible versions.

Besides that, elements (like sys_catagory) that are stored on the
root-level cannot be queried at all which leads to custom code in
the end just to perform these queries.

This change allows selections on the root-level and disables the
"pid<>-1" constraint if the opposite has been defined in the
pidInList property.

Example:
select.pidInList = root,-1

Resolves: #57168
Documentation: #57171
Releases: 6.2
Change-Id: I6fb91f89f70614d59c83d808f07e890833248880
Reviewed-on: https://review.typo3.org/28623
Reviewed-by: Ernesto Baschny
Reviewed-by: Oliver Hader
Tested-by: Oliver Hader
---
 .../Fixtures/Frontend/JsonRenderer.ts         |   9 +-
 .../Framework/Frontend/UserFunction.php       | 116 ------------------
 .../ContentObject/ContentObjectRenderer.php   |  19 ++-
 .../frontend/Classes/Page/PageRepository.php  |   2 +-
 .../ContentObjectRendererTest.php             |   9 +-
 5 files changed, 22 insertions(+), 133 deletions(-)
 delete mode 100644 typo3/sysext/core/Tests/Functional/Framework/Frontend/UserFunction.php

diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Frontend/JsonRenderer.ts b/typo3/sysext/core/Tests/Functional/Fixtures/Frontend/JsonRenderer.ts
index b96d82ac8fcb..e89f11783b61 100644
--- a/typo3/sysext/core/Tests/Functional/Fixtures/Frontend/JsonRenderer.ts
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Frontend/JsonRenderer.ts
@@ -54,14 +54,7 @@ page {
 				watcher.parentRecordField = categories
 				table = sys_category
 				select {
-					pidInList = 0
-					uidInList.preUserFunc = TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\UserFunction->getManyToManyIds
-					uidInList.preUserFunc {
-						uidForeign.data = field:_ORIG_uid // field:uid
-						manyToManyTableName = sys_category_record_mm
-						matchTableName = tt_content
-						matchFieldName = categories
-					}
+					pidInList = root,-1
 					selectFields = sys_category.*
 					join = sys_category_record_mm ON sys_category_record_mm.uid_local = sys_category.uid
 					where.data = field:_ORIG_uid // field:uid
diff --git a/typo3/sysext/core/Tests/Functional/Framework/Frontend/UserFunction.php b/typo3/sysext/core/Tests/Functional/Framework/Frontend/UserFunction.php
deleted file mode 100644
index fecb08c279c2..000000000000
--- a/typo3/sysext/core/Tests/Functional/Framework/Frontend/UserFunction.php
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Tests\Functional\Framework\Frontend;
-
-/***************************************************************
- * Copyright notice
- *
- * (c) 2014 Oliver Hader <oliver.hader@typo3.org>
- * 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.
- *
- * 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!
- ***************************************************************/
-
-/**
- * Model of frontend response
- */
-class UserFunction implements \TYPO3\CMS\Core\SingletonInterface {
-
-	/**
-	 * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
-	 */
-	public $cObj;
-
-	/**
-	 * @param string $content
-	 * @param array $configuration
-	 * @return string
-	 */
-	public function getManyToManyIds($content, array $configuration = NULL) {
-		$where = array();
-		$uidLocal = NULL;
-		$uidForeign = NULL;
-		$manyToManyTableName = NULL;
-
-		$uidLocal = (int)$this->getValue('uidLocal', $configuration);
-		$uidForeign = (int)$this->getValue('uidForeign', $configuration);
-		$manyToManyTableName = $this->getValue('manyToManyTableName', $configuration);
-
-		if (!($uidLocal xor $uidForeign) || empty($manyToManyTableName)) {
-			return $content;
-		}
-
-		if (!empty($uidLocal)) {
-			$selectField = 'uid_foreign';
-			$sortingField = 'sorting';
-			$where[] = 'uid_local=' . $uidLocal;
-		} else {
-			$selectField = 'uid_local';
-			$sortingField = 'sorting_foreign';
-			$where[] = 'uid_foreign=' . $uidForeign;
-		}
-
-		if (!empty($configuration['matchTableName'])) {
-			$where[] = 'tablenames=' . $this->getDatabaseConnection()->fullQuoteStr($configuration['matchTableName'], $manyToManyTableName);
-		}
-		if (!empty($configuration['matchFieldName'])) {
-			$where[] = 'fieldname=' . $this->getDatabaseConnection()->fullQuoteStr($configuration['matchFieldName'], $manyToManyTableName);
-		}
-
-		$references = $this->getDatabaseConnection()->exec_SELECTgetRows(
-			$selectField,
-			$manyToManyTableName,
-			implode(' AND ', $where),
-			'',
-			$sortingField,
-			'',
-			$selectField
-		);
-
-		if (empty($references)) {
-			return $content;
-		}
-
-		$content = implode(',', array_keys($references));
-		return $content;
-	}
-
-	/**
-	 * @param string $property
-	 * @param array $configuration
-	 * @return string
-	 */
-	protected function getValue($property, array $configuration = NULL) {
-		$value = '';
-
-		if (!empty($configuration[$property])) {
-			$value = $configuration[$property];
-		}
-		if (!empty($configuration[$property . '.'])) {
-			$value = $this->cObj->stdWrap($value, $configuration[$property . '.']);
-		}
-
-		return $value;
-	}
-
-	/**
-	 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
-	 */
-	protected function getDatabaseConnection() {
-		return $GLOBALS['TYPO3_DB'];
-	}
-
-}
diff --git a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
index 04180b4bed1e..fddde98f9860 100644
--- a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
+++ b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
@@ -7221,11 +7221,12 @@ class ContentObjectRenderer {
 	 *
 	 * @param string $table The table for which to get the where clause
 	 * @param boolean $show_hidden If set, then you want NOT to filter out hidden records. Otherwise hidden record are filtered based on the current preview settings.
+	 * @param array $ignore_array Array you can pass where keys can be "disabled", "starttime", "endtime", "fe_group" (keys from "enablefields" in TCA) and if set they will make sure that part of the clause is not added. Thus disables the specific part of the clause. For previewing etc.
 	 * @return string The part of the where clause on the form " AND [fieldname]=0 AND ...". Eg. " AND hidden=0 AND starttime < 123345567
-	 * @todo Define visibility
 	 */
-	public function enableFields($table, $show_hidden = 0) {
-		return $GLOBALS['TSFE']->sys_page->enableFields($table, $show_hidden ? $show_hidden : ($table == 'pages' ? $GLOBALS['TSFE']->showHiddenPage : $GLOBALS['TSFE']->showHiddenRecords));
+	public function enableFields($table, $show_hidden = FALSE, array $ignore_array = array()) {
+		$show_hidden = $show_hidden ?: ($table === 'pages' ? $GLOBALS['TSFE']->showHiddenPage : $GLOBALS['TSFE']->showHiddenRecords);
+		return $GLOBALS['TSFE']->sys_page->enableFields($table, $show_hidden, $ignore_array);
 	}
 
 	/**
@@ -7666,6 +7667,7 @@ class ContentObjectRenderer {
 		// Init:
 		$query = '';
 		$pid_uid_flag = 0;
+		$enableFieldsIgnore = array();
 		$queryParts = array(
 			'SELECT' => '',
 			'FROM' => '',
@@ -7703,8 +7705,15 @@ class ContentObjectRenderer {
 			$listArr = GeneralUtility::intExplode(',', str_replace('this', $GLOBALS['TSFE']->contentPid, $conf['pidInList']));
 			// Removes all pages which are not visible for the user!
 			$listArr = $this->checkPidArray($listArr);
+			if (GeneralUtility::inList($conf['pidInList'], 'root')) {
+				$listArr[] = 0;
+			}
+			if (GeneralUtility::inList($conf['pidInList'], '-1')) {
+				$listArr[] = -1;
+				$enableFieldsIgnore['pid'] = TRUE;
+			}
 			if (count($listArr)) {
-				$query .= ' AND ' . $table . '.pid IN (' . implode(',', $GLOBALS['TYPO3_DB']->cleanIntArray($listArr)) . ')';
+				$query .= ' AND ' . $table . '.pid IN (' . implode(',', array_map('intval', $listArr)) . ')';
 				$pid_uid_flag++;
 			} else {
 				// If not uid and not pid then uid is set to 0 - which results in nothing!!
@@ -7750,7 +7759,7 @@ class ContentObjectRenderer {
 		if ($table == 'pages') {
 			$query .= ' ' . $GLOBALS['TSFE']->sys_page->where_hid_del . $GLOBALS['TSFE']->sys_page->where_groupAccess;
 		} else {
-			$query .= $this->enableFields($table);
+			$query .= $this->enableFields($table, FALSE, $enableFieldsIgnore);
 		}
 		// MAKE WHERE:
 		if ($query) {
diff --git a/typo3/sysext/frontend/Classes/Page/PageRepository.php b/typo3/sysext/frontend/Classes/Page/PageRepository.php
index a81a1ff56596..768e92403ef7 100644
--- a/typo3/sysext/frontend/Classes/Page/PageRepository.php
+++ b/typo3/sysext/frontend/Classes/Page/PageRepository.php
@@ -898,7 +898,7 @@ class PageRepository {
 				}
 
 				// Filter out versioned records
-				if (!$noVersionPreview) {
+				if (!$noVersionPreview && empty($ignore_array['pid'])) {
 					$query .= ' AND ' . $table . '.pid<>-1';
 				}
 			}
diff --git a/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php b/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php
index f4a4147ca3b1..52844836f89d 100644
--- a/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/ContentObject/ContentObjectRendererTest.php
@@ -1280,10 +1280,12 @@ class ContentObjectRendererTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		);
 		$this->cObj->expects($this->at(0))
 			->method('getTreeList')
-			->with(-16, 15);
+			->with(-16, 15)
+			->will($this->returnValue('15,16'));
 		$this->cObj->expects($this->at(1))
 			->method('getTreeList')
-			->with(-35, 15);
+			->with(-35, 15)
+			->will($this->returnValue('15,35'));
 		$this->cObj->getQuery('tt_content', $conf, TRUE);
 	}
 
@@ -1316,7 +1318,8 @@ class ContentObjectRendererTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		);
 		$this->cObj->expects($this->once())
 			->method('getTreeList')
-			->with(-27);
+			->with(-27)
+			->will($this->returnValue('27'));
 		$this->cObj->getQuery('tt_content', $conf, TRUE);
 	}
 
-- 
GitLab