[FEATURE] Handle enable fields and deleted records settings granulary
authorJochen Rau <jochen.rau@typoplanet.de>
Mon, 30 Jan 2012 15:57:49 +0000 (16:57 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Tue, 14 Aug 2012 20:03:16 +0000 (22:03 +0200)
At the moment it's only possible to select either only visible
(non-hidden, matching start-/stop-time) or all (even deleted)
records.

To ensure a consistent handling in sync with TYPO3 Core behavior, it's
necessary to select disabled but not deleted records in the
backend.

This also effects findByUid() via the Typo3QuerySettings and
allows to write to disabled records in BE context.

* deprecates respectEnableFields in QuerySettingsInterface
* introduces ignoreEnableFields in QuerySettingsInterface
* introduces enableFieldsToBeIgnored in QuerySettingsInterface
* introduces includeDeleted in QuerySettingsInterface
* introduces feature flag ignoreAllEnableFieldsInBe to control default behavior
* select disabled but not deleted records in BE context

Fixes: #29501
Fixes: #8483
Change-Id: I11beb3b83589ad91d72d05116488408fff8d9e75
Releases: 6.0
Reviewed-on: http://review.typo3.org/8773
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
typo3/sysext/extbase/Classes/Persistence/Generic/Exception/InconsistentQuerySettings.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Persistence/Generic/QuerySettingsInterface.php
typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
typo3/sysext/extbase/Classes/Persistence/Generic/Typo3QuerySettings.php
typo3/sysext/extbase/Tests/Unit/Persistence/Storage/Typo3DbBackendTest.php
typo3/sysext/extbase/ext_typoscript_setup.txt

diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Exception/InconsistentQuerySettings.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Exception/InconsistentQuerySettings.php
new file mode 100644 (file)
index 0000000..ae1e0a6
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2012 Felix Oertel <the@extbase.pro>
+ *  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!
+ ***************************************************************/
+
+/**
+ * Thrown if a setting set is not available in the current context.
+ *
+ * @package Extbase
+ * @subpackage Persistence\Storage\Exception
+ * @version $ID:$
+ */
+class Tx_Extbase_Persistence_Generic_Exception_InconsistentQuerySettings extends Tx_Extbase_Persistence_Exception {
+}
+
+?>
\ No newline at end of file
index 4d4bc98..c64f1a6 100644 (file)
@@ -82,22 +82,65 @@ interface Tx_Extbase_Persistence_QuerySettingsInterface {
         */
        public function getRespectSysLanguage();
 
+
        /**
-        * Sets the flag if the visibility in the frontend should be respected.
+        * Sets a flag indicating whether all or some enable fields should be ignored. If TRUE, all enable fields are ignored.
+        * If--in addition to this--enableFieldsToBeIgnored is set, only fields specified there are ignored. If FALSE, all
+        * enable fields are taken into account, regardless of the enableFieldsToBeIgnored setting.
         *
-        * @param boolean $respectEnableFields TRUE if the visibility in the frontend should be respected. If TRUE, the "enable fields" of TYPO3 will be added to the query statement.
-        * @return Tx_Extbase_Persistence_QuerySettingsInterface instance of $this to allow method chaining
+        * @param boolean $ignoreEnableFields
+        * @see setEnableFieldsToBeIgnored()
+        * @api
+        */
+       public function setIgnoreEnableFields($ignoreEnableFields);
+
+       /**
+        * The returned value indicates whether all or some enable fields should be ignored.
+        *
+        * If TRUE, all enable fields are ignored. If--in addition to this--enableFieldsToBeIgnored is set, only fields specified there are ignored.
+        * If FALSE, all enable fields are taken into account, regardless of the enableFieldsToBeIgnored setting.
+        *
+        * @return boolean
+        * @see getEnableFieldsToBeIgnored()
+        */
+       public function getIgnoreEnableFields();
+
+       /**
+        * An array of column names in the enable columns array (array keys in $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']),
+        * to be ignored while building the query statement. Adding a column name here effectively switches off filtering
+        * by this column. This setting is only taken into account if $this->ignoreEnableFields = TRUE.
+        *
+        * @param array $enableFieldsToBeIgnored
+        * @return void
+        * @see setIgnoreEnableFields()
         * @api
         */
-       public function setRespectEnableFields($respectEnableFields);
+       public function setEnableFieldsToBeIgnored($enableFieldsToBeIgnored);
 
        /**
-        * Returns the state, if the visibility settings for the frontend should be respected for the query.
+        * An array of column names in the enable columns array (array keys in $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']),
+        * to be ignored while building the query statement.
         *
-        * @return boolean TRUE, if the visibility settings for the frontend should should be respected; otherwise FALSE.
+        * @return array
+        * @see getIgnoreEnableFields()
         */
-       public function getRespectEnableFields();
+       public function getEnableFieldsToBeIgnored();
 
+       /**
+        * Sets the flag if the query should return objects that are deleted.
+        *
+        * @param boolean $includeDeleted
+        * @return void
+        * @api
+        */
+       public function setIncludeDeleted($includeDeleted);
+
+       /**
+        * Returns if the query should return objects that are deleted.
+        *
+        * @return boolean
+        */
+       public function getIncludeDeleted();
        /**
         * Sets the state, if the QueryResult should be returned unmapped.
         *
index e008c34..fd8a9ee 100644 (file)
@@ -398,7 +398,7 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
                $sql['additionalWhereClause'] = array();
 
                $tableName = $dataMap->getTableName();
-               $this->addEnableFieldsStatement($tableName, $sql);
+               $this->addVisibilityConstraintStatement(new Tx_Extbase_Persistence_Typo3QuerySettings(), $tableName, $sql);
 
                $statement = 'SELECT * FROM ' . $tableName;
                $statement .= ' WHERE ' . implode(' AND ', $fields);
@@ -804,9 +804,8 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
         * @return void
         */
        protected function addAdditionalWhereClause(Tx_Extbase_Persistence_QuerySettingsInterface $querySettings, $tableName, &$sql) {
-               if ($querySettings->getRespectEnableFields()) {
-                       $this->addEnableFieldsStatement($tableName, $sql);
-               }
+               $this->addVisibilityConstraintStatement($querySettings, $tableName, $sql);
+
                if ($querySettings->getRespectSysLanguage()) {
                        $this->addSysLanguageStatement($tableName, $sql);
                }
@@ -821,10 +820,12 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
         * @param string $tableName The database table name
         * @param array &$sql The query parts
         * @return void
+        * @deprecated since Extbase 6.0, will be removed in Extbase 6.2.
         */
        protected function addEnableFieldsStatement($tableName, array &$sql) {
+               t3lib_div::logDeprecatedFunction();
                if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
-                       if (TYPO3_MODE === 'FE') {
+                       if ($this->getTypo3Mode() === 'FE') {
                                $statement = $GLOBALS['TSFE']->sys_page->enableFields($tableName);
                        } else { // TYPO3_MODE === 'BE'
                                $statement = t3lib_BEfunc::deleteClause($tableName);
@@ -837,6 +838,83 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
                }
        }
 
+       /**
+        * Adds enableFields and deletedClause to the query if necessary
+        *
+        * @param Tx_Extbase_Persistence_QuerySettingsInterface $querySettings
+        * @param string $tableName The database table name
+        * @param array &$sql The query parts
+        * @return void
+        */
+       protected function addVisibilityConstraintStatement(Tx_Extbase_Persistence_QuerySettingsInterface $querySettings, $tableName, array &$sql) {
+               $statement = '';
+               if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
+                       $ignoreEnableFields = $querySettings->getIgnoreEnableFields();
+                       $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
+                       $includeDeleted = $querySettings->getIncludeDeleted();
+
+                       if ($this->getTypo3Mode() === 'FE') {
+                               $statement .= $this->getFrontendConstraintStatement($tableName, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
+                       } else { // TYPO3_MODE === 'BE'
+                               $statement .= $this->getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted);
+                       }
+
+                       if(!empty($statement)) {
+                               $statement = (strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement);
+                               $sql['additionalWhereClause'][] = $statement;
+                       }
+               }
+       }
+
+       /**
+        * Returns constraint statement for frontend context
+        *
+        * @param string $tableName
+        * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
+        * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is NULL or an empty array (default) all enable fields are ignored.
+        * @param boolean $includeDeleted A flag indicating whether deleted records should be included
+        * @return string
+        * @throws Tx_Extbase_Persistence_Generic_Exception_InconsistentQuerySettings
+        */
+       protected function getFrontendConstraintStatement($tableName, $ignoreEnableFields, $enableFieldsToBeIgnored = array(), $includeDeleted) {
+               $statement = '';
+               if ($ignoreEnableFields && !$includeDeleted) {
+                       if(count($enableFieldsToBeIgnored)) {
+                                       // array_combine() is necessary because of the way t3lib_pageSelect::enableFields() is implemented
+                               $statement .= $GLOBALS['TSFE']->sys_page->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
+                       } else {
+                               $statement .= $GLOBALS['TSFE']->sys_page->deleteClause($tableName);
+                       }
+               } elseif (!$ignoreEnableFields && !$includeDeleted) {
+                       $statement .= $GLOBALS['TSFE']->sys_page->enableFields($tableName);
+               } elseif (!$ignoreEnableFields && $includeDeleted) {
+                       throw new Tx_Extbase_Persistence_Generic_Exception_InconsistentQuerySettings(
+                               'Query setting "ignoreEnableFields=FALSE" can not be used together with "includeDeleted=TRUE" in frontend context.',
+                               1327678173
+                       );
+               }
+               return $statement;
+       }
+
+       /**
+        * Returns constraint statement for backend context
+        *
+        * @param string $tableName
+        * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
+        * @param boolean $includeDeleted A flag indicating whether deleted records should be included
+        * @return string
+        */
+       protected function getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted) {
+               $statement = '';
+               if (!$ignoreEnableFields) {
+                       $statement .= t3lib_BEfunc::BEenableFields($tableName);
+               }
+               if (!$includeDeleted) {
+                       $statement .= t3lib_BEfunc::deleteClause($tableName);
+               }
+               return $statement;
+       }
+
        /**
         * Builds the language field statement
         *
@@ -986,7 +1064,7 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
                $overlayedRows = array();
                foreach ($rows as $row) {
                        if (!($this->pageSelectObject instanceof t3lib_pageSelect)) {
-                               if (TYPO3_MODE == 'FE') {
+                               if ($this->getTypo3Mode() === 'FE') {
                                        if (is_object($GLOBALS['TSFE'])) {
                                                $this->pageSelectObject = $GLOBALS['TSFE']->sys_page;
                                        } else {
@@ -1124,6 +1202,15 @@ class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persis
                // TODO check if we can hand this over to the Dispatcher to clear the page only once, this will save around 10% time while inserting and updating
                $this->cacheService->clearPageCache($pageIdsToClear);
        }
+
+       /**
+        * Returns the TYPO3 Mode ("FE" for front-end or "BE" for back-end). This method is necessary to enable unit tests to
+        * mock this constant.
+        * @return string
+        */
+       protected function getTypo3Mode() {
+               return TYPO3_MODE;
+       }
 }
 
 ?>
\ No newline at end of file
index 130cf86..803cf46 100644 (file)
@@ -49,10 +49,25 @@ class Tx_Extbase_Persistence_Typo3QuerySettings implements Tx_Extbase_Persistenc
        protected $storagePageIds = array();
 
        /**
-        * Flag if the visibility settings for the frontend should be respected.
+        * A flag indicating whether all or some enable fields should be ignored. If TRUE, all enable fields are ignored.
+        * If--in addition to this--enableFieldsToBeIgnored is set, only fields specified there are ignored. If FALSE, all
+        * enable fields are taken into account, regardless of the enableFieldsToBeIgnored setting.
         * @var boolean
         */
-       protected $respectEnableFields = TRUE;
+       protected $ignoreEnableFields = FALSE;
+
+       /**
+        * An array of column names in the enable columns array (array keys in $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']),
+        * to be ignored while building the query statement
+        * @var array
+        */
+       protected $enableFieldsToBeIgnored = array();
+
+       /**
+        * Flag whether deleted records should be included in the result set.
+        * @var boolean
+        */
+       protected $includeDeleted = FALSE;
 
        /**
         * Flag if the sys_language_uid should be respected (default is TRUE).
@@ -66,11 +81,25 @@ class Tx_Extbase_Persistence_Typo3QuerySettings implements Tx_Extbase_Persistenc
         */
        protected $returnRawQueryResult = FALSE;
 
+       /**
+        * As long as we use a feature flag ignoreAllEnableFieldsInBe to determine the default behavior, the
+        * initializeObject is responsible for handling that.
+        */
+       public function initializeObject() {
+               /** @var $objectManager Tx_Extbase_Object_ObjectManager */
+               $objectManager = t3lib_div::makeInstance('Tx_Extbase_Object_ObjectManager');
+               /** @var $configurationManager Tx_Extbase_Configuration_ConfigurationManagerInterface */
+               $configurationManager = $objectManager->get('Tx_Extbase_Configuration_ConfigurationManagerInterface');
+               if (TYPO3_MODE === 'BE' && $configurationManager->isFeatureEnabled('ignoreAllEnableFieldsInBe')) {
+                       $this->setIgnoreEnableFields(TRUE);
+               }
+       }
+
        /**
         * Sets the flag if the storage page should be respected for the query.
         *
-        * @param $respectStoragePage If TRUE the storage page ID will be determined and the statement will be extended accordingly.
-        * @return $this (fluent interface)
+        * @param boolean $respectStoragePage If TRUE the storage page ID will be determined and the statement will be extended accordingly.
+        * @return Tx_Extbase_Persistence_QuerySettingsInterface (fluent interface)
         * @api
         */
        public function setRespectStoragePage($respectStoragePage) {
@@ -110,8 +139,8 @@ class Tx_Extbase_Persistence_Typo3QuerySettings implements Tx_Extbase_Persistenc
        /**
         * Sets the flag if a  and language overlay should be performed.
         *
-        * @param $respectEnableFields TRUE if a  and language overlay should be performed.
-        * @return $this (fluent interface)
+        * @param boolean $respectEnableFields TRUE if a  and language overlay should be performed.
+        * @return Tx_Extbase_Persistence_QuerySettingsInterface (fluent interface)
         * @api
         */
        public function setRespectSysLanguage($respectSysLanguage) {
@@ -131,12 +160,17 @@ class Tx_Extbase_Persistence_Typo3QuerySettings implements Tx_Extbase_Persistenc
        /**
         * Sets the flag if the visibility in the frontend should be respected.
         *
-        * @param $respectEnableFields TRUE if the visibility in the frontend should be respected. If TRUE, the "enable fields" of TYPO3 will be added to the query statement.
-        * @return $this (fluent interface)
+        * @param boolean $respectEnableFields TRUE if the visibility in the frontend should be respected. If TRUE, the "enable fields" of TYPO3 will be added to the query statement.
+        * @return Tx_Extbase_Persistence_QuerySettingsInterface (fluent interface)
+        * @deprecated since Extbase 6.0, will be removed in Extbase 6.2. Use setIgnoreEnableFields() and setEnableFieldsToBeIgnored() instead.
+        * @see setIgnoreEnableFields()
+        * @see setEnableFieldsToBeIgnored()
         * @api
         */
        public function setRespectEnableFields($respectEnableFields) {
-               $this->respectEnableFields = $respectEnableFields;
+               t3lib_div::logDeprecatedFunction();
+               $this->setIgnoreEnableFields(!$respectEnableFields);
+               $this->setIncludeDeleted(!$respectEnableFields);
                return $this;
        }
 
@@ -144,9 +178,84 @@ class Tx_Extbase_Persistence_Typo3QuerySettings implements Tx_Extbase_Persistenc
         * Returns the state, if the visibility settings for the frontend should be respected for the query.
         *
         * @return boolean TRUE, if the visibility settings for the frontend should should be respected; otherwise FALSE.
+        * @deprecated since Extbase 6.0, will be removed in Extbase 6.2. Use getIgnoreEnableFields() and getEnableFieldsToBeIgnored() instead.
+        * @see getIgnoreEnableFields()
+        * @see getEnableFieldsToBeIgnored()
         */
        public function getRespectEnableFields() {
-               return $this->respectEnableFields;
+               t3lib_div::logDeprecatedFunction();
+               return !($this->getIgnoreEnableFields() && $this->getIncludeDeleted());
+       }
+
+       /**
+        * Sets a flag indicating whether all or some enable fields should be ignored. If TRUE, all enable fields are ignored.
+        * If--in addition to this--enableFieldsToBeIgnored is set, only fields specified there are ignored. If FALSE, all
+        * enable fields are taken into account, regardless of the enableFieldsToBeIgnored setting.
+        *
+        * @param boolean $ignoreEnableFields
+        * @see setEnableFieldsToBeIgnored()
+        * @api
+        */
+       public function setIgnoreEnableFields($ignoreEnableFields) {
+               $this->ignoreEnableFields = $ignoreEnableFields;
+       }
+
+       /**
+        * The returned value indicates whether all or some enable fields should be ignored.
+        *
+        * If TRUE, all enable fields are ignored. If--in addition to this--enableFieldsToBeIgnored is set, only fields specified there are ignored.
+        * If FALSE, all enable fields are taken into account, regardless of the enableFieldsToBeIgnored setting.
+        *
+        * @return boolean
+        * @see getEnableFieldsToBeIgnored()
+        */
+       public function getIgnoreEnableFields() {
+               return $this->ignoreEnableFields;
+       }
+
+       /**
+        * An array of column names in the enable columns array (array keys in $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']),
+        * to be ignored while building the query statement. Adding a column name here effectively switches off filtering
+        * by this column. This setting is only taken into account if $this->ignoreEnableFields = TRUE.
+        *
+        * @param array $enableFieldsToBeIgnored
+        * @return void
+        * @see setIgnoreEnableFields()
+        * @api
+        */
+       public function setEnableFieldsToBeIgnored($enableFieldsToBeIgnored) {
+               $this->enableFieldsToBeIgnored = $enableFieldsToBeIgnored;
+       }
+
+       /**
+        * An array of column names in the enable columns array (array keys in $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']),
+        * to be ignored while building the query statement.
+        *
+        * @return array
+        * @see getIgnoreEnableFields()
+        */
+       public function getEnableFieldsToBeIgnored() {
+               return $this->enableFieldsToBeIgnored;
+       }
+
+       /**
+        * Sets the flag if the query should return objects that are deleted.
+        *
+        * @param boolean $includeDeleted
+        * @return void
+        * @api
+        */
+       public function setIncludeDeleted($includeDeleted) {
+               $this->includeDeleted = $includeDeleted;
+       }
+
+       /**
+        * Returns if the query should return objects that are deleted.
+        *
+        * @return boolean
+        */
+       public function getIncludeDeleted() {
+               return $this->includeDeleted;
        }
 
        /**
index 004754b..645b52c 100644 (file)
@@ -35,8 +35,8 @@ class Tx_Extbase_Tests_Unit_Persistence_Storage_Typo3DbBackendTest extends Tx_Ex
        protected $backupGlobals = true;
 
        /**
-        * Exclude TYPO3_DB from backup/ restore of $GLOBALS
-        * because resource types cannot be handled during serializing
+        * Excludes TYPO3_DB from backup/restore of $GLOBALS because
+        * resource types cannot be handled during serializing.
         *
         * @var array
         */
@@ -231,8 +231,8 @@ class Tx_Extbase_Tests_Unit_Persistence_Storage_Typo3DbBackendTest extends Tx_Ex
                $mockTypo3DbBackend->_set('dataMapper', $mockDataMapper);
                $mockTypo3DbBackend->_callRef('parseOrderings', $orderings, $mockSource, $sql);
 
-               $expecedSql = array('orderings' => array('tx_myext_tablename.converted_fieldname ASC'));
-               $this->assertSame($expecedSql, $sql);
+               $expectedSql = array('orderings' => array('tx_myext_tablename.converted_fieldname ASC'));
+               $this->assertSame($expectedSql, $sql);
        }
 
        /**
@@ -274,8 +274,175 @@ class Tx_Extbase_Tests_Unit_Persistence_Storage_Typo3DbBackendTest extends Tx_Ex
                $mockTypo3DbBackend->_set('dataMapper', $mockDataMapper);
                $mockTypo3DbBackend->_callRef('parseOrderings', $orderings, $mockSource, $sql);
 
-               $expecedSql = array('orderings' => array('tx_myext_tablename.converted_fieldname ASC', 'tx_myext_tablename.converted_fieldname DESC'));
-               $this->assertSame($expecedSql, $sql);
+               $expectedSql = array('orderings' => array('tx_myext_tablename.converted_fieldname ASC', 'tx_myext_tablename.converted_fieldname DESC'));
+               $this->assertSame($expectedSql, $sql);
+       }
+
+       public function providerForVisibilityConstraintStatement() {
+               return array(
+                       'in be: include all' => array('BE', TRUE, array(), TRUE, NULL),
+                       'in be: ignore enable fields but do not include deleted' => array('BE', TRUE, array(), FALSE, array('tx_foo_table.deleted_column=0')),
+                       'in be: respect enable fields but include deleted' => array('BE', FALSE, array(), TRUE, array('tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789)')),
+                       'in be: respect enable fields and do not include deleted' => array('BE', FALSE, array(), FALSE, array('tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789) AND tx_foo_table.deleted_column=0')),
+                       'in fe: include all' => array('FE', TRUE, array(), TRUE, NULL),
+                       'in fe: ignore enable fields but do not include deleted' => array('FE', TRUE, array(), FALSE, array('tx_foo_table.deleted_column=0')),
+                       'in fe: ignore only starttime and do not include deleted' => array('FE', TRUE, array('starttime'), FALSE, array('tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0')),
+                       'in fe: respect enable fields and do not include deleted' => array('FE', FALSE, array(), FALSE, array('tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0 AND tx_foo_table.starttime_column<=123456789')),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider providerForVisibilityConstraintStatement
+        */
+       public function visibilityConstraintStatementIsGeneratedAccordingToTheQuerySettings($mode, $ignoreEnableFields, $enableFieldsToBeIgnored, $deletedValue, $expectedSql) {
+               $tableName = 'tx_foo_table';
+               $GLOBALS['TCA'][$tableName]['ctrl'] = array(
+                       'enablecolumns' => array(
+                               'disabled' => 'disabled_column',
+                               'starttime' => 'starttime_column'
+                       ),
+                       'delete' => 'deleted_column'
+               );
+               $GLOBALS['TSFE']->sys_page = new t3lib_pageSelect();
+               $GLOBALS['SIM_ACCESS_TIME'] = 123456789;
+
+               $mockQuerySettings = $this->getMock('Tx_Extbase_Persistence_Typo3QuerySettings', array('getIgnoreEnableFields', 'getEnableFieldsToBeIgnored', 'getIncludeDeleted'), array(), '', FALSE);
+               $mockQuerySettings->expects($this->once())->method('getIgnoreEnableFields')->will($this->returnValue($ignoreEnableFields));
+               $mockQuerySettings->expects($this->once())->method('getEnableFieldsToBeIgnored')->will($this->returnValue($enableFieldsToBeIgnored));
+               $mockQuerySettings->expects($this->once())->method('getIncludeDeleted')->will($this->returnValue($deletedValue));
+
+               $sql = array();
+               $mockTypo3DbBackend = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Persistence_Storage_Typo3DbBackend'), array('getTypo3Mode'), array(), '', FALSE);
+               $mockTypo3DbBackend->expects($this->any())->method('getTypo3Mode')->will($this->returnValue($mode));
+               $mockTypo3DbBackend->_callRef('addVisibilityConstraintStatement', $mockQuerySettings, $tableName, $sql);
+
+               $this->assertSame($expectedSql, $sql['additionalWhereClause']);
+
+               unset($GLOBALS['TCA'][$tableName]);
+       }
+
+       public function providerForRespectEnableFields() {
+               return array(
+                       'in be: respectEnableFields=false' => array('BE', FALSE, NULL),
+                       'in be: respectEnableFields=true' => array('BE', TRUE, array('tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789) AND tx_foo_table.deleted_column=0')),
+                       'in be: respectEnableFields=false' => array('FE', FALSE, NULL),
+                       'in be: respectEnableFields=true' => array('FE', TRUE, array('tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0 AND tx_foo_table.starttime_column<=123456789'))
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider providerForRespectEnableFields
+        */
+       public function respectEnableFieldsSettingGeneratesCorrectStatement($mode, $respectEnableFields, $expectedSql) {
+               $tableName = 'tx_foo_table';
+               $GLOBALS['TCA'][$tableName]['ctrl'] = array(
+                       'enablecolumns' => array(
+                               'disabled' => 'disabled_column',
+                               'starttime' => 'starttime_column'
+                       ),
+                       'delete' => 'deleted_column'
+               );
+               $GLOBALS['TSFE']->sys_page = new t3lib_pageSelect();
+               $GLOBALS['SIM_ACCESS_TIME'] = 123456789;
+
+               $mockQuerySettings = $this->getMock('Tx_Extbase_Persistence_Typo3QuerySettings', array('dummy'), array(), '', FALSE);
+               $mockQuerySettings->setRespectEnableFields($respectEnableFields);
+
+               $sql = array();
+               $mockTypo3DbBackend = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Persistence_Storage_Typo3DbBackend'), array('getTypo3Mode'), array(), '', FALSE);
+               $mockTypo3DbBackend->expects($this->any())->method('getTypo3Mode')->will($this->returnValue($mode));
+               $mockTypo3DbBackend->_callRef('addVisibilityConstraintStatement', $mockQuerySettings, $tableName, $sql);
+
+               $this->assertSame($expectedSql, $sql['additionalWhereClause']);
+
+               unset($GLOBALS['TCA'][$tableName]);
+       }
+
+       /**
+        * @test
+        * @expectedException Tx_Extbase_Persistence_Generic_Exception_InconsistentQuerySettings
+        */
+       public function visibilityConstraintStatementGenerationThrowsExceptionIfTheQuerySettingsAreInconsistent() {
+               $tableName = 'tx_foo_table';
+               $GLOBALS['TCA'][$tableName]['ctrl'] = array(
+                       'enablecolumns' => array(
+                               'disabled' => 'disabled_column'
+                       ),
+                       'delete' => 'deleted_column'
+               );
+
+               $mockQuerySettings = $this->getMock('Tx_Extbase_Persistence_Typo3QuerySettings', array('getIgnoreEnableFields', 'getEnableFieldsToBeIgnored', 'getIncludeDeleted'), array(), '', FALSE);
+               $mockQuerySettings->expects($this->once())->method('getIgnoreEnableFields')->will($this->returnValue(FALSE));
+               $mockQuerySettings->expects($this->once())->method('getEnableFieldsToBeIgnored')->will($this->returnValue(array()));
+               $mockQuerySettings->expects($this->once())->method('getIncludeDeleted')->will($this->returnValue(TRUE));
+
+               $sql = array();
+               $mockTypo3DbBackend = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Persistence_Storage_Typo3DbBackend'), array('getTypo3Mode'), array(), '', FALSE);
+               $mockTypo3DbBackend->expects($this->any())->method('getTypo3Mode')->will($this->returnValue('FE'));
+               $mockTypo3DbBackend->_callRef('addVisibilityConstraintStatement', $mockQuerySettings, $tableName, $sql);
+
+               unset($GLOBALS['TCA'][$tableName]);
+       }
+
+       /**
+        * @test
+        */
+       public function uidOfAlreadyPersistedValueObjectIsDeterminedCorrectly() {
+               $mockValueObject = $this->getMockForAbstractClass(
+                       'Tx_Extbase_DomainObject_AbstractValueObject',
+                       array('_getProperties'), '', FALSE
+               );
+               $mockValueObject->expects($this->any())
+                       ->method('_getProperties')
+                       ->will($this->returnValue(array('propertyName' => 'propertyValue')));
+
+               $mockColumnMap = $this->getMock(
+                       'Tx_Extbase_Persistence_Mapper_DataMap',
+                       array('isPersistableProperty', 'getColumnName'), array(), '', FALSE
+               );
+               $mockColumnMap->expects($this->any())->method('getColumnName')->will($this->returnValue('column_name'));
+
+               $tableName = 'tx_foo_table';
+
+               $mockDataMap = $this->getMock(
+                       'Tx_Extbase_Persistence_Mapper_DataMap',
+                       array('isPersistableProperty', 'getColumnMap', 'getTableName'), array(), '', FALSE);
+               $mockDataMap->expects($this->any())->method('isPersistableProperty')->will($this->returnValue(TRUE));
+               $mockDataMap->expects($this->any())->method('getColumnMap')->will($this->returnValue($mockColumnMap));
+               $mockDataMap->expects($this->any())->method('getTableName')->will($this->returnValue($tableName));
+
+               $mockDataMapper = $this->getMock('Tx_Extbase_Persistence_Mapper_DataMapper', array('getDataMap'), array(), '', FALSE);
+               $mockDataMapper->expects($this->any())->method('getDataMap')->will($this->returnValue($mockDataMap));
+
+               $expectedStatement = "SELECT * FROM tx_foo_table WHERE column_name=?";
+               $expectedParameters = array('plainPropertyValue');
+               $expectedUid = 52;
+
+               $mockDataBaseHandle = $this->getMock('t3lib_db', array('sql_query', 'sql_fetch_assoc'), array(), '', FALSE);
+               $mockDataBaseHandle->expects($this->once())->method('sql_query')->will($this->returnValue('resource'));
+               $mockDataBaseHandle->expects($this->any())
+                       ->method('sql_fetch_assoc')->with('resource')
+                       ->will($this->returnValue(array('uid' => $expectedUid)));
+
+               $mockTypo3DbBackend = $this->getMock(
+                       $this->buildAccessibleProxy('Tx_Extbase_Persistence_Storage_Typo3DbBackend'),
+                       array('getPlainValue', 'checkSqlErrors', 'replacePlaceholders', 'addVisibilityConstraintStatement'), array(), '', FALSE
+               );
+               $mockTypo3DbBackend->expects($this->once())->method('getPlainValue')->will($this->returnValue('plainPropertyValue'));
+               $mockTypo3DbBackend->expects($this->once())
+                       ->method('addVisibilityConstraintStatement')
+                       ->with($this->isInstanceOf('Tx_Extbase_Persistence_QuerySettingsInterface'), $tableName, $this->isType('array'));
+               $mockTypo3DbBackend->expects($this->once())
+                       ->method('replacePlaceholders')
+                       ->with($expectedStatement, $expectedParameters)
+                       ->will($this->returnValue('plainPropertyValue'));
+               $mockTypo3DbBackend->_set('dataMapper', $mockDataMapper);
+               $mockTypo3DbBackend->_set('databaseHandle', $mockDataBaseHandle);
+               $result = $mockTypo3DbBackend->_callRef('getUidOfAlreadyPersistedValueObject', $mockValueObject);
+
+               $this->assertSame($expectedUid, $result);
        }
 
        /**
index 15d8518..2512c50 100644 (file)
@@ -59,5 +59,7 @@ config.tx_extbase {
                rewrittenPropertyMapper = 0
                 # if enabled, default controller and/or action is skipped when creating URIs through the URI Builder (see http://forge.typo3.org/projects/typo3v4-mvc/wiki/Skip_default_arguments_in_URIs)
                skipDefaultArguments = 0
+               # if set to 1, the enable fields are ignored in BE context
+               ignoreAllEnableFieldsInBe = 0
        }
 }
\ No newline at end of file