Skip to content

Commit

Permalink
[FEATURE] White/blacklisting allowed CTypes in Column and Area
Browse files Browse the repository at this point in the history
Limits possible selections in the new content element wizard based on parameters defined in the Column or Area (or both) into which content is being inserted.

## Usage examples

In a content/plugin template:

```xml
<flux:grid.row>
	<flux:grid.column>
		<flux:form.content name="content">
			<flux:form.variable name="allowedContentTypes" value="text,textpic" />
		</flux:form.content>
	</flux:grid.column>
</flux:grid.row>
```

In a page template:

```xml
<flux:grid.row>
	<flux:grid.column colPos="1">
		<flux:form.variable name="allowedContentTypes" value="text,textpic" />
	</flux:grid.column>
</flux:grid.row>
```

Alternative syntax:

```xml
<flux:grid.row>
	<flux:grid.column colPos="1" variables="{allowedContentTypes: 'text,textpic'}" />
</flux:grid.row>
```

Using PHP (Flux Form Components):

```php
$grid = \FluidTYPO3\Flux\Grid::create();
$row = $grid->createContainer('Row');
$column = $row->createContainer('Column');
$column->setColumnPosition(1)
	->setVariable('allowedContentTypes', 'text,image');
```

To blacklist, use `deniedContentTypes` instead of `allowedContentTypes` as variable name. Both can be used together: first, the whitelist is applied if it has any types contained, and second, the blacklist is applied to remove elements from the list of allowed types (which is either the full list or the previously whitelist-filtered version, if a whitelist was specified).

Fixes: FluidTYPO3#197
  • Loading branch information
NamelessCoder committed Jan 27, 2014
1 parent d873835 commit 3880983
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 2 deletions.
18 changes: 18 additions & 0 deletions Classes/Form/AbstractFormComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,24 @@ public function getVariables() {
return $this->variables;
}

/**
* @param string $name
* @param mixed $value
* @return FormInterface
*/
public function setVariable($name, $value) {
$this->variables[$name] = $value;
return $this;
}

/**
* @param string $name
* @return mixed
*/
public function getVariable($name) {
return TRUE === isset($this->variables[$name]) ? $this->variables[$name] : NULL;
}

/**
* @return ContainerInterface
*/
Expand Down
13 changes: 13 additions & 0 deletions Classes/Form/FormInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ public function setVariables($variables);
*/
public function getVariables();

/**
* @param string $name
* @param mixed $value
* @return FormInterface
*/
public function setVariable($name, $value);

/**
* @param string $name
* @return mixed
*/
public function getVariable($name);

/**
* @return ContainerInterface
*/
Expand Down
187 changes: 187 additions & 0 deletions Classes/Hooks/WizardItemsHookSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<?php
namespace FluidTYPO3\Flux\Hooks;
/***************************************************************
* Copyright notice
*
* (c) 2014 Claus Due <claus@namelesscoder.net>
*
* 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!
***************************************************************/

use FluidTYPO3\Flux\Form\FormInterface;
use FluidTYPO3\Flux\Service\FluxService;
use TYPO3\CMS\Backend\Wizard\NewContentElementWizardHookInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;

/**
* WizardItems Hook Subscriber
* @package Fluidcontent
*/
class WizardItemsHookSubscriber implements NewContentElementWizardHookInterface {

/**
* @var ObjectManagerInterface
*/
protected $objectManager;

/**
* @var FluxService
*/
protected $configurationService;

/**
* @param ObjectManagerInterface $objectManager
* @return void
*/
public function injectObjectManager(ObjectManagerInterface $objectManager) {
$this->objectManager = $objectManager;
}

/**
* @param FluxService $configurationService
* @return void
*/
public function injectConfigurationService(FluxService $configurationService) {
$this->configurationService = $configurationService;
}

/**
* Constructor
*/
public function __construct() {
$objectManager = GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\ObjectManager');
$this->injectObjectManager($objectManager);
$configurationService = $this->objectManager->get('FluidTYPO3\Flux\Service\FluxService');
$this->injectConfigurationService($configurationService);
}

/**
* @param array $items
* @param \TYPO3\CMS\Backend\Controller\ContentElement\NewContentElementController
* @return void
*/
public function manipulateWizardItems(&$items, &$parentObject) {
$whitelist = array();
$blacklist = array();
// if a Provider is registered for the "pages" table, try to get a Grid from it. If the Grid
// returned contains a Column which matches the desired colPos value, attempt to read a list
// of allowed/denied content element types from it.
$pageRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'pages', "uid = '" . $parentObject->id . "'");
$pageProviders = $this->configurationService->resolveConfigurationProviders('pages', NULL, $pageRecord);
foreach ($pageProviders as $pageProvider) {
$grid = $pageProvider->getGrid($pageRecord);
if (NULL === $grid) {
continue;
}
foreach ($grid->getRows() as $row) {
foreach ($row->getColumns() as $column) {
if ($column->getColumnPosition() === $parentObject->colPos) {
list ($whitelist, $blacklist) = $this->appendToWhiteAndBlacklistFromComponent($column, $whitelist, $blacklist);
}
}
}
}
// Detect what was clicked in order to create the new content element; decide restrictions
// based on this.
$defaultValues = GeneralUtility::_GET('defVals');
if (0 > $parentObject->uid_pid) {
// pasting after another element means we should try to resolve the Flux content relation
// from that element instead of GET parameters (clicked: "create new" icon after other element)
$relativeRecordUid = abs($parentObject->uid_pid);
$relativeRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'tt_content', "uid = '" . $relativeRecordUid . "'");
$fluxAreaName = $relativeRecord['tx_flux_column'];
} elseif (TRUE === isset($defaultValues['tt_content']['tx_flux_column'])) {
// attempt to read the target Flux content area from GET parameters (clicked: "create new" icon
// in top of nested Flux content area
$fluxAreaName = $defaultValues['tt_content']['tx_flux_column'];
$relativeRecordUid = $defaultValues['tt_content']['tx_flux_parent'];
}
// if these variables now indicate that we are inserting content elements into a Flux-enabled content
// area inside another content element, attempt to read allowed/denied content types from the
// Grid returned by the Provider that applies to the parent element's type and configuration
// (admitted, that's quite a mouthful - but it's not that different from reading the values from
// a page template like above; it's the same principle).
if (0 < $relativeRecordUid && FALSE === empty($fluxAreaName)) {
$relativeRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'tt_content', "uid = '" . $relativeRecordUid . "'");
$contentProviders = $this->configurationService->resolveConfigurationProviders('tt_content', NULL, $relativeRecord);
foreach ($contentProviders as $contentProvider) {
$grid = $contentProvider->getGrid($relativeRecord);
if (NULL === $grid) {
continue;
}
foreach ($grid->getRows() as $row) {
foreach ($row->getColumns() as $column) {
foreach ($column->getAreas() as $area) {
if ($area->getName() === $fluxAreaName) {
list ($whitelist, $blacklist) = $this->appendToWhiteAndBlacklistFromComponent($area, $whitelist, $blacklist);
}
}
}
}
}
}
// White/blacklist filtering. If whitelist contains elements, filter the list
// of possible types by whitelist first. Then apply the blacklist, removing
// any element types recorded herein.
$whitelist = array_unique($whitelist);
$blacklist = array_unique($blacklist);
if (0 < count($whitelist)) {
foreach ($items as $name => $item) {
if (FALSE !== strpos($name, '_') && FALSE === in_array($item['tt_content_defValues']['CType'], $whitelist)) {
unset($items[$name]);
}
}
}
if (0 < count($blacklist)) {
foreach ($blacklist as $contentElementType) {
foreach ($items as $name => $item) {
if ($item['tt_content_defValues']['CType'] === $contentElementType) {
unset($items[$name]);
}
}
}
}
// Finally, loop through the items list and clean up any tabs with zero element types inside.
$preserveHeaders = array();
foreach ($items as $name => $item) {
if (FALSE !== strpos($name, '_')) {
array_push($preserveHeaders, reset(explode('_', $name)));
}
}
foreach ($items as $name => $item) {
if (FALSE === strpos($name, '_') && FALSE === in_array($name, $preserveHeaders)) {
unset($items[$name]);
}
}
}

protected function appendToWhiteAndBlacklistFromComponent(FormInterface $component, array $whitelist, array $blacklist) {
$allowed = $component->getVariable('allowedContentTypes');
if (NULL !== $allowed) {
$whitelist = array_merge($whitelist, GeneralUtility::trimExplode(',', $allowed));
}
$denied = $component->getVariable('deniedContentTypes');
if (NULL !== $denied) {
$blacklist = array_merge($blacklist, GeneralUtility::trimExplode(',', $denied));
}
return array($whitelist, $blacklist);
}

}
6 changes: 5 additions & 1 deletion Classes/ViewHelpers/Form/ContentViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ public function initializeArguments() {
public function render() {
/** @var FluidTYPO3\Flux\Form\Container\Content $content */
$content = $this->getForm()->createContainer('Content', $this->arguments['name'], $this->arguments['label']);
$this->getContainer()->add($content);
$container = $this->getContainer();
$container->add($content);
$this->setContainer($content);
$this->renderChildren();
$this->setContainer($container);
}

}
54 changes: 54 additions & 0 deletions Classes/ViewHelpers/Form/VariableViewHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
namespace FluidTYPO3\Flux\ViewHelpers\Form;
/***************************************************************
* Copyright notice
*
* (c) 2014 Claus Due <claus@namelesscoder.net>
*
* 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!
*****************************************************************/

use FluidTYPO3\Flux\ViewHelpers\AbstractFormViewHelper;

/**
* Sets an option in the Form instance
*
* @package Flux
* @subpackage ViewHelpers/Form
*/
class VariableViewHelper extends AbstractFormViewHelper {

/**
* Initialize arguments
* @return void
*/
public function initializeArguments() {
$this->registerArgument('name', 'string', 'Name of the option - valid values and their behaviours depend entirely on the consumer that will handle the Form instance', TRUE);
$this->registerArgument('value', 'mixed', 'Value of the option', TRUE);
}

/**
* Render method
* @return string
*/
public function render() {
$this->getContainer()->setVariable($this->arguments['name'], $this->arguments['value']);
}

}
3 changes: 2 additions & 1 deletion Classes/ViewHelpers/Grid/ColumnViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public function initializeArguments() {
$this->registerArgument('rowspan', 'integer', 'Row span');
$this->registerArgument('style', 'string', 'Inline style to add when rendering the column');
$this->registerArgument('variables', 'array', 'Freestyle variables which become assigned to the resulting Component - ' .
'can then be read from that Component outside this Fluid template and in other templates using the Form object from this template', FALSE, array());
'can then be read from that Component outside this Fluid template and in other templates using the Form object from this template. ' .
'Can also be set and/or overridden in tag content using <flux:form.variable />', FALSE, array());
}

/**
Expand Down
2 changes: 2 additions & 0 deletions ext_localconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@
\FluidTYPO3\Flux\Core::registerPipe('flashMessage');
\FluidTYPO3\Flux\Core::registerPipe('typeConverter');
}

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook']['flux'] = 'FluidTYPO3\Flux\Hooks\WizardItemsHookSubscriber';

0 comments on commit 3880983

Please sign in to comment.