[FEATURE] Introduce backend layout data providers 04/11804/43
authorOliver Hader <oliver@typo3.org>
Sat, 12 Oct 2013 16:47:22 +0000 (18:47 +0200)
committerOliver Hader <oliver.hader@typo3.org>
Mon, 14 Oct 2013 23:43:37 +0000 (01:43 +0200)
Backend layouts are currently stored in the database as
regular records. Since extension cannot ship their specific
backend layout definitions, data providers become handy in
providing the accordant information taken from e.g. static
files in the file system.

Data providers can be registered like shown in the follow and
need to be implement DataProviderInterface of the namespace
TYPO3\CMS\Backend\View\BackendLayout:

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']
['BackendLayoutDataProvider'][$_EXTKEY] = 'Classname';

A DataProviderContext object is used to transport submitted
data (e.g. table or field name) to the accordant data providers.

Change-Id: I2e3d39e720c6d1bffa9a586148f0eea4cab0210e
Resolves: #37208
Documentation: #52809
Releases: 6.2
Reviewed-on: https://review.typo3.org/11804
Reviewed-by: Oliver Hader
Tested-by: Oliver Hader
16 files changed:
typo3/sysext/backend/Classes/Controller/ContentElement/MoveElementController.php
typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php [new file with mode: 0644]
typo3/sysext/backend/Classes/View/BackendLayout/BackendLayoutCollection.php [new file with mode: 0644]
typo3/sysext/backend/Classes/View/BackendLayout/DataProviderCollection.php [new file with mode: 0644]
typo3/sysext/backend/Classes/View/BackendLayout/DataProviderContext.php [new file with mode: 0644]
typo3/sysext/backend/Classes/View/BackendLayout/DataProviderInterface.php [new file with mode: 0644]
typo3/sysext/backend/Classes/View/BackendLayout/DefaultDataProvider.php [new file with mode: 0644]
typo3/sysext/backend/Classes/View/BackendLayoutView.php
typo3/sysext/backend/Classes/View/PageLayoutView.php
typo3/sysext/backend/Tests/Unit/View/BackendLayout/BackendLayoutCollectionTest.php [new file with mode: 0644]
typo3/sysext/backend/Tests/Unit/View/BackendLayout/BackendLayoutTest.php [new file with mode: 0644]
typo3/sysext/backend/Tests/Unit/View/BackendLayout/DataProviderCollectionTest.php [new file with mode: 0644]
typo3/sysext/backend/Tests/Unit/View/BackendLayoutViewTest.php [new file with mode: 0644]
typo3/sysext/cms/locallang_tca.xlf
typo3/sysext/core/Configuration/TCA/pages.php
typo3/sysext/core/ext_tables.sql

index 07c3eea..d7fa048 100644 (file)
@@ -199,11 +199,12 @@ class MoveElementController {
                                        // SHARED page-TSconfig settings.
                                        $modTSconfig_SHARED = BackendUtility::getModTSconfig($this->page_id, 'mod.SHARED');
                                        $colPosArray = GeneralUtility::callUserFunction('TYPO3\\CMS\\Backend\\View\\BackendLayoutView->getColPosListItemsParsed', $this->page_id, $this);
+                                       $colPosIds = array();
                                        foreach ($colPosArray as $colPos) {
-                                               $colPosList .= $colPosList != '' ? ',' . $colPos[1] : $colPos[1];
+                                               $colPosIds[] = $colPos[1];
                                        }
                                        // Removing duplicates, if any
-                                       $colPosList = implode(',', array_unique(GeneralUtility::intExplode(',', $colPosList)));
+                                       $colPosList = implode(',', array_unique($colPosIds));
                                        // Adding parent page-header and the content element columns from position-map:
                                        $code = $hline . '<br />';
                                        $code .= $posMap->printContentElementColumns($this->page_id, $this->moveUid, $colPosList, 1, $this->R_URI);
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php b/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php
new file mode 100644 (file)
index 0000000..7770a01
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+namespace TYPO3\CMS\Backend\View\BackendLayout;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * Class to represent a backend layout.
+ *
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class BackendLayout {
+
+       /**
+        * @var string
+        */
+       protected $identifier;
+
+       /**
+        * @var string
+        */
+       protected $title;
+
+       /**
+        * @var string
+        */
+       protected $description;
+
+       /**
+        * @var string
+        */
+       protected $iconPath;
+
+       /**
+        * @var string
+        */
+       protected $configuration;
+
+       /**
+        * @var array
+        */
+       protected $data;
+
+       /**
+        * @param string $identifier
+        * @param string $title
+        * @param string $configuration
+        * @return BackendLayout
+        */
+       static public function create($identifier, $title, $configuration) {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
+                       'TYPO3\\CMS\\Backend\\View\\BackendLayout\\BackendLayout',
+                       $identifier,
+                       $title,
+                       $configuration
+               );
+       }
+
+       /**
+        * @param string $identifier
+        * @param string $title
+        * @param string $configuration
+        */
+       public function __construct($identifier, $title, $configuration) {
+               $this->setIdentifier($identifier);
+               $this->setTitle($title);
+               $this->setConfiguration($configuration);
+       }
+
+       /**
+        * @return string
+        */
+       public function getIdentifier() {
+               return $this->identifier;
+       }
+
+       /**
+        * @param string $identifier
+        * @throws \UnexpectedValueException
+        */
+       public function setIdentifier($identifier) {
+               if (strpos($identifier, '__') !== FALSE) {
+                       throw new \UnexpectedValueException(
+                               'Identifier "' . $identifier . '" must not contain "__"',
+                               1381597630
+                       );
+               }
+
+               $this->identifier = $identifier;
+       }
+
+       /**
+        * @return string
+        */
+       public function getTitle() {
+               return $this->title;
+       }
+
+       /**
+        * @param string $title
+        */
+       public function setTitle($title) {
+               $this->title = $title;
+       }
+
+       /**
+        * @return string
+        */
+       public function getDescription() {
+               return $this->description;
+       }
+
+       /**
+        * @param string $description
+        */
+       public function setDescription($description) {
+               $this->description = $description;
+       }
+
+       /**
+        * @return string
+        */
+       public function getIconPath() {
+               return $this->iconPath;
+       }
+
+       /**
+        * @param string $iconPath
+        */
+       public function setIconPath($iconPath) {
+               $this->iconPath = $iconPath;
+       }
+
+       /**
+        * @return string
+        */
+       public function getConfiguration() {
+               return $this->configuration;
+       }
+
+       /**
+        * @param string $configuration
+        */
+       public function setConfiguration($configuration) {
+               $this->configuration = $configuration;
+       }
+
+       /**
+        * @return array
+        */
+       public function getData() {
+               return $this->data;
+       }
+
+       /**
+        * @param array $data
+        */
+       public function setData(array $data) {
+               $this->data = $data;
+       }
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayoutCollection.php b/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayoutCollection.php
new file mode 100644 (file)
index 0000000..90a044e
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+namespace TYPO3\CMS\Backend\View\BackendLayout;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * Collection of backend layouts.
+ *
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class BackendLayoutCollection {
+
+       /**
+        * @var string
+        */
+       protected $identifier;
+
+       /**
+        * @var array|BackendLayout[]
+        */
+       protected $backendLayouts = array();
+
+       /**
+        * @param string $identifier
+        */
+       public function __construct($identifier) {
+               $this->setIdentifier($identifier);
+       }
+
+       /**
+        * @return string
+        */
+       public function getIdentifier() {
+               return $this->identifier;
+       }
+
+       /**
+        * @param string $identifier
+        * @throws \UnexpectedValueException
+        */
+       public function setIdentifier($identifier) {
+               if (strpos($identifier, '__') !== FALSE) {
+                       throw new \UnexpectedValueException(
+                               'Identifier "' . $identifier . '" must not contain "__"',
+                               1381597631
+                       );
+               }
+
+               $this->identifier = $identifier;
+       }
+
+       /**
+        * Adds a backend layout to this collection.
+        *
+        * @param BackendLayout $backendLayout
+        * @throws \LogicException
+        */
+       public function add(BackendLayout $backendLayout) {
+               $identifier = $backendLayout->getIdentifier();
+
+               if (strpos($identifier, '__') !== FALSE) {
+                       throw new \UnexpectedValueException(
+                               'BackendLayout Identifier "' . $identifier . '" must not contain "__"',
+                               1381597628
+                       );
+               }
+
+               if (isset($this->backendLayouts[$identifier])) {
+                       throw new \LogicException(
+                               'Backend Layout ' . $identifier . ' is already defined',
+                               1381559376
+                       );
+               }
+
+               $this->backendLayouts[$identifier] = $backendLayout;
+       }
+
+       /**
+        * Gets a backend layout by (regular) identifier.
+        *
+        * @param string $identifier
+        * @return NULL|BackendLayout
+        */
+       public function get($identifier) {
+               $backendLayout = NULL;
+
+               if (isset($this->backendLayouts[$identifier])) {
+                       $backendLayout = $this->backendLayouts[$identifier];
+               }
+
+               return $backendLayout;
+       }
+
+       /**
+        * Gets all backend layouts in this collection.
+        *
+        * @return array|BackendLayout[]
+        */
+       public function getAll() {
+               return $this->backendLayouts;
+       }
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/DataProviderCollection.php b/typo3/sysext/backend/Classes/View/BackendLayout/DataProviderCollection.php
new file mode 100644 (file)
index 0000000..7f93dc8
--- /dev/null
@@ -0,0 +1,139 @@
+<?php
+namespace TYPO3\CMS\Backend\View\BackendLayout;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * Collection of backend layout data providers.
+ *
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class DataProviderCollection implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * @var array|DataProviderInterface[]
+        */
+       protected $dataProviders = array();
+
+       /**
+        * @var array
+        */
+       protected $results = array();
+
+       /**
+        * Adds a data provider to this collection.
+        *
+        * @param string $identifier
+        * @param string|object $classNameOrObject
+        * @throws \UnexpectedValueException
+        * @throws \LogicException
+        */
+       public function add($identifier, $classNameOrObject) {
+               if (strpos($identifier, '__') !== FALSE) {
+                       throw new \UnexpectedValueException(
+                               'Identifier "' . $identifier . '" must not contain "__"',
+                               1381597629
+                       );
+               }
+
+               if (is_object($classNameOrObject)) {
+                       $className = get_class($classNameOrObject);
+                       $dataProvider = $classNameOrObject;
+               } else {
+                       $className = $classNameOrObject;
+                       $dataProvider = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($classNameOrObject);
+               }
+
+               if (!$dataProvider instanceof DataProviderInterface) {
+                       throw new \LogicException(
+                               $className . ' must implement interface TYPO3\\CMS\\Backend\\View\\BackendLayout\\DataProviderInterface',
+                               1381269811
+                       );
+               }
+
+               $this->dataProviders[$identifier] = $dataProvider;
+       }
+
+       /**
+        * Gets all backend layout collections and thus, all
+        * backend layouts. Each data provider returns its own
+        * backend layout collection.
+        *
+        * @param DataProviderContext $dataProviderContext
+        * @return array|BackendLayoutCollection[]
+        */
+       public function getBackendLayoutCollections(DataProviderContext $dataProviderContext) {
+               $result = array();
+
+               foreach ($this->dataProviders as $identifier => $dataProvider) {
+                       $backendLayoutCollection = $this->createBackendLayoutCollection($identifier);
+                       $dataProvider->addBackendLayouts($dataProviderContext, $backendLayoutCollection);
+                       $result[$identifier] = $backendLayoutCollection;
+               }
+
+               return $result;
+       }
+
+       /**
+        * Gets a backend layout by a combined identifier, which is
+        * e.g. "myextension_regular" and "myextension" is the identifier
+        * of the accordant data provider and "regular" the identifier of
+        * the accordant backend layout.
+        *
+        * @param string $combinedIdentifier
+        * @return NULL|BackendLayout
+        */
+       public function getBackendLayout($combinedIdentifier) {
+               $backendLayout = NULL;
+
+               if (strpos($combinedIdentifier, '__') === FALSE) {
+                       $dataProviderIdentifier = 'default';
+                       $backendLayoutIdentifier = $combinedIdentifier;
+               } else {
+                       list($dataProviderIdentifier, $backendLayoutIdentifier) = explode('__', $combinedIdentifier, 2);
+               }
+
+               if (isset($this->dataProviders[$dataProviderIdentifier])) {
+                       $backendLayout = $this->dataProviders[$dataProviderIdentifier]->getBackendLayout($backendLayoutIdentifier);
+               }
+
+               return $backendLayout;
+       }
+
+       /**
+        * Creates a new backend layout collection.
+        *
+        * @param string $identifier
+        * @return BackendLayoutCollection
+        */
+       protected function createBackendLayoutCollection($identifier) {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
+                       'TYPO3\\CMS\\Backend\\View\\BackendLayout\\BackendLayoutCollection', $identifier
+               );
+       }
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/DataProviderContext.php b/typo3/sysext/backend/Classes/View/BackendLayout/DataProviderContext.php
new file mode 100644 (file)
index 0000000..2834824
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+namespace TYPO3\CMS\Backend\View\BackendLayout;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * Context that is forwared to backend layout data providers.
+ *
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class DataProviderContext implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * @var integer
+        */
+       protected $pageId;
+
+       /**
+        * @var string
+        */
+       protected $tableName;
+
+       /**
+        * @var string
+        */
+       protected $fieldName;
+
+       /**
+        * @var array
+        */
+       protected $data;
+
+       /**
+        * @var array
+        */
+       protected $pageTsConfig;
+
+       /**
+        * @return integer
+        */
+       public function getPageId() {
+               return $this->pageId;
+       }
+
+       /**
+        * @param integer $pageId
+        * @return DataProviderContext
+        */
+       public function setPageId($pageId) {
+               $this->pageId = $pageId;
+               return $this;
+       }
+
+       /**
+        * @return string
+        */
+       public function getTableName() {
+               return $this->tableName;
+       }
+
+       /**
+        * @param string $tableName
+        * @return DataProviderContext
+        */
+       public function setTableName($tableName) {
+               $this->tableName = $tableName;
+               return $this;
+       }
+
+       /**
+        * @return string
+        */
+       public function getFieldName() {
+               return $this->fieldName;
+       }
+
+       /**
+        * @param string $fieldName
+        * @return DataProviderContext
+        */
+       public function setFieldName($fieldName) {
+               $this->field = $fieldName;
+               return $this;
+       }
+
+       /**
+        * @return array
+        */
+       public function getData() {
+               return $this->data;
+       }
+
+       /**
+        * @param array $data
+        * @return DataProviderContext
+        */
+       public function setData(array $data) {
+               $this->data = $data;
+               return $this;
+       }
+
+       /**
+        * @return array
+        */
+       public function getPageTsConfig() {
+               return $this->pageTsConfig;
+       }
+
+       /**
+        * @param array $pageTsConfig
+        * @return DataProviderContext
+        */
+       public function setPageTsConfig(array $pageTsConfig) {
+               $this->pageTsConfig = $pageTsConfig;
+               return $this;
+       }
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/DataProviderInterface.php b/typo3/sysext/backend/Classes/View/BackendLayout/DataProviderInterface.php
new file mode 100644 (file)
index 0000000..cd8cf3e
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+namespace TYPO3\CMS\Backend\View\BackendLayout;
+
+       /***************************************************************
+        *  Copyright notice
+        *
+        *  (c) 2008-2013 Jo Hasenau <info@cybercraft.de>
+        *  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.
+        *  A copy is found in the textfile GPL.txt and important notices to the license
+        *  from the author is found in LICENSE.txt distributed with these scripts.
+        *
+        *  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!
+        *
+        ***************************************************************/
+
+/**
+ * Interface for classes which hook into BackendLayoutDataProvider
+ * to provide additional backend layouts from various sources.
+ *
+ * @author Jo Hasenau <info@cybercraft.de>
+ */
+interface DataProviderInterface {
+
+       /**
+        * Adds backend layouts to the given backend layout collection.
+        *
+        * @param DataProviderContext $dataProviderContext
+        * @param BackendLayoutCollection $backendLayoutCollection
+        * @return void
+        */
+       public function addBackendLayouts(DataProviderContext $dataProviderContext, BackendLayoutCollection $backendLayoutCollection);
+
+       /**
+        * Gets a backend layout by (regular) identifier.
+        *
+        * @param string $identifier
+        * @return NULL|BackendLayout
+        */
+       public function getBackendLayout($identifier);
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/DefaultDataProvider.php b/typo3/sysext/backend/Classes/View/BackendLayout/DefaultDataProvider.php
new file mode 100644 (file)
index 0000000..3b30b04
--- /dev/null
@@ -0,0 +1,208 @@
+<?php
+namespace TYPO3\CMS\Backend\View\BackendLayout;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2008-2013 Jo Hasenau <info@cybercraft.de>
+ *  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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+
+/**
+ * Backend layout data provider class
+ *
+ * @author Jo Hasenau <info@cybercraft.de>
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class DefaultDataProvider implements DataProviderInterface {
+
+       /**
+        * Adds backend layouts to the given backend layout collection.
+        * The default backend layout ('default_default') is not added
+        * since it's the default fallback if nothing is specified.
+        *
+        * @param DataProviderContext $dataProviderContext
+        * @param BackendLayoutCollection $backendLayoutCollection
+        * @return void
+        */
+       public function addBackendLayouts(DataProviderContext $dataProviderContext, BackendLayoutCollection $backendLayoutCollection) {
+               $layoutData = $this->getLayoutData(
+                       $dataProviderContext->getFieldName(),
+                       $dataProviderContext->getPageTsConfig(),
+                       $dataProviderContext->getPageId()
+               );
+
+               foreach ($layoutData as $data) {
+                       $backendLayout = $this->createBackendLayout($data);
+                       $backendLayoutCollection->add($backendLayout);
+               }
+       }
+
+       /**
+        * Gets a backend layout by (regular) identifier.
+        *
+        * @param string $identifier
+        * @return NULL|BackendLayout
+        */
+       public function getBackendLayout($identifier) {
+               $backendLayout = NULL;
+
+               if ((string) $identifier === 'default') {
+                       return $this->createDefaultBackendLayout();
+               }
+
+               $data = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
+                       '*',
+                       'backend_layout',
+                       'uid=' . intval($identifier) . BackendUtility::BEenableFields('backend_layout')
+               );
+               if (is_array($data)) {
+                       $backendLayout = $this->createBackendLayout($data);
+               }
+
+               return $backendLayout;
+       }
+
+       /**
+        * Creates a backend layout with the default configuration.
+        *
+        * @return BackendLayout
+        */
+       protected function createDefaultBackendLayout() {
+               return BackendLayout::create(
+                       'default',
+                       'LLL:EXT:cms/locallang_tca.xlf:pages.backend_layout.default',
+                       \TYPO3\CMS\Backend\View\BackendLayoutView::getDefaultColumnLayout()
+               );
+       }
+
+       /**
+        * Creates a new backend layout using the given record data.
+        *
+        * @param array $data
+        * @return BackendLayout
+        */
+       protected function createBackendLayout(array $data) {
+               $backendLayout = BackendLayout::create($data['uid'], $data['title'], $data['config']);
+               $backendLayout->setIconPath($this->getIconPath($data['icon']));
+               $backendLayout->setData($data);
+               return $backendLayout;
+       }
+
+       /**
+        * Gets and sanitizes the icon path.
+        *
+        * @param string $icon Name of the icon file
+        * @return string
+        */
+       protected function getIconPath($icon) {
+               $iconPath = '';
+
+               if (!empty($icon)) {
+                       $path = rtrim($GLOBALS['TCA']['backend_layout']['ctrl']['selicon_field_path'], '/') . '/';
+                       $iconPath = $path . $icon;
+               }
+
+               return $iconPath;
+       }
+
+       /**
+        * Get all layouts from the core's default data provider.
+        *
+        * @param string $fieldName the name of the field the layouts are provided for (either backend_layout or backend_layout_next_level)
+        * @param array $pageTsConfig PageTSconfig of the given page
+        * @param integer $pageUid the ID of the page wea re getting the layouts for
+        * @return array $layouts A collection of layout data of the registered provider
+        */
+       protected function getLayoutData($fieldName, array $pageTsConfig, $pageUid) {
+               $storagePid = $this->getStoragePid($pageTsConfig);
+               $pageTsConfigId = $this->getPageTSconfigIds($pageTsConfig);
+
+               // Add layout records
+               $results = $this->getDatabaseConnection()->exec_SELECTgetRows(
+                       '*',
+                       'backend_layout',
+                               '(
+                                       ( ' . intval($pageTsConfigId[$fieldName]) . ' = 0 AND ' . intval($storagePid) . ' = 0 )
+                                       OR ( backend_layout.pid = ' . intval($pageTsConfigId[$fieldName]) . ' OR backend_layout.pid = ' . intval($storagePid) . ' )
+                                       OR ( ' . intval($pageTsConfigId[$fieldName]) . ' = 0 AND backend_layout.pid = ' . intval($pageUid) . ' )
+                               )' . BackendUtility::BEenableFields('backend_layout'),
+                       '',
+                       'sorting ASC'
+               );
+
+               if (!is_array($results)) {
+                       $results = array();
+               }
+
+               return $results;
+       }
+
+       /**
+        * Returns the storage PID from TCEFORM.
+        *
+        * @param array $pageTsConfig
+        * @return integer
+        */
+       protected function getStoragePid(array $pageTsConfig) {
+               $storagePid = 0;
+
+               if (!empty($pageTsConfig['TCEFORM.']['pages.']['_STORAGE_PID'])) {
+                       $storagePid = (int) $pageTsConfig['TCEFORM.']['pages.']['_STORAGE_PID'];
+               }
+
+               return $storagePid;
+       }
+
+       /**
+        * Returns the page TSconfig from TCEFORM.
+        *
+        * @param array $pageTsConfig
+        * @return array
+        */
+       protected function getPageTSconfigIds(array $pageTsConfig) {
+               $pageTsConfigIds = array(
+                       'backend_layout' => 0,
+                       'backend_layout_next_level' => 0,
+               );
+
+               if (!empty($pageTsConfig['TCEFORM.']['pages.']['backend_layout.']['PAGE_TSCONFIG_ID'])) {
+                       $pageTsConfigIds['backend_layout'] = (int) $pageTsConfig['TCEFORM.']['pages.']['backend_layout.']['PAGE_TSCONFIG_ID'];
+               }
+
+               if (!empty($pageTsConfig['TCEFORM.']['pages.']['backend_layout_next_level.']['PAGE_TSCONFIG_ID'])) {
+                       $pageTsConfigIds['backend_layout_next_level'] = (int) $pageTsConfig['TCEFORM.']['pages.']['backend_layout_next_level.']['PAGE_TSCONFIG_ID'];
+               }
+
+               return $pageTsConfigIds;
+       }
+
+       /**
+        * @return \TYPO3\CMS\Core\Database\DatabaseConnection
+        */
+       protected function getDatabaseConnection() {
+               return $GLOBALS['TYPO3_DB'];
+       }
+
+}
\ No newline at end of file
index ce6446a..9f6c3d2 100644 (file)
@@ -28,6 +28,7 @@ namespace TYPO3\CMS\Backend\View;
  ***************************************************************/
 
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -35,24 +36,215 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  *
  * @author GridView Team
  */
-class BackendLayoutView {
+class BackendLayoutView implements \TYPO3\CMS\Core\SingletonInterface {
 
        /**
-        * ItemProcFunc for colpos items
+        * @var BackendLayout\DataProviderCollection
+        */
+       protected $dataProviderCollection;
+
+       /**
+        * @var array
+        */
+       protected $selectedCombinedIdentifier = array();
+
+       /**
+        * @var array
+        */
+       protected $selectedBackendLayout = array();
+
+       /**
+        * Creates this object and initializes data providers.
+        */
+       public function __construct() {
+               $this->initializeDataProviderCollection();
+       }
+
+       /**
+        * Initializes data providers
         *
-        * @param array $params
         * @return void
         */
-       public function colPosListItemProcFunc(&$params) {
-               if ($params['row']['pid'] > 0) {
-                       $params['items'] = $this->addColPosListLayoutItems($params['row']['pid'], $params['items']);
-               } else {
-                       // Negative uid_pid values indicate that the element has been inserted after an existing element
+       protected function initializeDataProviderCollection() {
+               /** @var $dataProviderCollection BackendLayout\DataProviderCollection */
+               $dataProviderCollection = GeneralUtility::makeInstance(
+                       'TYPO3\\CMS\\Backend\\View\\BackendLayout\\DataProviderCollection'
+               );
+
+               $dataProviderCollection->add(
+                       'default',
+                       'TYPO3\\CMS\\Backend\\View\\BackendLayout\\DefaultDataProvider'
+               );
+
+               if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider'])) {
+                       $dataProviders = (array) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider'];
+                       foreach ($dataProviders as $identifier => $className) {
+                               $dataProviderCollection->add($identifier, $className);
+                       }
+               }
+
+               $this->setDataProviderCollection($dataProviderCollection);
+       }
+
+       /**
+        * @param BackendLayout\DataProviderCollection $dataProviderCollection
+        */
+       public function setDataProviderCollection(BackendLayout\DataProviderCollection $dataProviderCollection) {
+               $this->dataProviderCollection = $dataProviderCollection;
+       }
+
+       /**
+        * @return BackendLayout\DataProviderCollection
+        */
+       public function getDataProviderCollection() {
+               return $this->dataProviderCollection;
+       }
+
+       /**
+        * Gets backend layout items to be shown in the forms engine.
+        * This method is called as "itemsProcFunc" with the accordant context
+        * for pages.backend_layout and pages.backend_layout_next_level.
+        *
+        * @param array $parameters
+        */
+       public function addBackendLayoutItems(array $parameters) {
+               $pageId = $this->determinePageId($parameters['table'], $parameters['row']);
+               $pageTsConfig = (array) BackendUtility::getPagesTSconfig($pageId);
+               $identifiersToBeExcluded = $this->getIdentifiersToBeExcluded($pageTsConfig);
+
+               $dataProviderContext = $this->createDataProviderContext()
+                       ->setPageId($pageId)
+                       ->setData($parameters['row'])
+                       ->setTableName($parameters['table'])
+                       ->setFieldName($parameters['field'])
+                       ->setPageTsConfig($pageTsConfig);
+
+               $backendLayoutCollections = $this->getDataProviderCollection()->getBackendLayoutCollections($dataProviderContext);
+               foreach ($backendLayoutCollections as $backendLayoutCollection) {
+                       $combinedIdentifierPrefix = '';
+                       if ($backendLayoutCollection->getIdentifier() !== 'default') {
+                               $combinedIdentifierPrefix = $backendLayoutCollection->getIdentifier() . '__';
+                       }
+
+                       foreach ($backendLayoutCollection->getAll() as $backendLayout) {
+                               $combinedIdentifier = $combinedIdentifierPrefix . $backendLayout->getIdentifier();
+
+                               if (in_array($combinedIdentifier, $identifiersToBeExcluded, TRUE)) {
+                                       continue;
+                               }
+
+                               $parameters['items'][] = array(
+                                       $this->getLanguageService()->sL($backendLayout->getTitle()),
+                                       $combinedIdentifier,
+                                       $backendLayout->getIconPath(),
+                               );
+                       }
+               }
+       }
+
+       /**
+        * Determines the page id for a given record of a database table.
+        *
+        * @param string $tableName
+        * @param array $data
+        * @return NULL|integer
+        */
+       protected function determinePageId($tableName, array $data) {
+               $pageId = NULL;
+
+               if (strpos($data['uid'], 'NEW') === 0) {
+                       // negative uid_pid values of content elements indicate that the element has been inserted after an existing element
                        // so there is no pid to get the backendLayout for and we have to get that first
-                       $existingElement = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('pid', 'tt_content', 'uid=' . -intval($params['row']['pid']));
-                       if ($existingElement['pid'] > 0) {
-                               $params['items'] = $this->addColPosListLayoutItems($existingElement['pid'], $params['items']);
+                       if ($data['pid'] < 0) {
+                               $existingElement = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
+                                       'pid', $tableName, 'uid='  . abs($data['pid'])
+                               );
+                               if ($existingElement !== NULL) {
+                                       $pageId = $existingElement['pid'];
+                               }
+                       } else {
+                               $pageId = $data['pid'];
                        }
+               } elseif ($tableName === 'pages') {
+                       $pageId = $data['uid'];
+               } else {
+                       $pageId = $data['pid'];
+               }
+
+               return $pageId;
+       }
+
+       /**
+        * Returns the backend layout which should be used for this page.
+        *
+        * @param integer $pageId
+        * @return boolean|string Identifier of the backend layout to be used, or FALSE if none
+        */
+       public function getSelectedCombinedIdentifier($pageId) {
+               if (!isset($this->selectedCombinedIdentifier[$pageId])) {
+                       $page = $this->getPage($pageId);
+                       $this->selectedCombinedIdentifier[$pageId] = (string) $page['backend_layout'];
+
+                       if ($this->selectedCombinedIdentifier[$pageId] === '-1') {
+                               // If it is set to "none" - don't use any
+                               $this->selectedCombinedIdentifier[$pageId] = FALSE;
+                       } elseif ($this->selectedCombinedIdentifier[$pageId] === '0') {
+                               // If it not set check the root-line for a layout on next level and use this
+                               // (root-line starts with current page and has page "0" at the end)
+                               $rootLine = $this->getRootLine($pageId);
+                               // Remove first and last element (current and root page)
+                               array_shift($rootLine);
+                               array_pop($rootLine);
+                               foreach ($rootLine as $rootLinePage) {
+                                       $this->selectedCombinedIdentifier[$pageId] = (string) $rootLinePage['backend_layout_next_level'];
+                                       if ($this->selectedCombinedIdentifier[$pageId] === '-1') {
+                                               // If layout for "next level" is set to "none" - don't use any and stop searching
+                                               $this->selectedCombinedIdentifier[$pageId] = FALSE;
+                                               break;
+                                       } elseif ($this->selectedCombinedIdentifier[$pageId] !== '0') {
+                                               // Stop searching if a layout for "next level" is set
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               // If it is set to a positive value use this
+               return $this->selectedCombinedIdentifier[$pageId];
+       }
+
+       /**
+        * Gets backend layout identifiers to be excluded
+        *
+        * @param array $pageTSconfig
+        * @return array
+        */
+       protected function getIdentifiersToBeExcluded(array $pageTSconfig) {
+               $identifiersToBeExcluded = array();
+
+               if (ArrayUtility::isValidPath($pageTSconfig, 'options./backendLayout./exclude')) {
+                       $identifiersToBeExcluded = GeneralUtility::trimExplode(
+                               ',',
+                               ArrayUtility::getValueByPath($pageTSconfig, 'options./backendLayout./exclude'),
+                               TRUE
+                       );
+               }
+
+               return $identifiersToBeExcluded;
+       }
+
+       /**
+        * Gets colPos items to be shown in the forms engine.
+        * This method is called as "itemsProcFunc" with the accordant context
+        * for tt_content.colPos.
+        *
+        * @param array $parameters
+        * @return void
+        */
+       public function colPosListItemProcFunc(array $parameters) {
+               $pageId = $this->determinePageId($parameters['table'], $parameters['row']);
+
+               if ($pageId !== NULL) {
+                       $params['items'] = $this->addColPosListLayoutItems($pageId, $parameters['items']);
                }
        }
 
@@ -100,64 +292,58 @@ class BackendLayoutView {
        /**
         * Gets the selected backend layout
         *
-        * @param integer $id
+        * @param integer $pageId
         * @return array|NULL $backendLayout
         */
-       public function getSelectedBackendLayout($id) {
-               $rootline = BackendUtility::BEgetRootLine($id);
-               $backendLayoutUid = NULL;
-               for ($i = count($rootline); $i > 0; $i--) {
-                       $page = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid, pid, backend_layout, backend_layout_next_level', 'pages', 'uid=' . intval($rootline[$i]['uid']));
-                       BackendUtility::workspaceOL('pages', $page);
-                       $selectedBackendLayout = intval($page['backend_layout']);
-                       $selectedBackendLayoutNextLevel = intval($page['backend_layout_next_level']);
-                       if ($selectedBackendLayout != 0 && $page['uid'] == $id) {
-                               if ($selectedBackendLayout > 0) {
-                                       // Backend layout for current page is set
-                                       $backendLayoutUid = $selectedBackendLayout;
-                               }
-                               break;
-                       } elseif ($selectedBackendLayoutNextLevel == -1 && $page['uid'] != $id) {
-                               // Some previous page in our rootline sets layout_next to "None"
-                               break;
-                       } elseif ($selectedBackendLayoutNextLevel > 0 && $page['uid'] != $id) {
-                               // Some previous page in our rootline sets some backend_layout, use it
-                               $backendLayoutUid = $selectedBackendLayoutNextLevel;
-                               break;
-                       }
+       public function getSelectedBackendLayout($pageId) {
+               if (isset($this->selectedBackendLayout[$pageId])) {
+                       return $this->selectedBackendLayout[$pageId];
                }
-               $backendLayout = NULL;
-               if ($backendLayoutUid) {
-                       $backendLayout = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'backend_layout', 'uid=' . $backendLayoutUid);
-               } else {
-                       $backendLayout['config'] = self::getDefaultColumnLayout();
+
+               $backendLayoutData = NULL;
+
+               $selectedCombinedIdentifier = $this->getSelectedCombinedIdentifier($pageId);
+
+               // If no backend layout is selected, use default
+               if (empty($selectedCombinedIdentifier)) {
+                       $selectedCombinedIdentifier = 'default';
                }
-               if ($backendLayout) {
+
+               $backendLayout = $this->getDataProviderCollection()->getBackendLayout($selectedCombinedIdentifier);
+
+               if (!empty($backendLayout)) {
                        /** @var $parser \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser */
                        $parser = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
                        /** @var \TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher $conditionMatcher */
                        $conditionMatcher = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Configuration\\TypoScript\\ConditionMatching\\ConditionMatcher');
-                       $parser->parse($parser->checkIncludeLines($backendLayout['config']), $conditionMatcher);
-                       $backendLayout['__config'] = $parser->setup;
-                       $backendLayout['__items'] = array();
-                       $backendLayout['__colPosList'] = array();
+                       $parser->parse($parser->checkIncludeLines($backendLayout->getConfiguration()), $conditionMatcher);
+
+                       $backendLayoutData = array();
+                       $backendLayoutData['config'] = $backendLayout->getConfiguration();
+                       $backendLayoutData['__config'] = $parser->setup;
+                       $backendLayoutData['__items'] = array();
+                       $backendLayoutData['__colPosList'] = array();
+
                        // create items and colPosList
-                       if ($backendLayout['__config']['backend_layout.'] && $backendLayout['__config']['backend_layout.']['rows.']) {
-                               foreach ($backendLayout['__config']['backend_layout.']['rows.'] as $row) {
-                                       if (isset($row['columns.']) && is_array($row['columns.'])) {
+                       if (!empty($backendLayoutData['__config']['backend_layout.']['rows.'])) {
+                               foreach ($backendLayoutData['__config']['backend_layout.']['rows.'] as $row) {
+                                       if (!empty($row['columns.'])) {
                                                foreach ($row['columns.'] as $column) {
-                                                       $backendLayout['__items'][] = array(
-                                                               GeneralUtility::isFirstPartOfStr($column['name'], 'LLL:') ? $GLOBALS['LANG']->sL($column['name']) : $column['name'],
+                                                       $backendLayoutData['__items'][] = array(
+                                                               GeneralUtility::isFirstPartOfStr($column['name'], 'LLL:') ? $this->getLanguageService()->sL($column['name']) : $column['name'],
                                                                $column['colPos'],
                                                                NULL
                                                        );
-                                                       $backendLayout['__colPosList'][] = $column['colPos'];
+                                                       $backendLayoutData['__colPosList'][] = $column['colPos'];
                                                }
                                        }
                                }
                        }
+
+                       $this->selectedBackendLayout[$pageId] = $backendLayoutData;
                }
-               return $backendLayout;
+
+               return $backendLayoutData;
        }
 
        /**
@@ -197,4 +383,53 @@ class BackendLayoutView {
                ';
        }
 
+       /**
+        * Gets a page record.
+        *
+        * @param integer $pageId
+        * @return NULL|array
+        */
+       protected function getPage($pageId) {
+               $page = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
+                       'uid, pid, backend_layout',
+                       'pages',
+                       'uid=' . intval($pageId)
+               );
+               BackendUtility::workspaceOL('pages', $page);
+               return $page;
+       }
+
+       /**
+        * Gets the page root-line.
+        *
+        * @param integer $pageId
+        * @return array
+        */
+       protected function getRootLine($pageId) {
+               return BackendUtility::BEgetRootLine($pageId, '', TRUE);
+       }
+
+       /**
+        * @return BackendLayout\DataProviderContext
+        */
+       protected function createDataProviderContext() {
+               return GeneralUtility::makeInstance(
+                       'TYPO3\\CMS\\Backend\\View\\BackendLayout\\DataProviderContext'
+               );
+       }
+
+       /**
+        * @return \TYPO3\CMS\Core\Database\DatabaseConnection
+        */
+       protected function getDatabaseConnection() {
+               return $GLOBALS['TYPO3_DB'];
+       }
+
+       /**
+        * @return \TYPO3\CMS\Lang\LanguageService
+        */
+       protected function getLanguageService() {
+               return $GLOBALS['LANG'];
+       }
+
 }
index 1987ccc..f0540e4 100644 (file)
@@ -336,34 +336,12 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
         * Returns the backend layout which should be used for this page.
         *
         * @param integer $id Uid of the current page
-        * @return mixed Uid of the backend layout record or NULL if no layout should be used
-        * @todo Define visibility
+        * @return boolean|string Identifier of the backend layout to be used, or FALSE if none
+        * @deprecated since TYPO3 CMS 6.2, will be removed two versions later
         */
        public function getSelectedBackendLayoutUid($id) {
-               // uid and pid are needed for workspaceOL()
-               $page = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid, pid, backend_layout', 'pages', 'uid=' . $id);
-               BackendUtility::workspaceOL('pages', $page);
-               $backendLayoutUid = intval($page['backend_layout']);
-               if ($backendLayoutUid == -1) {
-                       // If it is set to "none" - don't use any
-                       $backendLayoutUid = NULL;
-               } elseif ($backendLayoutUid == 0) {
-                       // If it not set check the rootline for a layout on next level and use this
-                       $rootline = BackendUtility::BEgetRootLine($id, '', TRUE);
-                       for ($i = count($rootline) - 2; $i > 0; $i--) {
-                               $backendLayoutUid = intval($rootline[$i]['backend_layout_next_level']);
-                               if ($backendLayoutUid > 0) {
-                                       // Stop searching if a layout for "next level" is set
-                                       break;
-                               } elseif ($backendLayoutUid == -1) {
-                                       // If layout for "next level" is set to "none" - don't use any and stop searching
-                                       $backendLayoutUid = NULL;
-                                       break;
-                               }
-                       }
-               }
-               // If it is set to a positive value use this
-               return $backendLayoutUid;
+               GeneralUtility::logDeprecatedFunction();
+               return $this->getBackendLayoutView()->getSelectedCombinedIdentifier($id);
        }
 
        /**
@@ -527,18 +505,12 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                                }
                                        }
                                } else {
-                                       $backendLayoutRecord = $this->getBackendLayoutConfiguration();
+                                       $backendLayout = $this->getBackendLayoutView()->getSelectedBackendLayout($this->id);
                                        // GRID VIEW:
-                                       // Initialize TS parser to parse config to array
-                                       /** @var \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser $parser */
-                                       $parser = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
-                                       /** @var \TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher $conditionMatcher */
-                                       $conditionMatcher = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Configuration\\TypoScript\\ConditionMatching\\ConditionMatcher');
-                                       $parser->parse($parser->checkIncludeLines($backendLayoutRecord['config']), $conditionMatcher);
                                        $grid .= '<div class="t3-gridContainer"><table border="0" cellspacing="0" cellpadding="0" width="100%" height="100%" class="t3-page-columns t3-gridTable">';
                                        // Add colgroups
-                                       $colCount = intval($parser->setup['backend_layout.']['colCount']);
-                                       $rowCount = intval($parser->setup['backend_layout.']['rowCount']);
+                                       $colCount = intval($backendLayout['__config']['backend_layout.']['colCount']);
+                                       $rowCount = intval($backendLayout['__config']['backend_layout.']['rowCount']);
                                        $grid .= '<colgroup>';
                                        for ($i = 0; $i < $colCount; $i++) {
                                                $grid .= '<col style="width:' . 100 / $colCount . '%"></col>';
@@ -546,7 +518,7 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                        $grid .= '</colgroup>';
                                        // Cycle through rows
                                        for ($row = 1; $row <= $rowCount; $row++) {
-                                               $rowConfig = $parser->setup['backend_layout.']['rows.'][$row . '.'];
+                                               $rowConfig = $backendLayout['__config']['backend_layout.']['rows.'][$row . '.'];
                                                if (!isset($rowConfig)) {
                                                        continue;
                                                }
@@ -793,15 +765,11 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
         * Get backend layout configuration
         *
         * @return array
+        * @deprecated since TYPO3 CMS 6.2, will be removed two versions later
         */
        public function getBackendLayoutConfiguration() {
-               $backendLayoutUid = $this->getSelectedBackendLayoutUid($this->id);
-               if (!$backendLayoutUid) {
-                       return array(
-                               'config' => \TYPO3\CMS\Backend\View\BackendLayoutView::getDefaultColumnLayout()
-                       );
-               }
-               return BackendUtility::getRecord('backend_layout', intval($backendLayoutUid));
+               GeneralUtility::logDeprecatedFunction();
+               return $this->getBackendLayoutView()->getSelectedBackendLayout($this->id);
        }
 
        /**********************************
@@ -1967,4 +1935,13 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                return strip_tags($content);
        }
 
+       /**
+        * @return BackendLayoutView
+        */
+       protected function getBackendLayoutView() {
+               return GeneralUtility::makeInstance(
+                       'TYPO3\\CMS\\Backend\\View\\BackendLayoutView'
+               );
+       }
+
 }
diff --git a/typo3/sysext/backend/Tests/Unit/View/BackendLayout/BackendLayoutCollectionTest.php b/typo3/sysext/backend/Tests/Unit/View/BackendLayout/BackendLayoutCollectionTest.php
new file mode 100644 (file)
index 0000000..4db9386
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+namespace TYPO3\CMS\Backend\Tests\Unit\View\BackendLayout;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * Testing collection of backend layouts.
+ *
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class BackendLayoutCollectionTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * @test
+        * @expectedException \UnexpectedValueException
+        */
+       public function invalidIdentifierIsRecognizedOnCreation() {
+               $identifier = uniqid('identifier__');
+               new \TYPO3\CMS\Backend\View\BackendLayout\BackendLayoutCollection($identifier);
+       }
+
+       /**
+        * @test
+        */
+       public function objectIsCreated() {
+               $identifier = uniqid('identifier');
+               $backendLayoutCollection = new \TYPO3\CMS\Backend\View\BackendLayout\BackendLayoutCollection($identifier);
+
+               $this->assertEquals($identifier, $backendLayoutCollection->getIdentifier());
+       }
+
+       /**
+        * @test
+        * @expectedException \UnexpectedValueException
+        */
+       public function invalidBackendLayoutIsRecognizedOnAdding() {
+               $identifier = uniqid('identifier');
+               $backendLayoutCollection = new \TYPO3\CMS\Backend\View\BackendLayout\BackendLayoutCollection($identifier);
+               $backendLayoutIdentifier = uniqid('identifier__');
+               $backendLayoutMock = $this->getMock('TYPO3\\CMS\\Backend\\View\\BackendLayout\\BackendLayout', array('getIdentifier'), array(), '', FALSE);
+               $backendLayoutMock->expects($this->once())->method('getIdentifier')->will($this->returnValue($backendLayoutIdentifier));
+
+               $backendLayoutCollection->add($backendLayoutMock);
+       }
+
+       /**
+        * @test
+        * @expectedException \LogicException
+        */
+       public function duplicateBackendLayoutIsRecognizedOnAdding() {
+               $identifier = uniqid('identifier');
+               $backendLayoutCollection = new \TYPO3\CMS\Backend\View\BackendLayout\BackendLayoutCollection($identifier);
+               $backendLayoutIdentifier = uniqid('identifier');
+               $firstBackendLayoutMock = $this->getMock('TYPO3\\CMS\\Backend\\View\\BackendLayout\\BackendLayout', array('getIdentifier'), array(), '', FALSE);
+               $firstBackendLayoutMock->expects($this->once())->method('getIdentifier')->will($this->returnValue($backendLayoutIdentifier));
+               $secondBackendLayoutMock = $this->getMock('TYPO3\\CMS\\Backend\\View\\BackendLayout\\BackendLayout', array('getIdentifier'), array(), '', FALSE);
+               $secondBackendLayoutMock->expects($this->once())->method('getIdentifier')->will($this->returnValue($backendLayoutIdentifier));
+
+               $backendLayoutCollection->add($firstBackendLayoutMock);
+               $backendLayoutCollection->add($secondBackendLayoutMock);
+       }
+
+       /**
+        * @test
+        */
+       public function backendLayoutCanBeFetched() {
+               $identifier = uniqid('identifier');
+               $backendLayoutCollection = new \TYPO3\CMS\Backend\View\BackendLayout\BackendLayoutCollection($identifier);
+               $backendLayoutIdentifier = uniqid('identifier');
+               $backendLayoutMock = $this->getMock('TYPO3\\CMS\\Backend\\View\\BackendLayout\\BackendLayout', array('getIdentifier'), array(), '', FALSE);
+               $backendLayoutMock->expects($this->once())->method('getIdentifier')->will($this->returnValue($backendLayoutIdentifier));
+
+               $backendLayoutCollection->add($backendLayoutMock);
+
+               $this->assertEquals($backendLayoutMock, $backendLayoutCollection->get($backendLayoutIdentifier));
+       }
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/Unit/View/BackendLayout/BackendLayoutTest.php b/typo3/sysext/backend/Tests/Unit/View/BackendLayout/BackendLayoutTest.php
new file mode 100644 (file)
index 0000000..56e0a6b
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+namespace TYPO3\CMS\Backend\Tests\Unit\View\BackendLayout;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * Testing backend layout representation.
+ *
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class BackendLayoutTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * @test
+        * @expectedException \UnexpectedValueException
+        */
+       public function invalidIdentifierIsRecognizedOnCreation() {
+               $identifier = uniqid('identifier__');
+               $title = uniqid('title');
+               $configuration = uniqid('configuration');
+               new \TYPO3\CMS\Backend\View\BackendLayout\BackendLayout($identifier, $title, $configuration);
+       }
+
+       /**
+        * @test
+        */
+       public function objectIsCreated() {
+               $identifier = uniqid('identifier');
+               $title = uniqid('title');
+               $configuration = uniqid('configuration');
+               $backendLayout = new \TYPO3\CMS\Backend\View\BackendLayout\BackendLayout($identifier, $title, $configuration);
+
+               $this->assertEquals($identifier, $backendLayout->getIdentifier());
+               $this->assertEquals($title, $backendLayout->getTitle());
+               $this->assertEquals($configuration, $backendLayout->getConfiguration());
+       }
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/Unit/View/BackendLayout/DataProviderCollectionTest.php b/typo3/sysext/backend/Tests/Unit/View/BackendLayout/DataProviderCollectionTest.php
new file mode 100644 (file)
index 0000000..2203aa1
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+namespace TYPO3\CMS\Backend\Tests\Unit\View\BackendLayout;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * Testing collection of backend layout data providers.
+ *
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class DataProviderCollectionTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * @var \TYPO3\CMS\Backend\View\BackendLayout\DataProviderCollection
+        */
+       protected $dataProviderCollection;
+
+       /**
+        * Sets up this test case.
+        */
+       protected function setUp() {
+               $this->dataProviderCollection = new \TYPO3\CMS\Backend\View\BackendLayout\DataProviderCollection();
+       }
+
+       /**
+        * Tears down this test case.
+        */
+       protected function tearDown() {
+               unset($this->dataProviderCollection);
+       }
+
+       /**
+        * @test
+        * @expectedException \UnexpectedValueException
+        */
+       public function invalidIdentifierIsRecognizedOnAdding() {
+               $identifier = uniqid('identifier__');
+               $dataProviderMock = $this->getMock('stdClass');
+
+               $this->dataProviderCollection->add($identifier, get_class($dataProviderMock));
+       }
+
+       /**
+        * @test
+        * @expectedException \LogicException
+        */
+       public function invalidInterfaceIsRecognizedOnAdding() {
+               $identifier = uniqid('identifier');
+               $dataProviderMock = $this->getMock('stdClass');
+
+               $this->dataProviderCollection->add($identifier, get_class($dataProviderMock));
+       }
+
+       /**
+        * @test
+        */
+       public function defaultBackendLayoutIsFound() {
+               $backendLayoutIdentifier = uniqid('identifier');
+
+               $dataProviderMock = $this->getMock('TYPO3\\CMS\\Backend\\View\\BackendLayout\\DefaultDataProvider', array('getBackendLayout'), array(), '', FALSE);
+               $backendLayoutMock = $this->getMock('TYPO3\\CMS\\Backend\\View\\BackendLayout\\BackendLayout', array('getIdentifier'), array(), '', FALSE);
+               $backendLayoutMock->expects($this->any())->method('getIdentifier')->will($this->returnValue($backendLayoutIdentifier));
+               $dataProviderMock->expects($this->once())->method('getBackendLayout')->will($this->returnValue($backendLayoutMock));
+
+               $this->dataProviderCollection->add('default', $dataProviderMock);
+               $providedBackendLayout = $this->dataProviderCollection->getBackendLayout($backendLayoutIdentifier);
+
+               $this->assertNotNull($providedBackendLayout);
+               $this->assertEquals($backendLayoutIdentifier, $providedBackendLayout->getIdentifier());
+       }
+
+       /**
+        * @test
+        */
+       public function providedBackendLayoutIsFound() {
+               $dataProviderIdentifier = uniqid('custom');
+               $backendLayoutIdentifier = uniqid('identifier');
+
+               $dataProviderMock = $this->getMock('TYPO3\\CMS\\Backend\\View\\BackendLayout\\DefaultDataProvider', array('getBackendLayout'), array(), '', FALSE);
+               $backendLayoutMock = $this->getMock('TYPO3\\CMS\\Backend\\View\\BackendLayout\\BackendLayout', array('getIdentifier'), array(), '', FALSE);
+               $backendLayoutMock->expects($this->any())->method('getIdentifier')->will($this->returnValue($backendLayoutIdentifier));
+               $dataProviderMock->expects($this->once())->method('getBackendLayout')->will($this->returnValue($backendLayoutMock));
+
+               $this->dataProviderCollection->add($dataProviderIdentifier, $dataProviderMock);
+               $providedBackendLayout = $this->dataProviderCollection->getBackendLayout($dataProviderIdentifier . '__' . $backendLayoutIdentifier);
+
+               $this->assertNotNull($providedBackendLayout);
+               $this->assertEquals($backendLayoutIdentifier, $providedBackendLayout->getIdentifier());
+       }
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/Unit/View/BackendLayoutViewTest.php b/typo3/sysext/backend/Tests/Unit/View/BackendLayoutViewTest.php
new file mode 100644 (file)
index 0000000..9ac32d6
--- /dev/null
@@ -0,0 +1,222 @@
+<?php
+namespace TYPO3\CMS\Backend\Tests\Unit\View;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * Testing behaviour of \TYPO3\CMS\Backend\View\BackendLayoutView
+ *
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ */
+class BackendLayoutViewTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * @var \TYPO3\CMS\Backend\View\BackendLayoutView|\PHPUnit_Framework_MockObject_MockObject
+        */
+       protected $backendLayoutView;
+
+       /**
+        * Sets up this test case.
+        */
+       protected function setUp() {
+               $this->backendLayoutView = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Backend\\View\\BackendLayoutView',
+                       array('getPage', 'getRootLine'),
+                       array(), '', FALSE
+               );
+       }
+
+       /**
+        * Tears down this test case.
+        */
+       protected function tearDown() {
+               unset($this->backendLayoutView);
+       }
+
+       /**
+        * @param boolean|string $expected
+        * @param array $page
+        * @param array $rootLine
+        * @test
+        * @dataProvider selectedCombinedIdentifierIsDeterminedDataProvider
+        */
+       public function selectedCombinedIdentifierIsDetermined($expected, array $page, array $rootLine) {
+               $pageId = $page['uid'];
+
+               $this->backendLayoutView->expects($this->once())
+                       ->method('getPage')->with($this->equalTo($pageId))
+                       ->will($this->returnValue($page));
+               $this->backendLayoutView->expects($this->any())
+                       ->method('getRootLine')->with($this->equalTo($pageId))
+                       ->will($this->returnValue($rootLine));
+
+               $selectedCombinedIdentifier = $this->backendLayoutView->_call('getSelectedCombinedIdentifier', $pageId);
+               $this->assertEquals($expected, $selectedCombinedIdentifier);
+       }
+
+       /**
+        * @return array
+        */
+       public function selectedCombinedIdentifierIsDeterminedDataProvider() {
+               return array(
+                       'first level w/o layout' => array(
+                               '0',
+                               array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'first level with layout' => array(
+                               '1',
+                               array('uid' => 1, 'pid' => 0, 'backend_layout' => '1', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '1', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'first level with provided layout' => array(
+                               'mine_current',
+                               array('uid' => 1, 'pid' => 0, 'backend_layout' => 'mine_current', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => 'mine_current', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'first level with next layout' => array(
+                               '0',
+                               array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '1'),
+                               array(
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'first level with provided next layout' => array(
+                               '0',
+                               array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => 'mine_next'),
+                               array(
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => 'mine_next'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'second level w/o layout, first level with layout' => array(
+                               '0',
+                               array('uid' => 2, 'pid' => 1, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '1', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'second level w/o layout, first level with next layout' => array(
+                               '1',
+                               array('uid' => 2, 'pid' => 1, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'second level with layout, first level with next layout' => array(
+                               '2',
+                               array('uid' => 2, 'pid' => 1, 'backend_layout' => '2', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '2', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'second level with layouts, first level resetting all layouts' => array(
+                               '1',
+                               array('uid' => 2, 'pid' => 1, 'backend_layout' => '1', 'backend_layout_next_level' => '1'),
+                               array(
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '1', 'backend_layout_next_level' => '1'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '-1', 'backend_layout_next_level' => '-1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'second level with provided layouts, first level resetting all layouts' => array(
+                               'mine_current',
+                               array('uid' => 2, 'pid' => 1, 'backend_layout' => 'mine_current', 'backend_layout_next_level' => 'mine_next'),
+                               array(
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => 'mine_current', 'backend_layout_next_level' => 'mine_next'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '-1', 'backend_layout_next_level' => '-1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'second level resetting layout, first level with next layout' => array(
+                               FALSE,
+                               array('uid' => 2, 'pid' => 1, 'backend_layout' => '-1', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '-1', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'second level resetting next layout, first level with next layout' => array(
+                               '1',
+                               array('uid' => 2, 'pid' => 1, 'backend_layout' => '0', 'backend_layout_next_level' => '-1'),
+                               array(
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '0', 'backend_layout_next_level' => '-1'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'third level w/o layout, second level resetting layout, first level with next layout' => array(
+                               '1',
+                               array('uid' => 3, 'pid' => 2, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 3, 'pid' => 2, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '-1', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'third level w/o layout, second level resetting next layout, first level with next layout' => array(
+                               FALSE,
+                               array('uid' => 3, 'pid' => 2, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                               array(
+                                       array('uid' => 3, 'pid' => 2, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '0', 'backend_layout_next_level' => '-1'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '0', 'backend_layout_next_level' => '1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+                       'third level with provided layouts, second level w/o layout, first level resetting layouts' => array(
+                               'mine_current',
+                               array('uid' => 3, 'pid' => 2, 'backend_layout' => 'mine_current', 'backend_layout_next_level' => 'mine_next'),
+                               array(
+                                       array('uid' => 3, 'pid' => 2, 'backend_layout' => 'mine_current', 'backend_layout_next_level' => 'mine_next'),
+                                       array('uid' => 2, 'pid' => 1, 'backend_layout' => '0', 'backend_layout_next_level' => '0'),
+                                       array('uid' => 1, 'pid' => 0, 'backend_layout' => '-1', 'backend_layout_next_level' => '-1'),
+                                       array('uid' => 0, 'pid' => NULL,),
+                               )
+                       ),
+               );
+       }
+
+}
\ No newline at end of file
index e7a85ef..c768ba2 100644 (file)
                        <trans-unit id="pages.backend_layout.none" xml:space="preserve">
                                <source>None</source>
                        </trans-unit>
+                       <trans-unit id="pages.backend_layout.default" xml:space="preserve">
+                               <source>Default</source>
+                       </trans-unit>
                        <trans-unit id="tt_content" xml:space="preserve">
                                <source>Page Content</source>
                        </trans-unit>
index 49908a7..f8db27a 100644 (file)
@@ -759,12 +759,11 @@ return array(
                        'label' => 'LLL:EXT:cms/locallang_tca.xlf:pages.backend_layout_formlabel',
                        'config' => array(
                                'type' => 'select',
-                               'foreign_table' => 'backend_layout',
-                               'foreign_table_where' => 'AND ( ( ###PAGE_TSCONFIG_ID### = 0 AND ###STORAGE_PID### = 0 ) OR ( backend_layout.pid = ###PAGE_TSCONFIG_ID### OR backend_layout.pid = ###STORAGE_PID### ) OR ( ###PAGE_TSCONFIG_ID### = 0 AND backend_layout.pid = ###THIS_UID### ) ) AND backend_layout.hidden = 0 ORDER BY backend_layout.sorting',
                                'items' => array(
                                        array('', 0),
                                        array('LLL:EXT:cms/locallang_tca.xlf:pages.backend_layout.none', -1)
                                ),
+                               'itemsProcFunc' => 'TYPO3\\CMS\\Backend\\View\\BackendLayoutView->addBackendLayoutItems',
                                'selicon_cols' => 5,
                                'size' => 1,
                                'maxitems' => 1,
@@ -776,12 +775,11 @@ return array(
                        'label' => 'LLL:EXT:cms/locallang_tca.xlf:pages.backend_layout_next_level_formlabel',
                        'config' => array(
                                'type' => 'select',
-                               'foreign_table' => 'backend_layout',
-                               'foreign_table_where' => 'AND ( ( ###PAGE_TSCONFIG_ID### = 0 AND ###STORAGE_PID### = 0 ) OR ( backend_layout.pid = ###PAGE_TSCONFIG_ID### OR backend_layout.pid = ###STORAGE_PID### ) OR ( ###PAGE_TSCONFIG_ID### = 0 AND backend_layout.pid = ###THIS_UID### ) ) AND backend_layout.hidden = 0 ORDER BY backend_layout.sorting',
                                'items' => array(
                                        array('', 0),
                                        array('LLL:EXT:cms/locallang_tca.xlf:pages.backend_layout.none', -1)
                                ),
+                               'itemsProcFunc' => 'TYPO3\\CMS\\Backend\\View\\BackendLayoutView->addBackendLayoutItems',
                                'selicon_cols' => 5,
                                'size' => 1,
                                'maxitems' => 1,
index c0262f4..d002947 100644 (file)
@@ -173,8 +173,8 @@ CREATE TABLE pages (
   alias varchar(32) DEFAULT '' NOT NULL,
   l18n_cfg tinyint(4) DEFAULT '0' NOT NULL,
   fe_login_mode tinyint(4) DEFAULT '0' NOT NULL,
-  backend_layout int(10) DEFAULT '0' NOT NULL,
-  backend_layout_next_level int(10) DEFAULT '0' NOT NULL,
+  backend_layout varchar(64) DEFAULT '0' NOT NULL,
+  backend_layout_next_level varchar(64) DEFAULT '0' NOT NULL,
   PRIMARY KEY (uid),
   KEY t3ver_oid (t3ver_oid,t3ver_wsid),
   KEY parent (pid,deleted,sorting),